Small refactor and interface update

This commit is contained in:
Alex Hultman
2018-06-24 00:16:54 +02:00
parent d731bfcc47
commit a2fe871e63
7 changed files with 264 additions and 329 deletions
+12 -24
View File
@@ -4,32 +4,20 @@ CONFIG -= app_bundle
CONFIG -= qt
SOURCES += \
main.cpp \
uSockets/src/eventing/epoll.c \
uSockets/src/context.c \
uSockets/src/socket.c \
uSockets/src/eventing/libuv.c \
uSockets/src/ssl.c \
uSockets/src/loop.c
main.cpp \
uSockets/src/eventing/epoll.c \
uSockets/src/context.c \
uSockets/src/socket.c \
uSockets/src/eventing/libuv.c \
uSockets/src/ssl.c \
uSockets/src/loop.c
HEADERS += \
src/Hub.h \
src/Http.h \
src/Context.h \
src/uWS.h \
src/HttpRouter.h
#uSockets/libusockets.h \
#uSockets/internal/eventing/epoll.h \
#uSockets/internal/networking/bsd.h \
#uSockets/internal/eventing/libuv.h \
#uSockets/internal/common.h \
#uSockets/interfaces/timer.h \
#uSockets/interfaces/socket.h \
#uSockets/interfaces/poll.h \
#uSockets/interfaces/context.h \
#uSockets/interfaces/loop.h \
#uSockets/interfaces/ssl.h \
#uSockets/internal/loop.h
src/Http.h \
src/uWS.h \
src/HttpRouter.h \
src/Loop.h \
src/App.h
INCLUDEPATH += uSockets/src src
#QMAKE_CXXFLAGS += -fsanitize=address
+11 -12
View File
@@ -1,7 +1,7 @@
// 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 "Context.h"
#include "App.h"
#include "HttpRouter.h"
int main() {
@@ -9,24 +9,23 @@ int main() {
std::cout << "HttpSocket size: " << sizeof(HttpSocket<true>::Data) << std::endl;
char *buffer = new char[512];
// 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::SSLApp c(uWS::SSLOptions()
.keyFileName("/home/alexhultman/uWebSockets/misc/ssl/key.pem")
.certFileName("/home/alexhultman/uWebSockets/misc/ssl/cert.pem")
.passphrase("1234"));
//uWS::App c;
//uWS::SSLContext c(options);
uWS::Context c;
c.route("GET", "/", [buffer](auto *s, HttpRequest *req, auto *args) {
c.onGet("/", [buffer](auto *s, HttpRequest *req, auto *args) {
s->writeStatus(200)->writeHeader("Hello", "World")->end(buffer, 512);
}).route("GET", "/wrong", [buffer](auto *s, HttpRequest *req, auto *args) {
}).onGet("/wrong", [buffer](auto *s, HttpRequest *req, auto *args) {
std::cout << "Wrong way!" << std::endl;
}).onWebSocket([]() {
}).listen("localhost", 3000, 0);
uWS::defaultHub.run();
uWS::run();
}
+181
View File
@@ -0,0 +1,181 @@
#ifndef CONTEXT_H
#define CONTEXT_H
#include "libusockets.h"
#include <type_traits>
#include "Loop.h"
#include "Http.h"
#include "HttpRouter.h"
namespace uWS {
template <bool SSL>
class AppBase {
protected:
template <class A, class B>
static constexpr 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() {
// default http handler is a router
onHttpRequest = [this](auto *s, HttpRequest *req) {
UserData user = {s, req};
r.route("GET", 3, req->getUrl().data(), req->getUrl().length(), &user);
};
}
struct UserData {
HttpSocket<SSL> *httpSocket;
HttpRequest *httpRequest;
};
HttpRouter<UserData *> r;
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;
// 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
// us_create_derived_socket_context(us_socket_context)
// client protocols
void init(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) {
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!
if (!data->onHttpConnection) {
return;
}
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));
if (!data->onHttpDisconnection) {
return;
}
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;
}
});
}
public:
// 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>));
}
AppBase &onPost(std::string pattern, std::function<void(HttpSocket<SSL> *, HttpRequest *, std::vector<std::string_view> *)> handler) {
data->r.add("POST", pattern.c_str(), [handler](typename Data::UserData *user, auto *args) {
handler(user->httpSocket, user->httpRequest, args);
});
return *this;
}
AppBase &onGet(std::string pattern, std::function<void(HttpSocket<SSL> *, HttpRequest *, std::vector<std::string_view> *)> handler) {
data->r.add("GET", pattern.c_str(), [handler](typename Data::UserData *user, auto *args) {
handler(user->httpSocket, user->httpRequest, args);
});
return *this;
}
// for client and server
AppBase &onWebSocket(std::function<void()> handler) {
return *this;
}
~AppBase() {
}
};
class App : public AppBase<false> {
public:
App() {
httpServerContext = us_create_socket_context(defaultLoop.loop, sizeof(Data));
init(defaultLoop.loop);
}
};
class SSLOptions {
public:
us_ssl_socket_context_options options = {};
SSLOptions() {
}
SSLOptions &keyFileName(std::string fileName) {
options.key_file_name = fileName.c_str();
return *this;
}
SSLOptions &certFileName(std::string fileName) {
options.cert_file_name = fileName.c_str();
return *this;
}
SSLOptions &passphrase(std::string passphrase) {
options.passphrase = passphrase.c_str();
return *this;
}
};
class SSLApp : public AppBase<true> {
public:
SSLApp(SSLOptions &sslOptions) {
httpServerContext = us_create_ssl_socket_context(defaultLoop.loop, sizeof(Data), sslOptions.options);
init(defaultLoop.loop);
}
};
}
#endif // CONTEXT_H
-204
View File
@@ -1,204 +0,0 @@
#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"
#include "HttpRouter.h"
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
// we have a router too
struct UserData {
// pass whatever you need as user data
HttpSocket<SSL> *httpSocket;
HttpRequest *httpRequest;
};
HttpRouter<UserData *> r;
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;
}
ContextBase &route(std::string method, std::string pattern, std::function<void(HttpSocket<SSL> *, HttpRequest *, std::vector<std::string_view> *)> handler) {
// calling this function overrides any other onHttpRequest!
onHttpRequest([this](auto *s, HttpRequest *req) {
UserData user = {s, req};
r.route("GET", 3, req->getUrl().data(), req->getUrl().length(), &user);
});
r.add(method.c_str(), pattern.c_str(), [handler](UserData *user, auto *args) {
handler(user->httpSocket, user->httpRequest, args);
});
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
-88
View File
@@ -1,88 +0,0 @@
// should lie in hub and be connected with pre/post callbacks
char *corkBuffer = new char[1024];
int corkOffset = 0;
#ifndef HUB_H
#define HUB_H
#include "libusockets.h"
#include <functional>
#include <new>
#include <string_view>
#include <iostream>
#include "Http.h"
#include "Context.h"
struct Hub;
// 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?
// 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()
// 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?
// 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?
// ListenToken.pause(), .resume(), .close()
// 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();
// hubben är underförstådd och du agerar egentligen alltid på ett context som antingen är ssl eller inte
struct Hub {
us_loop *loop;
struct Data {
Data() {
}
} *data;
static void wakeupCb(us_loop *loop) {
}
static void preCb(us_loop *loop) {
}
static void postCb(us_loop *loop) {
}
Hub() : loop(us_create_loop(wakeupCb, preCb, postCb, sizeof(Data))) {
new (data = (Data *) us_loop_ext(loop)) Data();
}
void run() {
us_loop_run(loop);
}
~Hub() {
}
};
namespace uWS {
thread_local Hub defaultHub;
}
#endif // HUB_H
+59
View File
@@ -0,0 +1,59 @@
// should lie in hub and be connected with pre/post callbacks
char *corkBuffer = new char[1024];
int corkOffset = 0;
#ifndef HUB_H
#define HUB_H
#include "libusockets.h"
#include <functional>
#include <new>
#include <string_view>
#include <iostream>
namespace uWS {
struct Loop {
us_loop *loop;
struct Data {
Data() {
}
} *data;
static void wakeupCb(us_loop *loop) {
}
static void preCb(us_loop *loop) {
}
static void postCb(us_loop *loop) {
}
Loop() : loop(us_create_loop(wakeupCb, preCb, postCb, sizeof(Data))) {
new (data = (Data *) us_loop_ext(loop)) Data();
}
void run() {
us_loop_run(loop);
}
~Loop() {
}
};
// dessa måste ligga i en sourcefil
thread_local Loop defaultLoop;
static void run() {
defaultLoop.run();
}
}
#endif // HUB_H
+1 -1
View File
@@ -1,6 +1,6 @@
#ifndef UWS_H
#define UWS_H
#include "Hub.h"
#include "Loop.h"
#endif // UWS_H