diff --git a/src/App.h b/src/App.h index 1f79723..b349eb3 100644 --- a/src/App.h +++ b/src/App.h @@ -25,8 +25,6 @@ #include "HttpResponse.h" #include "WebSocketContext.h" #include "WebSocket.h" -#include "WebSocketExtensions.h" -#include "WebSocketHandshake.h" #include "PerMessageDeflate.h" namespace uWS { @@ -168,69 +166,18 @@ public: // handle close here + } else { + /* Default handler upgrades to WebSocket */ + std::string_view secWebSocketProtocol = req->getHeader("sec-websocket-protocol"); + std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions"); + + res->upgrade(secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions, behavior.compression); } - - /* Note: OpenSSL can be used here to speed this up somewhat */ - char secWebSocketAccept[29] = {}; - WebSocketHandshake::generate(secWebSocketKey.data(), secWebSocketAccept); - - res->writeStatus("101 Switching Protocols") - ->writeHeader("Upgrade", "websocket") - ->writeHeader("Connection", "Upgrade") - ->writeHeader("Sec-WebSocket-Accept", secWebSocketAccept); - - /* Select first subprotocol if present */ - std::string_view secWebSocketProtocol = req->getHeader("sec-websocket-protocol"); - if (secWebSocketProtocol.length()) { - res->writeHeader("Sec-WebSocket-Protocol", secWebSocketProtocol.substr(0, secWebSocketProtocol.find(','))); - } - - /* 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 & SHARED_COMPRESSOR; - if (behavior.compression != DISABLED) { - std::string_view extensions = req->getHeader("sec-websocket-extensions"); - if (extensions.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 == SHARED_COMPRESSOR) { - /* Disable per-socket compressor */ - wantedOptions |= SERVER_NO_CONTEXT_TAKEOVER; - } - - /* isServer = true */ - ExtensionsNegotiator extensionsNegotiator(wantedOptions); - extensionsNegotiator.readOffer(extensions); - - /* Todo: remove these mid string copies */ - std::string offer = extensionsNegotiator.generateOffer(); - if (offer.length()) { - res->writeHeader("Sec-WebSocket-Extensions", offer); - } - - /* Did we negotiate permessage-deflate? */ - if (extensionsNegotiator.getNegotiatedOptions() & PERMESSAGE_DEFLATE) { - perMessageDeflate = true; - } - - /* Is the server allowed to compress with a sliding window? */ - if (!(extensionsNegotiator.getNegotiatedOptions() & SERVER_NO_CONTEXT_TAKEOVER)) { - compressOptions = behavior.compression; - } - } - } - - /* This will add our mark */ - res->upgrade(); - /* Move any backpressure */ std::string backpressure(std::move(((AsyncSocketData *) res->getHttpResponseData())->buffer)); @@ -249,7 +196,7 @@ public: /* Initialize websocket with any moved backpressure intact */ httpContext->upgradeToWebSocket( - webSocket->init(perMessageDeflate, compressOptions, std::move(backpressure)) + webSocket->init(/*perMessageDeflate*/ false, /*compressOptions*/ 0, std::move(backpressure)) ); /* Arm idleTimeout */ diff --git a/src/HttpResponse.h b/src/HttpResponse.h index 4173203..f164213 100644 --- a/src/HttpResponse.h +++ b/src/HttpResponse.h @@ -25,6 +25,9 @@ #include "HttpContextData.h" #include "Utilities.h" +#include "WebSocketExtensions.h" +#include "WebSocketHandshake.h" + #include "f2/function2.hpp" /* todo: tryWrite is missing currently, only send smaller segments with write */ @@ -162,7 +165,61 @@ private: } /* This call is identical to end, but will never write content-length and is thus suitable for upgrades */ - void upgrade() { + void upgrade(std::string_view secWebSocketKey, std::string_view secWebSocketProtocol, std::string_view secWebSocketExtensions, int compression) { + + /* Note: OpenSSL can be used here to speed this up somewhat */ + char secWebSocketAccept[29] = {}; + WebSocketHandshake::generate(secWebSocketKey.data(), secWebSocketAccept); + + writeStatus("101 Switching Protocols") + ->writeHeader("Upgrade", "websocket") + ->writeHeader("Connection", "Upgrade") + ->writeHeader("Sec-WebSocket-Accept", secWebSocketAccept); + + /* Select first subprotocol if present */ + //std::string_view secWebSocketProtocol = req->getHeader("sec-websocket-protocol"); + if (secWebSocketProtocol.length()) { + writeHeader("Sec-WebSocket-Protocol", secWebSocketProtocol.substr(0, secWebSocketProtocol.find(','))); + } + + /* 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"); + 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) { + /* Disable per-socket compressor */ + wantedOptions |= SERVER_NO_CONTEXT_TAKEOVER; + } + + /* isServer = true */ + ExtensionsNegotiator extensionsNegotiator(wantedOptions); + extensionsNegotiator.readOffer(secWebSocketExtensions); + + /* Todo: remove these mid string copies */ + std::string offer = extensionsNegotiator.generateOffer(); + if (offer.length()) { + writeHeader("Sec-WebSocket-Extensions", offer); + } + + /* Did we negotiate permessage-deflate? */ + if (extensionsNegotiator.getNegotiatedOptions() & PERMESSAGE_DEFLATE) { + perMessageDeflate = true; + } + + /* Is the server allowed to compress with a sliding window? */ + if (!(extensionsNegotiator.getNegotiatedOptions() & SERVER_NO_CONTEXT_TAKEOVER)) { + compressOptions = /*behavior.*/compression; + } + } + } + internalEnd({nullptr, 0}, 0, false, false); }