From 5130b6f8c26da3094ddd4e64be45b6796e0bff7b Mon Sep 17 00:00:00 2001 From: Alex Hultman Date: Sun, 30 Sep 2018 03:38:18 +0200 Subject: [PATCH] Update Http example server --- examples/HttpServer.cpp | 69 +++++++++++-------------- examples/helpers/AsyncFileStreamer.h | 75 ++++++++++++++++++++++++++++ misc/main.cpp | 68 +------------------------ 3 files changed, 104 insertions(+), 108 deletions(-) create mode 100644 examples/helpers/AsyncFileStreamer.h diff --git a/examples/HttpServer.cpp b/examples/HttpServer.cpp index d17e38d..4cc2534 100644 --- a/examples/HttpServer.cpp +++ b/examples/HttpServer.cpp @@ -4,6 +4,7 @@ /* Helpers for this example */ #include "helpers/AsyncFileReader.h" +#include "helpers/AsyncFileStreamer.h" #include "helpers/Middleware.h" /* optparse */ @@ -19,68 +20,54 @@ int main(int argc, char **argv) { struct optparse_long longopts[] = { {"port", 'p', OPTPARSE_REQUIRED}, {"help", 'h', OPTPARSE_NONE}, - {"color", 'c', OPTPARSE_REQUIRED}, - {"delay", 'd', OPTPARSE_OPTIONAL}, + {"passphrase", 'a', OPTPARSE_REQUIRED}, + {"key", 'k', OPTPARSE_REQUIRED}, + {"cert", 'c', OPTPARSE_REQUIRED}, + {"dh_params", 'd', OPTPARSE_REQUIRED}, {0} }; int port = 3000; + struct us_ssl_socket_context_options ssl_options = {}; while ((option = optparse_long(&options, longopts, nullptr)) != -1) { switch (option) { case 'p': - port = /*options.optarg ? */atoi(options.optarg);// : port; + port = atoi(options.optarg); break; - case 'h': - - //break; - /*case 'b': - brief = true; + case 'a': + ssl_options.passphrase = options.optarg; break; case 'c': - color = options.optarg; + ssl_options.cert_file_name = options.optarg; + break; + case 'k': + ssl_options.key_file_name = options.optarg; break; case 'd': - delay = options.optarg ? atoi(options.optarg) : 1; - break;*/ + ssl_options.dh_params_file_name = options.optarg; + break; + case 'h': case '?': - std::cout << "Usage: " << argv[0] << " [--help] [--port ] [--ssl ] [--passphrase ] [--dh_params ] " << std::endl; - + fail: + std::cout << "Usage: " << argv[0] << " [--help] [--port ] [--key ] [--cert ] [--passphrase ] [--dh_params ] " << std::endl; return 0; - //fprintf(stderr, "%s: %s\n", argv[0], options.errmsg); - //exit(EXIT_FAILURE); } } - /* Print remaining arguments. */ - char *arg; - while ((arg = optparse_arg(&options))) - printf("%s\n", arg); - - std::cout << "Port is " << port << std::endl; - - return 0; - - if (argc < 3 || argc > 7) { - std::cout << "Usage: HttpServer root port [ssl_cert ssl_key ssl_dh_params ssl_passphrase]" << std::endl; - return 0; + char *root = optparse_arg(&options); + if (!root) { + goto fail; } - //int port = atoi(argv[2]); - const char *root = argv[1]; - /* Cache files of root folder */ - //FileCache fileCache(root); + AsyncFileStreamer asyncFileStreamer(root); /* Either serve over HTTP or HTTPS */ - if (argc > 5) { + struct us_ssl_socket_context_options empty_ssl_options = {}; + if (memcmp(&ssl_options, &empty_ssl_options, sizeof(empty_ssl_options))) { /* HTTPS */ - uWS::SSLApp({ - .cert_file_name = argv[3], /* Required */ - .key_file_name = argv[4], /* Required */ - .dh_params_file_name = argv[5], /* Required (in this example) */ - .passphrase = (argc == 7 ? argv[6] : nullptr) /* Passphrase is optional */ - }).get("/*", [](auto *res, auto *req) { - //serveFile(res, req)->write(fileCache.getFile(req->getUrl())); + uWS::SSLApp(ssl_options).get("/*", [&asyncFileStreamer](auto *res, auto *req) { + //asyncFileStreamer.streamFile(res, req->getUrl()); }).listen(port, [port, root](auto *token) { if (token) { std::cout << "Serving " << root << " over HTTPS a " << port << std::endl; @@ -88,8 +75,8 @@ int main(int argc, char **argv) { }).run(); } else { /* HTTP */ - uWS::App().get("/*", [](auto *res, auto *req) { - //serveFile(res, req)->write(fileCache.getFile(req->getUrl())); + uWS::App().get("/*", [&asyncFileStreamer](auto *res, auto *req) { + asyncFileStreamer.streamFile(res, req->getUrl()); }).listen(port, [port, root](auto *token) { if (token) { std::cout << "Serving " << root << " over HTTP a " << port << std::endl; diff --git a/examples/helpers/AsyncFileStreamer.h b/examples/helpers/AsyncFileStreamer.h new file mode 100644 index 0000000..f209144 --- /dev/null +++ b/examples/helpers/AsyncFileStreamer.h @@ -0,0 +1,75 @@ +#include + +struct AsyncFileStreamer { + + std::map asyncFileReaders; + std::string root; + + AsyncFileStreamer(std::string root) : root(root) { + // for all files in this path, init the map of AsyncFileReaders + updateRootCache(); + } + + void updateRootCache() { + // todo: if the root folder changes, we want to reload the cache + for(auto &p : std::experimental::filesystem::recursive_directory_iterator(root)) { + std::string url = p.path().string().substr(root.length()); + if (url == "/index.html") { + url = "/"; + } + + char *key = new char[url.length()]; + memcpy(key, url.data(), url.length()); + asyncFileReaders[std::string_view(key, url.length())] = new AsyncFileReader(p.path().string()); + } + } + + void streamFile(uWS::HttpResponse *res, std::string_view url) { + auto it = asyncFileReaders.find(url); + if (it == asyncFileReaders.end()) { + std::cout << "Did not find file: " << url << std::endl; + } else { + streamFile(res, it->second); + } + } + + static void streamFile(uWS::HttpResponse *res, AsyncFileReader *asyncFileReader) { + /* Peek from cache */ + std::string_view chunk = asyncFileReader->peek(res->getWriteOffset()); + if (!chunk.length() || res->tryEnd(chunk, asyncFileReader->getFileSize())) { + /* Request new chunk */ + // todo: we need to abort this callback if peer closed! + // this also means Loop::defer needs to support aborting + + // us_socket_up_ref eftersom vi delar ägandeskapet + + asyncFileReader->request(res->getWriteOffset(), [res, asyncFileReader](std::string_view chunk) { + // check if we were closed in the mean time + //if (us_socket_is_closed()) { + // free it here + //return; + //} + + /* We were aborted for some reason */ + if (!chunk.length()) { + // todo: make sure to check for is_closed internally after all callbacks! + res->close(); + } else { + streamFile(res, asyncFileReader); + } + }); + } else { + /* We failed writing everything, so let's continue when we can */ + res->onWritable([res, asyncFileReader](int offset) { + + // här kan skiten avbrytas! + + streamFile(res, asyncFileReader); + // todo: I don't really know what this is supposed to mean? + return false; + })->onAborted([]() { + std::cout << "ABORTED!" << std::endl; + }); + } + } +}; diff --git a/misc/main.cpp b/misc/main.cpp index 58c225b..3659726 100644 --- a/misc/main.cpp +++ b/misc/main.cpp @@ -1,73 +1,7 @@ #include "App.h" #include "../examples/helpers/AsyncFileReader.h" - -#include - -struct AsyncFileStreamer { - - std::map asyncFileReaders; - std::string root; - - AsyncFileStreamer(std::string root) : root(root) { - // for all files in this path, init the map of AsyncFileReaders - updateRootCache(); - } - - void updateRootCache() { - // todo: if the root folder changes, we want to reload the cache - for(auto &p : std::experimental::filesystem::recursive_directory_iterator(root)) { - std::string url = p.path().string().substr(root.length()); - if (url == "/index.html") { - url = "/"; - } - - char *key = new char[url.length()]; - memcpy(key, url.data(), url.length()); - asyncFileReaders[std::string_view(key, url.length())] = new AsyncFileReader(p.path().string()); - } - } - - void streamFile(uWS::HttpResponse *res, std::string_view url) { - auto it = asyncFileReaders.find(url); - if (it == asyncFileReaders.end()) { - std::cout << "Did not find file: " << url << std::endl; - } else { - streamFile(res, it->second); - } - } - - static void streamFile(uWS::HttpResponse *res, AsyncFileReader *asyncFileReader) { - /* Peek from cache */ - std::string_view chunk = asyncFileReader->peek(res->getWriteOffset()); - if (!chunk.length() || res->tryEnd(chunk, asyncFileReader->getFileSize())) { - /* Request new chunk */ - // todo: we need to abort this callback if peer closed! - // this also means Loop::defer needs to support aborting - asyncFileReader->request(res->getWriteOffset(), [res, asyncFileReader](std::string_view chunk) { - /* We were aborted for some reason */ - if (!chunk.length()) { - // todo: make sure to check for is_closed internally after all callbacks! - res->close(); - } else { - streamFile(res, asyncFileReader); - } - }); - } else { - /* We failed writing everything, so let's continue when we can */ - res->onWritable([res, asyncFileReader](int offset) { - - // här kan skiten avbrytas! - - streamFile(res, asyncFileReader); - // todo: I don't really know what this is supposed to mean? - return false; - })->onAborted([]() { - std::cout << "ABORTED!" << std::endl; - }); - } - } -}; +#include "../examples/helpers/AsyncFileStreamer.h" int main(int argc, char **argv) {