File indexing completed on 2025-12-16 09:44:03
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #ifndef BOOST_BEAST_WEBSOCKET_DETAIL_IMPL_BASE_HPP
0011 #define BOOST_BEAST_WEBSOCKET_DETAIL_IMPL_BASE_HPP
0012
0013 #include <boost/beast/websocket/option.hpp>
0014 #include <boost/beast/websocket/detail/frame.hpp>
0015 #include <boost/beast/websocket/detail/pmd_extension.hpp>
0016 #include <boost/beast/core/buffer_traits.hpp>
0017 #include <boost/beast/core/role.hpp>
0018 #include <boost/beast/http/empty_body.hpp>
0019 #include <boost/beast/http/message.hpp>
0020 #include <boost/beast/http/string_body.hpp>
0021 #include <boost/beast/zlib/deflate_stream.hpp>
0022 #include <boost/beast/zlib/inflate_stream.hpp>
0023 #include <boost/beast/core/buffers_suffix.hpp>
0024 #include <boost/beast/core/error.hpp>
0025 #include <boost/beast/core/detail/clamp.hpp>
0026 #include <boost/asio/buffer.hpp>
0027 #include <cstdint>
0028 #include <memory>
0029 #include <stdexcept>
0030
0031 namespace boost {
0032 namespace beast {
0033 namespace websocket {
0034 namespace detail {
0035
0036
0037
0038 template<bool deflateSupported>
0039 struct impl_base;
0040
0041 template<>
0042 struct impl_base<true>
0043 {
0044
0045 struct pmd_type
0046 {
0047
0048 bool rd_set = false;
0049
0050 zlib::deflate_stream zo;
0051 zlib::inflate_stream zi;
0052 };
0053
0054 std::unique_ptr<pmd_type> pmd_;
0055 permessage_deflate pmd_opts_;
0056 detail::pmd_offer pmd_config_;
0057
0058
0059 bool
0060 rd_deflated() const
0061 {
0062 return pmd_ && pmd_->rd_set;
0063 }
0064
0065
0066
0067 bool
0068 rd_deflated(bool rsv1)
0069 {
0070 if(pmd_)
0071 {
0072 pmd_->rd_set = rsv1;
0073 return true;
0074 }
0075 return ! rsv1;
0076 }
0077
0078
0079
0080
0081 template<class ConstBufferSequence>
0082 bool
0083 deflate(
0084 net::mutable_buffer& out,
0085 buffers_suffix<ConstBufferSequence>& cb,
0086 bool fin,
0087 std::size_t& total_in,
0088 error_code& ec)
0089 {
0090 BOOST_ASSERT(out.size() >= 6);
0091 auto& zo = this->pmd_->zo;
0092 zlib::z_params zs;
0093 zs.avail_in = 0;
0094 zs.next_in = nullptr;
0095 zs.avail_out = out.size();
0096 zs.next_out = out.data();
0097 for(auto in : beast::buffers_range_ref(cb))
0098 {
0099 zs.avail_in = in.size();
0100 if(zs.avail_in == 0)
0101 continue;
0102 zs.next_in = in.data();
0103 zo.write(zs, zlib::Flush::none, ec);
0104 if(ec)
0105 {
0106 if(ec != zlib::error::need_buffers)
0107 return false;
0108 BOOST_ASSERT(zs.avail_out == 0);
0109 BOOST_ASSERT(zs.total_out == out.size());
0110 ec = {};
0111 break;
0112 }
0113 if(zs.avail_out == 0)
0114 {
0115 BOOST_ASSERT(zs.total_out == out.size());
0116 break;
0117 }
0118 BOOST_ASSERT(zs.avail_in == 0);
0119 }
0120 total_in = zs.total_in;
0121 cb.consume(zs.total_in);
0122 if(zs.avail_out > 0 && fin)
0123 {
0124 auto const remain = buffer_bytes(cb);
0125 if(remain == 0)
0126 {
0127
0128
0129
0130
0131
0132 zo.write(zs, zlib::Flush::block, ec);
0133 BOOST_ASSERT(! ec || ec == zlib::error::need_buffers);
0134 if(ec == zlib::error::need_buffers)
0135 ec = {};
0136 if(ec)
0137 return false;
0138 if(zs.avail_out >= 6)
0139 {
0140 zo.write(zs, zlib::Flush::sync, ec);
0141 BOOST_ASSERT(! ec);
0142
0143 zs.total_out -= 4;
0144 out = net::buffer(out.data(), zs.total_out);
0145 return false;
0146 }
0147 }
0148 }
0149 ec = {};
0150 out = net::buffer(out.data(), zs.total_out);
0151 return true;
0152 }
0153
0154 void
0155 do_context_takeover_write(role_type role)
0156 {
0157 if((role == role_type::client &&
0158 this->pmd_config_.client_no_context_takeover) ||
0159 (role == role_type::server &&
0160 this->pmd_config_.server_no_context_takeover))
0161 {
0162 this->pmd_->zo.reset();
0163 }
0164 }
0165
0166 void
0167 inflate(
0168 zlib::z_params& zs,
0169 zlib::Flush flush,
0170 error_code& ec)
0171 {
0172 pmd_->zi.write(zs, flush, ec);
0173 }
0174
0175 void
0176 do_context_takeover_read(role_type role)
0177 {
0178 if((role == role_type::client &&
0179 pmd_config_.server_no_context_takeover) ||
0180 (role == role_type::server &&
0181 pmd_config_.client_no_context_takeover))
0182 {
0183 pmd_->zi.clear();
0184 }
0185 }
0186
0187 template<class Body, class Allocator>
0188 void
0189 build_response_pmd(
0190 http::response<http::string_body>& res,
0191 http::request<Body,
0192 http::basic_fields<Allocator>> const& req);
0193
0194 void
0195 on_response_pmd(
0196 http::response<http::string_body> const& res)
0197 {
0198 detail::pmd_offer offer;
0199 detail::pmd_read(offer, res);
0200
0201
0202 pmd_config_ = offer;
0203 }
0204
0205 template<class Allocator>
0206 void
0207 do_pmd_config(
0208 http::basic_fields<Allocator> const& h)
0209 {
0210 detail::pmd_read(pmd_config_, h);
0211 }
0212
0213 void
0214 set_option_pmd(permessage_deflate const& o)
0215 {
0216 if( o.server_max_window_bits > 15 ||
0217 o.server_max_window_bits < 9)
0218 BOOST_THROW_EXCEPTION(std::invalid_argument{
0219 "invalid server_max_window_bits"});
0220 if( o.client_max_window_bits > 15 ||
0221 o.client_max_window_bits < 9)
0222 BOOST_THROW_EXCEPTION(std::invalid_argument{
0223 "invalid client_max_window_bits"});
0224 if( o.compLevel < 0 ||
0225 o.compLevel > 9)
0226 BOOST_THROW_EXCEPTION(std::invalid_argument{
0227 "invalid compLevel"});
0228 if( o.memLevel < 1 ||
0229 o.memLevel > 9)
0230 BOOST_THROW_EXCEPTION(std::invalid_argument{
0231 "invalid memLevel"});
0232 pmd_opts_ = o;
0233 }
0234
0235 void
0236 get_option_pmd(permessage_deflate& o)
0237 {
0238 o = pmd_opts_;
0239 }
0240
0241
0242 void
0243 build_request_pmd(http::request<http::empty_body>& req)
0244 {
0245 if(pmd_opts_.client_enable)
0246 {
0247 detail::pmd_offer config;
0248 config.accept = true;
0249 config.server_max_window_bits =
0250 pmd_opts_.server_max_window_bits;
0251 config.client_max_window_bits =
0252 pmd_opts_.client_max_window_bits;
0253 config.server_no_context_takeover =
0254 pmd_opts_.server_no_context_takeover;
0255 config.client_no_context_takeover =
0256 pmd_opts_.client_no_context_takeover;
0257 detail::pmd_write(req, config);
0258 }
0259 }
0260
0261 void
0262 open_pmd(role_type role)
0263 {
0264 if(((role == role_type::client &&
0265 pmd_opts_.client_enable) ||
0266 (role == role_type::server &&
0267 pmd_opts_.server_enable)) &&
0268 pmd_config_.accept)
0269 {
0270 detail::pmd_normalize(pmd_config_);
0271 pmd_.reset(::new pmd_type);
0272 if(role == role_type::client)
0273 {
0274 pmd_->zi.reset(
0275 pmd_config_.server_max_window_bits);
0276 pmd_->zo.reset(
0277 pmd_opts_.compLevel,
0278 pmd_config_.client_max_window_bits,
0279 pmd_opts_.memLevel,
0280 zlib::Strategy::normal);
0281 }
0282 else
0283 {
0284 pmd_->zi.reset(
0285 pmd_config_.client_max_window_bits);
0286 pmd_->zo.reset(
0287 pmd_opts_.compLevel,
0288 pmd_config_.server_max_window_bits,
0289 pmd_opts_.memLevel,
0290 zlib::Strategy::normal);
0291 }
0292 }
0293 }
0294
0295 void close_pmd()
0296 {
0297 pmd_.reset();
0298 }
0299
0300 bool pmd_enabled() const
0301 {
0302 return pmd_ != nullptr;
0303 }
0304
0305 bool should_compress(std::size_t n_bytes) const
0306 {
0307 return n_bytes >= pmd_opts_.msg_size_threshold;
0308 }
0309
0310 std::size_t
0311 read_size_hint_pmd(
0312 std::size_t initial_size,
0313 bool rd_done,
0314 std::size_t rd_msg_max,
0315 std::uint64_t rd_remain,
0316 frame_header const& rd_fh) const
0317 {
0318 using beast::detail::clamp;
0319 std::size_t result;
0320 BOOST_ASSERT(initial_size > 0);
0321 if(! pmd_ || (! rd_done && ! pmd_->rd_set))
0322 {
0323
0324
0325 if(rd_done)
0326 {
0327
0328 result = initial_size;
0329 goto done;
0330 }
0331 else if(rd_fh.fin)
0332 {
0333
0334 BOOST_ASSERT(rd_remain > 0);
0335 result = clamp(rd_remain);
0336 goto done;
0337 }
0338 }
0339 result = (std::max)(
0340 initial_size, clamp(rd_remain));
0341 done:
0342 BOOST_ASSERT(result != 0);
0343
0344 if(rd_msg_max)
0345 result = clamp(result, rd_msg_max);
0346 return result;
0347 }
0348
0349 void
0350 get_config_pmd(detail::pmd_offer &pmd)
0351 {
0352 pmd = pmd_config_;
0353 }
0354 };
0355
0356
0357
0358 template<>
0359 struct impl_base<false>
0360 {
0361
0362
0363
0364 bool
0365 rd_deflated() const
0366 {
0367 return false;
0368 }
0369
0370 bool
0371 rd_deflated(bool rsv1)
0372 {
0373 return ! rsv1;
0374 }
0375
0376 template<class ConstBufferSequence>
0377 bool
0378 deflate(
0379 net::mutable_buffer&,
0380 buffers_suffix<ConstBufferSequence>&,
0381 bool,
0382 std::size_t&,
0383 error_code&)
0384 {
0385 return false;
0386 }
0387
0388 void
0389 do_context_takeover_write(role_type)
0390 {
0391 }
0392
0393 void
0394 inflate(
0395 zlib::z_params&,
0396 zlib::Flush,
0397 error_code&)
0398 {
0399 }
0400
0401 void
0402 do_context_takeover_read(role_type)
0403 {
0404 }
0405
0406 template<class Body, class Allocator>
0407 void
0408 build_response_pmd(
0409 http::response<http::string_body>&,
0410 http::request<Body,
0411 http::basic_fields<Allocator>> const&);
0412
0413 void
0414 on_response_pmd(
0415 http::response<http::string_body> const&)
0416 {
0417 }
0418
0419 template<class Allocator>
0420 void
0421 do_pmd_config(http::basic_fields<Allocator> const&)
0422 {
0423 }
0424
0425 void
0426 set_option_pmd(permessage_deflate const& o)
0427 {
0428 if(o.client_enable || o.server_enable)
0429 {
0430
0431
0432
0433 BOOST_THROW_EXCEPTION(std::invalid_argument{
0434 "deflateSupported == false"});
0435 }
0436 }
0437
0438 void
0439 get_option_pmd(permessage_deflate& o)
0440 {
0441 o = {};
0442 o.client_enable = false;
0443 o.server_enable = false;
0444 }
0445
0446 void
0447 build_request_pmd(
0448 http::request<http::empty_body>&)
0449 {
0450 }
0451
0452 void open_pmd(role_type)
0453 {
0454 }
0455
0456 void close_pmd()
0457 {
0458 }
0459
0460 bool pmd_enabled() const
0461 {
0462 return false;
0463 }
0464
0465 bool should_compress(std::size_t) const
0466 {
0467 return false;
0468 }
0469
0470 std::size_t
0471 read_size_hint_pmd(
0472 std::size_t initial_size,
0473 bool rd_done,
0474 std::size_t rd_msg_max,
0475 std::uint64_t rd_remain,
0476 frame_header const& rd_fh) const
0477 {
0478 using beast::detail::clamp;
0479 std::size_t result;
0480 BOOST_ASSERT(initial_size > 0);
0481
0482 if(rd_done)
0483 {
0484
0485 result = initial_size;
0486 }
0487 else if(rd_fh.fin)
0488 {
0489
0490 BOOST_ASSERT(rd_remain > 0);
0491 result = clamp(rd_remain);
0492 }
0493 else
0494 {
0495 result = (std::max)(
0496 initial_size, clamp(rd_remain));
0497 }
0498 BOOST_ASSERT(result != 0);
0499
0500 if(rd_msg_max)
0501 result = clamp(result, rd_msg_max);
0502 return result;
0503 }
0504
0505 void
0506 get_config_pmd(detail::pmd_offer &pmd)
0507 {
0508 pmd = {};
0509 }
0510 };
0511
0512 }
0513 }
0514 }
0515 }
0516
0517 #endif