Start hooking up various modules

This commit is contained in:
Alex Hultman
2018-08-28 21:42:21 +02:00
parent e74d41537f
commit 6edb189d0d
5 changed files with 323 additions and 87 deletions
+152 -82
View File
@@ -6,6 +6,7 @@
#include "Loop.h"
#include "HttpSocket.h"
#include "HttpRouter.h"
#include "libwshandshake.hpp"
namespace uWS {
@@ -25,9 +26,11 @@ protected:
}
}
typedef typename std::conditional<SSL, us_ssl_socket, us_socket>::type SOCKET_TYPE;
typedef typename std::conditional<SSL, us_ssl_socket_context, us_socket_context>::type SOCKET_CONTEXT_TYPE;
typedef typename HttpSocket<SSL>::Data HTTP_SOCKET_DATA_TYPE;
// todo_ rename to HttpContextData
struct Data {
Data() {
@@ -51,96 +54,116 @@ protected:
std::function<void(HttpSocket<SSL> *, HttpRequest *)> onHttpRequest;
} *data;
struct WebSocketServerContextData {
WebSocketServerContextData() {
}
std::function<void(HttpSocket<SSL> *, std::string_view)> onMessage;
} *webSocketServerContextData;
// server protocols
SOCKET_CONTEXT_TYPE *httpServerContext;
SOCKET_CONTEXT_TYPE *webSocketServerContext;
// a us_socket_context can be constructed from another us_socket_context and will then act purely as a behavior (shared SSL_CTX)
// only socket contexts that come from construction from the start can be listened or connected to
// client protocols (todo?)
// us_create_derived_socket_context(us_socket_context)
void initWebSocketContexts(us_loop *loop) {
new (webSocketServerContextData = (WebSocketServerContextData *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(webSocketServerContext)) WebSocketServerContextData();
// client protocols
static_dispatch(us_ssl_socket_context_on_data, us_socket_context_on_data)(webSocketServerContext, [](auto *s, char *data, int length) {
WebSocketServerContextData *webSocketServerContextData = (WebSocketServerContextData *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s));
void init(us_loop *loop) {
new (data = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(httpServerContext)) Data();
// this is set via the setter?
webSocketServerContextData->onMessage((HttpSocket<SSL> *) s, std::string_view(data, length));
static_dispatch(us_ssl_socket_context_on_open, us_socket_context_on_open)(httpServerContext, [](auto *s, int is_client) {
Data *appData = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s));
return s;
});
}
void initHttpContexts(us_loop *loop) {
new (data = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(httpServerContext)) Data();
static_dispatch(us_ssl_socket_context_on_open, us_socket_context_on_open)(httpServerContext, [](auto *s, int is_client) {
Data *appData = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s));
static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S);
new (static_dispatch(us_ssl_socket_ext, us_socket_ext)(s)) HTTP_SOCKET_DATA_TYPE;
if (appData->onHttpConnection) {
appData->onHttpConnection((HttpSocket<SSL> *) s);
}
return s;
});
static_dispatch(us_ssl_socket_context_on_close, us_socket_context_on_close)(httpServerContext, [](auto *s) {
Data *appData = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s));
((HTTP_SOCKET_DATA_TYPE *) static_dispatch(us_ssl_socket_ext, us_socket_ext)(s))->~HTTP_SOCKET_DATA_TYPE();
if (appData->onHttpDisconnection) {
appData->onHttpDisconnection((HttpSocket<SSL> *) s);
}
return s;
});
static_dispatch(us_ssl_socket_context_on_data, us_socket_context_on_data)(httpServerContext, [](auto *s, char *data, int length) {
Data *appData = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s));
// warning: should NOT reset timer on any data, ONLY reset data on full HTTP requests!
// warning: if we are in shutdown state, resetting the timer is a security issue!
static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S);
// onHttpRequest should probably be hard-coded to HttpRouter
((HttpSocket<SSL> *) s)->onData(data, length, appData->onHttpRequest);
// compared to routing directly
//typename Data::UserData user = {(HttpSocket<SSL> *) s, nullptr};
//appData->r.route("GET", 3, "/", 1, &user);
return s;
});
static_dispatch(us_ssl_socket_context_on_writable, us_socket_context_on_writable)(httpServerContext, [](auto *s) {
// what if the client
// I think it's fair to never mind this one -> if we keep writing data after shutting down then that's an issue for us
static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S);
((HttpSocket<SSL> *) s)->onWritable();
return s;
});
static_dispatch(us_ssl_socket_context_on_end, us_socket_context_on_end)(httpServerContext, [](auto *s) {
std::cout << "Socket was half-closed!" << std::endl;
return s;
});
static_dispatch(us_ssl_socket_context_on_timeout, us_socket_context_on_timeout)(httpServerContext, [](auto *s) {
if (static_dispatch(us_ssl_socket_is_shut_down, us_socket_is_shut_down)(s)) {
std::cout << "Forcefully closing socket since shutdown was not answered in time" << std::endl;
static_dispatch(us_ssl_socket_close, us_socket_close)(s);
} else {
std::cout << "Shutting down socket now" << std::endl;
static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S);
static_dispatch(us_ssl_socket_shutdown, us_socket_shutdown)(s);
}
new (static_dispatch(us_ssl_socket_ext, us_socket_ext)(s)) HTTP_SOCKET_DATA_TYPE;
return s;
if (appData->onHttpConnection) {
appData->onHttpConnection((HttpSocket<SSL> *) s);
}
return s;
});
static_dispatch(us_ssl_socket_context_on_close, us_socket_context_on_close)(httpServerContext, [](auto *s) {
Data *appData = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s));
((HTTP_SOCKET_DATA_TYPE *) static_dispatch(us_ssl_socket_ext, us_socket_ext)(s))->~HTTP_SOCKET_DATA_TYPE();
if (appData->onHttpDisconnection) {
appData->onHttpDisconnection((HttpSocket<SSL> *) s);
}
return s;
});
static_dispatch(us_ssl_socket_context_on_data, us_socket_context_on_data)(httpServerContext, [](auto *s, char *data, int length) {
Data *appData = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(static_dispatch(us_ssl_socket_get_context, us_socket_get_context)(s));
// warning: should NOT reset timer on any data, ONLY reset data on full HTTP requests!
// warning: if we are in shutdown state, resetting the timer is a security issue!
static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S);
// onHttpRequest should probably be hard-coded to HttpRouter
((HttpSocket<SSL> *) s)->onData(data, length, appData->onHttpRequest);
// compared to routing directly
//typename Data::UserData user = {(HttpSocket<SSL> *) s, nullptr};
//appData->r.route("GET", 3, "/", 1, &user);
return s;
});
static_dispatch(us_ssl_socket_context_on_writable, us_socket_context_on_writable)(httpServerContext, [](auto *s) {
// what if the client
// I think it's fair to never mind this one -> if we keep writing data after shutting down then that's an issue for us
static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S);
((HttpSocket<SSL> *) s)->onWritable();
return s;
});
static_dispatch(us_ssl_socket_context_on_end, us_socket_context_on_end)(httpServerContext, [](auto *s) {
std::cout << "Socket was half-closed!" << std::endl;
return s;
});
static_dispatch(us_ssl_socket_context_on_timeout, us_socket_context_on_timeout)(httpServerContext, [](auto *s) {
if (static_dispatch(us_ssl_socket_is_shut_down, us_socket_is_shut_down)(s)) {
std::cout << "Forcefully closing socket since shutdown was not answered in time" << std::endl;
static_dispatch(us_ssl_socket_close, us_socket_close)(s);
} else {
std::cout << "Shutting down socket now" << std::endl;
static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S);
static_dispatch(us_ssl_socket_shutdown, us_socket_shutdown)(s);
}
return s;
});
}
});
}
public:
@@ -178,8 +201,49 @@ public:
return *this;
}
// for client and server
AppBase &onWebSocket(std::string pattern, std::function<void()> handler) {
// for client and server? fuck no!
template <class UserData>
AppBase &onWebSocket(std::string pattern, std::function<void(HttpSocket<SSL> *, HttpRequest *, std::vector<std::string_view> *)> handler) {
// todo: GET should probably be get since the parser only leaves lower case
data->r.add("GET", pattern.c_str(), [this, handler](typename Data::UserData *user, auto *args) {
std::string_view secWebSocketKey = user->httpRequest->getHeader("sec-websocket-key");
if (secWebSocketKey.length()) {
// note: OpenSSL can be used here to speed this up somewhat
char secWebSocketAccept[29] = {};
WebSocketHandshake::generate(secWebSocketKey.data(), secWebSocketAccept);
user->httpSocket->writeStatus("101 Switching Protocols")
->writeHeader("Upgrade", "websocket")
->writeHeader("Connection", "Upgrade")
->writeHeader("Sec-WebSocket-Accept", secWebSocketAccept)
->end("");
// todo: transform the socket into a websocket and hand it over
static_dispatch(us_ssl_socket_context_adopt_socket, us_socket_context_adopt_socket)(webSocketServerContext, (SOCKET_TYPE *) user->httpSocket, 0);
handler(user->httpSocket, user->httpRequest, args);
} else {
// note: this calls the http close handler inline
user->httpSocket->close();
}
});
return *this;
}
AppBase &onMessage(std::function<void(HttpSocket<SSL> *, std::string_view)> handler) {
// actually we dynamically create a new server websocket context when we call onWebSocket!
webSocketServerContextData->onMessage = handler;
return *this;
}
AppBase &onClose(std::function<void()>) {
return *this;
}
@@ -192,7 +256,10 @@ class App : public AppBase<false> {
public:
App() {
httpServerContext = us_create_socket_context(defaultLoop.loop, sizeof(Data));
init(defaultLoop.loop);
initHttpContexts(defaultLoop.loop);
webSocketServerContext = us_create_socket_context(defaultLoop.loop, sizeof(Data)); // this should not have Data! WebSocketData!
initWebSocketContexts(defaultLoop.loop);
}
};
@@ -225,7 +292,10 @@ class SSLApp : public AppBase<true> {
public:
SSLApp(SSLOptions &sslOptions) {
httpServerContext = us_create_ssl_socket_context(defaultLoop.loop, sizeof(Data), sslOptions.options);
init(defaultLoop.loop);
initHttpContexts(defaultLoop.loop);
webSocketServerContext = us_create_ssl_socket_context(defaultLoop.loop, sizeof(Data), sslOptions.options); // this should not have Data! WebSocketData!
initWebSocketContexts(defaultLoop.loop);
}
};