diff --git a/examples/EchoServer.cpp b/examples/EchoServer.cpp index 80b03f2..10a4c5b 100644 --- a/examples/EchoServer.cpp +++ b/examples/EchoServer.cpp @@ -8,7 +8,6 @@ int main() { /* ws->getUserData returns one of these */ struct PerSocketData { /* Fill with user data */ - int something; }; /* Keep in mind that uWS::SSLApp({options}) is the same as uWS::App() when compiled without SSL support. @@ -25,68 +24,8 @@ int main() { .idleTimeout = 10, .maxBackpressure = 1 * 1024 * 1024, /* Handlers */ - - //.syncUpgrade() - - .upgrade = [](auto *res, auto *req, auto *context) { - - /* Immediate path */ - res->template upgrade({.something = 13}, req->getHeader("sec-websocket-key"), - req->getHeader("sec-websocket-protocol"), - req->getHeader("sec-websocket-extensions"), - context); - - return; - - std::cout << "Upgrade request!" << std::endl; - - /* Async path, you have to COPY headers */ - std::string secWebSocketKey(req->getHeader("sec-websocket-key")); - std::string secWebSocketProtocol(req->getHeader("sec-websocket-protocol")); - std::string secWebSocketExtensions(req->getHeader("sec-websocket-extensions")); - - /* If the client disconnects inbetween, you MUST avoid upgrading */ - bool *aborted = new bool(false); - res->onAborted([=]() { - std::cout << "WebSocket upgrade aborted!" << std::endl; - *aborted = true; - }); - - /* Simulate checking auth for 10 seconds */ - struct us_loop_t *loop = (struct us_loop_t *) uWS::Loop::get(); - struct us_timer_t *delayTimer = us_create_timer(loop, 0, 0); - us_timer_set(delayTimer, [](struct us_timer_t *t) { - std::cout << "Timer triggered!" << std::endl; - - us_timer_close(t); - }, 5000, 0); - - /* Simulate doing async work by deferring upgrade to next event loop iteration */ - uWS::Loop::get()->defer([=](){ - - std::cout << "Upgrading now!" << std::endl; - - if (!*aborted) { - - // this should get some kind of ticket - res->template upgrade({}, secWebSocketKey, - secWebSocketProtocol, - secWebSocketExtensions, - context); - } - - delete aborted; - }); - - /* Immediate path is in all seriousness a simple return bool */ - - // not exactly, you need to pass UserData - return a moved UserData and bool? - - - }, .open = [](auto *ws) { /* Open event here, you may access ws->getUserData() which points to a PerSocketData struct */ - std::cout << "Something is: " << static_cast(ws->getUserData())->something << std::endl; }, .message = [](auto *ws, std::string_view message, uWS::OpCode opCode) { ws->send(message, opCode); diff --git a/src/App.h b/src/App.h index 3d08051..e55ba53 100644 --- a/src/App.h +++ b/src/App.h @@ -157,23 +157,7 @@ public: /* Emit upgrade handler */ if (behavior.upgrade) { - - - // a regular HttpResponse does not know about UserData or any of the Websocket upgrade procedure - - // behavior.compression, (struct us_socket_context_t *) webSocketContext, httpContext, behavior.idleTimeout, behavior.open - - // webSocketContext håller behavior - håller den även httpContext? - - // int, void *, void *, int, - behavior.upgrade(res, req, (struct us_socket_context_t *) webSocketContext); - - // if upgrade handler does not upgrade or end within the callback, lift a token? - - - - // handle close here } else { /* Default handler upgrades to WebSocket */ std::string_view secWebSocketProtocol = req->getHeader("sec-websocket-protocol"); @@ -182,11 +166,6 @@ public: res->template upgrade({}, secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions, (struct us_socket_context_t *) webSocketContext); } - /* Emit open event and start the timeout */ - if (behavior.open) { - //behavior.open(webSocket, req); - } - /* We are going to get uncorked by the Http get return */ /* We do not need to check for any close or shutdown here as we immediately return from get handler */ diff --git a/src/HttpContext.h b/src/HttpContext.h index fed3ee7..6e82591 100644 --- a/src/HttpContext.h +++ b/src/HttpContext.h @@ -317,16 +317,6 @@ private: return this; } - /* Used by App in its WebSocket handler */ - void upgradeToWebSocket(void *newSocket) { - HttpContextData *httpContextData = getSocketContextData(); - - /* We should only mark this if inside the parser; if upgrading "async" we cannot set this */ - if (httpContextData->isParsingHttp) { - httpContextData->upgradedWebSocket = newSocket; - } - } - public: /* Construct a new HttpContext using specified loop */ static HttpContext *create(Loop *loop, us_socket_context_options_t options = {}) { diff --git a/src/HttpResponse.h b/src/HttpResponse.h index e347a41..d617f59 100644 --- a/src/HttpResponse.h +++ b/src/HttpResponse.h @@ -30,8 +30,6 @@ #include "WebSocket.h" #include "WebSocketContextData.h" -#include "HttpContext.h" - #include "f2/function2.hpp" /* todo: tryWrite is missing currently, only send smaller segments with write */ @@ -169,7 +167,8 @@ private: } public: - /* This call is identical to end, but will never write content-length and is thus suitable for upgrades */ + /* Manually upgrade to WebSocket. Typically called in upgrade handler. Immediately calls open handler. + * NOTE: Will invalidate 'this' as socket might change location in memory. Throw away aftert use. */ template void upgrade(UserData &&userData, std::string_view secWebSocketKey, std::string_view secWebSocketProtocol, std::string_view secWebSocketExtensions, @@ -177,8 +176,6 @@ public: /* Extract needed parameters from WebSocketContextData */ WebSocketContextData *webSocketContextData = (WebSocketContextData *) us_socket_context_ext(SSL, webSocketContext); - int compression = webSocketContextData->compression; - int idleTimeout = webSocketContextData->idleTimeout; /* Note: OpenSSL can be used here to speed this up somewhat */ char secWebSocketAccept[29] = {}; @@ -197,15 +194,14 @@ public: /* Negotiate compression, we may use a smaller compression window than we negotiate */ bool perMessageDeflate = false; /* We are always allowed to share compressor, if perMessageDeflate */ - int compressOptions = /*behavior.compression*/ compression & SHARED_COMPRESSOR; - if (/*behavior.compression*/ compression != DISABLED) { - //std::string_view extensions = req->getHeader("sec-websocket-extensions"); + int compressOptions = webSocketContextData->compression & SHARED_COMPRESSOR; + if (webSocketContextData->compression != DISABLED) { if (secWebSocketExtensions.length()) { /* We never support client context takeover (the client cannot compress with a sliding window). */ int wantedOptions = PERMESSAGE_DEFLATE | CLIENT_NO_CONTEXT_TAKEOVER; /* Shared compressor is the default */ - if (/*behavior.compression*/ compression == SHARED_COMPRESSOR) { + if (webSocketContextData->compression == SHARED_COMPRESSOR) { /* Disable per-socket compressor */ wantedOptions |= SERVER_NO_CONTEXT_TAKEOVER; } @@ -227,7 +223,7 @@ public: /* Is the server allowed to compress with a sliding window? */ if (!(extensionsNegotiator.getNegotiatedOptions() & SERVER_NO_CONTEXT_TAKEOVER)) { - compressOptions = /*behavior.*/compression; + compressOptions = webSocketContextData->compression; } } } @@ -237,43 +233,44 @@ public: /* Grab the httpContext from res */ HttpContext *httpContext = (HttpContext *) us_socket_context(SSL, (struct us_socket_t *) this); - /* Move any backpressure */ + /* Move any backpressure out of HttpResponse */ std::string backpressure(std::move(((AsyncSocketData *) getHttpResponseData())->buffer)); - /* Keep any fallback buffer alive until we returned from open event, keeping req valid */ - std::string fallback(std::move(getHttpResponseData()->salvageFallbackBuffer())); - /* Destroy HttpResponseData */ getHttpResponseData()->~HttpResponseData(); + /* Before we adopt and potentially change socket, check if we are corked */ + bool wasCorked = Super::isCorked(); + /* Adopting a socket invalidates it, do not rely on it directly to carry any data */ WebSocket *webSocket = (WebSocket *) us_socket_context_adopt_socket(SSL, (us_socket_context_t *) webSocketContext, (us_socket_t *) this, sizeof(WebSocketData) + sizeof(UserData)); - /* Update corked socket in case we got a new one (assuming we always are corked in handlers). */ - webSocket->AsyncSocket::cork(); + /* For whatever reason we were corked, update cork to the new socket */ + if (wasCorked) { + webSocket->AsyncSocket::cork(); + } /* Initialize websocket with any moved backpressure intact */ + webSocket->init(perMessageDeflate, compressOptions, std::move(backpressure)); - - /* Todo: this is the only use of HttpContext! Move that code in here! */ - /* We should not depend on the HttpContext.h! */ - httpContext->upgradeToWebSocket( - webSocket->init(perMessageDeflate, compressOptions, std::move(backpressure)) - ); + /* We should only mark this if inside the parser; if upgrading "async" we cannot set this */ + HttpContextData *httpContextData = httpContext->getSocketContextData(); + if (httpContextData->isParsingHttp) { + /* We need to tell the Http parser that we changed socket */ + httpContextData->upgradedWebSocket = webSocket; + } /* Arm idleTimeout */ - us_socket_timeout(SSL, (us_socket_t *) webSocket, /*behavior.*/idleTimeout); + us_socket_timeout(SSL, (us_socket_t *) webSocket, webSocketContextData->idleTimeout); - /* Default construct the UserData right before calling open handler */ + /* Move construct the UserData right before calling open handler */ new (webSocket->getUserData()) UserData(std::move(userData)); /* Emit open event and start the timeout */ if (webSocketContextData->openHandler) { webSocketContextData->openHandler(webSocket); } - - // if we weren't corked then uncork here! otherwise we were called from httpContext! } /* Immediately terminate this Http response */ @@ -281,11 +278,6 @@ public: using Super::getRemoteAddress; - /* Manually upgrade to WebSocket, called in upgrade handler of a WebSocket route */ - /*void upgrade() { - - }*/ - /* Note: Headers are not checked in regards to timeout. * We only check when you actively push data or end the request */