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
+2 -1
View File
@@ -18,7 +18,8 @@ HEADERS += \
src/Loop.h \
src/App.h \
src/HttpSocket.h \
src/HttpParser.h
src/HttpParser.h \
src/libwshandshake.hpp
INCLUDEPATH += uSockets/src src
#QMAKE_CXXFLAGS += -fsanitize=address
+34 -4
View File
@@ -62,9 +62,39 @@ int main(int argc, char **argv) {
uWS::App app;
#endif
// todo: add timeouts and socket shutdown!
struct UserData {
auto serve = [](auto *s, auto *req, auto *args) {
};
// serve a chat page with pub/sub and index.html and get (should be the main app of use)
// basically take the old web chat page and upgrade it
uWS::App a; // or uWS::SSLApp(options) for SSL
a/*.onGet("/", [](auto *s, auto *req, auto *args) {
std::cout << "URL: /" << std::endl;
std::cout << "user-agent: " << req->getHeader("user-agent") << std::endl;
std::cout << "upgrade: " << req->getHeader("upgrade") << std::endl;
})*/.onWebSocket<UserData>("/", [](auto *ws, auto *req, auto *args) {
std::cout << "WebSocket connected to /wsApi" << std::endl;
}).onMessage([](auto *ws, auto message/*, auto opCode*/) {
std::cout << "WebSocket data: " << message << std::endl;
//ws->send(message, opCode);
}).onClose([](/*auto *ws, int code, auto message*/) {
std::cout << "WebSocket disconnected from /wsApi" << std::endl;
}).listen("localhost", 3000, 0);
/*auto serve = [](auto *s, auto *req, auto *args) {
//std::cout << "URL: " << req->getUrl() << std::endl;
@@ -82,7 +112,7 @@ int main(int argc, char **argv) {
}
}
/*writeHeader("Content-type", "text/html; charset=utf-8")->*/s->write([file](int offset) {
s->write([file](int offset) {
return std::string_view(file.data() + offset, file.size() - offset);
}, file.size());
@@ -112,7 +142,7 @@ int main(int argc, char **argv) {
std::cout << "Connections: " << ++connections << std::endl;
}).onHttpDisconnection([](auto *s) {
std::cout << "Connections: " << --connections << std::endl;
}).listen(nullptr, 3000, 0);
}).listen(nullptr, 3000, 0);*/
uWS::run();
// loop.run();
+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);
}
};
+4
View File
@@ -174,6 +174,10 @@ struct HttpSocket {
httpData->inStream = stream;
}
void close() {
static_dispatch(us_ssl_socket_close, us_socket_close)((SOCKET_TYPE *) this);
}
HttpSocket() = delete;
};
+131
View File
@@ -0,0 +1,131 @@
// Copyright (c) 2016 Alex Hultman and contributors
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
#ifndef LIBWSHANDSHAKE_H
#define LIBWSHANDSHAKE_H
#include <cstdint>
#include <cstddef>
class WebSocketHandshake {
template <int N, typename T>
struct static_for {
void operator()(uint32_t *a, uint32_t *b) {
static_for<N - 1, T>()(a, b);
T::template f<N - 1>(a, b);
}
};
template <typename T>
struct static_for<0, T> {
void operator()(uint32_t *a, uint32_t *hash) {}
};
template <int state>
struct Sha1Loop {
static inline uint32_t rol(uint32_t value, size_t bits) {return (value << bits) | (value >> (32 - bits));}
static inline uint32_t blk(uint32_t b[16], size_t i) {
return rol(b[(i + 13) & 15] ^ b[(i + 8) & 15] ^ b[(i + 2) & 15] ^ b[i], 1);
}
template <int i>
static inline void f(uint32_t *a, uint32_t *b) {
switch (state) {
case 1:
a[i % 5] += ((a[(3 + i) % 5] & (a[(2 + i) % 5] ^ a[(1 + i) % 5])) ^ a[(1 + i) % 5]) + b[i] + 0x5a827999 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 2:
b[i] = blk(b, i);
a[(1 + i) % 5] += ((a[(4 + i) % 5] & (a[(3 + i) % 5] ^ a[(2 + i) % 5])) ^ a[(2 + i) % 5]) + b[i] + 0x5a827999 + rol(a[(5 + i) % 5], 5);
a[(4 + i) % 5] = rol(a[(4 + i) % 5], 30);
break;
case 3:
b[(i + 4) % 16] = blk(b, (i + 4) % 16);
a[i % 5] += (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) + b[(i + 4) % 16] + 0x6ed9eba1 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 4:
b[(i + 8) % 16] = blk(b, (i + 8) % 16);
a[i % 5] += (((a[(3 + i) % 5] | a[(2 + i) % 5]) & a[(1 + i) % 5]) | (a[(3 + i) % 5] & a[(2 + i) % 5])) + b[(i + 8) % 16] + 0x8f1bbcdc + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 5:
b[(i + 12) % 16] = blk(b, (i + 12) % 16);
a[i % 5] += (a[(3 + i) % 5] ^ a[(2 + i) % 5] ^ a[(1 + i) % 5]) + b[(i + 12) % 16] + 0xca62c1d6 + rol(a[(4 + i) % 5], 5);
a[(3 + i) % 5] = rol(a[(3 + i) % 5], 30);
break;
case 6:
b[i] += a[4 - i];
}
}
};
static inline void sha1(uint32_t hash[5], uint32_t b[16]) {
uint32_t a[5] = {hash[4], hash[3], hash[2], hash[1], hash[0]};
static_for<16, Sha1Loop<1>>()(a, b);
static_for<4, Sha1Loop<2>>()(a, b);
static_for<20, Sha1Loop<3>>()(a, b);
static_for<20, Sha1Loop<4>>()(a, b);
static_for<20, Sha1Loop<5>>()(a, b);
static_for<5, Sha1Loop<6>>()(a, hash);
}
static inline void base64(unsigned char *src, char *dst) {
const char *b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
for (int i = 0; i < 18; i += 3) {
*dst++ = b64[(src[i] >> 2) & 63];
*dst++ = b64[((src[i] & 3) << 4) | ((src[i + 1] & 240) >> 4)];
*dst++ = b64[((src[i + 1] & 15) << 2) | ((src[i + 2] & 192) >> 6)];
*dst++ = b64[src[i + 2] & 63];
}
*dst++ = b64[(src[18] >> 2) & 63];
*dst++ = b64[((src[18] & 3) << 4) | ((src[19] & 240) >> 4)];
*dst++ = b64[((src[19] & 15) << 2)];
*dst++ = '=';
}
public:
static inline void generate(const char input[24], char output[28]) {
uint32_t b_output[5] = {
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0
};
uint32_t b_input[16] = {
0, 0, 0, 0, 0, 0, 0x32353845, 0x41464135, 0x2d453931, 0x342d3437, 0x44412d39,
0x3543412d, 0x43354142, 0x30444338, 0x35423131, 0x80000000
};
for (int i = 0; i < 6; i++) {
b_input[i] = (input[4 * i + 3] & 0xff) | (input[4 * i + 2] & 0xff) << 8 | (input[4 * i + 1] & 0xff) << 16 | (input[4 * i + 0] & 0xff) << 24;
}
sha1(b_output, b_input);
uint32_t last_b[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 480};
sha1(b_output, last_b);
for (int i = 0; i < 5; i++) {
uint32_t tmp = b_output[i];
char *bytes = (char *) &b_output[i];
bytes[3] = tmp & 0xff;
bytes[2] = (tmp >> 8) & 0xff;
bytes[1] = (tmp >> 16) & 0xff;
bytes[0] = (tmp >> 24) & 0xff;
}
base64((unsigned char *) b_output, output);
}
};
#endif // LIBWSHANDSHAKE_H