Seamless SSL/non-SSL swapping, new Context-centric interface

This commit is contained in:
Alex Hultman
2018-06-20 22:26:40 +02:00
parent d3901aceae
commit 73aa9cae6b
5 changed files with 217 additions and 123 deletions
+12 -7
View File
@@ -1,23 +1,28 @@
// this is roughly the interfaces I plan for (with changes and additional helpers added with time)
// much speaks for a header-only or header-mostly implementation now that uSockets is properly isolating its internal headers
#include "uWS.h"
#include "Context.h"
int main() {
std::cout << "HttpSocket size: " << sizeof(HttpSocket::Data) << std::endl;
std::cout << "HttpSocket size: " << sizeof(HttpSocket<true>::Data) << std::endl;
char *buffer = new char[512];
// either use this, or use onHttpRoute but not both
uWS::defaultHub.onHttpRequest([buffer](HttpSocket *s, HttpRequest *req) {
// SSL options are given via uSockets structure
us_ssl_socket_context_options options = {};
options.key_file_name = "/home/alexhultman/uWebSockets/misc/ssl/key.pem";
options.cert_file_name = "/home/alexhultman/uWebSockets/misc/ssl/cert.pem";
options.passphrase = "1234";
uWS::SSLContext(options).onHttpRequest([buffer](auto *s, HttpRequest *req) {
if (req->getUrl() == "/") {
s->writeStatus(200)->writeHeader("Server", "µWebSockets v0.15")->end(buffer, 512);
s->writeStatus(200)->writeHeader("Hello", "World")->end(buffer, 512);
} else {
std::cout << "Got HTTP request at URL: " << req->getUrl() << std::endl;
}
}).listen("localhost", 3000, 0).run();
}).listen("localhost", 3000, 0);
// todo: important swapping to SSL should be .secureListen(same interfaces) and work out of the box!
uWS::defaultHub.run();
}
+175
View File
@@ -1,6 +1,181 @@
#ifndef CONTEXT_H
#define CONTEXT_H
#include "Hub.h"
#include "libusockets.h"
#include <type_traits>
// represents a mix of two us_socket contexts either in ssl or not (one for http, one for websocket)
// if you want to share the same openssl context between the two us socket ssl contexts? how?
#include "Http.h"
//#define SWAP_F [](auto a, auto b){ if constexpr(SSL) return a; else return b; }
namespace uWS {
// holds 4 protocols: server http, client http, server websocket, client websocket (for either SSL or not)
template <bool SSL>
class ContextBase {
protected:
template <class A, class B>
static typename std::conditional<SSL, A, B>::type *static_dispatch(A *a, B *b) {
if constexpr(SSL) {
return a;
} else {
return b;
}
}
typedef typename std::conditional<SSL, us_ssl_socket_context, us_socket_context>::type SOCKET_CONTEXT_TYPE;
struct Data {
Data() {
}
std::function<void(HttpSocket<SSL> *)> onHttpConnection;
std::function<void(HttpSocket<SSL> *)> onHttpDisconnection;
std::function<void(HttpSocket<SSL> *, HttpRequest *)> onHttpRequest;
} *data;
// server protocols
SOCKET_CONTEXT_TYPE *httpServerContext;
SOCKET_CONTEXT_TYPE *webSocketServerContext;
// client protocols
public:
// the shared constructor
void init(us_loop *loop) {
new (data = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(httpServerContext)) Data();
// register shims
static_dispatch(us_ssl_socket_context_on_open, us_socket_context_on_open)(httpServerContext, [](auto *s) {
Data *data = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s));
// here we need to construct a HTTP socket on the ext!
// we always give pointers to us_socket?
// same mistake as before?
if (!data->onHttpConnection) {
return;
}
// note: this is VERY tricky to keep bug-free!
// we could give the ext here, and skip all bugs?
data->onHttpConnection((HttpSocket<SSL> *) s);
});
static_dispatch(us_ssl_socket_context_on_close, us_socket_context_on_close)(httpServerContext, [](auto *s) {
Data *data = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s));
// we always give pointers to us_socket?
// same mistake as before?
if (!data->onHttpDisconnection) {
return;
}
// note: this is VERY tricky to keep bug-free!
// we could give the ext here, and skip all bugs?
data->onHttpDisconnection((HttpSocket<SSL> *) s);
});
static_dispatch(us_ssl_socket_context_on_data, us_socket_context_on_data)(httpServerContext, [](auto *s, char *data, int length) {
Data *contextData = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s));
// a HttpSocket is basically the Http state and everything needed for it, we use it to parse the data and it knows about its context
//
HttpRequest req(data, length);
if (req.isComplete()) {
contextData->onHttpRequest((HttpSocket<SSL> *) s, &req);
} else {
std::cout << "Got chunked HTTP headers!" << std::endl;
}
// here we run the HTTP parser on this data
// we always give pointers to us_socket?
// same mistake as before?
// note: this is VERY tricky to keep bug-free!
// we could give the ext here, and skip all bugs?
//data->((HttpSocket *) s);
});
}
// for server
void listen(const char *host, int port, int options) {
static_dispatch(us_ssl_socket_context_listen, us_socket_context_listen)(httpServerContext, host, port, options, sizeof(HttpSocket<SSL>));
}
// for server
ContextBase &onHttpRequest(decltype(Data::onHttpRequest) handler) {
data->onHttpRequest = handler;
return *this;
}
// for client and server
ContextBase &onWebSocketConnection() {
return *this;
}
~ContextBase() {
}
};
class Context : public ContextBase<false> {
public:
Context() {
// we create the context ourselves here
httpServerContext = us_create_socket_context(defaultHub.loop, sizeof(Data));
// init from base
init(defaultHub.loop);
}
};
class SSLContext : public ContextBase<true> {
public:
// denna finns i barnet SSLContext!
SSLContext(us_ssl_socket_context_options options) {
// we create the context ourselves here
httpServerContext = us_create_ssl_socket_context(defaultHub.loop, sizeof(Data), options);
// init from base
init(defaultHub.loop);
}
};
}
#endif // CONTEXT_H
+10 -2
View File
@@ -71,6 +71,7 @@ struct HttpRequest {
// HttpSocket is an alias for us_socket
template <bool SSL>
struct HttpSocket {
// chunked response will be tricky with this buffering scheme
@@ -115,14 +116,21 @@ struct HttpSocket {
return this;
}
// this depends on SSL!
void end(char *data, int length) {
us_socket *s = (us_socket *) this;
//us_ssl_socket *s = (us_ssl_socket *) this;
char *corkBuffer = getCorkBuffer();
memcpy(corkBuffer + corkOffset, data, length);
corkOffset += length;
us_socket_write(s, corkBuffer, corkOffset, 0);
if constexpr(SSL) {
us_ssl_socket_write((us_ssl_socket *) this, corkBuffer, corkOffset);
} else {
us_socket_write((us_socket *) this, corkBuffer, corkOffset, 0);
}
corkOffset = 0;
}
+19 -113
View File
@@ -16,130 +16,38 @@ int corkOffset = 0;
struct Hub;
// maybe a Context is both TCP and SSL in one?
template <bool isServer, bool isSSL>
struct Context {
// Hub::listen has to create the socket context (with SSL options) via first time init
// what if you want to listen to another port with another SSL cert?
// every listen has to correspond to a listen socket and a socket context, created for each call to listen
// same with connect?
Hub &hub;
// just remove Context altogether and just let Hub have all of this stuff
// it really only needs to hold the listen socket and the socket context wrapped as ListenToken instead
// ListenToken.close()
struct Data {
Data() {
// ListenContext?
}
// instead of Context, have some kind of token for Listening and allow it to be stopped but not cloesed (you stop listening but keep sockets alive)
// what if you then want to listen again? you don't want to create a new context that time?
std::function<void(HttpSocket *)> onHttpConnection;
std::function<void(HttpSocket *)> onHttpDisconnection;
std::function<void(HttpSocket *, HttpRequest *)> onHttpRequest;
} *data;
// imagine listening, then stopping, then continuing - maybe support PAUSING a listen socket without closing it? that way you listen once, pause listening, etc
// this way we can solve the accept issue with listen-pause and listen timers?
us_socket_context *httpContext;
Context(Hub &hub) : hub(hub) {
}
// one for each? ssl and non-ssl?
void init(us_loop *loop) {
httpContext = us_create_socket_context(loop, sizeof(Data));
new (data = (Data *) us_socket_context_ext(httpContext)) Data();
// register shims
us_socket_context_on_open(httpContext, [](us_socket *s) {
Data *data = (Data *) us_socket_context_ext(us_socket_get_context(s));
// ListenToken.pause(), .resume(), .close()
// here we need to construct a HTTP socket on the ext!
// eller så bara ändrar man gränssnittet så att man explicit skapar ett context?
// createContext(ssl options eller inga options avgör om SSL eller inte)
// eller bara sslContext(options).onHttplalala.listen().Hub().run();
// we always give pointers to us_socket?
// same mistake as before?
// hubben är underförstådd och du agerar egentligen alltid på ett context som antingen är ssl eller inte
if (!data->onHttpConnection) {
return;
}
// note: this is VERY tricky to keep bug-free!
// we could give the ext here, and skip all bugs?
data->onHttpConnection((HttpSocket *) s);
});
us_socket_context_on_close(httpContext, [](us_socket *s) {
Data *data = (Data *) us_socket_context_ext(us_socket_get_context(s));
// we always give pointers to us_socket?
// same mistake as before?
if (!data->onHttpDisconnection) {
return;
}
// note: this is VERY tricky to keep bug-free!
// we could give the ext here, and skip all bugs?
data->onHttpDisconnection((HttpSocket *) s);
});
us_socket_context_on_data(httpContext, [](us_socket *s, char *data, int length) {
Data *contextData = (Data *) us_socket_context_ext(us_socket_get_context(s));
// a HttpSocket is basically the Http state and everything needed for it, we use it to parse the data and it knows about its context
//
HttpRequest req(data, length);
if (req.isComplete()) {
contextData->onHttpRequest((HttpSocket *) s, &req);
} else {
std::cout << "Got chunked HTTP headers!" << std::endl;
}
// here we run the HTTP parser on this data
// we always give pointers to us_socket?
// same mistake as before?
// note: this is VERY tricky to keep bug-free!
// we could give the ext here, and skip all bugs?
//data->((HttpSocket *) s);
});
}
void onHttpConnection(decltype(Data::onHttpConnection) handler) {
data->onHttpConnection = handler;
}
void onHttpDisconnection(decltype(Data::onHttpDisconnection) handler) {
data->onHttpDisconnection = handler;
}
Hub &onHttpRequest(decltype(Data::onHttpRequest) handler) {
data->onHttpRequest = handler;
return hub;
}
// this should only be enabled if we are server context!
// should return Loop, not hub? or simply make it so that ALL contexts stem from some Hub? Hub is essentually the Loop abstraction? could work
Hub &listen(const char *host, int port, int options) {
us_socket_context_listen(httpContext, host, port, options, sizeof(HttpSocket));
return hub;
}
};
// are we SSL or regular hub?
struct Hub : Context<true, false> {
struct Hub {
us_loop *loop;
using Context::onHttpConnection;
using Context<true, false>::listen;
struct Data {
Data() {
@@ -160,10 +68,8 @@ struct Hub : Context<true, false> {
}
Hub() : loop(us_create_loop(wakeupCb, preCb, postCb, sizeof(Data))), Context<true, false>(*this) {
Hub() : loop(us_create_loop(wakeupCb, preCb, postCb, sizeof(Data))) {
new (data = (Data *) us_loop_ext(loop)) Data();
Context<true, false>::init(loop);
}
void run() {