aboutsummaryrefslogtreecommitdiffhomepage
path: root/node_modules/uws/src/Extensions.cpp
blob: ef8f9da7e4bd4afcbc96ed506490c5eec2a1d6c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#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 <bool isServer>
ExtensionsNegotiator<isServer>::ExtensionsNegotiator(int wantedOptions) {
    options = wantedOptions;
}

template <bool isServer>
std::string ExtensionsNegotiator<isServer>::generateOffer() {
    std::string extensionsOffer;
    if (options & Options::PERMESSAGE_DEFLATE) {
        extensionsOffer += "permessage-deflate";

        if (options & Options::CLIENT_NO_CONTEXT_TAKEOVER) {
            extensionsOffer += "; client_no_context_takeover";
        }

        if (options & Options::SERVER_NO_CONTEXT_TAKEOVER) {
            extensionsOffer += "; server_no_context_takeover";
        }
    }

    return extensionsOffer;
}

template <bool isServer>
void ExtensionsNegotiator<isServer>::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 <bool isServer>
int ExtensionsNegotiator<isServer>::getNegotiatedOptions() {
    return options;
}

template class ExtensionsNegotiator<true>;
template class ExtensionsNegotiator<false>;

}