From 6fadfabec1c53ec3e2df0afc8c3509022a74ccca Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sat, 15 Sep 2018 23:56:30 +0200 Subject: [PATCH] Add convenience wrapper App (again) --- 15.pro | 3 +- main.cpp | 179 +++++---------------------------------------- src/App.h | 52 +++++++++++++ src/AsyncSocket.h | 2 + src/HttpContext.h | 4 +- src/HttpResponse.h | 7 ++ src/Loop.h | 4 - 7 files changed, 83 insertions(+), 168 deletions(-) create mode 100644 src/App.h diff --git a/15.pro b/15.pro index a941946..283f0e6 100644 --- a/15.pro +++ b/15.pro @@ -27,7 +27,8 @@ HEADERS += \ src/LoopData.h \ src/AsyncSocket.h \ src/AsyncSocketData.h \ - src/Loop.h + src/Loop.h \ + src/App.h INCLUDEPATH += uSockets/src src QMAKE_CXXFLAGS += -fsanitize=address diff --git a/main.cpp b/main.cpp index e4f322e..5b93709 100644 --- a/main.cpp +++ b/main.cpp @@ -1,22 +1,12 @@ +#include "App.h" + +#include +#include #include #include #include -std::string buffer; -int connections = 0; - -template -void respond(T *s) { - s->writeStatus("200 OK")->write([](int offset) { - return std::string_view(buffer.data() + offset, SIZE - offset); - }, SIZE); -} - -#define USE_SSL - -#include -#include - +// should probably fix this one up a bit some time std::string_view getFile(std::string_view file) { static std::map cache; @@ -45,155 +35,22 @@ std::string_view getFile(std::string_view file) { } } -#include "HttpContext.h" -#include "HttpResponse.h" - int main(int argc, char **argv) { - // test per-thread loopery - uWS::Loop::defaultLoop(); - new std::thread([]() { - uWS::Loop::defaultLoop(); - uWS::Loop::defaultLoop(); - }); - uWS::Loop::defaultLoop(); + // this part is a bit too C-ish? + us_ssl_socket_context_options sslOptions; + sslOptions.key_file_name = "/home/alexhultman/uWebSockets/misc/ssl/key.pem"; + sslOptions.cert_file_name = "/home/alexhultman/uWebSockets/misc/ssl/cert.pem"; + sslOptions.passphrase = "1234"; - us_ssl_socket_context_options ssl_options; - ssl_options.key_file_name = "/home/alexhultman/uWebSockets/misc/ssl/key.pem"; - ssl_options.cert_file_name = "/home/alexhultman/uWebSockets/misc/ssl/cert.pem"; - ssl_options.passphrase = "1234"; - - uWS::HttpContext *httpContext = uWS::HttpContext::create(uWS::Loop::defaultLoop(), &ssl_options); - - // req, res? - httpContext->onGet("/:folder/:file", [](auto *res, auto *req) { - // what file are we serving? - std::string_view fileName = req->getUrl(); - if (fileName == "/") { - // this is our index - fileName = "/rocket_files/rocket.html"; + uWS::SSLApp(sslOptions).get("/hello", [](auto *res, auto *req) { + res->writeStatus(uWS::HTTP_200_OK)->write("Hello world!"); + }).get("/:folder/:file", [](auto *res, auto *req) { + res->writeStatus(uWS::HTTP_200_OK)->write(getFile((req->getUrl() == "/" ? "/rocket_files/rocket.html" : req->getUrl()).substr(1))); + }).listen(3000, [](auto *token) { + if (token) { + std::cout << "Listening on port " << 3000 << std::endl; } + }).run(); - // load the file from cache and stream it as response - std::string_view file = getFile(fileName.substr(1)); - res->writeStatus(uWS::HTTP_200_OK)->write([file](int offset) { - return file.substr(offset); - }, file.length()); - }); - - httpContext->onGet("/yolo", [](auto *res, auto *req) { - std::cout << "URL (/yolo route): <" << req->getUrl() << ">" << std::endl; - std::cout << "Query: <" << req->getQuery() << ">" << std::endl; - std::cout << "User-Agent: <" << req->getHeader("user-agent") << ">" << std::endl; - }); - - httpContext->listen(nullptr, 3000, 0); - - uWS::run(); - - httpContext->free(); - uWS::Loop::defaultLoop()->free(); - - return 0; - - //////////////////////////////////////////////////////////////////////////////// - - - // 50 mb for huge - /* buffer.resize(52428800); - - //uWS::init(); - //uWS::Loop loop(); - -#ifdef USE_SSL - uWS::SSLApp app(uWS::SSLOptions() - .keyFileName("/home/alexhultman/uWebSockets/misc/ssl/key.pem") - .certFileName("/home/alexhultman/uWebSockets/misc/ssl/cert.pem") - .passphrase("1234")); -#else - uWS::App app; -#endif - - struct UserData { - - }; - - // 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: <" << req->getUrl() << ">" << std::endl; - std::cout << "Query: <" << req->getQuery() << ">" << std::endl; - std::cout << "User-Agent: <" << req->getHeader("user-agent") << ">" << std::endl; - - }).onWebSocket("/ws", [](auto *ws, auto *req, auto *args) { - - std::cout << "WebSocket connected to /wsApi" << std::endl; - - }).onMessage([](auto *ws, auto message) { - - std::cout << "WebSocket data: " << message << std::endl; - - //ws->send(message, opCode); - - }).onClose([]() { - - //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; - - s->writeStatus("200 OK"); - std::string_view file; - if (args->size() != 2) { - file = getFile("rocket.html"); - } else { - file = getFile((*args)[1]); - - std::string_view name = (*args)[1]; - - if (name.length() > 4 && name.substr(name.length() - 4) == ".svg") { - s->writeHeader("Content-type", "image/svg+xml"); - } - } - - s->write([file](int offset) { - return std::string_view(file.data() + offset, file.size() - offset); - }, file.size()); - - }; - - app.onGet("/", serve).onGet("/:folder/:file", serve).onGet("/tiny", [](auto *s, auto *req, auto *args) { - respond<512>(s); - }).onGet("/small", [](auto *s, auto *req, auto *args) { - respond<4096>(s); - }).onGet("/medium", [](auto *s, auto *req, auto *args) { - respond<16384>(s); - }).onGet("/large", [](auto *s, auto *req, auto *args) { - respond<51200>(s); - }).onGet("/huge", [](auto *s, auto *req, auto *args) { - respond<52428800>(s); - }).onPost("/upload", [](auto *s, auto *req, auto *args) { - - s->read([s](std::string_view chunk) { - std::cout << "Received chunk on URL /upload: <" << chunk << ">" << std::endl; - - s->writeStatus("200 OK")->end("Thanks for posting!"); - }); - - }).onWebSocket("/wsApi", []() { - - }).onHttpConnection([](auto *s) { - std::cout << "Connections: " << ++connections << std::endl; - }).onHttpDisconnection([](auto *s) { - std::cout << "Connections: " << --connections << std::endl; - }).listen(nullptr, 3000, 0);*/ - - // loop.run(); } diff --git a/src/App.h b/src/App.h new file mode 100644 index 0000000..7e3cd8b --- /dev/null +++ b/src/App.h @@ -0,0 +1,52 @@ +#ifndef APP_H +#define APP_H + +/* An app is a convenience wrapper of some of the most used fuctionalities and allows a + * builder-pattern kind of init. Apps operate on the implicit thread local Loop */ + +#include "HttpContext.h" +#include "HttpResponse.h" + +namespace uWS { +template +struct TemplatedApp { +private: + HttpContext *httpContext; + +public: + + ~TemplatedApp() { + + } + + TemplatedApp(const TemplatedApp &other) { + httpContext = other.httpContext; + } + + TemplatedApp(us_ssl_socket_context_options sslOptions) { + httpContext = uWS::HttpContext::create(uWS::Loop::defaultLoop(), &sslOptions); + } + + TemplatedApp &get(std::string pattern, std::function *, HttpRequest *)> handler) { + httpContext->onGet(pattern, handler); + return *this; + } + + TemplatedApp &listen(int port, std::function handler) { + handler(httpContext->listen(nullptr, port, 0)); + return *this; + } + + TemplatedApp &run() { + uWS::run(); + return *this; + } + +}; + +typedef TemplatedApp App; +typedef TemplatedApp SSLApp; + +} + +#endif // APP_H diff --git a/src/AsyncSocket.h b/src/AsyncSocket.h index 334b343..ecc0287 100644 --- a/src/AsyncSocket.h +++ b/src/AsyncSocket.h @@ -137,6 +137,8 @@ public: /* We should only return with new writes, not things written to cork already */ return write(src, length, optionally, 0); } + + return 0; } /* Drain any socket-buffer while also optionally sending a chunk */ diff --git a/src/HttpContext.h b/src/HttpContext.h index 1d8d2a3..3fed80b 100644 --- a/src/HttpContext.h +++ b/src/HttpContext.h @@ -199,8 +199,8 @@ public: } /* Listen to port using this HttpContext */ - void listen(const char *host, int port, int options) { - static_dispatch(us_ssl_socket_context_listen, us_socket_context_listen)(getSocketContext(), host, port, options, sizeof(HttpResponseData)); + us_listen_socket *listen(const char *host, int port, int options) { + return static_dispatch(us_ssl_socket_context_listen, us_socket_context_listen)(getSocketContext(), host, port, options, sizeof(HttpResponseData)); } }; diff --git a/src/HttpResponse.h b/src/HttpResponse.h index 8fd11c5..e8e0074 100644 --- a/src/HttpResponse.h +++ b/src/HttpResponse.h @@ -75,6 +75,13 @@ public: } } + /* Convenience function for static data (I don't like this one!) */ + void write(std::string_view data) { + write([data](int offset) { + return data.substr(offset); + }, data.length()); + } + /* Attach a read handler for data sent. Will be called with a chunk of size 0 when FIN */ void read(std::function handler) { HttpResponseData *data = getHttpResponseData(); diff --git a/src/Loop.h b/src/Loop.h index 3318397..70e0f42 100644 --- a/src/Loop.h +++ b/src/Loop.h @@ -44,10 +44,8 @@ public: if (!defaultLoop) { ownsDefaultLoop = true; defaultLoop = create(true); - std::cout << "Created default loop " << defaultLoop << " for thread " << std::this_thread::get_id() << std::endl; return defaultLoop; } else if (ownsDefaultLoop) { - std::cout << "Returned default loop " << defaultLoop << " for thread " << std::this_thread::get_id() << std::endl; return defaultLoop; } @@ -55,10 +53,8 @@ public: static thread_local Loop *threadLocalLoop; if (!threadLocalLoop) { threadLocalLoop = create(false); - std::cout << "Created non-default loop " << threadLocalLoop << " for thread " << std::this_thread::get_id() << std::endl; return threadLocalLoop; } - std::cout << "Returned non-default loop " << threadLocalLoop << " for thread " << std::this_thread::get_id() << std::endl; return threadLocalLoop; }