enabled.hpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. /*
  2. * Copyright (c) 2014, Peter Thorson. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions are met:
  6. * * Redistributions of source code must retain the above copyright
  7. * notice, this list of conditions and the following disclaimer.
  8. * * Redistributions in binary form must reproduce the above copyright
  9. * notice, this list of conditions and the following disclaimer in the
  10. * documentation and/or other materials provided with the distribution.
  11. * * Neither the name of the WebSocket++ Project nor the
  12. * names of its contributors may be used to endorse or promote products
  13. * derived from this software without specific prior written permission.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  18. * ARE DISCLAIMED. IN NO EVENT SHALL PETER THORSON BE LIABLE FOR ANY
  19. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  20. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  21. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  22. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  24. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. *
  26. */
  27. #ifndef WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
  28. #define WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP
  29. #include <websocketpp/common/cpp11.hpp>
  30. #include <websocketpp/common/memory.hpp>
  31. #include <websocketpp/common/platforms.hpp>
  32. #include <websocketpp/common/system_error.hpp>
  33. #include <websocketpp/error.hpp>
  34. #include <websocketpp/extensions/extension.hpp>
  35. #include "zlib.h"
  36. #include <algorithm>
  37. #include <string>
  38. #include <vector>
  39. namespace websocketpp {
  40. namespace extensions {
  41. /// Implementation of the draft permessage-deflate WebSocket extension
  42. /**
  43. * ### permessage-deflate interface
  44. *
  45. * **is_implemented**\n
  46. * `bool is_implemented()`\n
  47. * Returns whether or not the object implements the extension or not
  48. *
  49. * **is_enabled**\n
  50. * `bool is_enabled()`\n
  51. * Returns whether or not the extension was negotiated for the current
  52. * connection
  53. *
  54. * **generate_offer**\n
  55. * `std::string generate_offer() const`\n
  56. * Create an extension offer string based on local policy
  57. *
  58. * **validate_response**\n
  59. * `lib::error_code validate_response(http::attribute_list const & response)`\n
  60. * Negotiate the parameters of extension use
  61. *
  62. * **negotiate**\n
  63. * `err_str_pair negotiate(http::attribute_list const & attributes)`\n
  64. * Negotiate the parameters of extension use
  65. *
  66. * **compress**\n
  67. * `lib::error_code compress(std::string const & in, std::string & out)`\n
  68. * Compress the bytes in `in` and append them to `out`
  69. *
  70. * **decompress**\n
  71. * `lib::error_code decompress(uint8_t const * buf, size_t len, std::string &
  72. * out)`\n
  73. * Decompress `len` bytes from `buf` and append them to string `out`
  74. */
  75. namespace permessage_deflate {
  76. /// Permessage deflate error values
  77. namespace error {
  78. enum value {
  79. /// Catch all
  80. general = 1,
  81. /// Invalid extension attributes
  82. invalid_attributes,
  83. /// Invalid extension attribute value
  84. invalid_attribute_value,
  85. /// Invalid megotiation mode
  86. invalid_mode,
  87. /// Unsupported extension attributes
  88. unsupported_attributes,
  89. /// Invalid value for max_window_bits
  90. invalid_max_window_bits,
  91. /// ZLib Error
  92. zlib_error,
  93. /// Uninitialized
  94. uninitialized,
  95. };
  96. /// Permessage-deflate error category
  97. class category : public lib::error_category {
  98. public:
  99. category() {}
  100. char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
  101. return "websocketpp.extension.permessage-deflate";
  102. }
  103. std::string message(int value) const {
  104. switch(value) {
  105. case general:
  106. return "Generic permessage-compress error";
  107. case invalid_attributes:
  108. return "Invalid extension attributes";
  109. case invalid_attribute_value:
  110. return "Invalid extension attribute value";
  111. case invalid_mode:
  112. return "Invalid permessage-deflate negotiation mode";
  113. case unsupported_attributes:
  114. return "Unsupported extension attributes";
  115. case invalid_max_window_bits:
  116. return "Invalid value for max_window_bits";
  117. case zlib_error:
  118. return "A zlib function returned an error";
  119. case uninitialized:
  120. return "Object must be initialized before use";
  121. default:
  122. return "Unknown permessage-compress error";
  123. }
  124. }
  125. };
  126. /// Get a reference to a static copy of the permessage-deflate error category
  127. lib::error_category const & get_category() {
  128. static category instance;
  129. return instance;
  130. }
  131. /// Create an error code in the permessage-deflate category
  132. lib::error_code make_error_code(error::value e) {
  133. return lib::error_code(static_cast<int>(e), get_category());
  134. }
  135. } // namespace error
  136. } // namespace permessage_deflate
  137. } // namespace extensions
  138. } // namespace websocketpp
  139. _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
  140. template<> struct is_error_code_enum
  141. <websocketpp::extensions::permessage_deflate::error::value>
  142. {
  143. static bool const value = true;
  144. };
  145. _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
  146. namespace websocketpp {
  147. namespace extensions {
  148. namespace permessage_deflate {
  149. /// Default value for s2c_max_window_bits as defined by RFC6455
  150. static uint8_t const default_s2c_max_window_bits = 15;
  151. /// Minimum value for s2c_max_window_bits as defined by RFC6455
  152. static uint8_t const min_s2c_max_window_bits = 8;
  153. /// Maximum value for s2c_max_window_bits as defined by RFC6455
  154. static uint8_t const max_s2c_max_window_bits = 15;
  155. /// Default value for c2s_max_window_bits as defined by RFC6455
  156. static uint8_t const default_c2s_max_window_bits = 15;
  157. /// Minimum value for c2s_max_window_bits as defined by RFC6455
  158. static uint8_t const min_c2s_max_window_bits = 8;
  159. /// Maximum value for c2s_max_window_bits as defined by RFC6455
  160. static uint8_t const max_c2s_max_window_bits = 15;
  161. namespace mode {
  162. enum value {
  163. /// Accept any value the remote endpoint offers
  164. accept = 1,
  165. /// Decline any value the remote endpoint offers. Insist on defaults.
  166. decline,
  167. /// Use the largest value common to both offers
  168. largest,
  169. /// Use the smallest value common to both offers
  170. smallest
  171. };
  172. } // namespace mode
  173. template <typename config>
  174. class enabled {
  175. public:
  176. enabled()
  177. : m_enabled(false)
  178. , m_s2c_no_context_takeover(false)
  179. , m_c2s_no_context_takeover(false)
  180. , m_s2c_max_window_bits(15)
  181. , m_c2s_max_window_bits(15)
  182. , m_s2c_max_window_bits_mode(mode::accept)
  183. , m_c2s_max_window_bits_mode(mode::accept)
  184. , m_initialized(false)
  185. , m_compress_buffer_size(16384)
  186. {
  187. m_dstate.zalloc = Z_NULL;
  188. m_dstate.zfree = Z_NULL;
  189. m_dstate.opaque = Z_NULL;
  190. m_istate.zalloc = Z_NULL;
  191. m_istate.zfree = Z_NULL;
  192. m_istate.opaque = Z_NULL;
  193. m_istate.avail_in = 0;
  194. m_istate.next_in = Z_NULL;
  195. }
  196. ~enabled() {
  197. if (!m_initialized) {
  198. return;
  199. }
  200. int ret = deflateEnd(&m_dstate);
  201. if (ret != Z_OK) {
  202. //std::cout << "error cleaning up zlib compression state"
  203. // << std::endl;
  204. }
  205. ret = inflateEnd(&m_istate);
  206. if (ret != Z_OK) {
  207. //std::cout << "error cleaning up zlib decompression state"
  208. // << std::endl;
  209. }
  210. }
  211. /// Initialize zlib state
  212. /**
  213. *
  214. * @todo memory level, strategy, etc are hardcoded
  215. * @todo server detection is hardcoded
  216. */
  217. lib::error_code init() {
  218. uint8_t deflate_bits;
  219. uint8_t inflate_bits;
  220. if (true /*is_server*/) {
  221. deflate_bits = m_s2c_max_window_bits;
  222. inflate_bits = m_c2s_max_window_bits;
  223. } else {
  224. deflate_bits = m_c2s_max_window_bits;
  225. inflate_bits = m_s2c_max_window_bits;
  226. }
  227. int ret = deflateInit2(
  228. &m_dstate,
  229. Z_DEFAULT_COMPRESSION,
  230. Z_DEFLATED,
  231. -1*deflate_bits,
  232. 8, // memory level 1-9
  233. /*Z_DEFAULT_STRATEGY*/Z_FIXED
  234. );
  235. if (ret != Z_OK) {
  236. return make_error_code(error::zlib_error);
  237. }
  238. ret = inflateInit2(
  239. &m_istate,
  240. -1*inflate_bits
  241. );
  242. if (ret != Z_OK) {
  243. return make_error_code(error::zlib_error);
  244. }
  245. m_compress_buffer.reset(new unsigned char[m_compress_buffer_size]);
  246. m_initialized = true;
  247. return lib::error_code();
  248. }
  249. /// Test if this object implements the permessage-deflate specification
  250. /**
  251. * Because this object does implieent it, it will always return true.
  252. *
  253. * @return Whether or not this object implements permessage-deflate
  254. */
  255. bool is_implemented() const {
  256. return true;
  257. }
  258. /// Test if the extension was negotiated for this connection
  259. /**
  260. * Retrieves whether or not this extension is in use based on the initial
  261. * handshake extension negotiations.
  262. *
  263. * @return Whether or not the extension is in use
  264. */
  265. bool is_enabled() const {
  266. return m_enabled;
  267. }
  268. /// Reset server's outgoing LZ77 sliding window for each new message
  269. /**
  270. * Enabling this setting will cause the server's compressor to reset the
  271. * compression state (the LZ77 sliding window) for every message. This
  272. * means that the compressor will not look back to patterns in previous
  273. * messages to improve compression. This will reduce the compression
  274. * efficiency for large messages somewhat and small messages drastically.
  275. *
  276. * This option may reduce server compressor memory usage and client
  277. * decompressor memory usage.
  278. * @todo Document to what extent memory usage will be reduced
  279. *
  280. * For clients, this option is dependent on server support. Enabling it
  281. * via this method does not guarantee that it will be successfully
  282. * negotiated, only that it will be requested.
  283. *
  284. * For servers, no client support is required. Enabling this option on a
  285. * server will result in its use. The server will signal to clients that
  286. * the option will be in use so they can optimize resource usage if they
  287. * are able.
  288. */
  289. void enable_s2c_no_context_takeover() {
  290. m_s2c_no_context_takeover = true;
  291. }
  292. /// Reset client's outgoing LZ77 sliding window for each new message
  293. /**
  294. * Enabling this setting will cause the client's compressor to reset the
  295. * compression state (the LZ77 sliding window) for every message. This
  296. * means that the compressor will not look back to patterns in previous
  297. * messages to improve compression. This will reduce the compression
  298. * efficiency for large messages somewhat and small messages drastically.
  299. *
  300. * This option may reduce client compressor memory usage and server
  301. * decompressor memory usage.
  302. * @todo Document to what extent memory usage will be reduced
  303. *
  304. * This option is supported by all compliant clients and servers. Enabling
  305. * it via either endpoint should be sufficient to ensure it is used.
  306. */
  307. void enable_c2s_no_context_takeover() {
  308. m_c2s_no_context_takeover = true;
  309. }
  310. /// Limit server LZ77 sliding window size
  311. /**
  312. * The bits setting is the base 2 logarithm of the maximum window size that
  313. * the server must use to compress outgoing messages. The permitted range
  314. * is 8 to 15 inclusive. 8 represents a 256 byte window and 15 a 32KiB
  315. * window. The default setting is 15.
  316. *
  317. * Mode Options:
  318. * - accept: Accept whatever the remote endpoint offers.
  319. * - decline: Decline any offers to deviate from the defaults
  320. * - largest: Accept largest window size acceptable to both endpoints
  321. * - smallest: Accept smallest window size acceptiable to both endpoints
  322. *
  323. * This setting is dependent on server support. A client requesting this
  324. * setting may be rejected by the server or have the exact value used
  325. * adjusted by the server. A server may unilaterally set this value without
  326. * client support.
  327. *
  328. * @param bits The size to request for the outgoing window size
  329. * @param mode The mode to use for negotiating this parameter
  330. * @return A status code
  331. */
  332. lib::error_code set_s2c_max_window_bits(uint8_t bits, mode::value mode) {
  333. if (bits < min_s2c_max_window_bits || bits > max_s2c_max_window_bits) {
  334. return error::make_error_code(error::invalid_max_window_bits);
  335. }
  336. m_s2c_max_window_bits = bits;
  337. m_s2c_max_window_bits_mode = mode;
  338. return lib::error_code();
  339. }
  340. /// Limit client LZ77 sliding window size
  341. /**
  342. * The bits setting is the base 2 logarithm of the window size that the
  343. * client must use to compress outgoing messages. The permitted range is 8
  344. * to 15 inclusive. 8 represents a 256 byte window and 15 a 32KiB window.
  345. * The default setting is 15.
  346. *
  347. * Mode Options:
  348. * - accept: Accept whatever the remote endpoint offers.
  349. * - decline: Decline any offers to deviate from the defaults
  350. * - largest: Accept largest window size acceptable to both endpoints
  351. * - smallest: Accept smallest window size acceptiable to both endpoints
  352. *
  353. * This setting is dependent on client support. A client may limit its own
  354. * outgoing window size unilaterally. A server may only limit the client's
  355. * window size if the remote client supports that feature.
  356. *
  357. * @param bits The size to request for the outgoing window size
  358. * @param mode The mode to use for negotiating this parameter
  359. * @return A status code
  360. */
  361. lib::error_code set_c2s_max_window_bits(uint8_t bits, mode::value mode) {
  362. if (bits < min_c2s_max_window_bits || bits > max_c2s_max_window_bits) {
  363. return error::make_error_code(error::invalid_max_window_bits);
  364. }
  365. m_c2s_max_window_bits = bits;
  366. m_c2s_max_window_bits_mode = mode;
  367. return lib::error_code();
  368. }
  369. /// Generate extension offer
  370. /**
  371. * Creates an offer string to include in the Sec-WebSocket-Extensions
  372. * header of outgoing client requests.
  373. *
  374. * @return A WebSocket extension offer string for this extension
  375. */
  376. std::string generate_offer() const {
  377. return "";
  378. }
  379. /// Validate extension response
  380. /**
  381. * Confirm that the server has negotiated settings compatible with our
  382. * original offer and apply those settings to the extension state.
  383. *
  384. * @param response The server response attribute list to validate
  385. * @return Validation error or 0 on success
  386. */
  387. lib::error_code validate_offer(http::attribute_list const &) {
  388. return make_error_code(error::general);
  389. }
  390. /// Negotiate extension
  391. /**
  392. * Confirm that the client's extension negotiation offer has settings
  393. * compatible with local policy. If so, generate a reply and apply those
  394. * settings to the extension state.
  395. *
  396. * @param offer Attribute from client's offer
  397. * @return Status code and value to return to remote endpoint
  398. */
  399. err_str_pair negotiate(http::attribute_list const & offer) {
  400. err_str_pair ret;
  401. http::attribute_list::const_iterator it;
  402. for (it = offer.begin(); it != offer.end(); ++it) {
  403. if (it->first == "s2c_no_context_takeover") {
  404. negotiate_s2c_no_context_takeover(it->second,ret.first);
  405. } else if (it->first == "c2s_no_context_takeover") {
  406. negotiate_c2s_no_context_takeover(it->second,ret.first);
  407. } else if (it->first == "s2c_max_window_bits") {
  408. negotiate_s2c_max_window_bits(it->second,ret.first);
  409. } else if (it->first == "c2s_max_window_bits") {
  410. negotiate_c2s_max_window_bits(it->second,ret.first);
  411. } else {
  412. ret.first = make_error_code(error::invalid_attributes);
  413. }
  414. if (ret.first) {
  415. break;
  416. }
  417. }
  418. if (ret.first == lib::error_code()) {
  419. m_enabled = true;
  420. ret.second = generate_response();
  421. }
  422. return ret;
  423. }
  424. /// Compress bytes
  425. /**
  426. * @param [in] in String to compress
  427. * @param [out] out String to append compressed bytes to
  428. * @return Error or status code
  429. */
  430. lib::error_code compress(std::string const & in, std::string & out) {
  431. if (!m_initialized) {
  432. return make_error_code(error::uninitialized);
  433. }
  434. size_t output;
  435. m_dstate.avail_out = m_compress_buffer_size;
  436. m_dstate.next_in = (unsigned char *)(const_cast<char *>(in.data()));
  437. do {
  438. // Output to local buffer
  439. m_dstate.avail_out = m_compress_buffer_size;
  440. m_dstate.next_out = m_compress_buffer.get();
  441. deflate(&m_dstate, Z_SYNC_FLUSH);
  442. output = m_compress_buffer_size - m_dstate.avail_out;
  443. out.append((char *)(m_compress_buffer.get()),output);
  444. } while (m_dstate.avail_out == 0);
  445. return lib::error_code();
  446. }
  447. /// Decompress bytes
  448. /**
  449. * @param buf Byte buffer to decompress
  450. * @param len Length of buf
  451. * @param out String to append decompressed bytes to
  452. * @return Error or status code
  453. */
  454. lib::error_code decompress(uint8_t const * buf, size_t len, std::string &
  455. out)
  456. {
  457. if (!m_initialized) {
  458. return make_error_code(error::uninitialized);
  459. }
  460. int ret;
  461. m_istate.avail_in = len;
  462. m_istate.next_in = const_cast<unsigned char *>(buf);
  463. do {
  464. m_istate.avail_out = m_compress_buffer_size;
  465. m_istate.next_out = m_compress_buffer.get();
  466. ret = inflate(&m_istate, Z_SYNC_FLUSH);
  467. if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
  468. return make_error_code(error::zlib_error);
  469. }
  470. out.append(
  471. reinterpret_cast<char *>(m_compress_buffer.get()),
  472. m_compress_buffer_size - m_istate.avail_out
  473. );
  474. } while (m_istate.avail_out == 0);
  475. return lib::error_code();
  476. }
  477. private:
  478. /// Generate negotiation response
  479. /**
  480. * @return Generate extension negotiation reponse string to send to client
  481. */
  482. std::string generate_response() {
  483. std::string ret = "permessage-deflate";
  484. if (m_s2c_no_context_takeover) {
  485. ret += "; s2c_no_context_takeover";
  486. }
  487. if (m_c2s_no_context_takeover) {
  488. ret += "; c2s_no_context_takeover";
  489. }
  490. if (m_s2c_max_window_bits < default_s2c_max_window_bits) {
  491. std::stringstream s;
  492. s << int(m_s2c_max_window_bits);
  493. ret += "; s2c_max_window_bits="+s.str();
  494. }
  495. if (m_c2s_max_window_bits < default_c2s_max_window_bits) {
  496. std::stringstream s;
  497. s << int(m_c2s_max_window_bits);
  498. ret += "; c2s_max_window_bits="+s.str();
  499. }
  500. return ret;
  501. }
  502. /// Negotiate s2c_no_context_takeover attribute
  503. /**
  504. * @param [in] value The value of the attribute from the offer
  505. * @param [out] ec A reference to the error code to return errors via
  506. */
  507. void negotiate_s2c_no_context_takeover(std::string const & value,
  508. lib::error_code & ec)
  509. {
  510. if (!value.empty()) {
  511. ec = make_error_code(error::invalid_attribute_value);
  512. return;
  513. }
  514. m_s2c_no_context_takeover = true;
  515. }
  516. /// Negotiate c2s_no_context_takeover attribute
  517. /**
  518. * @param [in] value The value of the attribute from the offer
  519. * @param [out] ec A reference to the error code to return errors via
  520. */
  521. void negotiate_c2s_no_context_takeover(std::string const & value,
  522. lib::error_code & ec)
  523. {
  524. if (!value.empty()) {
  525. ec = make_error_code(error::invalid_attribute_value);
  526. return;
  527. }
  528. m_c2s_no_context_takeover = true;
  529. }
  530. /// Negotiate s2c_max_window_bits attribute
  531. /**
  532. * When this method starts, m_s2c_max_window_bits will contain the server's
  533. * preferred value and m_s2c_max_window_bits_mode will contain the mode the
  534. * server wants to use to for negotiation. `value` contains the value the
  535. * client requested that we use.
  536. *
  537. * options:
  538. * - decline (refuse to use the attribute)
  539. * - accept (use whatever the client says)
  540. * - largest (use largest possible value)
  541. * - smallest (use smallest possible value)
  542. *
  543. * @param [in] value The value of the attribute from the offer
  544. * @param [out] ec A reference to the error code to return errors via
  545. */
  546. void negotiate_s2c_max_window_bits(std::string const & value,
  547. lib::error_code & ec)
  548. {
  549. uint8_t bits = uint8_t(atoi(value.c_str()));
  550. if (bits < min_s2c_max_window_bits || bits > max_s2c_max_window_bits) {
  551. ec = make_error_code(error::invalid_attribute_value);
  552. m_s2c_max_window_bits = default_s2c_max_window_bits;
  553. return;
  554. }
  555. switch (m_s2c_max_window_bits_mode) {
  556. case mode::decline:
  557. m_s2c_max_window_bits = default_s2c_max_window_bits;
  558. break;
  559. case mode::accept:
  560. m_s2c_max_window_bits = bits;
  561. break;
  562. case mode::largest:
  563. m_s2c_max_window_bits = (std::min)(bits,m_s2c_max_window_bits);
  564. break;
  565. case mode::smallest:
  566. m_s2c_max_window_bits = min_s2c_max_window_bits;
  567. break;
  568. default:
  569. ec = make_error_code(error::invalid_mode);
  570. m_s2c_max_window_bits = default_s2c_max_window_bits;
  571. }
  572. }
  573. /// Negotiate c2s_max_window_bits attribute
  574. /**
  575. * When this method starts, m_c2s_max_window_bits and m_c2s_max_window_mode
  576. * will contain the server's preferred values for window size and
  577. * negotiation mode.
  578. *
  579. * options:
  580. * - decline (refuse to use the attribute)
  581. * - accept (use whatever the client says)
  582. * - largest (use largest possible value)
  583. * - smallest (use smallest possible value)
  584. *
  585. * @param [in] value The value of the attribute from the offer
  586. * @param [out] ec A reference to the error code to return errors via
  587. */
  588. void negotiate_c2s_max_window_bits(std::string const & value,
  589. lib::error_code & ec)
  590. {
  591. uint8_t bits = uint8_t(atoi(value.c_str()));
  592. if (value.empty()) {
  593. bits = default_c2s_max_window_bits;
  594. } else if (bits < min_c2s_max_window_bits ||
  595. bits > max_c2s_max_window_bits)
  596. {
  597. ec = make_error_code(error::invalid_attribute_value);
  598. m_c2s_max_window_bits = default_c2s_max_window_bits;
  599. return;
  600. }
  601. switch (m_c2s_max_window_bits_mode) {
  602. case mode::decline:
  603. m_c2s_max_window_bits = default_c2s_max_window_bits;
  604. break;
  605. case mode::accept:
  606. m_c2s_max_window_bits = bits;
  607. break;
  608. case mode::largest:
  609. m_c2s_max_window_bits = std::min(bits,m_c2s_max_window_bits);
  610. break;
  611. case mode::smallest:
  612. m_c2s_max_window_bits = min_c2s_max_window_bits;
  613. break;
  614. default:
  615. ec = make_error_code(error::invalid_mode);
  616. m_c2s_max_window_bits = default_c2s_max_window_bits;
  617. }
  618. }
  619. bool m_enabled;
  620. bool m_s2c_no_context_takeover;
  621. bool m_c2s_no_context_takeover;
  622. uint8_t m_s2c_max_window_bits;
  623. uint8_t m_c2s_max_window_bits;
  624. mode::value m_s2c_max_window_bits_mode;
  625. mode::value m_c2s_max_window_bits_mode;
  626. bool m_initialized;
  627. size_t m_compress_buffer_size;
  628. lib::unique_ptr_uchar_array m_compress_buffer;
  629. z_stream m_dstate;
  630. z_stream m_istate;
  631. };
  632. } // namespace permessage_deflate
  633. } // namespace extensions
  634. } // namespace websocketpp
  635. #endif // WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP