pmd_extension.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. //
  2. // Copyright (c) 2016-2017 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. #ifndef BOOST_BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_HPP
  10. #define BOOST_BEAST_WEBSOCKET_DETAIL_PMD_EXTENSION_HPP
  11. #include <boost/beast/core/error.hpp>
  12. #include <boost/beast/core/buffers_suffix.hpp>
  13. #include <boost/beast/core/read_size.hpp>
  14. #include <boost/beast/zlib/deflate_stream.hpp>
  15. #include <boost/beast/zlib/inflate_stream.hpp>
  16. #include <boost/beast/websocket/option.hpp>
  17. #include <boost/beast/http/rfc7230.hpp>
  18. #include <boost/asio/buffer.hpp>
  19. #include <utility>
  20. #include <type_traits>
  21. namespace boost {
  22. namespace beast {
  23. namespace websocket {
  24. namespace detail {
  25. // permessage-deflate offer parameters
  26. //
  27. // "context takeover" means:
  28. // preserve sliding window across messages
  29. //
  30. struct pmd_offer
  31. {
  32. bool accept;
  33. // 0 = absent, or 8..15
  34. int server_max_window_bits;
  35. // -1 = present, 0 = absent, or 8..15
  36. int client_max_window_bits;
  37. // `true` if server_no_context_takeover offered
  38. bool server_no_context_takeover;
  39. // `true` if client_no_context_takeover offered
  40. bool client_no_context_takeover;
  41. };
  42. template<class = void>
  43. int
  44. parse_bits(string_view s)
  45. {
  46. if(s.size() == 0)
  47. return -1;
  48. if(s.size() > 2)
  49. return -1;
  50. if(s[0] < '1' || s[0] > '9')
  51. return -1;
  52. unsigned i = 0;
  53. for(auto c : s)
  54. {
  55. if(c < '0' || c > '9')
  56. return -1;
  57. auto const i0 = i;
  58. i = 10 * i + (c - '0');
  59. if(i < i0)
  60. return -1;
  61. }
  62. return static_cast<int>(i);
  63. }
  64. // Parse permessage-deflate request fields
  65. //
  66. template<class Allocator>
  67. void
  68. pmd_read(pmd_offer& offer,
  69. http::basic_fields<Allocator> const& fields)
  70. {
  71. offer.accept = false;
  72. offer.server_max_window_bits= 0;
  73. offer.client_max_window_bits = 0;
  74. offer.server_no_context_takeover = false;
  75. offer.client_no_context_takeover = false;
  76. http::ext_list list{
  77. fields["Sec-WebSocket-Extensions"]};
  78. for(auto const& ext : list)
  79. {
  80. if(iequals(ext.first, "permessage-deflate"))
  81. {
  82. for(auto const& param : ext.second)
  83. {
  84. if(iequals(param.first,
  85. "server_max_window_bits"))
  86. {
  87. if(offer.server_max_window_bits != 0)
  88. {
  89. // The negotiation offer contains multiple
  90. // extension parameters with the same name.
  91. //
  92. return; // MUST decline
  93. }
  94. if(param.second.empty())
  95. {
  96. // The negotiation offer extension
  97. // parameter is missing the value.
  98. //
  99. return; // MUST decline
  100. }
  101. offer.server_max_window_bits =
  102. parse_bits(param.second);
  103. if( offer.server_max_window_bits < 8 ||
  104. offer.server_max_window_bits > 15)
  105. {
  106. // The negotiation offer contains an
  107. // extension parameter with an invalid value.
  108. //
  109. return; // MUST decline
  110. }
  111. }
  112. else if(iequals(param.first,
  113. "client_max_window_bits"))
  114. {
  115. if(offer.client_max_window_bits != 0)
  116. {
  117. // The negotiation offer contains multiple
  118. // extension parameters with the same name.
  119. //
  120. return; // MUST decline
  121. }
  122. if(! param.second.empty())
  123. {
  124. offer.client_max_window_bits =
  125. parse_bits(param.second);
  126. if( offer.client_max_window_bits < 8 ||
  127. offer.client_max_window_bits > 15)
  128. {
  129. // The negotiation offer contains an
  130. // extension parameter with an invalid value.
  131. //
  132. return; // MUST decline
  133. }
  134. }
  135. else
  136. {
  137. offer.client_max_window_bits = -1;
  138. }
  139. }
  140. else if(iequals(param.first,
  141. "server_no_context_takeover"))
  142. {
  143. if(offer.server_no_context_takeover)
  144. {
  145. // The negotiation offer contains multiple
  146. // extension parameters with the same name.
  147. //
  148. return; // MUST decline
  149. }
  150. if(! param.second.empty())
  151. {
  152. // The negotiation offer contains an
  153. // extension parameter with an invalid value.
  154. //
  155. return; // MUST decline
  156. }
  157. offer.server_no_context_takeover = true;
  158. }
  159. else if(iequals(param.first,
  160. "client_no_context_takeover"))
  161. {
  162. if(offer.client_no_context_takeover)
  163. {
  164. // The negotiation offer contains multiple
  165. // extension parameters with the same name.
  166. //
  167. return; // MUST decline
  168. }
  169. if(! param.second.empty())
  170. {
  171. // The negotiation offer contains an
  172. // extension parameter with an invalid value.
  173. //
  174. return; // MUST decline
  175. }
  176. offer.client_no_context_takeover = true;
  177. }
  178. else
  179. {
  180. // The negotiation offer contains an extension
  181. // parameter not defined for use in an offer.
  182. //
  183. return; // MUST decline
  184. }
  185. }
  186. offer.accept = true;
  187. return;
  188. }
  189. }
  190. }
  191. // Set permessage-deflate fields for a client offer
  192. //
  193. template<class Allocator>
  194. void
  195. pmd_write(http::basic_fields<Allocator>& fields,
  196. pmd_offer const& offer)
  197. {
  198. static_string<512> s;
  199. s = "permessage-deflate";
  200. if(offer.server_max_window_bits != 0)
  201. {
  202. if(offer.server_max_window_bits != -1)
  203. {
  204. s += "; server_max_window_bits=";
  205. s += to_static_string(
  206. offer.server_max_window_bits);
  207. }
  208. else
  209. {
  210. s += "; server_max_window_bits";
  211. }
  212. }
  213. if(offer.client_max_window_bits != 0)
  214. {
  215. if(offer.client_max_window_bits != -1)
  216. {
  217. s += "; client_max_window_bits=";
  218. s += to_static_string(
  219. offer.client_max_window_bits);
  220. }
  221. else
  222. {
  223. s += "; client_max_window_bits";
  224. }
  225. }
  226. if(offer.server_no_context_takeover)
  227. {
  228. s += "; server_no_context_takeover";
  229. }
  230. if(offer.client_no_context_takeover)
  231. {
  232. s += "; client_no_context_takeover";
  233. }
  234. fields.set(http::field::sec_websocket_extensions, s);
  235. }
  236. // Negotiate a permessage-deflate client offer
  237. //
  238. template<class Allocator>
  239. void
  240. pmd_negotiate(
  241. http::basic_fields<Allocator>& fields,
  242. pmd_offer& config,
  243. pmd_offer const& offer,
  244. permessage_deflate const& o)
  245. {
  246. if(! (offer.accept && o.server_enable))
  247. {
  248. config.accept = false;
  249. return;
  250. }
  251. config.accept = true;
  252. static_string<512> s = "permessage-deflate";
  253. config.server_no_context_takeover =
  254. offer.server_no_context_takeover ||
  255. o.server_no_context_takeover;
  256. if(config.server_no_context_takeover)
  257. s += "; server_no_context_takeover";
  258. config.client_no_context_takeover =
  259. o.client_no_context_takeover ||
  260. offer.client_no_context_takeover;
  261. if(config.client_no_context_takeover)
  262. s += "; client_no_context_takeover";
  263. if(offer.server_max_window_bits != 0)
  264. config.server_max_window_bits = (std::min)(
  265. offer.server_max_window_bits,
  266. o.server_max_window_bits);
  267. else
  268. config.server_max_window_bits =
  269. o.server_max_window_bits;
  270. if(config.server_max_window_bits < 15)
  271. {
  272. // ZLib's deflateInit silently treats 8 as
  273. // 9 due to a bug, so prevent 8 from being used.
  274. //
  275. if(config.server_max_window_bits < 9)
  276. config.server_max_window_bits = 9;
  277. s += "; server_max_window_bits=";
  278. s += to_static_string(
  279. config.server_max_window_bits);
  280. }
  281. switch(offer.client_max_window_bits)
  282. {
  283. case -1:
  284. // extension parameter is present with no value
  285. config.client_max_window_bits =
  286. o.client_max_window_bits;
  287. if(config.client_max_window_bits < 15)
  288. {
  289. s += "; client_max_window_bits=";
  290. s += to_static_string(
  291. config.client_max_window_bits);
  292. }
  293. break;
  294. case 0:
  295. /* extension parameter is absent.
  296. If a received extension negotiation offer doesn't have the
  297. "client_max_window_bits" extension parameter, the corresponding
  298. extension negotiation response to the offer MUST NOT include the
  299. "client_max_window_bits" extension parameter.
  300. */
  301. if(o.client_max_window_bits == 15)
  302. config.client_max_window_bits = 15;
  303. else
  304. config.accept = false;
  305. break;
  306. default:
  307. // extension parameter has value in [8..15]
  308. config.client_max_window_bits = (std::min)(
  309. o.client_max_window_bits,
  310. offer.client_max_window_bits);
  311. s += "; client_max_window_bits=";
  312. s += to_static_string(
  313. config.client_max_window_bits);
  314. break;
  315. }
  316. if(config.accept)
  317. fields.set(http::field::sec_websocket_extensions, s);
  318. }
  319. // Normalize the server's response
  320. //
  321. inline
  322. void
  323. pmd_normalize(pmd_offer& offer)
  324. {
  325. if(offer.accept)
  326. {
  327. if( offer.server_max_window_bits == 0)
  328. offer.server_max_window_bits = 15;
  329. if( offer.client_max_window_bits == 0 ||
  330. offer.client_max_window_bits == -1)
  331. offer.client_max_window_bits = 15;
  332. }
  333. }
  334. } // detail
  335. } // websocket
  336. } // beast
  337. } // boost
  338. #endif