Begin implementing AsyncSocket & host Rocket homepage again
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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];
|
||||||
|
|||||||
Reference in New Issue
Block a user