enabled.hpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817
  1. /*
  2. * Copyright (c) 2015, 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/stdint.hpp>
  33. #include <websocketpp/common/system_error.hpp>
  34. #include <websocketpp/error.hpp>
  35. #include <websocketpp/extensions/extension.hpp>
  36. #include "zlib.h"
  37. #include <algorithm>
  38. #include <string>
  39. #include <vector>
  40. namespace websocketpp {
  41. namespace extensions {
  42. /// Implementation of RFC 7692, the permessage-deflate WebSocket extension
  43. /**
  44. * ### permessage-deflate interface
  45. *
  46. * **init**\n
  47. * `lib::error_code init(bool is_server)`\n
  48. * Performs initialization
  49. *
  50. * **is_implimented**\n
  51. * `bool is_implimented()`\n
  52. * Returns whether or not the object impliments the extension or not
  53. *
  54. * **is_enabled**\n
  55. * `bool is_enabled()`\n
  56. * Returns whether or not the extension was negotiated for the current
  57. * connection
  58. *
  59. * **generate_offer**\n
  60. * `std::string generate_offer() const`\n
  61. * Create an extension offer string based on local policy
  62. *
  63. * **validate_response**\n
  64. * `lib::error_code validate_response(http::attribute_list const & response)`\n
  65. * Negotiate the parameters of extension use
  66. *
  67. * **negotiate**\n
  68. * `err_str_pair negotiate(http::attribute_list const & attributes)`\n
  69. * Negotiate the parameters of extension use
  70. *
  71. * **compress**\n
  72. * `lib::error_code compress(std::string const & in, std::string & out)`\n
  73. * Compress the bytes in `in` and append them to `out`
  74. *
  75. * **decompress**\n
  76. * `lib::error_code decompress(uint8_t const * buf, size_t len, std::string &
  77. * out)`\n
  78. * Decompress `len` bytes from `buf` and append them to string `out`
  79. */
  80. namespace permessage_deflate {
  81. /// Permessage deflate error values
  82. namespace error {
  83. enum value {
  84. /// Catch all
  85. general = 1,
  86. /// Invalid extension attributes
  87. invalid_attributes,
  88. /// Invalid extension attribute value
  89. invalid_attribute_value,
  90. /// Invalid megotiation mode
  91. invalid_mode,
  92. /// Unsupported extension attributes
  93. unsupported_attributes,
  94. /// Invalid value for max_window_bits
  95. invalid_max_window_bits,
  96. /// ZLib Error
  97. zlib_error,
  98. /// Uninitialized
  99. uninitialized,
  100. };
  101. /// Permessage-deflate error category
  102. class category : public lib::error_category {
  103. public:
  104. category() {}
  105. char const * name() const _WEBSOCKETPP_NOEXCEPT_TOKEN_ {
  106. return "websocketpp.extension.permessage-deflate";
  107. }
  108. std::string message(int value) const {
  109. switch(value) {
  110. case general:
  111. return "Generic permessage-compress error";
  112. case invalid_attributes:
  113. return "Invalid extension attributes";
  114. case invalid_attribute_value:
  115. return "Invalid extension attribute value";
  116. case invalid_mode:
  117. return "Invalid permessage-deflate negotiation mode";
  118. case unsupported_attributes:
  119. return "Unsupported extension attributes";
  120. case invalid_max_window_bits:
  121. return "Invalid value for max_window_bits";
  122. case zlib_error:
  123. return "A zlib function returned an error";
  124. case uninitialized:
  125. return "Deflate extension must be initialized before use";
  126. default:
  127. return "Unknown permessage-compress error";
  128. }
  129. }
  130. };
  131. /// Get a reference to a static copy of the permessage-deflate error category
  132. inline lib::error_category const & get_category() {
  133. static category instance;
  134. return instance;
  135. }
  136. /// Create an error code in the permessage-deflate category
  137. inline lib::error_code make_error_code(error::value e) {
  138. return lib::error_code(static_cast<int>(e), get_category());
  139. }
  140. } // namespace error
  141. } // namespace permessage_deflate
  142. } // namespace extensions
  143. } // namespace websocketpp
  144. _WEBSOCKETPP_ERROR_CODE_ENUM_NS_START_
  145. template<> struct is_error_code_enum
  146. <websocketpp::extensions::permessage_deflate::error::value>
  147. {
  148. static bool const value = true;
  149. };
  150. _WEBSOCKETPP_ERROR_CODE_ENUM_NS_END_
  151. namespace websocketpp {
  152. namespace extensions {
  153. namespace permessage_deflate {
  154. /// Default value for server_max_window_bits as defined by RFC 7692
  155. static uint8_t const default_server_max_window_bits = 15;
  156. /// Minimum value for server_max_window_bits as defined by RFC 7692
  157. /**
  158. * NOTE: A value of 8 is not actually supported by zlib, the deflate
  159. * library that WebSocket++ uses. To preserve backwards compatibility
  160. * with RFC 7692 and previous versions of the library a value of 8
  161. * is accepted by the library but will always be negotiated as 9.
  162. */
  163. static uint8_t const min_server_max_window_bits = 8;
  164. /// Maximum value for server_max_window_bits as defined by RFC 7692
  165. static uint8_t const max_server_max_window_bits = 15;
  166. /// Default value for client_max_window_bits as defined by RFC 7692
  167. static uint8_t const default_client_max_window_bits = 15;
  168. /// Minimum value for client_max_window_bits as defined by RFC 7692
  169. /**
  170. * NOTE: A value of 8 is not actually supported by zlib, the deflate
  171. * library that WebSocket++ uses. To preserve backwards compatibility
  172. * with RFC 7692 and previous versions of the library a value of 8
  173. * is accepted by the library but will always be negotiated as 9.
  174. */
  175. static uint8_t const min_client_max_window_bits = 8;
  176. /// Maximum value for client_max_window_bits as defined by RFC 7692
  177. static uint8_t const max_client_max_window_bits = 15;
  178. namespace mode {
  179. enum value {
  180. /// Accept any value the remote endpoint offers
  181. accept = 1,
  182. /// Decline any value the remote endpoint offers. Insist on defaults.
  183. decline,
  184. /// Use the largest value common to both offers
  185. largest,
  186. /// Use the smallest value common to both offers
  187. smallest
  188. };
  189. } // namespace mode
  190. template <typename config>
  191. class enabled {
  192. public:
  193. enabled()
  194. : m_enabled(false)
  195. , m_server_no_context_takeover(false)
  196. , m_client_no_context_takeover(false)
  197. , m_server_max_window_bits(15)
  198. , m_client_max_window_bits(15)
  199. , m_server_max_window_bits_mode(mode::accept)
  200. , m_client_max_window_bits_mode(mode::accept)
  201. , m_initialized(false)
  202. , m_compress_buffer_size(8192)
  203. {
  204. m_dstate.zalloc = Z_NULL;
  205. m_dstate.zfree = Z_NULL;
  206. m_dstate.opaque = Z_NULL;
  207. m_istate.zalloc = Z_NULL;
  208. m_istate.zfree = Z_NULL;
  209. m_istate.opaque = Z_NULL;
  210. m_istate.avail_in = 0;
  211. m_istate.next_in = Z_NULL;
  212. }
  213. ~enabled() {
  214. if (!m_initialized) {
  215. return;
  216. }
  217. int ret = deflateEnd(&m_dstate);
  218. if (ret != Z_OK) {
  219. //std::cout << "error cleaning up zlib compression state"
  220. // << std::endl;
  221. }
  222. ret = inflateEnd(&m_istate);
  223. if (ret != Z_OK) {
  224. //std::cout << "error cleaning up zlib decompression state"
  225. // << std::endl;
  226. }
  227. }
  228. /// Initialize zlib state
  229. /**
  230. * Note: this should be called *after* the negotiation methods. It will use
  231. * information from the negotiation to determine how to initialize the zlib
  232. * data structures.
  233. *
  234. * @todo memory level, strategy, etc are hardcoded
  235. *
  236. * @param is_server True to initialize as a server, false for a client.
  237. * @return A code representing the error that occurred, if any
  238. */
  239. lib::error_code init(bool is_server) {
  240. uint8_t deflate_bits;
  241. uint8_t inflate_bits;
  242. if (is_server) {
  243. deflate_bits = m_server_max_window_bits;
  244. inflate_bits = m_client_max_window_bits;
  245. } else {
  246. deflate_bits = m_client_max_window_bits;
  247. inflate_bits = m_server_max_window_bits;
  248. }
  249. int ret = deflateInit2(
  250. &m_dstate,
  251. Z_DEFAULT_COMPRESSION,
  252. Z_DEFLATED,
  253. -1*deflate_bits,
  254. 4, // memory level 1-9
  255. Z_DEFAULT_STRATEGY
  256. );
  257. if (ret != Z_OK) {
  258. return make_error_code(error::zlib_error);
  259. }
  260. ret = inflateInit2(
  261. &m_istate,
  262. -1*inflate_bits
  263. );
  264. if (ret != Z_OK) {
  265. return make_error_code(error::zlib_error);
  266. }
  267. m_compress_buffer.reset(new unsigned char[m_compress_buffer_size]);
  268. m_decompress_buffer.reset(new unsigned char[m_compress_buffer_size]);
  269. if ((m_server_no_context_takeover && is_server) ||
  270. (m_client_no_context_takeover && !is_server))
  271. {
  272. m_flush = Z_FULL_FLUSH;
  273. } else {
  274. m_flush = Z_SYNC_FLUSH;
  275. }
  276. m_initialized = true;
  277. return lib::error_code();
  278. }
  279. /// Test if this object implements the permessage-deflate specification
  280. /**
  281. * Because this object does implieent it, it will always return true.
  282. *
  283. * @return Whether or not this object implements permessage-deflate
  284. */
  285. bool is_implemented() const {
  286. return true;
  287. }
  288. /// Test if the extension was negotiated for this connection
  289. /**
  290. * Retrieves whether or not this extension is in use based on the initial
  291. * handshake extension negotiations.
  292. *
  293. * @return Whether or not the extension is in use
  294. */
  295. bool is_enabled() const {
  296. return m_enabled;
  297. }
  298. /// Reset server's outgoing LZ77 sliding window for each new message
  299. /**
  300. * Enabling this setting will cause the server's compressor to reset the
  301. * compression state (the LZ77 sliding window) for every message. This
  302. * means that the compressor will not look back to patterns in previous
  303. * messages to improve compression. This will reduce the compression
  304. * efficiency for large messages somewhat and small messages drastically.
  305. *
  306. * This option may reduce server compressor memory usage and client
  307. * decompressor memory usage.
  308. * @todo Document to what extent memory usage will be reduced
  309. *
  310. * For clients, this option is dependent on server support. Enabling it
  311. * via this method does not guarantee that it will be successfully
  312. * negotiated, only that it will be requested.
  313. *
  314. * For servers, no client support is required. Enabling this option on a
  315. * server will result in its use. The server will signal to clients that
  316. * the option will be in use so they can optimize resource usage if they
  317. * are able.
  318. */
  319. void enable_server_no_context_takeover() {
  320. m_server_no_context_takeover = true;
  321. }
  322. /// Reset client's outgoing LZ77 sliding window for each new message
  323. /**
  324. * Enabling this setting will cause the client's compressor to reset the
  325. * compression state (the LZ77 sliding window) for every message. This
  326. * means that the compressor will not look back to patterns in previous
  327. * messages to improve compression. This will reduce the compression
  328. * efficiency for large messages somewhat and small messages drastically.
  329. *
  330. * This option may reduce client compressor memory usage and server
  331. * decompressor memory usage.
  332. * @todo Document to what extent memory usage will be reduced
  333. *
  334. * This option is supported by all compliant clients and servers. Enabling
  335. * it via either endpoint should be sufficient to ensure it is used.
  336. */
  337. void enable_client_no_context_takeover() {
  338. m_client_no_context_takeover = true;
  339. }
  340. /// Limit server LZ77 sliding window size
  341. /**
  342. * The bits setting is the base 2 logarithm of the maximum window size that
  343. * the server must use to compress outgoing messages. The permitted range
  344. * is 9 to 15 inclusive. 9 represents a 512 byte window and 15 a 32KiB
  345. * window. 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 server support. A client requesting this
  354. * setting may be rejected by the server or have the exact value used
  355. * adjusted by the server. A server may unilaterally set this value without
  356. * client support.
  357. *
  358. * NOTE: The permessage-deflate spec specifies that a value of 8 is allowed.
  359. * Prior to version 0.8.0 a value of 8 was also allowed by this library.
  360. * zlib, the deflate compression library that WebSocket++ uses has always
  361. * silently adjusted a value of 8 to 9. In recent versions of zlib (1.2.9
  362. * and greater) a value of 8 is now explicitly rejected. WebSocket++ 0.8.0
  363. * continues to perform the 8->9 conversion for backwards compatibility
  364. * purposes but this should be considered deprecated functionality.
  365. *
  366. * @param bits The size to request for the outgoing window size
  367. * @param mode The mode to use for negotiating this parameter
  368. * @return A status code
  369. */
  370. lib::error_code set_server_max_window_bits(uint8_t bits, mode::value mode) {
  371. if (bits < min_server_max_window_bits || bits > max_server_max_window_bits) {
  372. return error::make_error_code(error::invalid_max_window_bits);
  373. }
  374. // See note in doc comment above about what is happening here
  375. if (bits == 8) {
  376. bits = 9;
  377. }
  378. m_server_max_window_bits = bits;
  379. m_server_max_window_bits_mode = mode;
  380. return lib::error_code();
  381. }
  382. /// Limit client LZ77 sliding window size
  383. /**
  384. * The bits setting is the base 2 logarithm of the window size that the
  385. * client must use to compress outgoing messages. The permitted range is 9
  386. * to 15 inclusive. 9 represents a 512 byte window and 15 a 32KiB window.
  387. * The default setting is 15.
  388. *
  389. * Mode Options:
  390. * - accept: Accept whatever the remote endpoint offers.
  391. * - decline: Decline any offers to deviate from the defaults
  392. * - largest: Accept largest window size acceptable to both endpoints
  393. * - smallest: Accept smallest window size acceptiable to both endpoints
  394. *
  395. * This setting is dependent on client support. A client may limit its own
  396. * outgoing window size unilaterally. A server may only limit the client's
  397. * window size if the remote client supports that feature.
  398. *
  399. * NOTE: The permessage-deflate spec specifies that a value of 8 is allowed.
  400. * Prior to version 0.8.0 a value of 8 was also allowed by this library.
  401. * zlib, the deflate compression library that WebSocket++ uses has always
  402. * silently adjusted a value of 8 to 9. In recent versions of zlib (1.2.9
  403. * and greater) a value of 8 is now explicitly rejected. WebSocket++ 0.8.0
  404. * continues to perform the 8->9 conversion for backwards compatibility
  405. * purposes but this should be considered deprecated functionality.
  406. *
  407. * @param bits The size to request for the outgoing window size
  408. * @param mode The mode to use for negotiating this parameter
  409. * @return A status code
  410. */
  411. lib::error_code set_client_max_window_bits(uint8_t bits, mode::value mode) {
  412. if (bits < min_client_max_window_bits || bits > max_client_max_window_bits) {
  413. return error::make_error_code(error::invalid_max_window_bits);
  414. }
  415. // See note in doc comment above about what is happening here
  416. if (bits == 8) {
  417. bits = 9;
  418. }
  419. m_client_max_window_bits = bits;
  420. m_client_max_window_bits_mode = mode;
  421. return lib::error_code();
  422. }
  423. /// Generate extension offer
  424. /**
  425. * Creates an offer string to include in the Sec-WebSocket-Extensions
  426. * header of outgoing client requests.
  427. *
  428. * @return A WebSocket extension offer string for this extension
  429. */
  430. std::string generate_offer() const {
  431. // TODO: this should be dynamically generated based on user settings
  432. return "permessage-deflate; client_no_context_takeover; client_max_window_bits";
  433. }
  434. /// Validate extension response
  435. /**
  436. * Confirm that the server has negotiated settings compatible with our
  437. * original offer and apply those settings to the extension state.
  438. *
  439. * @param response The server response attribute list to validate
  440. * @return Validation error or 0 on success
  441. */
  442. lib::error_code validate_offer(http::attribute_list const &) {
  443. return lib::error_code();
  444. }
  445. /// Negotiate extension
  446. /**
  447. * Confirm that the client's extension negotiation offer has settings
  448. * compatible with local policy. If so, generate a reply and apply those
  449. * settings to the extension state.
  450. *
  451. * @param offer Attribute from client's offer
  452. * @return Status code and value to return to remote endpoint
  453. */
  454. err_str_pair negotiate(http::attribute_list const & offer) {
  455. err_str_pair ret;
  456. http::attribute_list::const_iterator it;
  457. for (it = offer.begin(); it != offer.end(); ++it) {
  458. if (it->first == "server_no_context_takeover") {
  459. negotiate_server_no_context_takeover(it->second,ret.first);
  460. } else if (it->first == "client_no_context_takeover") {
  461. negotiate_client_no_context_takeover(it->second,ret.first);
  462. } else if (it->first == "server_max_window_bits") {
  463. negotiate_server_max_window_bits(it->second,ret.first);
  464. } else if (it->first == "client_max_window_bits") {
  465. negotiate_client_max_window_bits(it->second,ret.first);
  466. } else {
  467. ret.first = make_error_code(error::invalid_attributes);
  468. }
  469. if (ret.first) {
  470. break;
  471. }
  472. }
  473. if (ret.first == lib::error_code()) {
  474. m_enabled = true;
  475. ret.second = generate_response();
  476. }
  477. return ret;
  478. }
  479. /// Compress bytes
  480. /**
  481. * @todo: avail_in/out is 32 bit, need to fix for cases of >32 bit frames
  482. * on 64 bit machines.
  483. *
  484. * @param [in] in String to compress
  485. * @param [out] out String to append compressed bytes to
  486. * @return Error or status code
  487. */
  488. lib::error_code compress(std::string const & in, std::string & out) {
  489. if (!m_initialized) {
  490. return make_error_code(error::uninitialized);
  491. }
  492. size_t output;
  493. if (in.empty()) {
  494. uint8_t buf[6] = {0x02, 0x00, 0x00, 0x00, 0xff, 0xff};
  495. out.append((char *)(buf),6);
  496. return lib::error_code();
  497. }
  498. m_dstate.avail_in = in.size();
  499. m_dstate.next_in = (unsigned char *)(const_cast<char *>(in.data()));
  500. do {
  501. // Output to local buffer
  502. m_dstate.avail_out = m_compress_buffer_size;
  503. m_dstate.next_out = m_compress_buffer.get();
  504. deflate(&m_dstate, m_flush);
  505. output = m_compress_buffer_size - m_dstate.avail_out;
  506. out.append((char *)(m_compress_buffer.get()),output);
  507. } while (m_dstate.avail_out == 0);
  508. return lib::error_code();
  509. }
  510. /// Decompress bytes
  511. /**
  512. * @param buf Byte buffer to decompress
  513. * @param len Length of buf
  514. * @param out String to append decompressed bytes to
  515. * @return Error or status code
  516. */
  517. lib::error_code decompress(uint8_t const * buf, size_t len, std::string &
  518. out)
  519. {
  520. if (!m_initialized) {
  521. return make_error_code(error::uninitialized);
  522. }
  523. int ret;
  524. m_istate.avail_in = len;
  525. m_istate.next_in = const_cast<unsigned char *>(buf);
  526. do {
  527. m_istate.avail_out = m_compress_buffer_size;
  528. m_istate.next_out = m_decompress_buffer.get();
  529. ret = inflate(&m_istate, Z_SYNC_FLUSH);
  530. if (ret == Z_NEED_DICT || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
  531. return make_error_code(error::zlib_error);
  532. }
  533. out.append(
  534. reinterpret_cast<char *>(m_decompress_buffer.get()),
  535. m_compress_buffer_size - m_istate.avail_out
  536. );
  537. } while (m_istate.avail_out == 0);
  538. return lib::error_code();
  539. }
  540. private:
  541. /// Generate negotiation response
  542. /**
  543. * @return Generate extension negotiation reponse string to send to client
  544. */
  545. std::string generate_response() {
  546. std::string ret = "permessage-deflate";
  547. if (m_server_no_context_takeover) {
  548. ret += "; server_no_context_takeover";
  549. }
  550. if (m_client_no_context_takeover) {
  551. ret += "; client_no_context_takeover";
  552. }
  553. if (m_server_max_window_bits < default_server_max_window_bits) {
  554. std::stringstream s;
  555. s << int(m_server_max_window_bits);
  556. ret += "; server_max_window_bits="+s.str();
  557. }
  558. if (m_client_max_window_bits < default_client_max_window_bits) {
  559. std::stringstream s;
  560. s << int(m_client_max_window_bits);
  561. ret += "; client_max_window_bits="+s.str();
  562. }
  563. return ret;
  564. }
  565. /// Negotiate server_no_context_takeover attribute
  566. /**
  567. * @param [in] value The value of the attribute from the offer
  568. * @param [out] ec A reference to the error code to return errors via
  569. */
  570. void negotiate_server_no_context_takeover(std::string const & value,
  571. lib::error_code & ec)
  572. {
  573. if (!value.empty()) {
  574. ec = make_error_code(error::invalid_attribute_value);
  575. return;
  576. }
  577. m_server_no_context_takeover = true;
  578. }
  579. /// Negotiate client_no_context_takeover attribute
  580. /**
  581. * @param [in] value The value of the attribute from the offer
  582. * @param [out] ec A reference to the error code to return errors via
  583. */
  584. void negotiate_client_no_context_takeover(std::string const & value,
  585. lib::error_code & ec)
  586. {
  587. if (!value.empty()) {
  588. ec = make_error_code(error::invalid_attribute_value);
  589. return;
  590. }
  591. m_client_no_context_takeover = true;
  592. }
  593. /// Negotiate server_max_window_bits attribute
  594. /**
  595. * When this method starts, m_server_max_window_bits will contain the server's
  596. * preferred value and m_server_max_window_bits_mode will contain the mode the
  597. * server wants to use to for negotiation. `value` contains the value the
  598. * client requested that we use.
  599. *
  600. * options:
  601. * - decline (ignore value, offer our default instead)
  602. * - accept (use the value requested by the client)
  603. * - largest (use largest value acceptable to both)
  604. * - smallest (use smallest possible value)
  605. *
  606. * NOTE: As a value of 8 is no longer explicitly supported by zlib but might
  607. * be requested for negotiation by an older client/server, if the result of
  608. * the negotiation would be to send a value of 8, a value of 9 is offered
  609. * instead. This ensures that WebSocket++ will only ever negotiate connections
  610. * with compression settings explicitly supported by zlib.
  611. *
  612. * @param [in] value The value of the attribute from the offer
  613. * @param [out] ec A reference to the error code to return errors via
  614. */
  615. void negotiate_server_max_window_bits(std::string const & value,
  616. lib::error_code & ec)
  617. {
  618. uint8_t bits = uint8_t(atoi(value.c_str()));
  619. if (bits < min_server_max_window_bits || bits > max_server_max_window_bits) {
  620. ec = make_error_code(error::invalid_attribute_value);
  621. m_server_max_window_bits = default_server_max_window_bits;
  622. return;
  623. }
  624. switch (m_server_max_window_bits_mode) {
  625. case mode::decline:
  626. m_server_max_window_bits = default_server_max_window_bits;
  627. break;
  628. case mode::accept:
  629. m_server_max_window_bits = bits;
  630. break;
  631. case mode::largest:
  632. m_server_max_window_bits = std::min(bits,m_server_max_window_bits);
  633. break;
  634. case mode::smallest:
  635. m_server_max_window_bits = min_server_max_window_bits;
  636. break;
  637. default:
  638. ec = make_error_code(error::invalid_mode);
  639. m_server_max_window_bits = default_server_max_window_bits;
  640. }
  641. // See note in doc comment
  642. if (m_server_max_window_bits == 8) {
  643. m_server_max_window_bits = 9;
  644. }
  645. }
  646. /// Negotiate client_max_window_bits attribute
  647. /**
  648. * When this method starts, m_client_max_window_bits and m_c2s_max_window_mode
  649. * will contain the server's preferred values for window size and
  650. * negotiation mode.
  651. *
  652. * options:
  653. * - decline (ignore value, offer our default instead)
  654. * - accept (use the value requested by the client)
  655. * - largest (use largest value acceptable to both)
  656. * - smallest (use smallest possible value)
  657. *
  658. * NOTE: As a value of 8 is no longer explicitly supported by zlib but might
  659. * be requested for negotiation by an older client/server, if the result of
  660. * the negotiation would be to send a value of 8, a value of 9 is offered
  661. * instead. This ensures that WebSocket++ will only ever negotiate connections
  662. * with compression settings explicitly supported by zlib.
  663. *
  664. * @param [in] value The value of the attribute from the offer
  665. * @param [out] ec A reference to the error code to return errors via
  666. */
  667. void negotiate_client_max_window_bits(std::string const & value,
  668. lib::error_code & ec)
  669. {
  670. uint8_t bits = uint8_t(atoi(value.c_str()));
  671. if (value.empty()) {
  672. bits = default_client_max_window_bits;
  673. } else if (bits < min_client_max_window_bits ||
  674. bits > max_client_max_window_bits)
  675. {
  676. ec = make_error_code(error::invalid_attribute_value);
  677. m_client_max_window_bits = default_client_max_window_bits;
  678. return;
  679. }
  680. switch (m_client_max_window_bits_mode) {
  681. case mode::decline:
  682. m_client_max_window_bits = default_client_max_window_bits;
  683. break;
  684. case mode::accept:
  685. m_client_max_window_bits = bits;
  686. break;
  687. case mode::largest:
  688. m_client_max_window_bits = std::min(bits,m_client_max_window_bits);
  689. break;
  690. case mode::smallest:
  691. m_client_max_window_bits = min_client_max_window_bits;
  692. break;
  693. default:
  694. ec = make_error_code(error::invalid_mode);
  695. m_client_max_window_bits = default_client_max_window_bits;
  696. }
  697. // See note in doc comment
  698. if (m_client_max_window_bits == 8) {
  699. m_client_max_window_bits = 9;
  700. }
  701. }
  702. bool m_enabled;
  703. bool m_server_no_context_takeover;
  704. bool m_client_no_context_takeover;
  705. uint8_t m_server_max_window_bits;
  706. uint8_t m_client_max_window_bits;
  707. mode::value m_server_max_window_bits_mode;
  708. mode::value m_client_max_window_bits_mode;
  709. bool m_initialized;
  710. int m_flush;
  711. size_t m_compress_buffer_size;
  712. lib::unique_ptr_uchar_array m_compress_buffer;
  713. lib::unique_ptr_uchar_array m_decompress_buffer;
  714. z_stream m_dstate;
  715. z_stream m_istate;
  716. };
  717. } // namespace permessage_deflate
  718. } // namespace extensions
  719. } // namespace websocketpp
  720. #endif // WEBSOCKETPP_PROCESSOR_EXTENSION_PERMESSAGEDEFLATE_HPP