blob: 94bfeb2fd4375dea7ecea8391a164f7c018d0db6 [file] [log] [blame]
Brad Bishop316dfdd2018-06-25 12:45:53 -04001## Description: add some description
2## Origin/Author: add some origin or author
3## Bug: bug URL
4From 9ddb300d874a30db35e3ad58f188944bef0bf31b Mon Sep 17 00:00:00 2001
5From: Peter Thorson <git@zaphoyd.com>
6Date: Sun, 11 Jun 2017 15:24:43 -0500
7Subject: [PATCH] Update permessage-deflate support to reflect that zlib
8 doesn't support a 256 bit window. Improve extension negotiation error
9 checking and documentation. fixes #596 fixes #653
10
11---
12 changelog.md | 8 ++
13 test/extension/permessage_deflate.cpp | 153 +++++++++++++++++----
14 .../extensions/permessage_deflate/enabled.hpp | 94 ++++++++++---
15 websocketpp/impl/connection_impl.hpp | 10 +-
16 websocketpp/processors/hybi13.hpp | 28 +++-
17 5 files changed, 247 insertions(+), 46 deletions(-)
18
19diff --git a/changelog.md b/changelog.md
20index bba753cb..de98edd2 100644
21#--- a/changelog.md
22#+++ b/changelog.md
23#@@ -17,6 +17,14 @@ HEAD
24# - Compatibility: Update `telemetry_client` to use a slightly more cross platform
25# method of sleeping. Should work on windows now. Thank you Meir Yanovich for
26# reporting.
27#+- Compatibility: Updated permessage-deflate support to reflect that the zlib
28#+ library does not actually support a sliding window size of 256 bits.
29#+ WebSocket++ will no longer negotiate 256 bit deflate windows. If the user
30#+ of the library tries to request a 256 bit window a 512 bit window will be
31#+ specified instead (This was the previous behavior). #596 #653 Thank you
32#+ Vinnie Falco and Gianfranco Costamagna for reporting.
33#+- Compatibility: Better error handling and logging in cases where extension
34#+ requests parse correctly but negotiation fails.
35# - Bug: Store loggers in shared pointers to avoid crashes related to connections
36# trying to write logs entries after their respective endpoint has been
37# deallocated. Thank you Thalhammer for reporting and Jupp Müller for the
38diff --git a/test/extension/permessage_deflate.cpp b/test/extension/permessage_deflate.cpp
39index 4cd3e7b6..805afcc3 100644
40--- a/test/extension/permessage_deflate.cpp
41+++ b/test/extension/permessage_deflate.cpp
42@@ -186,15 +186,22 @@ BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_invalid ) {
43
44 BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_valid ) {
45 ext_vars v;
46+
47+ // confirm that a request for a value of 8 is interpreted as 9
48 v.attr["server_max_window_bits"] = "8";
49+ v.esp = v.exts.negotiate(v.attr);
50+ BOOST_CHECK( v.exts.is_enabled() );
51+ BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
52+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; server_max_window_bits=9");
53
54+ v.attr["server_max_window_bits"] = "9";
55 v.esp = v.exts.negotiate(v.attr);
56 BOOST_CHECK( v.exts.is_enabled() );
57 BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
58- BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; server_max_window_bits=8");
59+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; server_max_window_bits=9");
60
61- v.attr["server_max_window_bits"] = "15";
62
63+ v.attr["server_max_window_bits"] = "15";
64 v.esp = v.exts.negotiate(v.attr);
65 BOOST_CHECK( v.exts.is_enabled() );
66 BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
67@@ -213,7 +220,7 @@ BOOST_AUTO_TEST_CASE( invalid_set_server_max_window_bits ) {
68
69 BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_decline ) {
70 ext_vars v;
71- v.attr["server_max_window_bits"] = "8";
72+ v.attr["server_max_window_bits"] = "9";
73
74 v.ec = v.exts.set_server_max_window_bits(15,pmd_mode::decline);
75 v.esp = v.exts.negotiate(v.attr);
76@@ -223,7 +230,7 @@ BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_decline ) {
77 BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate");
78 }
79
80-BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_accept ) {
81+BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_accept_8 ) {
82 ext_vars v;
83 v.attr["server_max_window_bits"] = "8";
84
85@@ -232,10 +239,22 @@ BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_accept ) {
86 BOOST_CHECK( v.exts.is_enabled() );
87 BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
88 BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
89- BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; server_max_window_bits=8");
90+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; server_max_window_bits=9");
91 }
92
93-BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_largest ) {
94+BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_accept ) {
95+ ext_vars v;
96+ v.attr["server_max_window_bits"] = "9";
97+
98+ v.ec = v.exts.set_server_max_window_bits(15,pmd_mode::accept);
99+ v.esp = v.exts.negotiate(v.attr);
100+ BOOST_CHECK( v.exts.is_enabled() );
101+ BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
102+ BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
103+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; server_max_window_bits=9");
104+}
105+
106+BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_largest_8 ) {
107 ext_vars v;
108 v.attr["server_max_window_bits"] = "8";
109
110@@ -244,10 +263,22 @@ BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_largest ) {
111 BOOST_CHECK( v.exts.is_enabled() );
112 BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
113 BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
114- BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; server_max_window_bits=8");
115+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; server_max_window_bits=9");
116 }
117
118-BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_smallest ) {
119+BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_largest ) {
120+ ext_vars v;
121+ v.attr["server_max_window_bits"] = "9";
122+
123+ v.ec = v.exts.set_server_max_window_bits(15,pmd_mode::largest);
124+ v.esp = v.exts.negotiate(v.attr);
125+ BOOST_CHECK( v.exts.is_enabled() );
126+ BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
127+ BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
128+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; server_max_window_bits=9");
129+}
130+
131+BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_smallest_8 ) {
132 ext_vars v;
133 v.attr["server_max_window_bits"] = "8";
134
135@@ -256,7 +287,19 @@ BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_smallest ) {
136 BOOST_CHECK( v.exts.is_enabled() );
137 BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
138 BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
139- BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; server_max_window_bits=8");
140+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; server_max_window_bits=9");
141+}
142+
143+BOOST_AUTO_TEST_CASE( negotiate_server_max_window_bits_smallest ) {
144+ ext_vars v;
145+ v.attr["server_max_window_bits"] = "9";
146+
147+ v.ec = v.exts.set_server_max_window_bits(15,pmd_mode::smallest);
148+ v.esp = v.exts.negotiate(v.attr);
149+ BOOST_CHECK( v.exts.is_enabled() );
150+ BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
151+ BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
152+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; server_max_window_bits=9");
153 }
154
155 // Negotiate server_max_window_bits
156@@ -292,7 +335,13 @@ BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_valid ) {
157 v.esp = v.exts.negotiate(v.attr);
158 BOOST_CHECK( v.exts.is_enabled() );
159 BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
160- BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; client_max_window_bits=8");
161+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; client_max_window_bits=9");
162+
163+ v.attr["client_max_window_bits"] = "9";
164+ v.esp = v.exts.negotiate(v.attr);
165+ BOOST_CHECK( v.exts.is_enabled() );
166+ BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
167+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; client_max_window_bits=9");
168
169 v.attr["client_max_window_bits"] = "15";
170 v.esp = v.exts.negotiate(v.attr);
171@@ -311,7 +360,7 @@ BOOST_AUTO_TEST_CASE( invalid_set_client_max_window_bits ) {
172 BOOST_CHECK_EQUAL(v.ec,pmde::make_error_code(pmde::invalid_max_window_bits));
173 }
174
175-BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_decline ) {
176+BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_decline_8 ) {
177 ext_vars v;
178 v.attr["client_max_window_bits"] = "8";
179
180@@ -323,7 +372,19 @@ BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_decline ) {
181 BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate");
182 }
183
184-BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_accept ) {
185+BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_decline ) {
186+ ext_vars v;
187+ v.attr["client_max_window_bits"] = "9";
188+
189+ v.ec = v.exts.set_client_max_window_bits(9,pmd_mode::decline);
190+ v.esp = v.exts.negotiate(v.attr);
191+ BOOST_CHECK( v.exts.is_enabled() );
192+ BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
193+ BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
194+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate");
195+}
196+
197+BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_accept_8 ) {
198 ext_vars v;
199 v.attr["client_max_window_bits"] = "8";
200
201@@ -332,10 +393,22 @@ BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_accept ) {
202 BOOST_CHECK( v.exts.is_enabled() );
203 BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
204 BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
205- BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; client_max_window_bits=8");
206+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; client_max_window_bits=9");
207 }
208
209-BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_largest ) {
210+BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_accept ) {
211+ ext_vars v;
212+ v.attr["client_max_window_bits"] = "9";
213+
214+ v.ec = v.exts.set_client_max_window_bits(15,pmd_mode::accept);
215+ v.esp = v.exts.negotiate(v.attr);
216+ BOOST_CHECK( v.exts.is_enabled() );
217+ BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
218+ BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
219+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; client_max_window_bits=9");
220+}
221+
222+BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_largest_8 ) {
223 ext_vars v;
224 v.attr["client_max_window_bits"] = "8";
225
226@@ -344,10 +417,22 @@ BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_largest ) {
227 BOOST_CHECK( v.exts.is_enabled() );
228 BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
229 BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
230- BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; client_max_window_bits=8");
231+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; client_max_window_bits=9");
232 }
233
234-BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_smallest ) {
235+BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_largest ) {
236+ ext_vars v;
237+ v.attr["client_max_window_bits"] = "9";
238+
239+ v.ec = v.exts.set_client_max_window_bits(15,pmd_mode::largest);
240+ v.esp = v.exts.negotiate(v.attr);
241+ BOOST_CHECK( v.exts.is_enabled() );
242+ BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
243+ BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
244+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; client_max_window_bits=9");
245+}
246+
247+BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_smallest_8 ) {
248 ext_vars v;
249 v.attr["client_max_window_bits"] = "8";
250
251@@ -356,7 +441,19 @@ BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_smallest ) {
252 BOOST_CHECK( v.exts.is_enabled() );
253 BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
254 BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
255- BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; client_max_window_bits=8");
256+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; client_max_window_bits=9");
257+}
258+
259+BOOST_AUTO_TEST_CASE( negotiate_client_max_window_bits_smallest ) {
260+ ext_vars v;
261+ v.attr["client_max_window_bits"] = "9";
262+
263+ v.ec = v.exts.set_client_max_window_bits(15,pmd_mode::smallest);
264+ v.esp = v.exts.negotiate(v.attr);
265+ BOOST_CHECK( v.exts.is_enabled() );
266+ BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
267+ BOOST_CHECK_EQUAL( v.esp.first, websocketpp::lib::error_code() );
268+ BOOST_CHECK_EQUAL( v.esp.second, "permessage-deflate; client_max_window_bits=9");
269 }
270
271
272@@ -507,7 +604,8 @@ BOOST_AUTO_TEST_CASE( compress_data ) {
273 std::string compress_out;
274 std::string decompress_out;
275
276- v.exts.init(true);
277+ v.ec = v.exts.init(true);
278+ BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
279
280 v.ec = v.exts.compress(compress_in,compress_out);
281 BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
282@@ -520,7 +618,8 @@ BOOST_AUTO_TEST_CASE( compress_data ) {
283 BOOST_AUTO_TEST_CASE( compress_data_multiple ) {
284 ext_vars v;
285
286- v.exts.init(true);
287+ v.ec = v.exts.init(true);
288+ BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
289
290 for (int i = 0; i < 2; i++) {
291 std::string compress_in = "Hello";
292@@ -545,11 +644,12 @@ BOOST_AUTO_TEST_CASE( compress_data_large ) {
293
294 websocketpp::http::attribute_list alist;
295
296- alist["server_max_window_bits"] = "8";
297- v.exts.set_server_max_window_bits(8,websocketpp::extensions::permessage_deflate::mode::smallest);
298+ alist["server_max_window_bits"] = "9";
299+ v.exts.set_server_max_window_bits(9,websocketpp::extensions::permessage_deflate::mode::smallest);
300
301 v.exts.negotiate(alist);
302- v.exts.init(true);
303+ v.ec = v.exts.init(true);
304+ BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
305
306 v.ec = v.exts.compress(compress_in,compress_out);
307 BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
308@@ -573,7 +673,8 @@ BOOST_AUTO_TEST_CASE( compress_data_no_context_takeover ) {
309 v.exts.enable_server_no_context_takeover();
310
311 v.exts.negotiate(alist);
312- v.exts.init(true);
313+ v.ec = v.exts.init(true);
314+ BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
315
316 v.ec = v.exts.compress(compress_in,compress_out1);
317 BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
318@@ -609,7 +710,8 @@ BOOST_AUTO_TEST_CASE( compress_empty ) {
319 std::string compress_out;
320 std::string decompress_out;
321
322- v.exts.init(true);
323+ v.ec = v.exts.init(true);
324+ BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
325
326 v.ec = v.exts.compress(compress_in,compress_out);
327 BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
328@@ -640,7 +742,8 @@ BOOST_AUTO_TEST_CASE( decompress_data ) {
329 std::string out;
330 std::string reference = "Hello";
331
332- v.exts.init(true);
333+ v.ec = v.exts.init(true);
334+ BOOST_CHECK_EQUAL( v.ec, websocketpp::lib::error_code() );
335
336 v.ec = v.exts.decompress(in,11,out);
337
338diff --git a/websocketpp/extensions/permessage_deflate/enabled.hpp b/websocketpp/extensions/permessage_deflate/enabled.hpp
339index 1581f14c..f20a1b1d 100644
340--- a/websocketpp/extensions/permessage_deflate/enabled.hpp
341+++ b/websocketpp/extensions/permessage_deflate/enabled.hpp
342@@ -46,7 +46,7 @@
343 namespace websocketpp {
344 namespace extensions {
345
346-/// Implementation of the draft permessage-deflate WebSocket extension
347+/// Implementation of RFC 7692, the permessage-deflate WebSocket extension
348 /**
349 * ### permessage-deflate interface
350 *
351@@ -174,18 +174,30 @@ namespace websocketpp {
352 namespace extensions {
353 namespace permessage_deflate {
354
355-/// Default value for server_max_window_bits as defined by draft 17
356+/// Default value for server_max_window_bits as defined by RFC 7692
357 static uint8_t const default_server_max_window_bits = 15;
358-/// Minimum value for server_max_window_bits as defined by draft 17
359+/// Minimum value for server_max_window_bits as defined by RFC 7692
360+/**
361+ * NOTE: A value of 8 is not actually supported by zlib, the deflate
362+ * library that WebSocket++ uses. To preserve backwards compatibility
363+ * with RFC 7692 and previous versions of the library a value of 8
364+ * is accepted by the library but will always be negotiated as 9.
365+ */
366 static uint8_t const min_server_max_window_bits = 8;
367-/// Maximum value for server_max_window_bits as defined by draft 17
368+/// Maximum value for server_max_window_bits as defined by RFC 7692
369 static uint8_t const max_server_max_window_bits = 15;
370
371-/// Default value for client_max_window_bits as defined by draft 17
372+/// Default value for client_max_window_bits as defined by RFC 7692
373 static uint8_t const default_client_max_window_bits = 15;
374-/// Minimum value for client_max_window_bits as defined by draft 17
375+/// Minimum value for client_max_window_bits as defined by RFC 7692
376+/**
377+ * NOTE: A value of 8 is not actually supported by zlib, the deflate
378+ * library that WebSocket++ uses. To preserve backwards compatibility
379+ * with RFC 7692 and previous versions of the library a value of 8
380+ * is accepted by the library but will always be negotiated as 9.
381+ */
382 static uint8_t const min_client_max_window_bits = 8;
383-/// Maximum value for client_max_window_bits as defined by draft 17
384+/// Maximum value for client_max_window_bits as defined by RFC 7692
385 static uint8_t const max_client_max_window_bits = 15;
386
387 namespace mode {
388@@ -372,7 +384,7 @@ class enabled {
389 /**
390 * The bits setting is the base 2 logarithm of the maximum window size that
391 * the server must use to compress outgoing messages. The permitted range
392- * is 8 to 15 inclusive. 8 represents a 256 byte window and 15 a 32KiB
393+ * is 9 to 15 inclusive. 9 represents a 512 byte window and 15 a 32KiB
394 * window. The default setting is 15.
395 *
396 * Mode Options:
397@@ -386,6 +398,14 @@ class enabled {
398 * adjusted by the server. A server may unilaterally set this value without
399 * client support.
400 *
401+ * NOTE: The permessage-deflate spec specifies that a value of 8 is allowed.
402+ * Prior to version 0.8.0 a value of 8 was also allowed by this library.
403+ * zlib, the deflate compression library that WebSocket++ uses has always
404+ * silently adjusted a value of 8 to 9. In recent versions of zlib (1.2.9
405+ * and greater) a value of 8 is now explicitly rejected. WebSocket++ 0.8.0
406+ * continues to perform the 8->9 conversion for backwards compatibility
407+ * purposes but this should be considered deprecated functionality.
408+ *
409 * @param bits The size to request for the outgoing window size
410 * @param mode The mode to use for negotiating this parameter
411 * @return A status code
412@@ -394,6 +414,12 @@ class enabled {
413 if (bits < min_server_max_window_bits || bits > max_server_max_window_bits) {
414 return error::make_error_code(error::invalid_max_window_bits);
415 }
416+
417+ // See note in doc comment above about what is happening here
418+ if (bits == 8) {
419+ bits = 9;
420+ }
421+
422 m_server_max_window_bits = bits;
423 m_server_max_window_bits_mode = mode;
424
425@@ -403,8 +429,8 @@ class enabled {
426 /// Limit client LZ77 sliding window size
427 /**
428 * The bits setting is the base 2 logarithm of the window size that the
429- * client must use to compress outgoing messages. The permitted range is 8
430- * to 15 inclusive. 8 represents a 256 byte window and 15 a 32KiB window.
431+ * client must use to compress outgoing messages. The permitted range is 9
432+ * to 15 inclusive. 9 represents a 512 byte window and 15 a 32KiB window.
433 * The default setting is 15.
434 *
435 * Mode Options:
436@@ -417,6 +443,14 @@ class enabled {
437 * outgoing window size unilaterally. A server may only limit the client's
438 * window size if the remote client supports that feature.
439 *
440+ * NOTE: The permessage-deflate spec specifies that a value of 8 is allowed.
441+ * Prior to version 0.8.0 a value of 8 was also allowed by this library.
442+ * zlib, the deflate compression library that WebSocket++ uses has always
443+ * silently adjusted a value of 8 to 9. In recent versions of zlib (1.2.9
444+ * and greater) a value of 8 is now explicitly rejected. WebSocket++ 0.8.0
445+ * continues to perform the 8->9 conversion for backwards compatibility
446+ * purposes but this should be considered deprecated functionality.
447+ *
448 * @param bits The size to request for the outgoing window size
449 * @param mode The mode to use for negotiating this parameter
450 * @return A status code
451@@ -425,6 +459,12 @@ class enabled {
452 if (bits < min_client_max_window_bits || bits > max_client_max_window_bits) {
453 return error::make_error_code(error::invalid_max_window_bits);
454 }
455+
456+ // See note in doc comment above about what is happening here
457+ if (bits == 8) {
458+ bits = 9;
459+ }
460+
461 m_client_max_window_bits = bits;
462 m_client_max_window_bits_mode = mode;
463
464@@ -642,11 +682,17 @@ class enabled {
465 * client requested that we use.
466 *
467 * options:
468- * - decline (refuse to use the attribute)
469- * - accept (use whatever the client says)
470- * - largest (use largest possible value)
471+ * - decline (ignore value, offer our default instead)
472+ * - accept (use the value requested by the client)
473+ * - largest (use largest value acceptable to both)
474 * - smallest (use smallest possible value)
475 *
476+ * NOTE: As a value of 8 is no longer explicitly supported by zlib but might
477+ * be requested for negotiation by an older client/server, if the result of
478+ * the negotiation would be to send a value of 8, a value of 9 is offered
479+ * instead. This ensures that WebSocket++ will only ever negotiate connections
480+ * with compression settings explicitly supported by zlib.
481+ *
482 * @param [in] value The value of the attribute from the offer
483 * @param [out] ec A reference to the error code to return errors via
484 */
485@@ -678,6 +724,11 @@ class enabled {
486 ec = make_error_code(error::invalid_mode);
487 m_server_max_window_bits = default_server_max_window_bits;
488 }
489+
490+ // See note in doc comment
491+ if (m_server_max_window_bits == 8) {
492+ m_server_max_window_bits = 9;
493+ }
494 }
495
496 /// Negotiate client_max_window_bits attribute
497@@ -687,11 +738,17 @@ class enabled {
498 * negotiation mode.
499 *
500 * options:
501- * - decline (refuse to use the attribute)
502- * - accept (use whatever the client says)
503- * - largest (use largest possible value)
504+ * - decline (ignore value, offer our default instead)
505+ * - accept (use the value requested by the client)
506+ * - largest (use largest value acceptable to both)
507 * - smallest (use smallest possible value)
508 *
509+ * NOTE: As a value of 8 is no longer explicitly supported by zlib but might
510+ * be requested for negotiation by an older client/server, if the result of
511+ * the negotiation would be to send a value of 8, a value of 9 is offered
512+ * instead. This ensures that WebSocket++ will only ever negotiate connections
513+ * with compression settings explicitly supported by zlib.
514+ *
515 * @param [in] value The value of the attribute from the offer
516 * @param [out] ec A reference to the error code to return errors via
517 */
518@@ -727,6 +784,11 @@ class enabled {
519 ec = make_error_code(error::invalid_mode);
520 m_client_max_window_bits = default_client_max_window_bits;
521 }
522+
523+ // See note in doc comment
524+ if (m_client_max_window_bits == 8) {
525+ m_client_max_window_bits = 9;
526+ }
527 }
528
529 bool m_enabled;
530diff --git a/websocketpp/impl/connection_impl.hpp b/websocketpp/impl/connection_impl.hpp
531index 105911db..ae55c338 100644
532--- a/websocketpp/impl/connection_impl.hpp
533+++ b/websocketpp/impl/connection_impl.hpp
534@@ -1222,12 +1222,18 @@ lib::error_code connection<config>::process_handshake_request() {
535 std::pair<lib::error_code,std::string> neg_results;
536 neg_results = m_processor->negotiate_extensions(m_request);
537
538- if (neg_results.first) {
539+ if (neg_results.first == error::make_error_code(error::extension_parse_error)) {
540 // There was a fatal error in extension parsing that should result in
541 // a failed connection attempt.
542- m_alog.write(log::alevel::devel, "Bad request: " + neg_results.first.message());
543+ m_alog.write(log::alevel::info, "Bad request: " + neg_results.first.message());
544 m_response.set_status(http::status_code::bad_request);
545 return neg_results.first;
546+ } else if (neg_results.first) {
547+ // There was a fatal error in extension processing that is probably our
548+ // fault. Consider extension negotiation to have failed and continue as
549+ // if extensions were not supported
550+ m_alog.write(log::alevel::info,
551+ "Extension negotiation failed: " + neg_results.first.message());
552 } else {
553 // extension negotiation succeeded, set response header accordingly
554 // we don't send an empty extensions header because it breaks many
555diff --git a/websocketpp/processors/hybi13.hpp b/websocketpp/processors/hybi13.hpp
556index 79486654..a95bc649 100644
557--- a/websocketpp/processors/hybi13.hpp
558+++ b/websocketpp/processors/hybi13.hpp
559@@ -97,6 +97,11 @@ class hybi13 : public processor<config> {
560 /**
561 * This exists mostly because the code for requests and responses is
562 * identical and I can't have virtual template methods.
563+ *
564+ * NOTE: this method makes assumptions that the permessage-deflate
565+ * extension is the only one supported. If additional extensions are
566+ * ever supported it should be reviewed carefully. Most cases where
567+ * that assumption is made are explicitly noted.
568 */
569 template <typename header_type>
570 err_str_pair negotiate_extensions_helper(header_type const & header) {
571@@ -149,9 +154,26 @@ class hybi13 : public processor<config> {
572 } else {
573 // Note: this list will need commas if WebSocket++ ever
574 // supports more than one extension
575- ret.second += neg_ret.second;
576- m_permessage_deflate.init(base::m_server);
577- continue;
578+
579+ // Actually try to initialize the extension before we
580+ // deem negotiation complete
581+ ret.first = m_permessage_deflate.init(base::m_server);
582+ if (!ret.first) {
583+
584+ // TODO: support multiple extensions.
585+ // right now, because there is only one extension
586+ // supported, it failing to negotiate means we are
587+ // done with all negotiating. In the future if more
588+ // extensions are supported a better solution will
589+ // be needed here.
590+ break;
591+ } else {
592+ ret.second += neg_ret.second;
593+
594+ // continue looking for more extensions
595+ continue;
596+ }
597+
598 }
599 }
600 }