diff --git a/src/WebSocketExtensions.h b/src/WebSocketExtensions.h index d6e98fb..4de60c2 100644 --- a/src/WebSocketExtensions.h +++ b/src/WebSocketExtensions.h @@ -31,17 +31,6 @@ enum Options : unsigned int { SLIDING_DEFLATE_WINDOW = 16 }; -template -class ExtensionsNegotiator { -protected: - int options; -public: - ExtensionsNegotiator(int wantedOptions); - std::string generateOffer(); - void readOffer(std::string_view offer); - int getNegotiatedOptions(); -}; - enum ExtensionTokens { TOK_PERMESSAGE_DEFLATE = 1838, TOK_SERVER_NO_CONTEXT_TAKEOVER = 2807, @@ -50,6 +39,61 @@ enum ExtensionTokens { TOK_CLIENT_MAX_WINDOW_BITS = 2348 }; +template +class ExtensionsNegotiator { +protected: + int options; + +public: + ExtensionsNegotiator(int wantedOptions) { + options = wantedOptions; + } + + std::string generateOffer() { + std::string extensionsOffer; + if (options & Options::PERMESSAGE_DEFLATE) { + extensionsOffer += "permessage-deflate"; + + if (options & Options::CLIENT_NO_CONTEXT_TAKEOVER) { + extensionsOffer += "; client_no_context_takeover"; + } + + /* It is questionable sending this improves anything */ + /*if (options & Options::SERVER_NO_CONTEXT_TAKEOVER) { + extensionsOffer += "; server_no_context_takeover"; + }*/ + } + + return extensionsOffer; + } + + void readOffer(std::string_view offer) { + if (isServer) { + ExtensionsParser extensionsParser(offer.data(), offer.length()); + if ((options & PERMESSAGE_DEFLATE) && extensionsParser.perMessageDeflate) { + if (extensionsParser.clientNoContextTakeover || (options & CLIENT_NO_CONTEXT_TAKEOVER)) { + options |= CLIENT_NO_CONTEXT_TAKEOVER; + } + + /* We leave this option for us to read even if the client did not send it */ + if (extensionsParser.serverNoContextTakeover) { + options |= SERVER_NO_CONTEXT_TAKEOVER; + }/* else { + options &= ~SERVER_NO_CONTEXT_TAKEOVER; + }*/ + } else { + options &= ~PERMESSAGE_DEFLATE; + } + } else { + // todo! + } + } + + int getNegotiatedOptions() { + return options; + } +}; + class ExtensionsParser { private: int *lastInteger = nullptr; @@ -61,112 +105,57 @@ public: int serverMaxWindowBits = 0; int clientMaxWindowBits = 0; - int getToken(const char *&in, const char *stop); - ExtensionsParser(const char *data, size_t length); + int getToken(const char *&in, const char *stop) { + while (!isalnum(*in) && in != stop) { + in++; + } + + int hashedToken = 0; + while (isalnum(*in) || *in == '-' || *in == '_') { + if (isdigit(*in)) { + hashedToken = hashedToken * 10 - (*in - '0'); + } else { + hashedToken += *in; + } + in++; + } + return hashedToken; + } + + ExtensionsParser(const char *data, size_t length) { + const char *stop = data + length; + int token = 1; + for (; token && token != TOK_PERMESSAGE_DEFLATE; token = getToken(data, stop)); + + perMessageDeflate = (token == TOK_PERMESSAGE_DEFLATE); + while ((token = getToken(data, stop))) { + switch (token) { + case TOK_PERMESSAGE_DEFLATE: + return; + case TOK_SERVER_NO_CONTEXT_TAKEOVER: + serverNoContextTakeover = true; + break; + case TOK_CLIENT_NO_CONTEXT_TAKEOVER: + clientNoContextTakeover = true; + break; + case TOK_SERVER_MAX_WINDOW_BITS: + serverMaxWindowBits = 1; + lastInteger = &serverMaxWindowBits; + break; + case TOK_CLIENT_MAX_WINDOW_BITS: + clientMaxWindowBits = 1; + lastInteger = &clientMaxWindowBits; + break; + default: + if (token < 0 && lastInteger) { + *lastInteger = -token; + } + break; + } + } + } }; -int ExtensionsParser::getToken(const char *&in, const char *stop) { - while (!isalnum(*in) && in != stop) { - in++; - } - - int hashedToken = 0; - while (isalnum(*in) || *in == '-' || *in == '_') { - if (isdigit(*in)) { - hashedToken = hashedToken * 10 - (*in - '0'); - } else { - hashedToken += *in; - } - in++; - } - return hashedToken; -} - -ExtensionsParser::ExtensionsParser(const char *data, size_t length) { - const char *stop = data + length; - int token = 1; - for (; token && token != TOK_PERMESSAGE_DEFLATE; token = getToken(data, stop)); - - perMessageDeflate = (token == TOK_PERMESSAGE_DEFLATE); - while ((token = getToken(data, stop))) { - switch (token) { - case TOK_PERMESSAGE_DEFLATE: - return; - case TOK_SERVER_NO_CONTEXT_TAKEOVER: - serverNoContextTakeover = true; - break; - case TOK_CLIENT_NO_CONTEXT_TAKEOVER: - clientNoContextTakeover = true; - break; - case TOK_SERVER_MAX_WINDOW_BITS: - serverMaxWindowBits = 1; - lastInteger = &serverMaxWindowBits; - break; - case TOK_CLIENT_MAX_WINDOW_BITS: - clientMaxWindowBits = 1; - lastInteger = &clientMaxWindowBits; - break; - default: - if (token < 0 && lastInteger) { - *lastInteger = -token; - } - break; - } - } -} - -template -ExtensionsNegotiator::ExtensionsNegotiator(int wantedOptions) { - options = wantedOptions; -} - -template -std::string ExtensionsNegotiator::generateOffer() { - std::string extensionsOffer; - if (options & Options::PERMESSAGE_DEFLATE) { - extensionsOffer += "permessage-deflate"; - - if (options & Options::CLIENT_NO_CONTEXT_TAKEOVER) { - extensionsOffer += "; client_no_context_takeover"; - } - - /* It is questionable sending this improves anything */ - /*if (options & Options::SERVER_NO_CONTEXT_TAKEOVER) { - extensionsOffer += "; server_no_context_takeover"; - }*/ - } - - return extensionsOffer; -} - -template -void ExtensionsNegotiator::readOffer(std::string_view offer) { - if (isServer) { - ExtensionsParser extensionsParser(offer.data(), offer.length()); - if ((options & PERMESSAGE_DEFLATE) && extensionsParser.perMessageDeflate) { - if (extensionsParser.clientNoContextTakeover || (options & CLIENT_NO_CONTEXT_TAKEOVER)) { - options |= CLIENT_NO_CONTEXT_TAKEOVER; - } - - /* We leave this option for us to read even if the client did not send it */ - if (extensionsParser.serverNoContextTakeover) { - options |= SERVER_NO_CONTEXT_TAKEOVER; - }/* else { - options &= ~SERVER_NO_CONTEXT_TAKEOVER; - }*/ - } else { - options &= ~PERMESSAGE_DEFLATE; - } - } else { - // todo! - } -} - -template -int ExtensionsNegotiator::getNegotiatedOptions() { - return options; -} - } #endif // WEBSOCKETEXTENSIONS_H