Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-16 09:44:03

0001 //
0002 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
0003 //
0004 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0005 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0006 //
0007 // Official repository: https://github.com/boostorg/beast
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     // State information for the permessage-deflate extension
0045     struct pmd_type
0046     {
0047         // `true` if current read message is compressed
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_;           // pmd settings or nullptr
0055     permessage_deflate          pmd_opts_;      // local pmd options
0056     detail::pmd_offer           pmd_config_;    // offer (client) or negotiation (server)
0057 
0058     // return `true` if current message is deflated
0059     bool
0060     rd_deflated() const
0061     {
0062         return pmd_ && pmd_->rd_set;
0063     }
0064 
0065     // set whether current message is deflated
0066     // returns `false` on protocol violation
0067     bool
0068     rd_deflated(bool rsv1)
0069     {
0070         if(pmd_)
0071         {
0072             pmd_->rd_set = rsv1;
0073             return true;
0074         }
0075         return ! rsv1; // pmd not negotiated
0076     }
0077 
0078     // Compress a buffer sequence
0079     // Returns: `true` if more calls are needed
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                 // Inspired by Mark Adler
0128                 // https://github.com/madler/zlib/issues/149
0129                 //
0130                 // VFALCO We could do this flush twice depending
0131                 //        on how much space is in the output.
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                     // remove flush marker
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         // VFALCO see if offer satisfies pmd_config_,
0201         //        return an error if not.
0202         pmd_config_ = offer; // overwrite for now
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             // current message is uncompressed
0324 
0325             if(rd_done)
0326             {
0327                 // first message frame
0328                 result = initial_size;
0329                 goto done;
0330             }
0331             else if(rd_fh.fin)
0332             {
0333                 // last message frame
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         // Ensure offered size does not exceed rd_msg_max
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     // These stubs are for avoiding linking in the zlib
0362     // code when permessage-deflate is not enabled.
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             // Can't enable permessage-deflate
0431             // when deflateSupported == false.
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         // compression is not supported
0482         if(rd_done)
0483         {
0484             // first message frame
0485             result = initial_size;
0486         }
0487         else if(rd_fh.fin)
0488         {
0489             // last message frame
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         // Ensure offered size does not exceed rd_msg_max
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 } // detail
0513 } // websocket
0514 } // beast
0515 } // boost
0516 
0517 #endif