#include "Extensions.h" namespace uWS { enum ExtensionTokens { TOK_PERMESSAGE_DEFLATE = 1838, TOK_SERVER_NO_CONTEXT_TAKEOVER = 2807, TOK_CLIENT_NO_CONTEXT_TAKEOVER = 2783, TOK_SERVER_MAX_WINDOW_BITS = 2372, TOK_CLIENT_MAX_WINDOW_BITS = 2348 }; class ExtensionsParser { private: int *lastInteger = nullptr; public: bool perMessageDeflate = false; bool serverNoContextTakeover = false; bool clientNoContextTakeover = false; int serverMaxWindowBits = 0; int clientMaxWindowBits = 0; int getToken(const char *&in, const char *stop); ExtensionsParser(const char *data, size_t length); }; 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"; } // we do not support accepting this yet // todo: if we agree on this, do not allocate a compressor // per socket! // It is RECOMMENDED that a server supports the // "server_no_context_takeover" extension parameter in an extension // negotiation offer. if (options & Options::SERVER_NO_CONTEXT_TAKEOVER) { //extensionsOffer += "; server_no_context_takeover"; } } return extensionsOffer; } template void ExtensionsNegotiator::readOffer(std::string 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; } 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; } template class ExtensionsNegotiator; template class ExtensionsNegotiator; }