Various comments and fixes

This commit is contained in:
Alex Hultman
2018-09-23 18:00:18 +02:00
parent ede5fb304a
commit 7f3753e9db
4 changed files with 111 additions and 57 deletions
+8 -4
View File
@@ -47,8 +47,6 @@ int main(int argc, char **argv) {
for (auto *x : delayedResponses) { for (auto *x : delayedResponses) {
std::cout << "Resuming a response now!" << std::endl; std::cout << "Resuming a response now!" << std::endl;
// resume should take a string_view!
x->resume(); x->resume();
} }
@@ -76,10 +74,16 @@ int main(int argc, char **argv) {
/* This route streams back chunks of data in delayed fashion */ /* This route streams back chunks of data in delayed fashion */
res->writeStatus(uWS::HTTP_200_OK)->write([res](int offset) { res->writeStatus(uWS::HTTP_200_OK)->write([res](int offset) {
/* Handle aborted stream out */
if (offset == -1) {
std::cout << "Removing closed stream!" << std::endl;
delayedResponses.erase(res);
return uWS::HTTP_STREAM_FIN;
}
/* Delay stream out */
std::cout << "Delaying stream now" << std::endl; std::cout << "Delaying stream now" << std::endl;
delayedResponses.insert(res); delayedResponses.insert(res);
//asyncFetchData(res)
return uWS::HTTP_STREAM_PAUSE; return uWS::HTTP_STREAM_PAUSE;
}, 100); }, 100);
+12 -3
View File
@@ -148,7 +148,7 @@ public:
} }
/* Drain any socket-buffer while also optionally sending a chunk */ /* Drain any socket-buffer while also optionally sending a chunk */
int mergeDrain(std::string_view optionalChunk) { int mergeDrain(std::string_view optionalChunk = {}) {
// strategy: if we have two parts and both will fit in cork buffer then cork them and recursively send them off // strategy: if we have two parts and both will fit in cork buffer then cork them and recursively send them off
@@ -167,8 +167,17 @@ public:
return write(optionalChunk.data(), optionalChunk.length(), true, 0); return write(optionalChunk.data(), optionalChunk.length(), true, 0);
} }
void close() { /* These should not be public to the user! */
static_dispatch(us_ssl_socket_close, us_socket_close)((SOCKET_TYPE *) this); void timeout(unsigned int seconds) {
static_dispatch(us_ssl_socket_timeout, us_socket_timeout)((SOCKET_TYPE *) this, seconds);
}
void shutdown() {
static_dispatch(us_ssl_socket_shutdown, us_socket_shutdown)((SOCKET_TYPE *) this);
}
SOCKET_TYPE *close() {
return static_dispatch(us_ssl_socket_close, us_socket_close)((SOCKET_TYPE *) this);
} }
}; };
+75 -40
View File
@@ -44,16 +44,12 @@ private:
/* Init the HttpContext by registering libusockets event handlers */ /* Init the HttpContext by registering libusockets event handlers */
HttpContext<SSL> *init() { HttpContext<SSL> *init() {
//new (data = (Data *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)(httpServerContext)) Data();
/* Handle socket connections */ /* Handle socket connections */
static_dispatch(us_ssl_socket_context_on_open, us_socket_context_on_open)(getSocketContext(), [](auto *s, int is_client) { static_dispatch(us_ssl_socket_context_on_open, us_socket_context_on_open)(getSocketContext(), [](auto *s, int is_client) {
HttpContextData<SSL> *httpContextData = getSocketContextData(s); /* Any connected socket should timeout until it has a request */
std::cout << "Opened http connection" << std::endl;
static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S); static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S);
/* Init socket ext */
new (static_dispatch(us_ssl_socket_ext, us_socket_ext)(s)) HttpResponseData<SSL>; new (static_dispatch(us_ssl_socket_ext, us_socket_ext)(s)) HttpResponseData<SSL>;
return s; return s;
@@ -61,9 +57,21 @@ private:
/* Handle socket disconnections */ /* Handle socket disconnections */
static_dispatch(us_ssl_socket_context_on_close, us_socket_context_on_close)(getSocketContext(), [](auto *s) { static_dispatch(us_ssl_socket_context_on_close, us_socket_context_on_close)(getSocketContext(), [](auto *s) {
HttpContextData<SSL> *httpContextData = getSocketContextData(s); /* Get socket ext */
HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) static_dispatch(us_ssl_socket_ext, us_socket_ext)(s);
((HttpResponseData<SSL> *) static_dispatch(us_ssl_socket_ext, us_socket_ext)(s))->~HttpResponseData<SSL>(); /* Signal broken out stream */
if (httpResponseData->outStream) {
httpResponseData->outStream(-1);
}
/* Signal broken in stream */
if (httpResponseData->inStream) {
httpResponseData->inStream(std::string_view(nullptr, 0));
}
/* Destruct socket ext */
httpResponseData->~HttpResponseData<SSL>();
return s; return s;
}); });
@@ -72,7 +80,14 @@ private:
static_dispatch(us_ssl_socket_context_on_data, us_socket_context_on_data)(getSocketContext(), [](auto *s, char *data, int length) { static_dispatch(us_ssl_socket_context_on_data, us_socket_context_on_data)(getSocketContext(), [](auto *s, char *data, int length) {
HttpContextData<SSL> *httpContextData = getSocketContextData(s); HttpContextData<SSL> *httpContextData = getSocketContextData(s);
// cork this socket (move this to loop?) /* Do not accept any data while in shutdown state */
if (static_dispatch(us_ssl_socket_is_shut_down, us_socket_is_shut_down)((SOCKET_TYPE *) s)) {
return s;
}
// basically, when getting a new request we need to disable timeouts and enter paused mode!
/* Cork this socket */
((AsyncSocket<SSL> *) s)->cork(); ((AsyncSocket<SSL> *) s)->cork();
// pass this pointer to pointer along with the routing and change it if upgraded // pass this pointer to pointer along with the routing and change it if upgraded
@@ -81,9 +96,13 @@ private:
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) {
// whenever we get (the first) request we should disable timeout?
// warning: if we are in shutdown state, resetting the timer is a security issue! // warning: if we are in shutdown state, resetting the timer is a security issue!
// todo: do not reset timer, disable it to allow hang requests!
static_dispatch(us_ssl_socket_timeout, us_socket_timeout)((SOCKET_TYPE *) s, HTTP_IDLE_TIMEOUT_S); static_dispatch(us_ssl_socket_timeout, us_socket_timeout)((SOCKET_TYPE *) s, HTTP_IDLE_TIMEOUT_S);
/* Reset httpResponse */
HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) static_dispatch(us_ssl_socket_ext, us_socket_ext)((SOCKET_TYPE *) s); HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) static_dispatch(us_ssl_socket_ext, us_socket_ext)((SOCKET_TYPE *) s);
httpResponseData->offset = 0; httpResponseData->offset = 0;
httpResponseData->state = 0; httpResponseData->state = 0;
@@ -94,6 +113,8 @@ private:
}; };
httpContextData->router.route("get", 3, httpRequest->getUrl().data(), httpRequest->getUrl().length(), &userData); httpContextData->router.route("get", 3, httpRequest->getUrl().data(), httpRequest->getUrl().length(), &userData);
// here we can be closed and in shutdown?
}, [httpResponseData](void *user, std::string_view data) { }, [httpResponseData](void *user, std::string_view data) {
if (httpResponseData->inStream) { if (httpResponseData->inStream) {
httpResponseData->inStream(data); httpResponseData->inStream(data);
@@ -114,25 +135,41 @@ private:
/* Handle HTTP write out */ /* Handle HTTP write out */
static_dispatch(us_ssl_socket_context_on_writable, us_socket_context_on_writable)(getSocketContext(), [](auto *s) { static_dispatch(us_ssl_socket_context_on_writable, us_socket_context_on_writable)(getSocketContext(), [](auto *s) {
// I think it's fair to never mind this one -> if we keep writing data after shutting down then that's an issue for us /* Silence any spurious writable events due to SSL_read failing to write */
static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S);
AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
// get next chunk to send
HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getExt(); HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getExt();
if (httpResponseData->state & HttpResponseData<SSL>::HTTP_PAUSED_STREAM_OUT) {
if (httpResponseData->outStream) { return s;
auto [msg_more, chunk] = httpResponseData->outStream(httpResponseData->offset);
// send, including any buffered up
httpResponseData->offset += asyncSocket->mergeDrain(chunk);
} else {
std::cout << "We did not have any outStream!" << std::endl;
asyncSocket->mergeDrain(std::string_view(nullptr, 0));
} }
/* Writing data should reset the timeout */
static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S);
/* Are we already ended and just waiting for a drain / shutdown? */
if (httpResponseData->state & HttpResponseData<SSL>::HTTP_ENDED_STREAM_OUT) {
/* Try and send everything buffered up */
asyncSocket->mergeDrain();
/* If we succeed with drainage we can finally shut down */
if (!asyncSocket->hasBuffer()) {
asyncSocket->shutdown();
}
/* Nothing here for us */
return s;
}
if (httpResponseData->outStream) {
/* Regular path, request more data */
auto [msg_more, chunk] = httpResponseData->outStream(httpResponseData->offset);
httpResponseData->offset += asyncSocket->mergeDrain(chunk);
// todo: we should loop until we cannot send anymore just like we do in HttpResponse::write(stream)!
} else {
/* We can come here if we only have socket buffers to drain yet no attached stream */
asyncSocket->mergeDrain();
}
return s; return s;
}); });
@@ -140,26 +177,18 @@ private:
/* Handle FIN, HTTP does not support half-closed sockets, so simply close */ /* Handle FIN, HTTP does not support half-closed sockets, so simply close */
static_dispatch(us_ssl_socket_context_on_end, us_socket_context_on_end)(getSocketContext(), [](auto *s) { static_dispatch(us_ssl_socket_context_on_end, us_socket_context_on_end)(getSocketContext(), [](auto *s) {
// static_dispatch(us_ssl_socket_close, us_socket_close)(s); /* We do not care for half closed sockets */
AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s; AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
asyncSocket->close(); return asyncSocket->close();
return s;
}); });
/* Handle socket timeouts */ /* Handle socket timeouts, simply close them so to not confuse client with FIN */
static_dispatch(us_ssl_socket_context_on_timeout, us_socket_context_on_timeout)(getSocketContext(), [](auto *s) { static_dispatch(us_ssl_socket_context_on_timeout, us_socket_context_on_timeout)(getSocketContext(), [](auto *s) {
if (static_dispatch(us_ssl_socket_is_shut_down, us_socket_is_shut_down)(s)) { /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */
std::cout << "Forcefully closing socket since shutdown was not answered in time" << std::endl; AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
static_dispatch(us_ssl_socket_close, us_socket_close)(s); return asyncSocket->close();
} else {
std::cout << "Shutting down socket now" << std::endl;
static_dispatch(us_ssl_socket_timeout, us_socket_timeout)(s, HTTP_IDLE_TIMEOUT_S);
static_dispatch(us_ssl_socket_shutdown, us_socket_shutdown)(s);
}
return s;
}); });
@@ -177,16 +206,22 @@ public:
httpContext = (HttpContext *) us_create_socket_context((us_loop *) loop, sizeof(HttpContextData<SSL>)); httpContext = (HttpContext *) us_create_socket_context((us_loop *) loop, sizeof(HttpContextData<SSL>));
} }
if (!httpContext) {
return nullptr;
}
/* Init socket context data */
new ((HttpContextData<SSL> *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)((SOCKET_CONTEXT_TYPE *) httpContext)) HttpContextData<SSL>(); new ((HttpContextData<SSL> *) static_dispatch(us_ssl_socket_context_ext, us_socket_context_ext)((SOCKET_CONTEXT_TYPE *) httpContext)) HttpContextData<SSL>();
return httpContext->init(); return httpContext->init();
} }
/* Destruct the HttpContext, it does not follow RAII */ /* Destruct the HttpContext, it does not follow RAII */
void free() { void free() {
/* Destruct socket context data */
HttpContextData<SSL> *httpContextData = getSocketContextData();
httpContextData->~HttpContextData<SSL>();
// call destructor! /* Free the socket context in whole */
static_dispatch(us_ssl_socket_context_free, us_socket_context_free)(getSocketContext()); static_dispatch(us_ssl_socket_context_free, us_socket_context_free)(getSocketContext());
} }
+16 -10
View File
@@ -96,19 +96,25 @@ public:
return; return;
} }
std::cout << "Resume called and we really are paused" << std::endl;
/* Remove paused status */ /* Remove paused status */
httpResponseData->state &= ~HttpResponseData<SSL>::HTTP_PAUSED_STREAM_OUT; httpResponseData->state &= ~HttpResponseData<SSL>::HTTP_PAUSED_STREAM_OUT;
int written = AsyncSocket<SSL>::write(chunk.data(), chunk.length(), true); if (chunk.length()) {
int written = AsyncSocket<SSL>::write(chunk.data(), chunk.length(), true);
if (written == chunk.length()) { if (written == chunk.length()) {
// pull a new chunk from the callback (basically call onWritable) // pull a new chunk from the callback (basically call onWritable)
}
} }
// no, basically just write this off and if all written, call streamOut callback // no, basically just write this off and if all written, call streamOut callback
} }
/* Attach a write handler for sending data. Chunks might be read more than once */ /* Attach an output stream function. Chunks may be read more than once. Negative offset mean broken stream */
void write(std::function<std::pair<bool, std::string_view>(int)> cb, int length = 0) { void write(std::function<std::pair<bool, std::string_view>(int)> cb, int length = 0) {
HttpResponseData<SSL> *httpResponseData = getHttpResponseData(); HttpResponseData<SSL> *httpResponseData = getHttpResponseData();
@@ -140,21 +146,21 @@ public:
/* Handle PAUSE and FIN */ /* Handle PAUSE and FIN */
if (chunk.length() == 0) { if (chunk.length() == 0) {
if (chunk == HTTP_STREAM_FIN.second) { /* FIN */
if (chunk.data()) {
/* Try flush and shut down */ /* Try flush and shut down */
AsyncSocket<SSL>::uncork(); AsyncSocket<SSL>::uncork();
if (!AsyncSocket<SSL>::hasBuffer()) { if (!AsyncSocket<SSL>::hasBuffer()) {
/* Uncork finished with no buffered data */ /* Uncork finished with no buffered data */
us_socket_shutdown((us_socket *) this); AsyncSocket<SSL>::shutdown();
} else { } else {
/* Let it shut down when drained */ /* Let it shut down when drained */
httpResponseData->state |= HttpResponseData<SSL>::HTTP_ENDED_STREAM_OUT; httpResponseData->state |= HttpResponseData<SSL>::HTTP_ENDED_STREAM_OUT;
} }
} else { } else {
std::cout << "Paused stream is not implemented!" << std::endl; /* Disable timeout and mark this stream as paused (important to silence spurious onWritable events) */
AsyncSocket<SSL>::timeout(0);
// skip sending optional, yet keep refusing to call onWritable while in paused mode (SSL may poll for writable!) httpResponseData->state |= HttpResponseData<SSL>::HTTP_PAUSED_STREAM_OUT;
// we thus need a status: paused to check for before requesting more data (also check for this in resume call!)
} }
return; return;
} }