Small refactor and interface update
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
|
||||
Reference in New Issue
Block a user