/src/uWebSockets/src/WebSocketExtensions.h
Line | Count | Source |
1 | | /* |
2 | | * Authored by Alex Hultman, 2018-2021. |
3 | | * Intellectual property of third-party. |
4 | | |
5 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
6 | | * you may not use this file except in compliance with the License. |
7 | | * You may obtain a copy of the License at |
8 | | |
9 | | * http://www.apache.org/licenses/LICENSE-2.0 |
10 | | |
11 | | * Unless required by applicable law or agreed to in writing, software |
12 | | * distributed under the License is distributed on an "AS IS" BASIS, |
13 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | * See the License for the specific language governing permissions and |
15 | | * limitations under the License. |
16 | | */ |
17 | | |
18 | | #ifndef UWS_WEBSOCKETEXTENSIONS_H |
19 | | #define UWS_WEBSOCKETEXTENSIONS_H |
20 | | |
21 | | /* There is a new, huge bug scenario that needs to be fixed: |
22 | | * pub/sub does not support being in DEDICATED_COMPRESSOR-mode while having |
23 | | * some clients downgraded to SHARED_COMPRESSOR - we cannot allow the client to |
24 | | * demand a downgrade to SHARED_COMPRESSOR (yet) until we fix that scenario in pub/sub */ |
25 | | // #define UWS_ALLOW_SHARED_AND_DEDICATED_COMPRESSOR_MIX |
26 | | |
27 | | /* We forbid negotiating 8 windowBits since Zlib has a bug with this */ |
28 | | // #define UWS_ALLOW_8_WINDOW_BITS |
29 | | |
30 | | #include <climits> |
31 | | #include <cctype> |
32 | | #include <string> |
33 | | #include <string_view> |
34 | | #include <tuple> |
35 | | |
36 | | namespace uWS { |
37 | | |
38 | | enum ExtensionTokens { |
39 | | /* Standard permessage-deflate tokens */ |
40 | | TOK_PERMESSAGE_DEFLATE = 1838, |
41 | | TOK_SERVER_NO_CONTEXT_TAKEOVER = 2807, |
42 | | TOK_CLIENT_NO_CONTEXT_TAKEOVER = 2783, |
43 | | TOK_SERVER_MAX_WINDOW_BITS = 2372, |
44 | | TOK_CLIENT_MAX_WINDOW_BITS = 2348, |
45 | | /* Non-standard alias for Safari */ |
46 | | TOK_X_WEBKIT_DEFLATE_FRAME = 2149, |
47 | | TOK_NO_CONTEXT_TAKEOVER = 2049, |
48 | | TOK_MAX_WINDOW_BITS = 1614 |
49 | | |
50 | | }; |
51 | | |
52 | | struct ExtensionsParser { |
53 | | private: |
54 | | int *lastInteger = nullptr; |
55 | | |
56 | | public: |
57 | | /* Standard */ |
58 | | bool perMessageDeflate = false; |
59 | | bool serverNoContextTakeover = false; |
60 | | bool clientNoContextTakeover = false; |
61 | | int serverMaxWindowBits = 0; |
62 | | int clientMaxWindowBits = 0; |
63 | | |
64 | | /* Non-standard Safari */ |
65 | | bool xWebKitDeflateFrame = false; |
66 | | bool noContextTakeover = false; |
67 | | int maxWindowBits = 0; |
68 | | |
69 | 66.1k | int getToken(const char *&in, const char *stop) { |
70 | 142k | while (in != stop && !isalnum(*in)) { |
71 | 76.5k | in++; |
72 | 76.5k | } |
73 | | |
74 | | /* Don't care more than this for now */ |
75 | 66.1k | static_assert(SHRT_MIN > INT_MIN, "Integer overflow fix is invalid for this platform, report this as a bug!"); |
76 | | |
77 | 66.1k | int hashedToken = 0; |
78 | 6.87M | while (in != stop && (isalnum(*in) || *in == '-' || *in == '_')) { |
79 | 6.81M | if (isdigit(*in)) { |
80 | | /* This check is a quick and incorrect fix for integer overflow |
81 | | * in oss-fuzz but we don't care as it doesn't matter either way */ |
82 | 38.8k | if (hashedToken > SHRT_MIN && hashedToken < SHRT_MAX) { |
83 | 35.5k | hashedToken = hashedToken * 10 - (*in - '0'); |
84 | 35.5k | } |
85 | 6.77M | } else { |
86 | 6.77M | hashedToken += *in; |
87 | 6.77M | } |
88 | 6.81M | in++; |
89 | 6.81M | } |
90 | 66.1k | return hashedToken; |
91 | 66.1k | } |
92 | | |
93 | 13.4k | ExtensionsParser(const char *data, size_t length) { |
94 | 13.4k | const char *stop = data + length; |
95 | 13.4k | int token = 1; |
96 | | |
97 | | /* Ignore anything before permessage-deflate or x-webkit-deflate-frame */ |
98 | 36.9k | for (; token && token != TOK_PERMESSAGE_DEFLATE && token != TOK_X_WEBKIT_DEFLATE_FRAME; token = getToken(data, stop)); |
99 | | |
100 | | /* What protocol are we going to use? */ |
101 | 13.4k | perMessageDeflate = (token == TOK_PERMESSAGE_DEFLATE); |
102 | 13.4k | xWebKitDeflateFrame = (token == TOK_X_WEBKIT_DEFLATE_FRAME); |
103 | | |
104 | 42.7k | while ((token = getToken(data, stop))) { |
105 | 32.1k | switch (token) { |
106 | 620 | case TOK_X_WEBKIT_DEFLATE_FRAME: |
107 | | /* Duplicates not allowed/supported */ |
108 | 620 | return; |
109 | 1.43k | case TOK_NO_CONTEXT_TAKEOVER: |
110 | 1.43k | noContextTakeover = true; |
111 | 1.43k | break; |
112 | 1.65k | case TOK_MAX_WINDOW_BITS: |
113 | 1.65k | maxWindowBits = 1; |
114 | 1.65k | lastInteger = &maxWindowBits; |
115 | 1.65k | break; |
116 | 2.35k | case TOK_PERMESSAGE_DEFLATE: |
117 | | /* Duplicates not allowed/supported */ |
118 | 2.35k | return; |
119 | 959 | case TOK_SERVER_NO_CONTEXT_TAKEOVER: |
120 | 959 | serverNoContextTakeover = true; |
121 | 959 | break; |
122 | 851 | case TOK_CLIENT_NO_CONTEXT_TAKEOVER: |
123 | 851 | clientNoContextTakeover = true; |
124 | 851 | break; |
125 | 1.99k | case TOK_SERVER_MAX_WINDOW_BITS: |
126 | 1.99k | serverMaxWindowBits = 1; |
127 | 1.99k | lastInteger = &serverMaxWindowBits; |
128 | 1.99k | break; |
129 | 1.82k | case TOK_CLIENT_MAX_WINDOW_BITS: |
130 | 1.82k | clientMaxWindowBits = 1; |
131 | 1.82k | lastInteger = &clientMaxWindowBits; |
132 | 1.82k | break; |
133 | 20.4k | default: |
134 | 20.4k | if (token < 0 && lastInteger) { |
135 | 4.23k | *lastInteger = -token; |
136 | 4.23k | } |
137 | 20.4k | break; |
138 | 32.1k | } |
139 | 32.1k | } |
140 | 13.4k | } |
141 | | }; |
142 | | |
143 | | /* Takes what we (the server) wants, returns what we got */ |
144 | 14.1k | static inline std::tuple<bool, int, int, std::string_view> negotiateCompression(bool wantCompression, int wantedCompressionWindow, int wantedInflationWindow, std::string_view offer) { |
145 | | |
146 | | /* If we don't want compression then we are done here */ |
147 | 14.1k | if (!wantCompression) { |
148 | 678 | return {false, 0, 0, ""}; |
149 | 678 | } |
150 | | |
151 | 13.4k | ExtensionsParser ep(offer.data(), offer.length()); |
152 | | |
153 | 13.4k | static thread_local std::string response; |
154 | 13.4k | response = ""; |
155 | | |
156 | 13.4k | int compressionWindow = wantedCompressionWindow; |
157 | 13.4k | int inflationWindow = wantedInflationWindow; |
158 | 13.4k | bool compression = false; |
159 | | |
160 | 13.4k | if (ep.xWebKitDeflateFrame) { |
161 | | /* We now have compression */ |
162 | 2.53k | compression = true; |
163 | 2.53k | response = "x-webkit-deflate-frame"; |
164 | | |
165 | | /* If the other peer has DEMANDED us no sliding window, |
166 | | * we cannot compress with anything other than shared compressor */ |
167 | 2.53k | if (ep.noContextTakeover) { |
168 | | /* We must fail here right now (fix pub/sub) */ |
169 | 986 | #ifndef UWS_ALLOW_SHARED_AND_DEDICATED_COMPRESSOR_MIX |
170 | 986 | if (wantedCompressionWindow != 0) { |
171 | 368 | return {false, 0, 0, ""}; |
172 | 368 | } |
173 | 618 | #endif |
174 | | |
175 | 618 | compressionWindow = 0; |
176 | 618 | } |
177 | | |
178 | | /* If the other peer has DEMANDED us to use a limited sliding window, |
179 | | * we have to limit out compression sliding window */ |
180 | 2.16k | if (ep.maxWindowBits && ep.maxWindowBits < compressionWindow) { |
181 | 540 | compressionWindow = ep.maxWindowBits; |
182 | 540 | #ifndef UWS_ALLOW_8_WINDOW_BITS |
183 | | /* We cannot really deny this, so we have to disable compression in this case */ |
184 | 540 | if (compressionWindow == 8) { |
185 | 267 | return {false, 0, 0, ""}; |
186 | 267 | } |
187 | 540 | #endif |
188 | 540 | } |
189 | | |
190 | | /* We decide our own inflation sliding window (and their compression sliding window) */ |
191 | 1.89k | if (wantedInflationWindow < 15) { |
192 | 1.89k | if (!wantedInflationWindow) { |
193 | 1.89k | response += "; no_context_takeover"; |
194 | 1.89k | } else { |
195 | 0 | response += "; max_window_bits=" + std::to_string(wantedInflationWindow); |
196 | 0 | } |
197 | 1.89k | } |
198 | 10.9k | } else if (ep.perMessageDeflate) { |
199 | | /* We now have compression */ |
200 | 4.79k | compression = true; |
201 | 4.79k | response = "permessage-deflate"; |
202 | | |
203 | 4.79k | if (ep.clientNoContextTakeover) { |
204 | 398 | inflationWindow = 0; |
205 | 4.39k | } else if (ep.clientMaxWindowBits && ep.clientMaxWindowBits != 1) { |
206 | 783 | inflationWindow = std::min<int>(ep.clientMaxWindowBits, inflationWindow); |
207 | 783 | } |
208 | | |
209 | | /* Whatever we have now, write */ |
210 | 4.79k | if (inflationWindow < 15) { |
211 | 4.79k | if (!inflationWindow || !ep.clientMaxWindowBits) { |
212 | 4.79k | response += "; client_no_context_takeover"; |
213 | 4.79k | inflationWindow = 0; |
214 | 4.79k | } else { |
215 | 0 | response += "; client_max_window_bits=" + std::to_string(inflationWindow); |
216 | 0 | } |
217 | 4.79k | } |
218 | | |
219 | | /* This block basically lets the client lower it */ |
220 | 4.79k | if (ep.serverNoContextTakeover) { |
221 | | /* This is an important (temporary) fix since we haven't allowed |
222 | | * these two modes to mix, and pub/sub will not handle this case (yet) */ |
223 | | #ifdef UWS_ALLOW_SHARED_AND_DEDICATED_COMPRESSOR_MIX |
224 | | compressionWindow = 0; |
225 | | #endif |
226 | 4.36k | } else if (ep.serverMaxWindowBits) { |
227 | 1.39k | compressionWindow = std::min<int>(ep.serverMaxWindowBits, compressionWindow); |
228 | 1.39k | #ifndef UWS_ALLOW_8_WINDOW_BITS |
229 | | /* Zlib cannot do windowBits=8, memLevel=1 so we raise it up to 9 minimum */ |
230 | 1.39k | if (compressionWindow == 8) { |
231 | 587 | compressionWindow = 9; |
232 | 587 | } |
233 | 1.39k | #endif |
234 | 1.39k | } |
235 | | |
236 | | /* Whatever we have now, write */ |
237 | 4.79k | if (compressionWindow < 15) { |
238 | 4.43k | if (!compressionWindow) { |
239 | 2.73k | response += "; server_no_context_takeover"; |
240 | 2.73k | } else { |
241 | 1.69k | response += "; server_max_window_bits=" + std::to_string(compressionWindow); |
242 | 1.69k | } |
243 | 4.43k | } |
244 | 4.79k | } |
245 | | |
246 | | /* A final sanity check (this check does not actually catch too high values!) */ |
247 | 12.8k | if ((compressionWindow && compressionWindow < 8) || compressionWindow > 15 || (inflationWindow && inflationWindow < 8) || inflationWindow > 15) { |
248 | 323 | return {false, 0, 0, ""}; |
249 | 323 | } |
250 | | |
251 | 12.5k | return {compression, compressionWindow, inflationWindow, response}; |
252 | 12.8k | } Extensions.cpp:uWS::negotiateCompression(bool, int, int, std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 144 | 2.03k | static inline std::tuple<bool, int, int, std::string_view> negotiateCompression(bool wantCompression, int wantedCompressionWindow, int wantedInflationWindow, std::string_view offer) { | 145 | | | 146 | | /* If we don't want compression then we are done here */ | 147 | 2.03k | if (!wantCompression) { | 148 | 678 | return {false, 0, 0, ""}; | 149 | 678 | } | 150 | | | 151 | 1.35k | ExtensionsParser ep(offer.data(), offer.length()); | 152 | | | 153 | 1.35k | static thread_local std::string response; | 154 | 1.35k | response = ""; | 155 | | | 156 | 1.35k | int compressionWindow = wantedCompressionWindow; | 157 | 1.35k | int inflationWindow = wantedInflationWindow; | 158 | 1.35k | bool compression = false; | 159 | | | 160 | 1.35k | if (ep.xWebKitDeflateFrame) { | 161 | | /* We now have compression */ | 162 | 70 | compression = true; | 163 | 70 | response = "x-webkit-deflate-frame"; | 164 | | | 165 | | /* If the other peer has DEMANDED us no sliding window, | 166 | | * we cannot compress with anything other than shared compressor */ | 167 | 70 | if (ep.noContextTakeover) { | 168 | | /* We must fail here right now (fix pub/sub) */ | 169 | 2 | #ifndef UWS_ALLOW_SHARED_AND_DEDICATED_COMPRESSOR_MIX | 170 | 2 | if (wantedCompressionWindow != 0) { | 171 | 1 | return {false, 0, 0, ""}; | 172 | 1 | } | 173 | 1 | #endif | 174 | | | 175 | 1 | compressionWindow = 0; | 176 | 1 | } | 177 | | | 178 | | /* If the other peer has DEMANDED us to use a limited sliding window, | 179 | | * we have to limit out compression sliding window */ | 180 | 69 | if (ep.maxWindowBits && ep.maxWindowBits < compressionWindow) { | 181 | 8 | compressionWindow = ep.maxWindowBits; | 182 | 8 | #ifndef UWS_ALLOW_8_WINDOW_BITS | 183 | | /* We cannot really deny this, so we have to disable compression in this case */ | 184 | 8 | if (compressionWindow == 8) { | 185 | 1 | return {false, 0, 0, ""}; | 186 | 1 | } | 187 | 8 | #endif | 188 | 8 | } | 189 | | | 190 | | /* We decide our own inflation sliding window (and their compression sliding window) */ | 191 | 68 | if (wantedInflationWindow < 15) { | 192 | 68 | if (!wantedInflationWindow) { | 193 | 68 | response += "; no_context_takeover"; | 194 | 68 | } else { | 195 | 0 | response += "; max_window_bits=" + std::to_string(wantedInflationWindow); | 196 | 0 | } | 197 | 68 | } | 198 | 1.28k | } else if (ep.perMessageDeflate) { | 199 | | /* We now have compression */ | 200 | 112 | compression = true; | 201 | 112 | response = "permessage-deflate"; | 202 | | | 203 | 112 | if (ep.clientNoContextTakeover) { | 204 | 2 | inflationWindow = 0; | 205 | 110 | } else if (ep.clientMaxWindowBits && ep.clientMaxWindowBits != 1) { | 206 | 52 | inflationWindow = std::min<int>(ep.clientMaxWindowBits, inflationWindow); | 207 | 52 | } | 208 | | | 209 | | /* Whatever we have now, write */ | 210 | 112 | if (inflationWindow < 15) { | 211 | 112 | if (!inflationWindow || !ep.clientMaxWindowBits) { | 212 | 112 | response += "; client_no_context_takeover"; | 213 | 112 | inflationWindow = 0; | 214 | 112 | } else { | 215 | 0 | response += "; client_max_window_bits=" + std::to_string(inflationWindow); | 216 | 0 | } | 217 | 112 | } | 218 | | | 219 | | /* This block basically lets the client lower it */ | 220 | 112 | if (ep.serverNoContextTakeover) { | 221 | | /* This is an important (temporary) fix since we haven't allowed | 222 | | * these two modes to mix, and pub/sub will not handle this case (yet) */ | 223 | | #ifdef UWS_ALLOW_SHARED_AND_DEDICATED_COMPRESSOR_MIX | 224 | | compressionWindow = 0; | 225 | | #endif | 226 | 110 | } else if (ep.serverMaxWindowBits) { | 227 | 52 | compressionWindow = std::min<int>(ep.serverMaxWindowBits, compressionWindow); | 228 | 52 | #ifndef UWS_ALLOW_8_WINDOW_BITS | 229 | | /* Zlib cannot do windowBits=8, memLevel=1 so we raise it up to 9 minimum */ | 230 | 52 | if (compressionWindow == 8) { | 231 | 1 | compressionWindow = 9; | 232 | 1 | } | 233 | 52 | #endif | 234 | 52 | } | 235 | | | 236 | | /* Whatever we have now, write */ | 237 | 112 | if (compressionWindow < 15) { | 238 | 112 | if (!compressionWindow) { | 239 | 56 | response += "; server_no_context_takeover"; | 240 | 56 | } else { | 241 | 56 | response += "; server_max_window_bits=" + std::to_string(compressionWindow); | 242 | 56 | } | 243 | 112 | } | 244 | 112 | } | 245 | | | 246 | | /* A final sanity check (this check does not actually catch too high values!) */ | 247 | 1.35k | if ((compressionWindow && compressionWindow < 8) || compressionWindow > 15 || (inflationWindow && inflationWindow < 8) || inflationWindow > 15) { | 248 | 8 | return {false, 0, 0, ""}; | 249 | 8 | } | 250 | | | 251 | 1.34k | return {compression, compressionWindow, inflationWindow, response}; | 252 | 1.35k | } |
Unexecuted instantiation: EpollEchoServerPubSub.cpp:uWS::negotiateCompression(bool, int, int, std::__1::basic_string_view<char, std::__1::char_traits<char> >) EpollHelloWorld.cpp:uWS::negotiateCompression(bool, int, int, std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 144 | 6.08k | static inline std::tuple<bool, int, int, std::string_view> negotiateCompression(bool wantCompression, int wantedCompressionWindow, int wantedInflationWindow, std::string_view offer) { | 145 | | | 146 | | /* If we don't want compression then we are done here */ | 147 | 6.08k | if (!wantCompression) { | 148 | 0 | return {false, 0, 0, ""}; | 149 | 0 | } | 150 | | | 151 | 6.08k | ExtensionsParser ep(offer.data(), offer.length()); | 152 | | | 153 | 6.08k | static thread_local std::string response; | 154 | 6.08k | response = ""; | 155 | | | 156 | 6.08k | int compressionWindow = wantedCompressionWindow; | 157 | 6.08k | int inflationWindow = wantedInflationWindow; | 158 | 6.08k | bool compression = false; | 159 | | | 160 | 6.08k | if (ep.xWebKitDeflateFrame) { | 161 | | /* We now have compression */ | 162 | 669 | compression = true; | 163 | 669 | response = "x-webkit-deflate-frame"; | 164 | | | 165 | | /* If the other peer has DEMANDED us no sliding window, | 166 | | * we cannot compress with anything other than shared compressor */ | 167 | 669 | if (ep.noContextTakeover) { | 168 | | /* We must fail here right now (fix pub/sub) */ | 169 | 319 | #ifndef UWS_ALLOW_SHARED_AND_DEDICATED_COMPRESSOR_MIX | 170 | 319 | if (wantedCompressionWindow != 0) { | 171 | 0 | return {false, 0, 0, ""}; | 172 | 0 | } | 173 | 319 | #endif | 174 | | | 175 | 319 | compressionWindow = 0; | 176 | 319 | } | 177 | | | 178 | | /* If the other peer has DEMANDED us to use a limited sliding window, | 179 | | * we have to limit out compression sliding window */ | 180 | 669 | if (ep.maxWindowBits && ep.maxWindowBits < compressionWindow) { | 181 | 0 | compressionWindow = ep.maxWindowBits; | 182 | 0 | #ifndef UWS_ALLOW_8_WINDOW_BITS | 183 | | /* We cannot really deny this, so we have to disable compression in this case */ | 184 | 0 | if (compressionWindow == 8) { | 185 | 0 | return {false, 0, 0, ""}; | 186 | 0 | } | 187 | 0 | #endif | 188 | 0 | } | 189 | | | 190 | | /* We decide our own inflation sliding window (and their compression sliding window) */ | 191 | 669 | if (wantedInflationWindow < 15) { | 192 | 669 | if (!wantedInflationWindow) { | 193 | 669 | response += "; no_context_takeover"; | 194 | 669 | } else { | 195 | 0 | response += "; max_window_bits=" + std::to_string(wantedInflationWindow); | 196 | 0 | } | 197 | 669 | } | 198 | 5.41k | } else if (ep.perMessageDeflate) { | 199 | | /* We now have compression */ | 200 | 1.80k | compression = true; | 201 | 1.80k | response = "permessage-deflate"; | 202 | | | 203 | 1.80k | if (ep.clientNoContextTakeover) { | 204 | 200 | inflationWindow = 0; | 205 | 1.60k | } else if (ep.clientMaxWindowBits && ep.clientMaxWindowBits != 1) { | 206 | 431 | inflationWindow = std::min<int>(ep.clientMaxWindowBits, inflationWindow); | 207 | 431 | } | 208 | | | 209 | | /* Whatever we have now, write */ | 210 | 1.80k | if (inflationWindow < 15) { | 211 | 1.80k | if (!inflationWindow || !ep.clientMaxWindowBits) { | 212 | 1.80k | response += "; client_no_context_takeover"; | 213 | 1.80k | inflationWindow = 0; | 214 | 1.80k | } else { | 215 | 0 | response += "; client_max_window_bits=" + std::to_string(inflationWindow); | 216 | 0 | } | 217 | 1.80k | } | 218 | | | 219 | | /* This block basically lets the client lower it */ | 220 | 1.80k | if (ep.serverNoContextTakeover) { | 221 | | /* This is an important (temporary) fix since we haven't allowed | 222 | | * these two modes to mix, and pub/sub will not handle this case (yet) */ | 223 | | #ifdef UWS_ALLOW_SHARED_AND_DEDICATED_COMPRESSOR_MIX | 224 | | compressionWindow = 0; | 225 | | #endif | 226 | 1.60k | } else if (ep.serverMaxWindowBits) { | 227 | 407 | compressionWindow = std::min<int>(ep.serverMaxWindowBits, compressionWindow); | 228 | 407 | #ifndef UWS_ALLOW_8_WINDOW_BITS | 229 | | /* Zlib cannot do windowBits=8, memLevel=1 so we raise it up to 9 minimum */ | 230 | 407 | if (compressionWindow == 8) { | 231 | 0 | compressionWindow = 9; | 232 | 0 | } | 233 | 407 | #endif | 234 | 407 | } | 235 | | | 236 | | /* Whatever we have now, write */ | 237 | 1.80k | if (compressionWindow < 15) { | 238 | 1.80k | if (!compressionWindow) { | 239 | 1.80k | response += "; server_no_context_takeover"; | 240 | 1.80k | } else { | 241 | 0 | response += "; server_max_window_bits=" + std::to_string(compressionWindow); | 242 | 0 | } | 243 | 1.80k | } | 244 | 1.80k | } | 245 | | | 246 | | /* A final sanity check (this check does not actually catch too high values!) */ | 247 | 6.08k | if ((compressionWindow && compressionWindow < 8) || compressionWindow > 15 || (inflationWindow && inflationWindow < 8) || inflationWindow > 15) { | 248 | 0 | return {false, 0, 0, ""}; | 249 | 0 | } | 250 | | | 251 | 6.08k | return {compression, compressionWindow, inflationWindow, response}; | 252 | 6.08k | } |
EpollEchoServer.cpp:uWS::negotiateCompression(bool, int, int, std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 144 | 6.05k | static inline std::tuple<bool, int, int, std::string_view> negotiateCompression(bool wantCompression, int wantedCompressionWindow, int wantedInflationWindow, std::string_view offer) { | 145 | | | 146 | | /* If we don't want compression then we are done here */ | 147 | 6.05k | if (!wantCompression) { | 148 | 0 | return {false, 0, 0, ""}; | 149 | 0 | } | 150 | | | 151 | 6.05k | ExtensionsParser ep(offer.data(), offer.length()); | 152 | | | 153 | 6.05k | static thread_local std::string response; | 154 | 6.05k | response = ""; | 155 | | | 156 | 6.05k | int compressionWindow = wantedCompressionWindow; | 157 | 6.05k | int inflationWindow = wantedInflationWindow; | 158 | 6.05k | bool compression = false; | 159 | | | 160 | 6.05k | if (ep.xWebKitDeflateFrame) { | 161 | | /* We now have compression */ | 162 | 1.79k | compression = true; | 163 | 1.79k | response = "x-webkit-deflate-frame"; | 164 | | | 165 | | /* If the other peer has DEMANDED us no sliding window, | 166 | | * we cannot compress with anything other than shared compressor */ | 167 | 1.79k | if (ep.noContextTakeover) { | 168 | | /* We must fail here right now (fix pub/sub) */ | 169 | 665 | #ifndef UWS_ALLOW_SHARED_AND_DEDICATED_COMPRESSOR_MIX | 170 | 665 | if (wantedCompressionWindow != 0) { | 171 | 367 | return {false, 0, 0, ""}; | 172 | 367 | } | 173 | 298 | #endif | 174 | | | 175 | 298 | compressionWindow = 0; | 176 | 298 | } | 177 | | | 178 | | /* If the other peer has DEMANDED us to use a limited sliding window, | 179 | | * we have to limit out compression sliding window */ | 180 | 1.42k | if (ep.maxWindowBits && ep.maxWindowBits < compressionWindow) { | 181 | 532 | compressionWindow = ep.maxWindowBits; | 182 | 532 | #ifndef UWS_ALLOW_8_WINDOW_BITS | 183 | | /* We cannot really deny this, so we have to disable compression in this case */ | 184 | 532 | if (compressionWindow == 8) { | 185 | 266 | return {false, 0, 0, ""}; | 186 | 266 | } | 187 | 532 | #endif | 188 | 532 | } | 189 | | | 190 | | /* We decide our own inflation sliding window (and their compression sliding window) */ | 191 | 1.16k | if (wantedInflationWindow < 15) { | 192 | 1.16k | if (!wantedInflationWindow) { | 193 | 1.16k | response += "; no_context_takeover"; | 194 | 1.16k | } else { | 195 | 0 | response += "; max_window_bits=" + std::to_string(wantedInflationWindow); | 196 | 0 | } | 197 | 1.16k | } | 198 | 4.26k | } else if (ep.perMessageDeflate) { | 199 | | /* We now have compression */ | 200 | 2.88k | compression = true; | 201 | 2.88k | response = "permessage-deflate"; | 202 | | | 203 | 2.88k | if (ep.clientNoContextTakeover) { | 204 | 196 | inflationWindow = 0; | 205 | 2.68k | } else if (ep.clientMaxWindowBits && ep.clientMaxWindowBits != 1) { | 206 | 300 | inflationWindow = std::min<int>(ep.clientMaxWindowBits, inflationWindow); | 207 | 300 | } | 208 | | | 209 | | /* Whatever we have now, write */ | 210 | 2.88k | if (inflationWindow < 15) { | 211 | 2.88k | if (!inflationWindow || !ep.clientMaxWindowBits) { | 212 | 2.88k | response += "; client_no_context_takeover"; | 213 | 2.88k | inflationWindow = 0; | 214 | 2.88k | } else { | 215 | 0 | response += "; client_max_window_bits=" + std::to_string(inflationWindow); | 216 | 0 | } | 217 | 2.88k | } | 218 | | | 219 | | /* This block basically lets the client lower it */ | 220 | 2.88k | if (ep.serverNoContextTakeover) { | 221 | | /* This is an important (temporary) fix since we haven't allowed | 222 | | * these two modes to mix, and pub/sub will not handle this case (yet) */ | 223 | | #ifdef UWS_ALLOW_SHARED_AND_DEDICATED_COMPRESSOR_MIX | 224 | | compressionWindow = 0; | 225 | | #endif | 226 | 2.65k | } else if (ep.serverMaxWindowBits) { | 227 | 938 | compressionWindow = std::min<int>(ep.serverMaxWindowBits, compressionWindow); | 228 | 938 | #ifndef UWS_ALLOW_8_WINDOW_BITS | 229 | | /* Zlib cannot do windowBits=8, memLevel=1 so we raise it up to 9 minimum */ | 230 | 938 | if (compressionWindow == 8) { | 231 | 586 | compressionWindow = 9; | 232 | 586 | } | 233 | 938 | #endif | 234 | 938 | } | 235 | | | 236 | | /* Whatever we have now, write */ | 237 | 2.88k | if (compressionWindow < 15) { | 238 | 2.51k | if (!compressionWindow) { | 239 | 881 | response += "; server_no_context_takeover"; | 240 | 1.63k | } else { | 241 | 1.63k | response += "; server_max_window_bits=" + std::to_string(compressionWindow); | 242 | 1.63k | } | 243 | 2.51k | } | 244 | 2.88k | } | 245 | | | 246 | | /* A final sanity check (this check does not actually catch too high values!) */ | 247 | 5.42k | if ((compressionWindow && compressionWindow < 8) || compressionWindow > 15 || (inflationWindow && inflationWindow < 8) || inflationWindow > 15) { | 248 | 315 | return {false, 0, 0, ""}; | 249 | 315 | } | 250 | | | 251 | 5.11k | return {compression, compressionWindow, inflationWindow, response}; | 252 | 5.42k | } |
Unexecuted instantiation: AsyncEpollHelloWorld.cpp:uWS::negotiateCompression(bool, int, int, std::__1::basic_string_view<char, std::__1::char_traits<char> >) |
253 | | |
254 | | } |
255 | | |
256 | | #endif // UWS_WEBSOCKETEXTENSIONS_H |