Organize things

This commit is contained in:
Alex Hultman
2018-09-28 16:18:57 +02:00
parent 18316d7852
commit 3f11add86e
8 changed files with 2 additions and 2 deletions
+36
View File
@@ -0,0 +1,36 @@
TEMPLATE = app
CONFIG += console c++1z
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
HEADERS += \
src/HttpRouter.h \
src/HttpParser.h \
src/websocket/libwshandshake.hpp \
src/websocket/WebSocketProtocol.h \
src/websocket/WebSocket.h \
src/websocket/WebSocketApp.h \
src/HttpContext.h \
src/HttpContextData.h \
src/HttpResponseData.h \
src/HttpResponse.h \
src/StaticDispatch.h \
src/LoopData.h \
src/AsyncSocket.h \
src/AsyncSocketData.h \
src/Loop.h \
src/App.h \
src/Utilities.h
INCLUDEPATH += uSockets/src src
#QMAKE_CXXFLAGS += -fsanitize=address
LIBS += -pthread -lssl -lcrypto
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

+23
View File
@@ -0,0 +1,23 @@
## Motivation and goals
µWebSockets is a simple to use yet thoroughly optimized implementation of HTTP and WebSockets.
It comes with built-in pub/sub support, HTTP routing, TLS 1.3, IPv6, permessage-deflate and is thorougly battle tested as one of the most popular implementations.
Unlike other "pub/sub brokers", µWS does not assume or push any particular protocol but only operates over standard WebSockets.
The implementation is header-only C++17, cross-platform and compiles down to a tiny binary of a handful kilobytes.
It depends on µSockets, which is a standard C project for Linux, macOS & Windows.
Performance wise you can expect to outperform just about anything out there, that's the goal of the project.
I can show cases where µWS with SSL significantly outperforms Golang servers running non-SSL. You get the SSL for free in a sense.
Another goal of the project is minimalism, simplicity and elegance.
Design wise it follows an ExpressJS-like interface where you attach callbacks to different URL routes.
This way you can easily build complete REST/WebSocket services in a few lines of code.
The project is async only and runs local to one thread. You scale it as individual threads much like Node.js scales as individual processes. That is, the implementation only sees a single thread and is not thread-safe. There are simple ways to do threading via async delegates though, if you really need to.
## Node.js
µWS is available as "uws" for Node.js where it can serve as a major boost for web related tasks. You can find "uws" on this page.
## User manual
todo
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+92
View File
@@ -0,0 +1,92 @@
#include "App.h"
#include "examples/helpers/AsyncFileReader.h"
AsyncFileReader asyncFileReader("/home/alexhultman/sintel_small.mp4");
inline std::string slurp(const std::string &path) {
std::ostringstream buf;
std::ifstream input (path.c_str());
buf << input.rdbuf();
return buf.str();
}
std::string sintelMovie = slurp("/home/alexhultman/sintel_small.mp4");
int main(int argc, char **argv) {
std::cout << "Sintel movie is " << sintelMovie.length() << " bytes" << std::endl;
// läs in hela sintel-filmen här, testa strömmarna med den sen!
uWS::/*SSL*/App(/*{
.key_file_name = "/home/alexhultman/uWebSockets/misc/ssl/key.pem",
.cert_file_name = "/home/alexhultman/uWebSockets/misc/ssl/cert.pem",
.dh_params_file_name = "/home/alexhultman/dhparams.pem",
.passphrase = "1234"
}*/).get("/*", [](auto *res, auto *req) {
//res->writeStatus(uWS::HTTP_200_OK);
//res->writeHeader("Content-Type", "text/html;charset=utf-8");
std::cout << "Endar nu" << std::endl;
// buffer it up and end by draining it
res->end(sintelMovie);
// stream it here
/*if (!res->tryEnd(sintelMovie)) {
res->onWritable([res](int offset) {
std::cout << "Streaming data at offset " << offset << std::endl;
res->tryEnd(std::string_view(sintelMovie).substr(offset));
});
}*/
// tryWrite / tryEnd
// vad om man skriver tryEnd("<h1>Hallå!</h1>Din user-agent är: ", totalLength)
//res->write("<h1>Hallå!</h1>Din user-agent är: ");
//res->end(req->getHeader("user-agent"));
}).get("/async/sintel.mkv", [](auto *res, auto *req) {
// I guess it should return wherer or not it wants to be called again?
auto streamIt = [res](int offset) {
while(true) {
/* Peek from cache */
std::string_view chunk = asyncFileReader.peek(offset);
if (chunk.length()) {
/* We had parts of this file cached already */
res->tryEnd(chunk, asyncFileReader.getFileSize());
} else {
/* We had nothing readily available right now, request async chunk and pause the stream until we have */
asyncFileReader.request(offset, [res](std::string_view chunk) {
/* We were aborted */
if (!chunk.length()) {
std::cout << "Async File Read request was aborted!" << std::endl;
// close the socket here?
// we need a way to NOT resume a paused socket! essentially close!
} else {
/* We finally got the data, resume stream with this chunk */
res->tryEnd(chunk, asyncFileReader.getFileSize());
}
});
}
return true;
}
};
// basically do this
if (!streamIt(0)) {
// what should it return? will onWritable be called again depending on what it returns?
res->onWritable(streamIt);
}
}).listen(3000, [](auto *token) {
if (token) {
std::cout << "Listening on port " << 3000 << std::endl;
}
}).run();
}
+94
View File
@@ -0,0 +1,94 @@
#include "HttpParser.h"
#include <chrono>
#include <iostream>
// todo: random test of chunked http parsing of randomly generated requests
void testHttpParser() {
char headers[] = "GET /hello.htm HTTP/1.1\r\n"
"User-Agent: Mozilla/4.0 (compatible; MSIE5.01; Windows NT)\r\n"
"Host: www.tutorialspoint.com\r\n"
"Accept-Language: en-us\r\n"
"Accept-Encoding: gzip, deflate\r\n"
"Connection: Keep-Alive\r\n"
"Content-length: 1048576\r\n\r\n";
const int requestLength = sizeof(headers) - 1 + 1048576;
char *request = (char *) malloc(requestLength + 32);
memset(request, 0, requestLength);
memcpy(request, headers, sizeof(headers) - 1);
char *data = (char *) malloc(requestLength * 10);
int length = requestLength * 10;
int maxChunkSize = 10000;
char *paddedBuffer = (char *) malloc(maxChunkSize + 32);
// dela upp dessa 10 i 5 segment
HttpParser httpParser;
int validRequests = 0, numDataEmits = 0, numChunks = 0;
size_t dataBytes = 0;
for (int i = 0; i < 10; i++) {
memcpy(data + requestLength * i, request, requestLength);
}
for (int j = 0; j < 1000; j++) {
for (int currentOffset = 0; currentOffset != length; ) {
int chunkSize = rand() % 10000;
if (currentOffset + chunkSize > length) {
chunkSize = length - currentOffset;
}
memcpy(paddedBuffer, data + currentOffset, chunkSize);
httpParser.consumePostPadded(paddedBuffer, chunkSize, nullptr, [&validRequests](void *user, HttpRequest *req) {
validRequests++;
if (req->getUrl() != "/hello.htm") {
std::cout << "WRONG URL!" << std::endl;
exit(-1);
}
}, [&dataBytes, &numDataEmits](void *, std::string_view data) {
numDataEmits++;
dataBytes += data.length();
}, [](void *) {
std::cout << "Error!" << std::endl;
return;
});
numChunks++;
currentOffset += chunkSize;
}
}
std::cout << "validRequests: " << validRequests << std::endl;
std::cout << "Data bytes: " << dataBytes << std::endl;
std::cout << "Data emits: " << numDataEmits << std::endl;
std::cout << "Chunks parsed: " << numChunks << std::endl;
validRequests = 0;
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 10000000; i++) {
httpParser.consumePostPadded(request, requestLength, nullptr, [&validRequests](void *user, HttpRequest *req) {
validRequests++;
}, [](void *, std::string_view data) {
}, [](void *) {
});
}
auto stop = std::chrono::high_resolution_clock::now();
std::cout << "Parsed " << validRequests << " in " << std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count() << "ms" << std::endl;
}
int main() {
testHttpParser();
}