Begin implementing AsyncSocket & host Rocket homepage again

This commit is contained in:
Alex Hultman
2018-09-12 01:56:25 +02:00
parent ee2756c880
commit 80e64b59fb
5 changed files with 114 additions and 86 deletions
+13 -15
View File
@@ -25,7 +25,7 @@ std::string_view getFile(std::string_view file) {
if (it == cache.end()) { if (it == cache.end()) {
std::cout << "Did not have file: " << file << std::endl; std::cout << "Did not have file: " << file << std::endl;
std::ifstream fin("rocket_files/" + std::string(file), std::ios::binary); std::ifstream fin(std::string(file), std::ios::binary);
std::ostringstream oss; std::ostringstream oss;
oss << fin.rdbuf(); oss << fin.rdbuf();
@@ -59,21 +59,19 @@ int main(int argc, char **argv) {
uWS::HttpContext<false> *httpContext = uWS::HttpContext<false>::create(loop.loop); uWS::HttpContext<false> *httpContext = uWS::HttpContext<false>::create(loop.loop);
// req, res? // req, res?
httpContext->onGet("/", [](auto *res, auto *req) { // maybe use the terminology of HttpRequest for both? httpContext->onGet("/:folder/:file", [](auto *res, auto *req) {
// what file are we serving?
/*std::cout << "URL: <" << req->getUrl() << ">" << std::endl; std::string_view fileName = req->getUrl();
std::cout << "Query: <" << req->getQuery() << ">" << std::endl; if (fileName == "/") {
std::cout << "User-Agent: <" << req->getHeader("user-agent") << ">" << std::endl; // this is our index
*/ fileName = "/rocket_files/rocket.html";
// read some data being passed }
/*res->read([](std::string_view chunk) {
std::cout << "Reading some streamed in data:" << chunk << std::endl;
});*/
res->writeStatus(uWS::HTTP_200_OK)->write([](int offset) {
return std::string_view("Hello world!").substr(offset);
}, 12);
// 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) { httpContext->onGet("/yolo", [](auto *res, auto *req) {
+81 -62
View File
@@ -41,52 +41,110 @@ protected:
} }
public: public:
void *getExt() { void *getExt() {
return static_dispatch(us_ssl_socket_ext, us_socket_ext)((SOCKET_TYPE *) this); return static_dispatch(us_ssl_socket_ext, us_socket_ext)((SOCKET_TYPE *) this);
} }
// cork should bascially mark corked /* Cork this socket. Only one socket may ever be corked per-loop at any given time */
// if already corked, crash and burn!
void cork() { void cork() {
LoopData *loopData = getLoopData(); LoopData *loopData = getLoopData();
loopData->corked = true; loopData->corked = true;
} }
// if not in corked mode, write & buffer. if in corked mode: either write & buffer or buffer /* Write in three levels of prioritization: cork-buffer, syscall, socket-buffer */
// that is, corking is optional int write(const char *src, int length, bool optionally = false, int nextLength = 0) {
void write(const char *src, int length) {
LoopData *loopData = getLoopData(); LoopData *loopData = getLoopData();
// append it if (length == 0) {
memcpy(loopData->corkBuffer + loopData->corkOffset, src, length); return 0;
loopData->corkOffset += length; }
if (loopData->corked) {
// prio 1: write to cork
if (LoopData::CORK_BUFFER_SIZE - loopData->corkOffset >= length) {
memcpy(loopData->corkBuffer + loopData->corkOffset, src, length);
loopData->corkOffset += length;
} else {
/* Strategy differences between SSL and non-SSL */
if constexpr(SSL) {
/* For SSL we do not want to emit small chunks, so fill the cork (assuming the cork is about 16k) */
int remainingCork = LoopData::CORK_BUFFER_SIZE - loopData->corkOffset;
memcpy(loopData->corkBuffer + loopData->corkOffset, src, remainingCork);
loopData->corkOffset += remainingCork;
uncork(src + remainingCork, length - remainingCork);
} else {
/* For non-SSL we take the penalty of two syscalls */
uncork(src, length);
}
}
} else {
// not corked
// we should not write here if if have buffer!
int written = 0;
AsyncSocketData<SSL> *asyncSocketData = (AsyncSocketData<SSL> *) getExt();
if (!asyncSocketData->buffer.length()) {
written = static_dispatch(us_ssl_socket_write, us_socket_write)((SOCKET_TYPE *) this, src, length, nextLength != 0);
}
if (written < length) {
// bail out if we can
if (optionally) {
return written;
}
// shit, we are fucked
std::cout << "Buffering up per-socket" << std::endl;
AsyncSocketData<SSL> *asyncSocketData = (AsyncSocketData<SSL> *) getExt();
// at least reserve enough for next failure
if (nextLength) {
asyncSocketData->buffer.reserve(asyncSocketData->buffer.length() + length + nextLength);
}
asyncSocketData->buffer.append(src, length);
}
}
return length;
} }
// should follow same rules as for write /* Write an unsigned 32-bit integer */
void writeUnsigned(unsigned int value) { void writeUnsigned(unsigned int value) {
LoopData *loopData = getLoopData(); LoopData *loopData = getLoopData();
loopData->corkOffset += u32toa(value, loopData->corkBuffer + loopData->corkOffset); char buf[10];
int length = u32toa(value, buf);
// for now we do this copy
write(buf, length);
} }
// uncork should always send and clean the loop's cork buffer. anything that is not able to send, buffer it up in the socket /* Uncork this socket. It is essential to remember doing this. */
void uncork() { void uncork(const char *src = nullptr, int length = 0) {
LoopData *loopData = getLoopData(); LoopData *loopData = getLoopData();
loopData->corked = false; if (loopData->corked) {
loopData->corked = false;
// send it off now! if (loopData->corkOffset) {
write(loopData->corkBuffer, loopData->corkOffset, false, length);
int written = static_dispatch(us_ssl_socket_write, us_socket_write)((SOCKET_TYPE *) this, loopData->corkBuffer, loopData->corkOffset, false); write(src, length, false, 0);
loopData->corkOffset = 0;
loopData->corkOffset = 0; }
}
// buffer the rest up in the outbuffer of this asynsocket!
} }
// when we are writable AND have buffer length, that's a really bad situation that should not happen! /* Drain any socket-buffer while also optionally sending a chunk */
void drain(std::string_view optionalChunk) { void mergeDrain(std::string_view optionalChunk) {
std::cout << "mergeDrain" << std::endl;
// the question here is: should we recursively copy things to the cork buffer and try and send them off in one go? // the question here is: should we recursively copy things to the cork buffer and try and send them off in one go?
// it would work in a recursively manner and since optionalChunk is optional, it would never increase the buffer size // it would work in a recursively manner and since optionalChunk is optional, it would never increase the buffer size
@@ -96,45 +154,6 @@ public:
// the cunk is optional meaning we do not care to buffer it up // the cunk is optional meaning we do not care to buffer it up
} }
// this one will write up to length and simply leave things be
int writeOptionally(const char *src, int length) {
// not optional for now
AsyncSocket<SSL>::write(src, length);
return length;
/*
// kopiera upp till (SSL eller icke-ssl) max copy distance
// om mer än detta, fortsätt skicka
// this entire strategy should be made entirely in AsyncSocket!
// this strategy can be simplified to one, we can even have MAX_COPY_DISTANCE_SSL and MAX_COPY_DISTANCE
if (length < LoopData::MAX_COPY_DISTANCE) {
AsyncSocket<SSL>::write("Content-Length: ", 16);
AsyncSocket<SSL>::writeUnsigned(chunk.length());
AsyncSocket<SSL>::write("\r\n\r\n", 4);
AsyncSocket<SSL>::write(chunk.data(), chunk.length());
} else {
// copying some data with the headers is a good idea for SSL but probably not for non-SSL
//writeToCorkBufferAndReset(chunk.data(), LoopData::MAX_COPY_DISTANCE, length, true);
// just assume this went fine
HttpResponseData<SSL> *httpData = (HttpResponseData<SSL> *) static_dispatch(us_ssl_socket_ext, us_socket_ext)((SOCKET_TYPE *) this);
// write that off! (should never happen here!)
//static_dispatch(us_ssl_socket_write, us_socket_write)((SOCKET_TYPE *) this, chunk.data() + LoopData::MAX_COPY_DISTANCE, chunk.length() - LoopData::MAX_COPY_DISTANCE, 0);
// if offset is at the end, we are done
if (httpData->offset < length) {
httpData->outStream = cb;
}
}*/
}
void close() { void close() {
static_dispatch(us_ssl_socket_close, us_socket_close)((SOCKET_TYPE *) this); static_dispatch(us_ssl_socket_close, us_socket_close)((SOCKET_TYPE *) this);
} }
+17 -7
View File
@@ -75,6 +75,9 @@ private:
// cork this socket (move this to loop?) // cork this socket (move this to loop?)
((AsyncSocket<SSL> *) s)->cork(); ((AsyncSocket<SSL> *) s)->cork();
// pass this pointer to pointer along with the routing and change it if upgraded
SOCKET_TYPE *returnedSocket = s;
HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) static_dispatch(us_ssl_socket_ext, us_socket_ext)(s); HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) static_dispatch(us_ssl_socket_ext, us_socket_ext)(s);
httpResponseData->consumePostPadded(data, length, s, [httpContextData](void *s, uWS::HttpRequest *httpRequest) { httpResponseData->consumePostPadded(data, length, s, [httpContextData](void *s, uWS::HttpRequest *httpRequest) {
@@ -92,12 +95,11 @@ private:
httpResponseData->inStream(data); httpResponseData->inStream(data);
} }
}, [](void *user) { }, [](void *user) {
std::cout << "INVALID HTTP!" << std::endl; // close any socket on HTTP errors
//static_dispatch(us_ssl_socket_close, us_socket_close)((SOCKET_TYPE *) user);
// close it down
}); });
// uncork // uncork only if not closed
((AsyncSocket<SSL> *) s)->uncork(); ((AsyncSocket<SSL> *) s)->uncork();
// how do we return a new socket here, from the http route? // how do we return a new socket here, from the http route?
@@ -115,10 +117,18 @@ private:
// get next chunk to send // get next chunk to send
HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getExt(); HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getExt();
std::string_view chunk = httpResponseData->outStream(httpResponseData->offset);
// send, including any buffered up if (httpResponseData->outStream) {
asyncSocket->drain(chunk); std::string_view chunk = httpResponseData->outStream(httpResponseData->offset);
// send, including any buffered up
asyncSocket->mergeDrain(chunk);
} else {
std::cout << "We did not have any outStream!" << std::endl;
asyncSocket->mergeDrain(std::string_view(nullptr, 0));
}
return s; return s;
}); });
+2 -2
View File
@@ -19,7 +19,6 @@ private:
} }
public: public:
/* Write the HTTP status */ /* Write the HTTP status */
HttpResponse *writeStatus(std::string_view status) { HttpResponse *writeStatus(std::string_view status) {
AsyncSocket<SSL>::write("HTTP/1.1 ", 9); AsyncSocket<SSL>::write("HTTP/1.1 ", 9);
@@ -43,7 +42,8 @@ public:
AsyncSocket<SSL>::write("Content-Length: ", 16); AsyncSocket<SSL>::write("Content-Length: ", 16);
AsyncSocket<SSL>::writeUnsigned(chunk.length()); AsyncSocket<SSL>::writeUnsigned(chunk.length());
AsyncSocket<SSL>::write("\r\n\r\n", 4); AsyncSocket<SSL>::write("\r\n\r\n", 4);
if (AsyncSocket<SSL>::writeOptionally(chunk.data(), chunk.length()) < length) { if (AsyncSocket<SSL>::write(chunk.data(), chunk.length(), true) < length) {
std::cout << "HttpResponse::write failed to write everything" << std::endl;
getHttpResponseData()->outStream = cb; getHttpResponseData()->outStream = cb;
} }
} }
+1
View File
@@ -7,6 +7,7 @@ private:
public: public:
/* Good 16k for SSL perf. */
static const int CORK_BUFFER_SIZE = 16 * 1024; static const int CORK_BUFFER_SIZE = 16 * 1024;
char *corkBuffer = new char[CORK_BUFFER_SIZE]; char *corkBuffer = new char[CORK_BUFFER_SIZE];