]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/boost/beast/websocket/detail/pmd_extension.hpp
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / boost / boost / beast / websocket / detail / pmd_extension.hpp
CommitLineData
7c673cae 1//
b32b8144 2// Copyright (c) 2016-2017 Vinnie Falco (vinnie dot falco at gmail dot com)
7c673cae
FG
3//
4// Distributed under the Boost Software License, Version 1.0. (See accompanying
5// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6//
b32b8144
FG
7// Official repository: https://github.com/boostorg/beast
8//
7c673cae 9
b32b8144
FG
10#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_HPP
11#define BOOST_BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_HPP
7c673cae 12
b32b8144
FG
13#include <boost/beast/core/error.hpp>
14#include <boost/beast/core/buffers_suffix.hpp>
15#include <boost/beast/core/read_size.hpp>
16#include <boost/beast/zlib/deflate_stream.hpp>
17#include <boost/beast/zlib/inflate_stream.hpp>
18#include <boost/beast/websocket/option.hpp>
19#include <boost/beast/http/rfc7230.hpp>
7c673cae
FG
20#include <boost/asio/buffer.hpp>
21#include <utility>
11fdf7f2 22#include <type_traits>
7c673cae 23
b32b8144 24namespace boost {
7c673cae
FG
25namespace beast {
26namespace websocket {
27namespace detail {
28
29// permessage-deflate offer parameters
30//
31// "context takeover" means:
32// preserve sliding window across messages
33//
34struct pmd_offer
35{
36 bool accept;
37
38 // 0 = absent, or 8..15
39 int server_max_window_bits;
40
41 // -1 = present, 0 = absent, or 8..15
42 int client_max_window_bits;
43
44 // `true` if server_no_context_takeover offered
45 bool server_no_context_takeover;
46
47 // `true` if client_no_context_takeover offered
48 bool client_no_context_takeover;
49};
50
51template<class = void>
52int
b32b8144 53parse_bits(string_view s)
7c673cae
FG
54{
55 if(s.size() == 0)
56 return -1;
57 if(s.size() > 2)
58 return -1;
59 if(s[0] < '1' || s[0] > '9')
60 return -1;
b32b8144 61 unsigned i = 0;
7c673cae
FG
62 for(auto c : s)
63 {
64 if(c < '0' || c > '9')
65 return -1;
b32b8144 66 auto const i0 = i;
7c673cae 67 i = 10 * i + (c - '0');
b32b8144
FG
68 if(i < i0)
69 return -1;
7c673cae 70 }
b32b8144 71 return static_cast<int>(i);
7c673cae
FG
72}
73
74// Parse permessage-deflate request fields
75//
b32b8144 76template<class Allocator>
7c673cae 77void
b32b8144
FG
78pmd_read(pmd_offer& offer,
79 http::basic_fields<Allocator> const& fields)
7c673cae
FG
80{
81 offer.accept = false;
82 offer.server_max_window_bits= 0;
83 offer.client_max_window_bits = 0;
84 offer.server_no_context_takeover = false;
85 offer.client_no_context_takeover = false;
86
7c673cae
FG
87 http::ext_list list{
88 fields["Sec-WebSocket-Extensions"]};
89 for(auto const& ext : list)
90 {
b32b8144 91 if(iequals(ext.first, "permessage-deflate"))
7c673cae
FG
92 {
93 for(auto const& param : ext.second)
94 {
b32b8144 95 if(iequals(param.first,
7c673cae
FG
96 "server_max_window_bits"))
97 {
98 if(offer.server_max_window_bits != 0)
99 {
100 // The negotiation offer contains multiple
101 // extension parameters with the same name.
102 //
103 return; // MUST decline
104 }
105 if(param.second.empty())
106 {
107 // The negotiation offer extension
108 // parameter is missing the value.
109 //
110 return; // MUST decline
111 }
112 offer.server_max_window_bits =
113 parse_bits(param.second);
114 if( offer.server_max_window_bits < 8 ||
115 offer.server_max_window_bits > 15)
116 {
117 // The negotiation offer contains an
118 // extension parameter with an invalid value.
119 //
120 return; // MUST decline
121 }
122 }
b32b8144 123 else if(iequals(param.first,
7c673cae
FG
124 "client_max_window_bits"))
125 {
126 if(offer.client_max_window_bits != 0)
127 {
128 // The negotiation offer contains multiple
129 // extension parameters with the same name.
130 //
131 return; // MUST decline
132 }
133 if(! param.second.empty())
134 {
135 offer.client_max_window_bits =
136 parse_bits(param.second);
137 if( offer.client_max_window_bits < 8 ||
138 offer.client_max_window_bits > 15)
139 {
140 // The negotiation offer contains an
141 // extension parameter with an invalid value.
142 //
143 return; // MUST decline
144 }
145 }
146 else
147 {
148 offer.client_max_window_bits = -1;
149 }
150 }
b32b8144 151 else if(iequals(param.first,
7c673cae
FG
152 "server_no_context_takeover"))
153 {
154 if(offer.server_no_context_takeover)
155 {
156 // The negotiation offer contains multiple
157 // extension parameters with the same name.
158 //
159 return; // MUST decline
160 }
161 if(! param.second.empty())
162 {
163 // The negotiation offer contains an
164 // extension parameter with an invalid value.
165 //
166 return; // MUST decline
167 }
168 offer.server_no_context_takeover = true;
169 }
b32b8144 170 else if(iequals(param.first,
7c673cae
FG
171 "client_no_context_takeover"))
172 {
173 if(offer.client_no_context_takeover)
174 {
175 // The negotiation offer contains multiple
176 // extension parameters with the same name.
177 //
178 return; // MUST decline
179 }
180 if(! param.second.empty())
181 {
182 // The negotiation offer contains an
183 // extension parameter with an invalid value.
184 //
185 return; // MUST decline
186 }
187 offer.client_no_context_takeover = true;
188 }
189 else
190 {
191 // The negotiation offer contains an extension
192 // parameter not defined for use in an offer.
193 //
194 return; // MUST decline
195 }
196 }
197 offer.accept = true;
198 return;
199 }
200 }
201}
202
203// Set permessage-deflate fields for a client offer
204//
b32b8144 205template<class Allocator>
7c673cae 206void
b32b8144
FG
207pmd_write(http::basic_fields<Allocator>& fields,
208 pmd_offer const& offer)
7c673cae 209{
b32b8144 210 static_string<512> s;
7c673cae
FG
211 s = "permessage-deflate";
212 if(offer.server_max_window_bits != 0)
213 {
214 if(offer.server_max_window_bits != -1)
215 {
216 s += "; server_max_window_bits=";
b32b8144 217 s += to_static_string(
7c673cae
FG
218 offer.server_max_window_bits);
219 }
220 else
221 {
222 s += "; server_max_window_bits";
223 }
224 }
225 if(offer.client_max_window_bits != 0)
226 {
227 if(offer.client_max_window_bits != -1)
228 {
229 s += "; client_max_window_bits=";
b32b8144 230 s += to_static_string(
7c673cae
FG
231 offer.client_max_window_bits);
232 }
233 else
234 {
235 s += "; client_max_window_bits";
236 }
237 }
238 if(offer.server_no_context_takeover)
239 {
240 s += "; server_no_context_takeover";
241 }
242 if(offer.client_no_context_takeover)
243 {
244 s += "; client_no_context_takeover";
245 }
b32b8144 246 fields.set(http::field::sec_websocket_extensions, s);
7c673cae
FG
247}
248
249// Negotiate a permessage-deflate client offer
250//
b32b8144 251template<class Allocator>
7c673cae
FG
252void
253pmd_negotiate(
b32b8144 254 http::basic_fields<Allocator>& fields,
7c673cae
FG
255 pmd_offer& config,
256 pmd_offer const& offer,
257 permessage_deflate const& o)
258{
259 if(! (offer.accept && o.server_enable))
260 {
261 config.accept = false;
262 return;
263 }
264 config.accept = true;
265
b32b8144 266 static_string<512> s = "permessage-deflate";
7c673cae
FG
267
268 config.server_no_context_takeover =
269 offer.server_no_context_takeover ||
270 o.server_no_context_takeover;
271 if(config.server_no_context_takeover)
272 s += "; server_no_context_takeover";
273
274 config.client_no_context_takeover =
275 o.client_no_context_takeover ||
276 offer.client_no_context_takeover;
277 if(config.client_no_context_takeover)
278 s += "; client_no_context_takeover";
279
280 if(offer.server_max_window_bits != 0)
b32b8144 281 config.server_max_window_bits = (std::min)(
7c673cae
FG
282 offer.server_max_window_bits,
283 o.server_max_window_bits);
284 else
285 config.server_max_window_bits =
286 o.server_max_window_bits;
287 if(config.server_max_window_bits < 15)
288 {
289 // ZLib's deflateInit silently treats 8 as
290 // 9 due to a bug, so prevent 8 from being used.
291 //
292 if(config.server_max_window_bits < 9)
293 config.server_max_window_bits = 9;
294
295 s += "; server_max_window_bits=";
b32b8144 296 s += to_static_string(
7c673cae
FG
297 config.server_max_window_bits);
298 }
299
300 switch(offer.client_max_window_bits)
301 {
302 case -1:
303 // extension parameter is present with no value
304 config.client_max_window_bits =
305 o.client_max_window_bits;
306 if(config.client_max_window_bits < 15)
307 {
308 s += "; client_max_window_bits=";
b32b8144 309 s += to_static_string(
7c673cae
FG
310 config.client_max_window_bits);
311 }
312 break;
313
314 case 0:
315 /* extension parameter is absent.
316
317 If a received extension negotiation offer doesn't have the
318 "client_max_window_bits" extension parameter, the corresponding
319 extension negotiation response to the offer MUST NOT include the
320 "client_max_window_bits" extension parameter.
321 */
322 if(o.client_max_window_bits == 15)
323 config.client_max_window_bits = 15;
324 else
325 config.accept = false;
326 break;
327
328 default:
329 // extension parameter has value in [8..15]
b32b8144 330 config.client_max_window_bits = (std::min)(
7c673cae
FG
331 o.client_max_window_bits,
332 offer.client_max_window_bits);
333 s += "; client_max_window_bits=";
b32b8144 334 s += to_static_string(
7c673cae
FG
335 config.client_max_window_bits);
336 break;
337 }
338 if(config.accept)
b32b8144 339 fields.set(http::field::sec_websocket_extensions, s);
7c673cae
FG
340}
341
342// Normalize the server's response
343//
344inline
345void
346pmd_normalize(pmd_offer& offer)
347{
348 if(offer.accept)
349 {
350 if( offer.server_max_window_bits == 0)
351 offer.server_max_window_bits = 15;
352
353 if( offer.client_max_window_bits == 0 ||
354 offer.client_max_window_bits == -1)
355 offer.client_max_window_bits = 15;
356 }
357}
358
7c673cae
FG
359} // detail
360} // websocket
361} // beast
b32b8144 362} // boost
7c673cae
FG
363
364#endif