Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge websocket submodule #139

Merged
merged 12 commits into from
Feb 6, 2024
4 changes: 1 addition & 3 deletions core/http/transport_v1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -235,10 +235,8 @@ void V1TransportContext::respond(const Response& response) {
responseHeaders.set("server", "maddsua/lambda");

// set connection header to acknowledge keep-alive mode
if (this->m_keepalive) {
if (this->m_keepalive && !responseHeaders.has("connection")) {
responseHeaders.set("connection", "keep-alive");
} else {
responseHeaders.set("connection", "close");
}

std::string headerBuff = "HTTP/1.1 " + std::to_string(response.status.code()) + ' ' + response.status.text() + "\r\n";
Expand Down
77 changes: 77 additions & 0 deletions core/server/connection.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#include "./server.hpp"
#include "./internal.hpp"
#include "../http/http.hpp"
#include "../polyfill/polyfill.hpp"
#include "../crypto/crypto.hpp"
#include "../encoding/encoding.hpp"

using namespace Lambda;
using namespace Lambda::Server;
using namespace Lambda::Websocket;

static const std::string wsMagicString = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";

IncomingConnection::IncomingConnection(
Network::TCP::Connection& connInit,
const ServeOptions& optsInit
) : conn(connInit), opts(optsInit), ctx(conn, opts.transport) {}

std::optional<HTTP::Request> IncomingConnection::nextRequest() {

if (this->activeProto != ActiveProtocol::HTTP) {
throw std::runtime_error("Cannot read next http request: connection protocol was changed");
}

auto nextOpt = this->ctx.nextRequest();
if (!nextOpt.has_value()) return std::nullopt;
return nextOpt.value();
}

void IncomingConnection::respond(const HTTP::Response& response) {

if (this->activeProto != ActiveProtocol::HTTP) {
throw std::runtime_error("Cannot send http response to a connection that had it's protocol changed");
}

this->ctx.respond(response);
}

WebsocketContext IncomingConnection::upgrateToWebsocket(const HTTP::Request& initialRequest) {

auto headerUpgrade = Strings::toLowerCase(initialRequest.headers.get("Upgrade"));
auto headerWsKey = initialRequest.headers.get("Sec-WebSocket-Key");

if (headerUpgrade != "websocket" || !headerWsKey.size()) {
throw std::runtime_error("Websocket initialization aborted: Invalid connection header");
}

if (this->ctx.hasPartialData()) {
throw std::runtime_error("Websocket initialization aborted: Connection has unprocessed data");
}

auto combinedKey = headerWsKey + wsMagicString;

auto keyHash = Crypto::SHA1().update(combinedKey).digest();

auto handshakeReponse = HTTP::Response(101, {
{ "Upgrade", "websocket" },
{ "Connection", "Upgrade" },
{ "Sec-WebSocket-Accept", Encoding::toBase64(keyHash) }
});

this->respond(handshakeReponse);
this->ctx.reset();

this->activeProto = ActiveProtocol::WS;
return WebsocketContext(this->conn, this->opts.transport);
}

WebsocketContext IncomingConnection::upgrateToWebsocket() {

auto request = this->nextRequest();
if (!request.has_value()) {
throw std::runtime_error("Cannot establish websocket connection without handshake");
}

return this->upgrateToWebsocket(request.value());
}
32 changes: 0 additions & 32 deletions core/server/http/connection.cpp

This file was deleted.

44 changes: 0 additions & 44 deletions core/server/http/upgrade.cpp

This file was deleted.

36 changes: 0 additions & 36 deletions core/server/internal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,6 @@ namespace Lambda::Server {
void connectionHandler(Network::TCP::Connection& conn, const ServeOptions& config, const ConnectionCallback& handlerCallback);
};

namespace WSTransport {

enum struct OpCode : uint8_t {
Text = 0x01,
Binary = 0x02,
Close = 0x08,
Ping = 0x09,
Pong = 0x0A,
Continue = 0x00,
};

enum struct FrameControlBits : uint8_t {
BitFinal = 0x80,
BitContinue = 0x00
};

struct FrameHeader {
FrameControlBits finbit;
OpCode opcode;
size_t size;
size_t payloadSize;
static const size_t mask_size = 4;
static const size_t min_size = 2;
std::optional<std::array<uint8_t, mask_size>> mask;
};

struct MultipartMessageContext {
std::array<uint8_t, FrameHeader::mask_size> mask;
bool binary = false;
};

FrameHeader parseFrameHeader(const std::vector<uint8_t>& buffer);
std::vector <uint8_t> serializeMessage(const Websocket::Message& message);
std::vector <uint8_t> serializeFrameHeader(const FrameHeader& header);
};

namespace Pages {
HTTP::Response renderErrorPage(HTTP::Status code, const std::string& message, ErrorResponseType type);
HTTP::Response renderErrorPage(HTTP::Status code, const std::string& message);
Expand Down
27 changes: 2 additions & 25 deletions core/server/server.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#include <queue>
#include <future>


#include "../network/network.hpp"
#include "../network/tcp/listener.hpp"
#include "../http/transport.hpp"
Expand Down Expand Up @@ -56,28 +55,6 @@ namespace Lambda {
HTTP::Request request;
};

class WebsocketContext {
private:
Network::TCP::Connection& conn;
const HTTP::Transport::TransportOptions& topts;
std::future<void> m_reader;
std::queue<Websocket::Message> m_queue;
std::mutex m_read_lock;
bool m_stopped = false;
void asyncWorker();

public:

WebsocketContext(Network::TCP::Connection& connRef, const HTTP::Transport::TransportOptions& toptsRef);
~WebsocketContext();

bool awaitMessage();
bool hasMessage() const noexcept;
Websocket::Message nextMessage();
void sendMessage(const Websocket::Message& msg);
void close(Websocket::CloseReason reason);
};

struct IncomingConnection {
private:

Expand All @@ -99,8 +76,8 @@ namespace Lambda {
std::optional<HTTP::Request> nextRequest();
void respond(const HTTP::Response& response);

WebsocketContext upgrateToWebsocket();
WebsocketContext upgrateToWebsocket(const HTTP::Request& initialRequest);
Websocket::WebsocketContext upgrateToWebsocket();
Websocket::WebsocketContext upgrateToWebsocket(const HTTP::Request& initialRequest);
};

typedef std::function<HTTP::Response(const HTTP::Request&, const RequestContext&)> ServerlessCallback;
Expand Down
15 changes: 3 additions & 12 deletions core/server/server.mk
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

LIB_CORE_SERVER = core/server.a
LIB_CORE_SERVER_DEPS = core/server/instance.o core/server/http/connection.o core/server/http/upgrade.o core/server/handlers/serverless.o core/server/handlers/connection.o core/server/websocket/context.o core/server/websocket/transport.o core/server/pages/errorpage.o
LIB_CORE_SERVER_DEPS = core/server/instance.o core/server/connection.o core/server/handlers/serverless.o core/server/handlers/connection.o core/server/pages/errorpage.o

# server stuff
$(LIB_CORE_SERVER): $(LIB_CORE_SERVER_DEPS)
Expand All @@ -9,23 +9,14 @@ $(LIB_CORE_SERVER): $(LIB_CORE_SERVER_DEPS)
core/server/instance.o: core/server/instance.cpp
g++ -c $(CFLAGS) core/server/instance.cpp -o core/server/instance.o

core/server/http/connection.o: core/server/http/connection.cpp
g++ -c $(CFLAGS) core/server/http/connection.cpp -o core/server/http/connection.o

core/server/http/upgrade.o: core/server/http/upgrade.cpp
g++ -c $(CFLAGS) core/server/http/upgrade.cpp -o core/server/http/upgrade.o
core/server/connection.o: core/server/connection.cpp
g++ -c $(CFLAGS) core/server/connection.cpp -o core/server/connection.o

core/server/handlers/serverless.o: core/server/handlers/serverless.cpp
g++ -c $(CFLAGS) core/server/handlers/serverless.cpp -o core/server/handlers/serverless.o

core/server/handlers/connection.o: core/server/handlers/connection.cpp
g++ -c $(CFLAGS) core/server/handlers/connection.cpp -o core/server/handlers/connection.o

core/server/websocket/context.o: core/server/websocket/context.cpp
g++ -c $(CFLAGS) core/server/websocket/context.cpp -o core/server/websocket/context.o

core/server/websocket/transport.o: core/server/websocket/transport.cpp
g++ -c $(CFLAGS) core/server/websocket/transport.cpp -o core/server/websocket/transport.o

core/server/pages/errorpage.o: core/server/pages/errorpage.cpp
g++ -c $(CFLAGS) core/server/pages/errorpage.cpp -o core/server/pages/errorpage.o
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
#include "../server.hpp"
#include "../internal.hpp"
#include "../../utils/utils.hpp"
#include "./websocket.hpp"
#include "./transport.hpp"
#include "../utils/utils.hpp"

#include <cstring>

using namespace Lambda;
using namespace Lambda::Websocket;
using namespace Lambda::Server::WSTransport;
using namespace Lambda::Websocket::Transport;

// The recv function blocks execution infinitely until it receives somethig,
// which is not optimal for this usecase.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
#include "../server.hpp"
#include "../internal.hpp"
#include "./websocket.hpp"
#include "./transport.hpp"

using namespace Lambda;
using namespace Lambda::Websocket;
using namespace Lambda::Server;
using namespace Lambda::Server::WSTransport;
using namespace Lambda::Websocket::Transport;

static const std::string wsPingString = "ping/lambda/ws";

Expand All @@ -26,7 +25,7 @@ void WebsocketContext::sendMessage(const Websocket::Message& msg) {
this->conn.write(writeBuff);
}

FrameHeader WSTransport::parseFrameHeader(const std::vector<uint8_t>& buffer) {
FrameHeader Transport::parseFrameHeader(const std::vector<uint8_t>& buffer) {

FrameHeader header {
static_cast<FrameControlBits>(buffer.at(0) & 0xF0),
Expand Down Expand Up @@ -66,7 +65,7 @@ FrameHeader WSTransport::parseFrameHeader(const std::vector<uint8_t>& buffer) {
return header;
}

std::vector <uint8_t> WSTransport::serializeFrameHeader(const FrameHeader& header) {
std::vector <uint8_t> Transport::serializeFrameHeader(const FrameHeader& header) {

std::vector<uint8_t> resultBuffer;

Expand All @@ -91,7 +90,7 @@ std::vector <uint8_t> WSTransport::serializeFrameHeader(const FrameHeader& heade
return resultBuffer;
}

std::vector<uint8_t> WSTransport::serializeMessage(const Message& message) {
std::vector<uint8_t> Transport::serializeMessage(const Message& message) {

// create frame buffer
FrameHeader header {
Expand Down
48 changes: 48 additions & 0 deletions core/websocket/transport.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#ifndef __LIB_MADDSUA_LAMBDA_CORE_WEBSOCKET_TRANSPORT__
#define __LIB_MADDSUA_LAMBDA_CORE_WEBSOCKET_TRANSPORT__

#include "./websocket.hpp"

#include <cstdint>
#include <string>
#include <vector>
#include <optional>
#include <array>

namespace Lambda::Websocket::Transport {

enum struct OpCode : uint8_t {
Text = 0x01,
Binary = 0x02,
Close = 0x08,
Ping = 0x09,
Pong = 0x0A,
Continue = 0x00,
};

enum struct FrameControlBits : uint8_t {
BitFinal = 0x80,
BitContinue = 0x00
};

struct FrameHeader {
FrameControlBits finbit;
OpCode opcode;
size_t size;
size_t payloadSize;
static const size_t mask_size = 4;
static const size_t min_size = 2;
std::optional<std::array<uint8_t, mask_size>> mask;
};

struct MultipartMessageContext {
std::array<uint8_t, FrameHeader::mask_size> mask;
bool binary = false;
};

FrameHeader parseFrameHeader(const std::vector<uint8_t>& buffer);
std::vector <uint8_t> serializeMessage(const Websocket::Message& message);
std::vector <uint8_t> serializeFrameHeader(const FrameHeader& header);
};

#endif
Loading