Organize things
This commit is contained in:
+36
@@ -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
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@@ -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
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user