]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/boost/beast/websocket/detail/impl_base.hpp
import new upstream nautilus stable release 14.2.8
[ceph.git] / ceph / src / boost / boost / beast / websocket / detail / impl_base.hpp
1 //
2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
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 //
7 // Official repository: https://github.com/boostorg/beast
8 //
9
10 #ifndef BOOST_BEAST_WEBSOCKET_DETAIL_IMPL_BASE_HPP
11 #define BOOST_BEAST_WEBSOCKET_DETAIL_IMPL_BASE_HPP
12
13 #include <boost/beast/websocket/option.hpp>
14 #include <boost/beast/websocket/detail/frame.hpp>
15 #include <boost/beast/websocket/detail/pmd_extension.hpp>
16 #include <boost/beast/core/buffer_traits.hpp>
17 #include <boost/beast/core/role.hpp>
18 #include <boost/beast/http/empty_body.hpp>
19 #include <boost/beast/http/message.hpp>
20 #include <boost/beast/http/string_body.hpp>
21 #include <boost/beast/zlib/deflate_stream.hpp>
22 #include <boost/beast/zlib/inflate_stream.hpp>
23 #include <boost/beast/core/buffers_suffix.hpp>
24 #include <boost/beast/core/error.hpp>
25 #include <boost/beast/core/detail/clamp.hpp>
26 #include <boost/asio/buffer.hpp>
27 #include <cstdint>
28 #include <memory>
29 #include <stdexcept>
30
31 namespace boost {
32 namespace beast {
33 namespace websocket {
34 namespace detail {
35
36 //------------------------------------------------------------------------------
37
38 template<bool deflateSupported>
39 struct impl_base;
40
41 template<>
42 struct impl_base<true>
43 {
44 // State information for the permessage-deflate extension
45 struct pmd_type
46 {
47 // `true` if current read message is compressed
48 bool rd_set = false;
49
50 zlib::deflate_stream zo;
51 zlib::inflate_stream zi;
52 };
53
54 std::unique_ptr<pmd_type> pmd_; // pmd settings or nullptr
55 permessage_deflate pmd_opts_; // local pmd options
56 detail::pmd_offer pmd_config_; // offer (client) or negotiation (server)
57
58 // return `true` if current message is deflated
59 bool
60 rd_deflated() const
61 {
62 return pmd_ && pmd_->rd_set;
63 }
64
65 // set whether current message is deflated
66 // returns `false` on protocol violation
67 bool
68 rd_deflated(bool rsv1)
69 {
70 if(pmd_)
71 {
72 pmd_->rd_set = rsv1;
73 return true;
74 }
75 return ! rsv1; // pmd not negotiated
76 }
77
78 // Compress a buffer sequence
79 // Returns: `true` if more calls are needed
80 //
81 template<class ConstBufferSequence>
82 bool
83 deflate(
84 net::mutable_buffer& out,
85 buffers_suffix<ConstBufferSequence>& cb,
86 bool fin,
87 std::size_t& total_in,
88 error_code& ec)
89 {
90 BOOST_ASSERT(out.size() >= 6);
91 auto& zo = this->pmd_->zo;
92 zlib::z_params zs;
93 zs.avail_in = 0;
94 zs.next_in = nullptr;
95 zs.avail_out = out.size();
96 zs.next_out = out.data();
97 for(auto in : beast::buffers_range_ref(cb))
98 {
99 zs.avail_in = in.size();
100 if(zs.avail_in == 0)
101 continue;
102 zs.next_in = in.data();
103 zo.write(zs, zlib::Flush::none, ec);
104 if(ec)
105 {
106 if(ec != zlib::error::need_buffers)
107 return false;
108 BOOST_ASSERT(zs.avail_out == 0);
109 BOOST_ASSERT(zs.total_out == out.size());
110 ec = {};
111 break;
112 }
113 if(zs.avail_out == 0)
114 {
115 BOOST_ASSERT(zs.total_out == out.size());
116 break;
117 }
118 BOOST_ASSERT(zs.avail_in == 0);
119 }
120 total_in = zs.total_in;
121 cb.consume(zs.total_in);
122 if(zs.avail_out > 0 && fin)
123 {
124 auto const remain = buffer_bytes(cb);
125 if(remain == 0)
126 {
127 // Inspired by Mark Adler
128 // https://github.com/madler/zlib/issues/149
129 //
130 // VFALCO We could do this flush twice depending
131 // on how much space is in the output.
132 zo.write(zs, zlib::Flush::block, ec);
133 BOOST_ASSERT(! ec || ec == zlib::error::need_buffers);
134 if(ec == zlib::error::need_buffers)
135 ec = {};
136 if(ec)
137 return false;
138 if(zs.avail_out >= 6)
139 {
140 zo.write(zs, zlib::Flush::full, ec);
141 BOOST_ASSERT(! ec);
142 // remove flush marker
143 zs.total_out -= 4;
144 out = net::buffer(out.data(), zs.total_out);
145 return false;
146 }
147 }
148 }
149 ec = {};
150 out = net::buffer(out.data(), zs.total_out);
151 return true;
152 }
153
154 void
155 do_context_takeover_write(role_type role)
156 {
157 if((role == role_type::client &&
158 this->pmd_config_.client_no_context_takeover) ||
159 (role == role_type::server &&
160 this->pmd_config_.server_no_context_takeover))
161 {
162 this->pmd_->zo.reset();
163 }
164 }
165
166 void
167 inflate(
168 zlib::z_params& zs,
169 zlib::Flush flush,
170 error_code& ec)
171 {
172 pmd_->zi.write(zs, flush, ec);
173 }
174
175 void
176 do_context_takeover_read(role_type role)
177 {
178 if((role == role_type::client &&
179 pmd_config_.server_no_context_takeover) ||
180 (role == role_type::server &&
181 pmd_config_.client_no_context_takeover))
182 {
183 pmd_->zi.clear();
184 }
185 }
186
187 template<class Body, class Allocator>
188 void
189 build_response_pmd(
190 http::response<http::string_body>& res,
191 http::request<Body,
192 http::basic_fields<Allocator>> const& req);
193
194 void
195 on_response_pmd(
196 http::response<http::string_body> const& res)
197 {
198 detail::pmd_offer offer;
199 detail::pmd_read(offer, res);
200 // VFALCO see if offer satisfies pmd_config_,
201 // return an error if not.
202 pmd_config_ = offer; // overwrite for now
203 }
204
205 template<class Allocator>
206 void
207 do_pmd_config(
208 http::basic_fields<Allocator> const& h)
209 {
210 detail::pmd_read(pmd_config_, h);
211 }
212
213 void
214 set_option_pmd(permessage_deflate const& o)
215 {
216 if( o.server_max_window_bits > 15 ||
217 o.server_max_window_bits < 9)
218 BOOST_THROW_EXCEPTION(std::invalid_argument{
219 "invalid server_max_window_bits"});
220 if( o.client_max_window_bits > 15 ||
221 o.client_max_window_bits < 9)
222 BOOST_THROW_EXCEPTION(std::invalid_argument{
223 "invalid client_max_window_bits"});
224 if( o.compLevel < 0 ||
225 o.compLevel > 9)
226 BOOST_THROW_EXCEPTION(std::invalid_argument{
227 "invalid compLevel"});
228 if( o.memLevel < 1 ||
229 o.memLevel > 9)
230 BOOST_THROW_EXCEPTION(std::invalid_argument{
231 "invalid memLevel"});
232 pmd_opts_ = o;
233 }
234
235 void
236 get_option_pmd(permessage_deflate& o)
237 {
238 o = pmd_opts_;
239 }
240
241
242 void
243 build_request_pmd(http::request<http::empty_body>& req)
244 {
245 if(pmd_opts_.client_enable)
246 {
247 detail::pmd_offer config;
248 config.accept = true;
249 config.server_max_window_bits =
250 pmd_opts_.server_max_window_bits;
251 config.client_max_window_bits =
252 pmd_opts_.client_max_window_bits;
253 config.server_no_context_takeover =
254 pmd_opts_.server_no_context_takeover;
255 config.client_no_context_takeover =
256 pmd_opts_.client_no_context_takeover;
257 detail::pmd_write(req, config);
258 }
259 }
260
261 void
262 open_pmd(role_type role)
263 {
264 if(((role == role_type::client &&
265 pmd_opts_.client_enable) ||
266 (role == role_type::server &&
267 pmd_opts_.server_enable)) &&
268 pmd_config_.accept)
269 {
270 detail::pmd_normalize(pmd_config_);
271 pmd_.reset(::new pmd_type);
272 if(role == role_type::client)
273 {
274 pmd_->zi.reset(
275 pmd_config_.server_max_window_bits);
276 pmd_->zo.reset(
277 pmd_opts_.compLevel,
278 pmd_config_.client_max_window_bits,
279 pmd_opts_.memLevel,
280 zlib::Strategy::normal);
281 }
282 else
283 {
284 pmd_->zi.reset(
285 pmd_config_.client_max_window_bits);
286 pmd_->zo.reset(
287 pmd_opts_.compLevel,
288 pmd_config_.server_max_window_bits,
289 pmd_opts_.memLevel,
290 zlib::Strategy::normal);
291 }
292 }
293 }
294
295 void close_pmd()
296 {
297 pmd_.reset();
298 }
299
300 bool pmd_enabled() const
301 {
302 return pmd_ != nullptr;
303 }
304
305 std::size_t
306 read_size_hint_pmd(
307 std::size_t initial_size,
308 bool rd_done,
309 std::uint64_t rd_remain,
310 detail::frame_header const& rd_fh) const
311 {
312 using beast::detail::clamp;
313 std::size_t result;
314 BOOST_ASSERT(initial_size > 0);
315 if(! pmd_ || (! rd_done && ! pmd_->rd_set))
316 {
317 // current message is uncompressed
318
319 if(rd_done)
320 {
321 // first message frame
322 result = initial_size;
323 goto done;
324 }
325 else if(rd_fh.fin)
326 {
327 // last message frame
328 BOOST_ASSERT(rd_remain > 0);
329 result = clamp(rd_remain);
330 goto done;
331 }
332 }
333 result = (std::max)(
334 initial_size, clamp(rd_remain));
335 done:
336 BOOST_ASSERT(result != 0);
337 return result;
338 }
339 };
340
341 //------------------------------------------------------------------------------
342
343 template<>
344 struct impl_base<false>
345 {
346 // These stubs are for avoiding linking in the zlib
347 // code when permessage-deflate is not enabled.
348
349 bool
350 rd_deflated() const
351 {
352 return false;
353 }
354
355 bool
356 rd_deflated(bool rsv1)
357 {
358 return ! rsv1;
359 }
360
361 template<class ConstBufferSequence>
362 bool
363 deflate(
364 net::mutable_buffer&,
365 buffers_suffix<ConstBufferSequence>&,
366 bool,
367 std::size_t&,
368 error_code&)
369 {
370 return false;
371 }
372
373 void
374 do_context_takeover_write(role_type)
375 {
376 }
377
378 void
379 inflate(
380 zlib::z_params&,
381 zlib::Flush,
382 error_code&)
383 {
384 }
385
386 void
387 do_context_takeover_read(role_type)
388 {
389 }
390
391 template<class Body, class Allocator>
392 void
393 build_response_pmd(
394 http::response<http::string_body>&,
395 http::request<Body,
396 http::basic_fields<Allocator>> const&);
397
398 void
399 on_response_pmd(
400 http::response<http::string_body> const&)
401 {
402 }
403
404 template<class Allocator>
405 void
406 do_pmd_config(http::basic_fields<Allocator> const&)
407 {
408 }
409
410 void
411 set_option_pmd(permessage_deflate const& o)
412 {
413 if(o.client_enable || o.server_enable)
414 {
415 // Can't enable permessage-deflate
416 // when deflateSupported == false.
417 //
418 BOOST_THROW_EXCEPTION(std::invalid_argument{
419 "deflateSupported == false"});
420 }
421 }
422
423 void
424 get_option_pmd(permessage_deflate& o)
425 {
426 o = {};
427 o.client_enable = false;
428 o.server_enable = false;
429 }
430
431 void
432 build_request_pmd(
433 http::request<http::empty_body>&)
434 {
435 }
436
437 void open_pmd(role_type)
438 {
439 }
440
441 void close_pmd()
442 {
443 }
444
445 bool pmd_enabled() const
446 {
447 return false;
448 }
449
450 std::size_t
451 read_size_hint_pmd(
452 std::size_t initial_size,
453 bool rd_done,
454 std::uint64_t rd_remain,
455 frame_header const& rd_fh) const
456 {
457 using beast::detail::clamp;
458 std::size_t result;
459 BOOST_ASSERT(initial_size > 0);
460 // compression is not supported
461 if(rd_done)
462 {
463 // first message frame
464 result = initial_size;
465 }
466 else if(rd_fh.fin)
467 {
468 // last message frame
469 BOOST_ASSERT(rd_remain > 0);
470 result = clamp(rd_remain);
471 }
472 else
473 {
474 result = (std::max)(
475 initial_size, clamp(rd_remain));
476 }
477 BOOST_ASSERT(result != 0);
478 return result;
479 }
480 };
481
482 } // detail
483 } // websocket
484 } // beast
485 } // boost
486
487 #endif