@encharm/cws 4.5.3 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/CLAUDE.md +83 -0
  3. package/README.md +20 -4
  4. package/binding.gyp +8 -2
  5. package/dist/bindings/cws_darwin_arm64_node115.node +0 -0
  6. package/dist/bindings/cws_darwin_arm64_node127.node +0 -0
  7. package/dist/bindings/cws_darwin_arm64_node137.node +0 -0
  8. package/dist/bindings/cws_darwin_arm64_node147.node +0 -0
  9. package/dist/bindings/cws_linux_arm64_node115.node +0 -0
  10. package/dist/bindings/cws_linux_arm64_node127.node +0 -0
  11. package/dist/bindings/cws_linux_arm64_node137.node +0 -0
  12. package/dist/bindings/cws_linux_arm64_node147.node +0 -0
  13. package/dist/bindings/cws_linux_x64_node115.node +0 -0
  14. package/dist/bindings/cws_linux_x64_node127.node +0 -0
  15. package/dist/bindings/cws_linux_x64_node137.node +0 -0
  16. package/dist/bindings/cws_linux_x64_node147.node +0 -0
  17. package/dist/bindings/cws_win32_x64_node115.node +0 -0
  18. package/dist/bindings/cws_win32_x64_node127.node +0 -0
  19. package/dist/bindings/cws_win32_x64_node137.node +0 -0
  20. package/dist/bindings/cws_win32_x64_node147.node +0 -0
  21. package/dist/client.d.ts +1 -0
  22. package/dist/client.js +15 -1
  23. package/dist/index.d.ts +6 -1
  24. package/dist/index.js +2 -0
  25. package/dist/server.d.ts +1 -0
  26. package/dist/server.js +16 -2
  27. package/dist/shared.d.ts +1 -0
  28. package/dist/shared.js +3 -2
  29. package/package.json +1 -1
  30. package/src/Addon.cpp +4 -1
  31. package/src/Addon.h +80 -15
  32. package/src/Extensions.cpp +7 -1
  33. package/src/Extensions.h +2 -1
  34. package/src/Group.cpp +1 -1
  35. package/src/Group.h +4 -1
  36. package/src/HTTPSocket.cpp +1 -1
  37. package/src/Hub.cpp +11 -76
  38. package/src/Hub.h +12 -10
  39. package/src/Networking.h +11 -0
  40. package/src/Socket.h +201 -6
  41. package/src/WebSocket.cpp +8 -5
  42. package/src/Zlib.cpp +138 -0
  43. package/src/Zlib.h +38 -0
  44. package/src/cSNode.cpp +2 -0
  45. package/src/cSNode.h +4 -0
  46. package/src/headers/26/acorn_version.h +6 -0
  47. package/src/headers/26/aliased_buffer-inl.h +264 -0
  48. package/src/headers/26/aliased_buffer.h +205 -0
  49. package/src/headers/26/aliased_struct-inl.h +143 -0
  50. package/src/headers/26/aliased_struct.h +248 -0
  51. package/src/headers/26/amaro_version.h +6 -0
  52. package/src/headers/26/async_context_frame.h +33 -0
  53. package/src/headers/26/async_wrap-inl.h +123 -0
  54. package/src/headers/26/async_wrap.h +254 -0
  55. package/src/headers/26/base_object-inl.h +333 -0
  56. package/src/headers/26/base_object.h +339 -0
  57. package/src/headers/26/base_object_types.h +76 -0
  58. package/src/headers/26/blob_serializer_deserializer-inl.h +385 -0
  59. package/src/headers/26/blob_serializer_deserializer.h +142 -0
  60. package/src/headers/26/builtin_info.h +57 -0
  61. package/src/headers/26/callback_queue-inl.h +97 -0
  62. package/src/headers/26/callback_queue.h +79 -0
  63. package/src/headers/26/cares_wrap.h +475 -0
  64. package/src/headers/26/cleanup_queue-inl.h +36 -0
  65. package/src/headers/26/cleanup_queue.h +79 -0
  66. package/src/headers/26/compile_cache.h +134 -0
  67. package/src/headers/26/connect_wrap.h +26 -0
  68. package/src/headers/26/connection_wrap.h +30 -0
  69. package/src/headers/26/cppgc_helpers-inl.h +69 -0
  70. package/src/headers/26/cppgc_helpers.h +162 -0
  71. package/src/headers/26/crypto/crypto_aes.h +107 -0
  72. package/src/headers/26/crypto/crypto_argon2.h +88 -0
  73. package/src/headers/26/crypto/crypto_bio.h +193 -0
  74. package/src/headers/26/crypto/crypto_chacha20_poly1305.h +62 -0
  75. package/src/headers/26/crypto/crypto_cipher.h +291 -0
  76. package/src/headers/26/crypto/crypto_common.h +48 -0
  77. package/src/headers/26/crypto/crypto_context.h +219 -0
  78. package/src/headers/26/crypto/crypto_dh.h +104 -0
  79. package/src/headers/26/crypto/crypto_dsa.h +50 -0
  80. package/src/headers/26/crypto/crypto_ec.h +102 -0
  81. package/src/headers/26/crypto/crypto_hash.h +157 -0
  82. package/src/headers/26/crypto/crypto_hkdf.h +62 -0
  83. package/src/headers/26/crypto/crypto_hmac.h +92 -0
  84. package/src/headers/26/crypto/crypto_kem.h +131 -0
  85. package/src/headers/26/crypto/crypto_keygen.h +382 -0
  86. package/src/headers/26/crypto/crypto_keys.h +366 -0
  87. package/src/headers/26/crypto/crypto_kmac.h +79 -0
  88. package/src/headers/26/crypto/crypto_pbkdf2.h +76 -0
  89. package/src/headers/26/crypto/crypto_pqc.h +39 -0
  90. package/src/headers/26/crypto/crypto_random.h +127 -0
  91. package/src/headers/26/crypto/crypto_rsa.h +108 -0
  92. package/src/headers/26/crypto/crypto_scrypt.h +85 -0
  93. package/src/headers/26/crypto/crypto_sig.h +159 -0
  94. package/src/headers/26/crypto/crypto_spkac.h +21 -0
  95. package/src/headers/26/crypto/crypto_timing.h +20 -0
  96. package/src/headers/26/crypto/crypto_tls.h +342 -0
  97. package/src/headers/26/crypto/crypto_turboshake.h +105 -0
  98. package/src/headers/26/crypto/crypto_util.h +775 -0
  99. package/src/headers/26/crypto/crypto_x509.h +140 -0
  100. package/src/headers/26/dataqueue/queue.h +311 -0
  101. package/src/headers/26/debug_utils-inl.h +291 -0
  102. package/src/headers/26/debug_utils.h +200 -0
  103. package/src/headers/26/diagnosticfilename-inl.h +33 -0
  104. package/src/headers/26/embedded_data.h +17 -0
  105. package/src/headers/26/encoding_binding.h +59 -0
  106. package/src/headers/26/env-inl.h +956 -0
  107. package/src/headers/26/env.h +1294 -0
  108. package/src/headers/26/env_properties.h +562 -0
  109. package/src/headers/26/ffi/data.h +30 -0
  110. package/src/headers/26/ffi/fast.h +96 -0
  111. package/src/headers/26/ffi/jit_memory.h +26 -0
  112. package/src/headers/26/ffi/types.h +99 -0
  113. package/src/headers/26/handle_wrap.h +123 -0
  114. package/src/headers/26/histogram-inl.h +211 -0
  115. package/src/headers/26/histogram.h +474 -0
  116. package/src/headers/26/inspector/dom_storage_agent.h +68 -0
  117. package/src/headers/26/inspector/inspector_object_utils.h +38 -0
  118. package/src/headers/26/inspector/io_agent.h +30 -0
  119. package/src/headers/26/inspector/main_thread_interface.h +116 -0
  120. package/src/headers/26/inspector/network_agent.h +110 -0
  121. package/src/headers/26/inspector/network_inspector.h +45 -0
  122. package/src/headers/26/inspector/network_requests_buffer.h +195 -0
  123. package/src/headers/26/inspector/network_resource_manager.h +29 -0
  124. package/src/headers/26/inspector/node_json.h +24 -0
  125. package/src/headers/26/inspector/node_string.h +101 -0
  126. package/src/headers/26/inspector/notification_emitter.h +38 -0
  127. package/src/headers/26/inspector/protocol_helper.h +27 -0
  128. package/src/headers/26/inspector/runtime_agent.h +41 -0
  129. package/src/headers/26/inspector/storage_agent.h +33 -0
  130. package/src/headers/26/inspector/target_agent.h +59 -0
  131. package/src/headers/26/inspector/target_manager.h +70 -0
  132. package/src/headers/26/inspector/tracing_agent.h +43 -0
  133. package/src/headers/26/inspector/worker_agent.h +40 -0
  134. package/src/headers/26/inspector/worker_inspector.h +124 -0
  135. package/src/headers/26/inspector_agent.h +168 -0
  136. package/src/headers/26/inspector_io.h +78 -0
  137. package/src/headers/26/inspector_profiler.h +149 -0
  138. package/src/headers/26/inspector_socket.h +60 -0
  139. package/src/headers/26/inspector_socket_server.h +110 -0
  140. package/src/headers/26/js_native_api.h +634 -0
  141. package/src/headers/26/js_native_api_types.h +244 -0
  142. package/src/headers/26/js_native_api_v8.h +485 -0
  143. package/src/headers/26/js_native_api_v8_internals.h +45 -0
  144. package/src/headers/26/js_stream.h +57 -0
  145. package/src/headers/26/json_utils.h +172 -0
  146. package/src/headers/26/large_pages/node_large_page.h +34 -0
  147. package/src/headers/26/lru_cache-inl.h +69 -0
  148. package/src/headers/26/memory_tracker-inl.h +461 -0
  149. package/src/headers/26/memory_tracker.h +341 -0
  150. package/src/headers/26/module_wrap.h +231 -0
  151. package/src/headers/26/ncrypto.h +2008 -0
  152. package/src/headers/26/node.h +1693 -0
  153. package/src/headers/26/node_api.h +265 -0
  154. package/src/headers/26/node_api_internals.h +48 -0
  155. package/src/headers/26/node_api_types.h +58 -0
  156. package/src/headers/26/node_binding.h +170 -0
  157. package/src/headers/26/node_blob.h +160 -0
  158. package/src/headers/26/node_bob-inl.h +36 -0
  159. package/src/headers/26/node_bob.h +107 -0
  160. package/src/headers/26/node_buffer.h +92 -0
  161. package/src/headers/26/node_builtins.h +239 -0
  162. package/src/headers/26/node_concepts.h +46 -0
  163. package/src/headers/26/node_config_file.h +77 -0
  164. package/src/headers/26/node_constants.h +82 -0
  165. package/src/headers/26/node_context_data.h +171 -0
  166. package/src/headers/26/node_contextify.h +261 -0
  167. package/src/headers/26/node_crypto.h +66 -0
  168. package/src/headers/26/node_debug.h +30 -0
  169. package/src/headers/26/node_diagnostics_channel.h +121 -0
  170. package/src/headers/26/node_dir.h +52 -0
  171. package/src/headers/26/node_dotenv.h +45 -0
  172. package/src/headers/26/node_errors.h +378 -0
  173. package/src/headers/26/node_exit_code.h +54 -0
  174. package/src/headers/26/node_external_reference.h +171 -0
  175. package/src/headers/26/node_ffi.h +212 -0
  176. package/src/headers/26/node_file-inl.h +421 -0
  177. package/src/headers/26/node_file.h +561 -0
  178. package/src/headers/26/node_file_utils.h +39 -0
  179. package/src/headers/26/node_hash.h +212 -0
  180. package/src/headers/26/node_http2.h +1202 -0
  181. package/src/headers/26/node_http2_state.h +159 -0
  182. package/src/headers/26/node_http_common-inl.h +199 -0
  183. package/src/headers/26/node_http_common.h +535 -0
  184. package/src/headers/26/node_i18n.h +132 -0
  185. package/src/headers/26/node_internals.h +485 -0
  186. package/src/headers/26/node_locks.h +184 -0
  187. package/src/headers/26/node_main_instance.h +67 -0
  188. package/src/headers/26/node_mem-inl.h +113 -0
  189. package/src/headers/26/node_mem.h +45 -0
  190. package/src/headers/26/node_messaging.h +389 -0
  191. package/src/headers/26/node_metadata.h +172 -0
  192. package/src/headers/26/node_modules.h +102 -0
  193. package/src/headers/26/node_mutex.h +323 -0
  194. package/src/headers/26/node_object_wrap.h +145 -0
  195. package/src/headers/26/node_options-inl.h +524 -0
  196. package/src/headers/26/node_options.h +710 -0
  197. package/src/headers/26/node_perf.h +167 -0
  198. package/src/headers/26/node_perf_common.h +103 -0
  199. package/src/headers/26/node_platform.h +290 -0
  200. package/src/headers/26/node_process-inl.h +26 -0
  201. package/src/headers/26/node_process.h +112 -0
  202. package/src/headers/26/node_profiling.h +38 -0
  203. package/src/headers/26/node_realm-inl.h +156 -0
  204. package/src/headers/26/node_realm.h +236 -0
  205. package/src/headers/26/node_report.h +43 -0
  206. package/src/headers/26/node_revert.h +82 -0
  207. package/src/headers/26/node_root_certs.h +2937 -0
  208. package/src/headers/26/node_sea.h +101 -0
  209. package/src/headers/26/node_shadow_realm.h +46 -0
  210. package/src/headers/26/node_snapshot_builder.h +57 -0
  211. package/src/headers/26/node_snapshotable.h +167 -0
  212. package/src/headers/26/node_sockaddr-inl.h +248 -0
  213. package/src/headers/26/node_sockaddr.h +459 -0
  214. package/src/headers/26/node_sqlite.h +607 -0
  215. package/src/headers/26/node_stat_watcher.h +73 -0
  216. package/src/headers/26/node_task_runner.h +92 -0
  217. package/src/headers/26/node_threadsafe_cow-inl.h +54 -0
  218. package/src/headers/26/node_threadsafe_cow.h +105 -0
  219. package/src/headers/26/node_union_bytes.h +81 -0
  220. package/src/headers/26/node_url.h +101 -0
  221. package/src/headers/26/node_url_pattern.h +118 -0
  222. package/src/headers/26/node_v8.h +78 -0
  223. package/src/headers/26/node_v8_embedder.h +26 -0
  224. package/src/headers/26/node_v8_platform-inl.h +96 -0
  225. package/src/headers/26/node_version.h +116 -0
  226. package/src/headers/26/node_wasi.h +184 -0
  227. package/src/headers/26/node_wasm_web_api.h +55 -0
  228. package/src/headers/26/node_watchdog.h +164 -0
  229. package/src/headers/26/node_webstorage.h +62 -0
  230. package/src/headers/26/node_worker.h +162 -0
  231. package/src/headers/26/path.h +36 -0
  232. package/src/headers/26/permission/boolean_permission.h +50 -0
  233. package/src/headers/26/permission/fs_permission.h +192 -0
  234. package/src/headers/26/permission/permission.h +155 -0
  235. package/src/headers/26/permission/permission_base.h +79 -0
  236. package/src/headers/26/pipe_wrap.h +80 -0
  237. package/src/headers/26/quic/application.h +279 -0
  238. package/src/headers/26/quic/arena.h +373 -0
  239. package/src/headers/26/quic/bindingdata.h +440 -0
  240. package/src/headers/26/quic/cid.h +127 -0
  241. package/src/headers/26/quic/data.h +358 -0
  242. package/src/headers/26/quic/defs.h +417 -0
  243. package/src/headers/26/quic/endpoint.h +519 -0
  244. package/src/headers/26/quic/guard.h +5 -0
  245. package/src/headers/26/quic/http3.h +25 -0
  246. package/src/headers/26/quic/packet.h +166 -0
  247. package/src/headers/26/quic/preferredaddress.h +72 -0
  248. package/src/headers/26/quic/session.h +770 -0
  249. package/src/headers/26/quic/session_manager.h +107 -0
  250. package/src/headers/26/quic/sessionticket.h +110 -0
  251. package/src/headers/26/quic/streams.h +518 -0
  252. package/src/headers/26/quic/tlscontext.h +353 -0
  253. package/src/headers/26/quic/tokens.h +263 -0
  254. package/src/headers/26/quic/transportparams.h +184 -0
  255. package/src/headers/26/req_wrap-inl.h +175 -0
  256. package/src/headers/26/req_wrap.h +79 -0
  257. package/src/headers/26/spawn_sync.h +243 -0
  258. package/src/headers/26/stream_base-inl.h +170 -0
  259. package/src/headers/26/stream_base.h +487 -0
  260. package/src/headers/26/stream_pipe.h +76 -0
  261. package/src/headers/26/stream_wrap.h +137 -0
  262. package/src/headers/26/string_bytes.h +114 -0
  263. package/src/headers/26/string_decoder-inl.h +31 -0
  264. package/src/headers/26/string_decoder.h +49 -0
  265. package/src/headers/26/tcp_wrap.h +138 -0
  266. package/src/headers/26/threadpoolwork-inl.h +70 -0
  267. package/src/headers/26/timer_wrap-inl.h +32 -0
  268. package/src/headers/26/timer_wrap.h +87 -0
  269. package/src/headers/26/timers.h +70 -0
  270. package/src/headers/26/tracing/agent.h +125 -0
  271. package/src/headers/26/tracing/agent_legacy.h +146 -0
  272. package/src/headers/26/tracing/agent_perfetto.h +145 -0
  273. package/src/headers/26/tracing/node_trace_buffer.h +91 -0
  274. package/src/headers/26/tracing/node_trace_writer.h +79 -0
  275. package/src/headers/26/tracing/trace_event.h +27 -0
  276. package/src/headers/26/tracing/trace_event_helper.h +40 -0
  277. package/src/headers/26/tracing/trace_event_legacy.h +1113 -0
  278. package/src/headers/26/tracing/trace_event_legacy_inl.h +711 -0
  279. package/src/headers/26/tracing/trace_event_perfetto.h +47 -0
  280. package/src/headers/26/tracing/traced_value.h +167 -0
  281. package/src/headers/26/tty_wrap.h +65 -0
  282. package/src/headers/26/udp_wrap.h +231 -0
  283. package/src/headers/26/undici_version.h +6 -0
  284. package/src/headers/26/util-inl.h +788 -0
  285. package/src/headers/26/util.h +1207 -0
  286. package/src/headers/26/v8-cpp-heap-external.h +56 -0
  287. package/src/headers/26/v8-external-memory-accounter.h +60 -0
  288. package/src/headers/26/v8-fast-api-calls.h +784 -0
  289. package/src/headers/26/v8-inspector-protocol.h +13 -0
  290. package/src/headers/26/v8-inspector.h +458 -0
  291. package/src/headers/26/v8-metrics.h +273 -0
  292. package/src/headers/26/v8-trace-categories.h +28 -0
  293. package/src/headers/26/v8-unwinder-state.h +30 -0
  294. package/src/headers/26/v8-util.h +549 -0
  295. package/src/headers/26/v8-value-serializer-version.h +24 -0
  296. package/src/headers/26/v8-version-string.h +38 -0
  297. package/src/headers/26/v8-wasm-trap-handler-posix.h +31 -0
  298. package/src/headers/26/v8-wasm-trap-handler-win.h +28 -0
  299. package/src/headers/26/zlib_version.h +6 -0
  300. package/dist/bindings/cws_darwin_arm64_node115.node.dSYM/Contents/Info.plist +0 -20
  301. package/dist/bindings/cws_darwin_arm64_node115.node.dSYM/Contents/Resources/DWARF/cws_darwin_arm64_node115.node +0 -0
  302. package/dist/bindings/cws_darwin_arm64_node115.node.dSYM/Contents/Resources/Relocations/aarch64/cws_darwin_arm64_node115.node.yml +0 -931
  303. package/dist/bindings/cws_darwin_arm64_node127.node.dSYM/Contents/Info.plist +0 -20
  304. package/dist/bindings/cws_darwin_arm64_node127.node.dSYM/Contents/Resources/DWARF/cws_darwin_arm64_node127.node +0 -0
  305. package/dist/bindings/cws_darwin_arm64_node127.node.dSYM/Contents/Resources/Relocations/aarch64/cws_darwin_arm64_node127.node.yml +0 -931
package/src/Group.h CHANGED
@@ -41,6 +41,9 @@ protected:
41
41
  unsigned int maxPayload;
42
42
  Hub *hub;
43
43
  int extensionOptions;
44
+ int deflateWindowBits = 15;
45
+ int deflateMemLevel = 8;
46
+ int deflateLevel = 1;
44
47
  cS::Timer *timer = nullptr, *httpTimer = nullptr;
45
48
  const char *userPingMessage;
46
49
  size_t userPingMessageLength;
@@ -61,7 +64,7 @@ protected:
61
64
  void addHttpSocket(HttpSocket<isServer> *httpSocket);
62
65
  void removeHttpSocket(HttpSocket<isServer> *httpSocket);
63
66
 
64
- Group(int extensionOptions, unsigned int maxPayload, Hub *hub, cS::NodeData *nodeData);
67
+ Group(int extensionOptions, unsigned int maxPayload, Hub *hub, cS::NodeData *nodeData, int deflateWindowBits = 15, int deflateMemLevel = 8, int deflateLevel = 1);
65
68
  void stopListening();
66
69
 
67
70
  public:
@@ -210,7 +210,7 @@ void HttpSocket<isServer>::upgrade(const char *secKey, const char *extensions, s
210
210
  std::string extensionsResponse;
211
211
  if (extensionsLength) {
212
212
  Group<isServer> *group = Group<isServer>::from(this);
213
- ExtensionsNegotiator<cWS::SERVER> extensionsNegotiator(group->extensionOptions);
213
+ ExtensionsNegotiator<cWS::SERVER> extensionsNegotiator(group->extensionOptions, group->deflateWindowBits);
214
214
  extensionsNegotiator.readOffer(std::string(extensions, extensionsLength));
215
215
  extensionsResponse = extensionsNegotiator.generateOffer();
216
216
  if (extensionsNegotiator.getNegotiatedOptions() & PERMESSAGE_DEFLATE) {
package/src/Hub.cpp CHANGED
@@ -5,87 +5,22 @@
5
5
 
6
6
  namespace cWS {
7
7
 
8
- z_stream *Hub::allocateDefaultCompressor(z_stream *zStream) {
9
- deflateInit2(zStream, 1, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
10
- return zStream;
8
+ // windowBits 9..15 and memLevel 1..9 select the per-socket memory tier (zlib uses
9
+ // about 4x the window plus a 2^(memLevel+9) byte hash table): 15/8 = ~256 KB,
10
+ // 12/5 = ~32 KB, 10/3 = ~8 KB, 9/2 = ~4 KB. Smaller tiers trade ratio for memory.
11
+ // level 1..9: with zlib-ng level 1 is the very fast "quick" strategy (~8% worse
12
+ // ratio than zlib level 1), level 2 matches zlib level 1's ratio at higher speed.
13
+ zlib::Stream *Hub::allocateDefaultCompressor(int level, int windowBits, int memLevel) {
14
+ return zlib::createDeflate(level, windowBits, memLevel);
11
15
  }
12
16
 
13
- char *Hub::deflate(char *data, size_t &length, z_stream *slidingDeflateWindow) {
14
- dynamicZlibBuffer.clear();
15
-
16
- z_stream *compressor = slidingDeflateWindow ? slidingDeflateWindow : &deflationStream;
17
-
18
- compressor->next_in = (Bytef *) data;
19
- compressor->avail_in = (unsigned int) length;
20
-
21
- // note: zlib requires more than 6 bytes with Z_SYNC_FLUSH
22
- const int DEFLATE_OUTPUT_CHUNK = LARGE_BUFFER_SIZE;
23
-
24
- int err;
25
- do {
26
- compressor->next_out = (Bytef *) zlibBuffer;
27
- compressor->avail_out = DEFLATE_OUTPUT_CHUNK;
28
-
29
- err = ::deflate(compressor, Z_SYNC_FLUSH);
30
- if (Z_OK == err && compressor->avail_out == 0) {
31
- dynamicZlibBuffer.append(zlibBuffer, DEFLATE_OUTPUT_CHUNK - compressor->avail_out);
32
- continue;
33
- } else {
34
- break;
35
- }
36
- } while (true);
37
-
38
- // note: should not change avail_out
39
- if (!slidingDeflateWindow) {
40
- deflateReset(compressor);
41
- }
42
-
43
- if (dynamicZlibBuffer.length()) {
44
- dynamicZlibBuffer.append(zlibBuffer, DEFLATE_OUTPUT_CHUNK - compressor->avail_out);
45
-
46
- length = dynamicZlibBuffer.length() - 4;
47
- return (char *) dynamicZlibBuffer.data();
48
- }
49
-
50
- length = DEFLATE_OUTPUT_CHUNK - compressor->avail_out - 4;
51
- return zlibBuffer;
17
+ char *Hub::deflate(char *data, size_t &length, zlib::Stream *slidingDeflateWindow) {
18
+ zlib::Stream *compressor = slidingDeflateWindow ? slidingDeflateWindow : deflationStream;
19
+ return zlib::deflate(compressor, data, length, zlibBuffer, LARGE_BUFFER_SIZE, dynamicZlibBuffer, !slidingDeflateWindow);
52
20
  }
53
21
 
54
- // todo: let's go through this code once more some time!
55
22
  char *Hub::inflate(char *data, size_t &length, size_t maxPayload) {
56
- dynamicZlibBuffer.clear();
57
-
58
- inflationStream.next_in = (Bytef *) data;
59
- inflationStream.avail_in = (unsigned int) length;
60
-
61
- int err;
62
- do {
63
- inflationStream.next_out = (Bytef *) zlibBuffer;
64
- inflationStream.avail_out = LARGE_BUFFER_SIZE;
65
- err = ::inflate(&inflationStream, Z_FINISH);
66
- if (!inflationStream.avail_in) {
67
- break;
68
- }
69
-
70
- dynamicZlibBuffer.append(zlibBuffer, LARGE_BUFFER_SIZE - inflationStream.avail_out);
71
- } while (err == Z_BUF_ERROR && dynamicZlibBuffer.length() <= maxPayload);
72
-
73
- inflateReset(&inflationStream);
74
-
75
- if ((err != Z_BUF_ERROR && err != Z_OK) || dynamicZlibBuffer.length() > maxPayload) {
76
- length = 0;
77
- return nullptr;
78
- }
79
-
80
- if (dynamicZlibBuffer.length()) {
81
- dynamicZlibBuffer.append(zlibBuffer, LARGE_BUFFER_SIZE - inflationStream.avail_out);
82
-
83
- length = dynamicZlibBuffer.length();
84
- return (char *) dynamicZlibBuffer.data();
85
- }
86
-
87
- length = LARGE_BUFFER_SIZE - inflationStream.avail_out;
88
- return zlibBuffer;
23
+ return zlib::inflate(inflationStream, data, length, maxPayload, zlibBuffer, LARGE_BUFFER_SIZE, dynamicZlibBuffer);
89
24
  }
90
25
 
91
26
  void Hub::onServerAccept(cS::Socket *s) {
package/src/Hub.h CHANGED
@@ -4,7 +4,7 @@
4
4
  #include "Group.h"
5
5
  #include "cSNode.h"
6
6
  #include <string>
7
- #include <zlib.h>
7
+ #include "Zlib.h"
8
8
  #include <mutex>
9
9
  #include <map>
10
10
 
@@ -18,10 +18,10 @@ protected:
18
18
  Group<CLIENT> *group;
19
19
  };
20
20
 
21
- static z_stream *allocateDefaultCompressor(z_stream *zStream);
21
+ static zlib::Stream *allocateDefaultCompressor(int level = 1, int windowBits = 15, int memLevel = 8);
22
22
 
23
- z_stream inflationStream = {}, deflationStream = {};
24
- char *deflate(char *data, size_t &length, z_stream *slidingDeflateWindow);
23
+ zlib::Stream *inflationStream = nullptr, *deflationStream = nullptr;
24
+ char *deflate(char *data, size_t &length, zlib::Stream *slidingDeflateWindow);
25
25
  char *inflate(char *data, size_t &length, size_t maxPayload);
26
26
  char *zlibBuffer;
27
27
  std::string dynamicZlibBuffer;
@@ -32,8 +32,8 @@ protected:
32
32
 
33
33
  public:
34
34
  template <bool isServer>
35
- Group<isServer> *createGroup(int extensionOptions = 0, unsigned int maxPayload = 16777216) {
36
- return new Group<isServer>(extensionOptions, maxPayload, this, nodeData);
35
+ Group<isServer> *createGroup(int extensionOptions = 0, unsigned int maxPayload = 16777216, int deflateWindowBits = 15, int deflateMemLevel = 8, int deflateLevel = 1) {
36
+ return new Group<isServer>(extensionOptions, maxPayload, this, nodeData, deflateWindowBits, deflateMemLevel, deflateLevel);
37
37
  }
38
38
 
39
39
  template <bool isServer>
@@ -48,10 +48,11 @@ public:
48
48
 
49
49
  Hub(int extensionOptions = 0, bool useDefaultLoop = false, unsigned int maxPayload = 16777216) : cS::Node(LARGE_BUFFER_SIZE, WebSocketProtocol<SERVER, WebSocket<SERVER>>::CONSUME_PRE_PADDING, WebSocketProtocol<SERVER, WebSocket<SERVER>>::CONSUME_POST_PADDING, useDefaultLoop),
50
50
  Group<SERVER>(extensionOptions, maxPayload, this, nodeData), Group<CLIENT>(0, maxPayload, this, nodeData) {
51
- inflateInit2(&inflationStream, -15);
51
+ inflationStream = zlib::createInflate(15);
52
52
  zlibBuffer = new char[LARGE_BUFFER_SIZE];
53
53
 
54
- allocateDefaultCompressor(&deflationStream);
54
+ // shared compressor for groups without a per-socket sliding window
55
+ deflationStream = allocateDefaultCompressor();
55
56
 
56
57
  #ifdef CWS_THREADSAFE
57
58
  getLoop()->preCbData = nodeData;
@@ -67,14 +68,15 @@ public:
67
68
  }
68
69
 
69
70
  ~Hub() {
70
- inflateEnd(&inflationStream);
71
- deflateEnd(&deflationStream);
71
+ zlib::destroy(inflationStream);
72
+ zlib::destroy(deflationStream);
72
73
  delete [] zlibBuffer;
73
74
  }
74
75
 
75
76
  using cS::Node::run;
76
77
  using cS::Node::poll;
77
78
  using cS::Node::getLoop;
79
+ using cS::Node::getNodeData;
78
80
  using Group<SERVER>::onConnection;
79
81
  using Group<CLIENT>::onConnection;
80
82
  using Group<SERVER>::onTransfer;
package/src/Networking.h CHANGED
@@ -215,6 +215,17 @@ struct WIN32_EXPORT NodeData {
215
215
  std::vector<Poll *> changePollQueue;
216
216
  static void asyncCallback(Async *async);
217
217
 
218
+ // Write corking: every send() issued during one event-loop iteration is
219
+ // queued and flushed with a single gathered write per socket from the
220
+ // loop's prepare/check hooks (see Socket::flushCorked). One CorkState per
221
+ // loop, shared by pointer between every NodeData copy (Groups copy the
222
+ // Hub's NodeData). Disabled with the CWS_CORK=0 environment variable.
223
+ struct CorkState {
224
+ bool enabled = false;
225
+ std::vector<Poll *> pending;
226
+ };
227
+ CorkState *corkState = nullptr;
228
+
218
229
  static int getMemoryBlockIndex(size_t length) {
219
230
  return (int) ((length >> 4) + bool(length & 15));
220
231
  }
package/src/Socket.h CHANGED
@@ -2,6 +2,9 @@
2
2
  #define SOCKET_CWS_H
3
3
 
4
4
  #include "Networking.h"
5
+ #ifndef _WIN32
6
+ #include <sys/uio.h>
7
+ #endif
5
8
 
6
9
  namespace cS {
7
10
 
@@ -33,6 +36,12 @@ protected:
33
36
  void *user = nullptr;
34
37
  NodeData *nodeData;
35
38
 
39
+ // Corking (see NodeData::CorkState). Only WebSockets opt in; HttpSocket
40
+ // objects are deleted synchronously in Hub::upgrade so they must never
41
+ // sit in the pending list. Plain TCP only, SSL keeps the immediate path.
42
+ bool corkable = false;
43
+ bool corkPending = false;
44
+
36
45
  // this is not needed by HttpSocket!
37
46
  struct Queue {
38
47
  struct Message {
@@ -91,6 +100,7 @@ protected:
91
100
  }
92
101
 
93
102
  void transfer(NodeData *nodeData, void (*cb)(Poll *)) {
103
+ flushCorkedOnClose();
94
104
  // userData is invalid from now on till onTransfer
95
105
  setUserData(new TransferData({getFd(), ssl, getCb(), getPoll(), getUserData(), nodeData, cb}));
96
106
  stop(this->nodeData->loop);
@@ -160,10 +170,17 @@ protected:
160
170
  Queue::Message *messagePtr = socket->messageQueue.front();
161
171
  int sent = SSL_write(socket->ssl, messagePtr->data, (int) messagePtr->length);
162
172
  if (sent == (ssize_t) messagePtr->length) {
163
- if (messagePtr->callback) {
164
- messagePtr->callback(p, messagePtr->callbackData, false, messagePtr->reserved);
165
- }
173
+ // Pop before the callback: it may close/terminate this socket,
174
+ // which frees the queue (use-after-free otherwise).
175
+ auto callback = messagePtr->callback;
176
+ void *callbackData = messagePtr->callbackData, *reserved = messagePtr->reserved;
166
177
  socket->messageQueue.pop();
178
+ if (callback) {
179
+ callback(p, callbackData, false, reserved);
180
+ if (socket->isClosed()) {
181
+ return;
182
+ }
183
+ }
167
184
  if (socket->messageQueue.empty()) {
168
185
  if ((socket->state.poll & UV_WRITABLE) && SSL_want(socket->ssl) != SSL_WRITING) {
169
186
  socket->change(socket->nodeData->loop, socket, socket->setPoll(UV_READABLE));
@@ -235,10 +252,17 @@ protected:
235
252
  Queue::Message *messagePtr = socket->messageQueue.front();
236
253
  ssize_t sent = ::send(socket->getFd(), messagePtr->data, messagePtr->length, MSG_NOSIGNAL);
237
254
  if (sent == (ssize_t) messagePtr->length) {
238
- if (messagePtr->callback) {
239
- messagePtr->callback(p, messagePtr->callbackData, false, messagePtr->reserved);
240
- }
255
+ // Pop before the callback: it may close/terminate this socket,
256
+ // which frees the queue (use-after-free otherwise).
257
+ auto callback = messagePtr->callback;
258
+ void *callbackData = messagePtr->callbackData, *reserved = messagePtr->reserved;
241
259
  socket->messageQueue.pop();
260
+ if (callback) {
261
+ callback(p, callbackData, false, reserved);
262
+ if (socket->isClosed()) {
263
+ return;
264
+ }
265
+ }
242
266
  if (socket->messageQueue.empty()) {
243
267
  // todo, remove bit, don't set directly
244
268
  socket->change(socket->nodeData->loop, socket, socket->setPoll(UV_READABLE));
@@ -358,6 +382,21 @@ protected:
358
382
  void sendTransformed(const char *message, size_t length, void(*callback)(void *socket, void *data, bool cancelled, void *reserved), void *callbackData, D transformData) {
359
383
  size_t estimatedLength = T::estimate(message, length) + sizeof(Queue::Message);
360
384
 
385
+ if (corkActive()) {
386
+ // Frame now, write later: everything sent to this socket during the
387
+ // current loop iteration goes out in one gathered write (uncork()).
388
+ Queue::Message *messagePtr = allocMessage(estimatedLength - sizeof(Queue::Message));
389
+ messagePtr->length = T::transform(message, (char *) messagePtr->data, length, transformData);
390
+ messagePtr->callback = callback;
391
+ messagePtr->callbackData = callbackData;
392
+ enqueue(messagePtr);
393
+ if (!corkPending) {
394
+ corkPending = true;
395
+ nodeData->corkState->pending.push_back(this);
396
+ }
397
+ return;
398
+ }
399
+
361
400
  if (hasEmptyQueue()) {
362
401
  if (estimatedLength <= cS::NodeData::preAllocMaxSize) {
363
402
  int memoryLength = (int) estimatedLength;
@@ -477,8 +516,164 @@ public:
477
516
  }
478
517
  }
479
518
 
519
+ // ---- Write corking ------------------------------------------------------
520
+
521
+ bool corkActive() {
522
+ return corkable && !ssl && nodeData->corkState && nodeData->corkState->enabled;
523
+ }
524
+
525
+ void setCorkable(bool enable) {
526
+ corkable = enable;
527
+ }
528
+
529
+ void unregisterCork() {
530
+ if (!corkPending) {
531
+ return;
532
+ }
533
+ corkPending = false;
534
+ std::vector<Poll *> &pending = nodeData->corkState->pending;
535
+ for (size_t i = 0; i < pending.size(); i++) {
536
+ if (pending[i] == this) {
537
+ pending[i] = pending.back();
538
+ pending.pop_back();
539
+ break;
540
+ }
541
+ }
542
+ }
543
+
544
+ // One gathered write of (up to MAX_IOV) messages from the queue head.
545
+ // Returns bytes written or -1 with errno/WSAGetLastError set. Never pops.
546
+ ssize_t writeQueueGathered() {
547
+ static const int MAX_IOV = 64;
548
+ #ifdef _WIN32
549
+ WSABUF bufs[MAX_IOV];
550
+ DWORD n = 0;
551
+ for (Queue::Message *m = messageQueue.front(); m && n < MAX_IOV; m = m->nextMessage) {
552
+ bufs[n].buf = (CHAR *) m->data;
553
+ bufs[n].len = (ULONG) m->length;
554
+ n++;
555
+ }
556
+ DWORD sent = 0;
557
+ if (WSASend(getFd(), bufs, n, &sent, 0, nullptr, nullptr) == SOCKET_ERROR) {
558
+ return -1;
559
+ }
560
+ return (ssize_t) sent;
561
+ #else
562
+ iovec iov[MAX_IOV];
563
+ int n = 0;
564
+ for (Queue::Message *m = messageQueue.front(); m && n < MAX_IOV; m = m->nextMessage) {
565
+ iov[n].iov_base = (void *) m->data;
566
+ iov[n].iov_len = m->length;
567
+ n++;
568
+ }
569
+ msghdr msg = {};
570
+ msg.msg_iov = iov;
571
+ msg.msg_iovlen = n;
572
+ return ::sendmsg(getFd(), &msg, MSG_NOSIGNAL);
573
+ #endif
574
+ }
575
+
576
+ // Consumes `sent` bytes from the queue head. Fully written messages are
577
+ // popped (their callbacks appended to `callbacks` if given); a partially
578
+ // written head is advanced in place. Returns true if the write was partial.
579
+ struct PendingCallback {
580
+ void (*callback)(void *socket, void *data, bool cancelled, void *reserved);
581
+ void *callbackData, *reserved;
582
+ };
583
+
584
+ bool consumeSent(ssize_t sent, std::vector<PendingCallback> *callbacks) {
585
+ while (sent > 0 && !messageQueue.empty()) {
586
+ Queue::Message *m = messageQueue.front();
587
+ if ((size_t) sent >= m->length) {
588
+ sent -= m->length;
589
+ if (callbacks && m->callback) {
590
+ callbacks->push_back({m->callback, m->callbackData, m->reserved});
591
+ }
592
+ messageQueue.pop();
593
+ } else {
594
+ m->length -= sent;
595
+ m->data += sent;
596
+ messageQueue.totalLength -= sent;
597
+ return true;
598
+ }
599
+ }
600
+ return false;
601
+ }
602
+
603
+ // Flush this socket's corked queue. Called from the loop's prepare/check
604
+ // hooks via flushCorked(). Whatever cannot be written now is left to the
605
+ // regular UV_WRITABLE drain loop, which also surfaces hard errors (onEnd).
606
+ void uncork() {
607
+ corkPending = false;
608
+ if (isClosed() || messageQueue.empty()) {
609
+ return;
610
+ }
611
+
612
+ std::vector<PendingCallback> callbacks;
613
+ bool stalled = false;
614
+ while (!messageQueue.empty() && !stalled) {
615
+ ssize_t sent = writeQueueGathered();
616
+ if (sent < 0) {
617
+ stalled = true; // would block, or hard error: drain loop reports it
618
+ } else {
619
+ stalled = consumeSent(sent, &callbacks);
620
+ }
621
+ }
622
+
623
+ if (!messageQueue.empty()) {
624
+ if ((getPoll() & UV_WRITABLE) == 0) {
625
+ setPoll(getPoll() | UV_WRITABLE);
626
+ changePoll(this);
627
+ }
628
+ } else if (getPoll() & UV_WRITABLE) {
629
+ change(nodeData->loop, this, setPoll(UV_READABLE));
630
+ }
631
+
632
+ for (PendingCallback &c : callbacks) {
633
+ c.callback(this, c.callbackData, false, c.reserved);
634
+ if (isClosed()) {
635
+ break; // callback closed us; nothing further may touch the fd
636
+ }
637
+ }
638
+ }
639
+
640
+ // Best effort on close/terminate/transfer: push what was corked during this
641
+ // iteration to the kernel so send() immediately followed by terminate()
642
+ // still delivers. Callbacks are not invoked; the caller drains the queue.
643
+ void flushCorkedOnClose() {
644
+ if (!corkPending) {
645
+ return;
646
+ }
647
+ unregisterCork();
648
+ if (isClosed()) {
649
+ return;
650
+ }
651
+ while (!messageQueue.empty()) {
652
+ ssize_t sent = writeQueueGathered();
653
+ if (sent <= 0 || consumeSent(sent, nullptr)) {
654
+ break;
655
+ }
656
+ }
657
+ }
658
+
659
+ // Loop hook: flush every socket that corked writes since the last hook.
660
+ static void flushCorked(NodeData *nodeData) {
661
+ if (!nodeData->corkState || nodeData->corkState->pending.empty()) {
662
+ return;
663
+ }
664
+ // Swap out the list: callbacks run during uncork() may cork new writes
665
+ // (picked up by the next hook) or close other sockets (their deletion
666
+ // is deferred to libuv's close phase, so pointers stay valid here).
667
+ std::vector<Poll *> batch;
668
+ batch.swap(nodeData->corkState->pending);
669
+ for (Poll *p : batch) {
670
+ ((Socket *) p)->uncork();
671
+ }
672
+ }
673
+
480
674
  template <class T>
481
675
  void closeSocket() {
676
+ unregisterCork();
482
677
  uv_os_sock_t fd = getFd();
483
678
  Context *netContext = nodeData->netContext;
484
679
  stop(nodeData->loop);
package/src/WebSocket.cpp CHANGED
@@ -7,10 +7,12 @@ namespace cWS {
7
7
  template <bool isServer>
8
8
  WebSocket<isServer>::WebSocket(bool perMessageDeflate, cS::Socket *socket) : cS::Socket(std::move(*socket)) {
9
9
  compressionStatus = perMessageDeflate ? CompressionStatus::ENABLED : CompressionStatus::DISABLED;
10
+ setCorkable(true);
10
11
 
11
12
  // if we are created in a group with sliding deflate window allocate it here
12
13
  if (Group<isServer>::from(this)->extensionOptions & SLIDING_DEFLATE_WINDOW) {
13
- slidingDeflateWindow = Hub::allocateDefaultCompressor(new z_stream{});
14
+ Group<isServer> *group = Group<isServer>::from(this);
15
+ slidingDeflateWindow = Hub::allocateDefaultCompressor(group->deflateLevel, group->deflateWindowBits, group->deflateMemLevel);
14
16
  }
15
17
  }
16
18
 
@@ -51,7 +53,7 @@ void WebSocket<isServer>::send(const char *message, size_t length, OpCode opCode
51
53
 
52
54
  static size_t transform(const char *src, char *dst, size_t length, TransformData transformData) {
53
55
  if (transformData.compress) {
54
- char *deflated = Group<isServer>::from(transformData.s)->hub->deflate((char *) src, length, (z_stream *) transformData.s->slidingDeflateWindow);
56
+ char *deflated = Group<isServer>::from(transformData.s)->hub->deflate((char *) src, length, (zlib::Stream *) transformData.s->slidingDeflateWindow);
55
57
  return WebSocketProtocol<isServer, WebSocket<isServer>>::formatMessage(dst, deflated, length, transformData.opCode, length, true);
56
58
  }
57
59
 
@@ -214,10 +216,10 @@ void WebSocket<isServer>::terminate() {
214
216
 
215
217
  #ifdef CWS_THREADSAFE
216
218
  std::lock_guard<std::recursive_mutex> lockGuard(*nodeData->asyncMutex);
219
+ #endif
217
220
  if (isClosed()) {
218
221
  return;
219
222
  }
220
- #endif
221
223
 
222
224
  WebSocket<isServer>::onEnd(this);
223
225
  }
@@ -288,6 +290,8 @@ template <bool isServer>
288
290
  void WebSocket<isServer>::onEnd(cS::Socket *s) {
289
291
  WebSocket<isServer> *webSocket = static_cast<WebSocket<isServer> *>(s);
290
292
 
293
+ webSocket->flushCorkedOnClose();
294
+
291
295
  if (!webSocket->isShuttingDown()) {
292
296
  Group<isServer>::from(webSocket)->removeWebSocket(webSocket);
293
297
  Group<isServer>::from(webSocket)->disconnectionHandler(webSocket, 1006, nullptr, 0);
@@ -310,8 +314,7 @@ void WebSocket<isServer>::onEnd(cS::Socket *s) {
310
314
  // remove any per-websocket zlib memory
311
315
  if (webSocket->slidingDeflateWindow) {
312
316
  // this relates to Hub::allocateDefaultCompressor
313
- deflateEnd((z_stream *) webSocket->slidingDeflateWindow);
314
- delete (z_stream *) webSocket->slidingDeflateWindow;
317
+ zlib::destroy((zlib::Stream *) webSocket->slidingDeflateWindow);
315
318
  webSocket->slidingDeflateWindow = nullptr;
316
319
  }
317
320
  }
package/src/Zlib.cpp ADDED
@@ -0,0 +1,138 @@
1
+ #include "Zlib.h"
2
+
3
+ #ifdef CWS_ZLIB_NG
4
+ #include "zlib-ng.h"
5
+ typedef zng_stream ZStream;
6
+ #define ZFN(name) zng_##name
7
+ #define CWS_ZLIB_BACKEND "zlib-ng " ZLIBNG_VERSION
8
+ #else
9
+ #include <zlib.h>
10
+ typedef z_stream ZStream;
11
+ #define ZFN(name) name
12
+ #define CWS_ZLIB_BACKEND "zlib " ZLIB_VERSION
13
+ #endif
14
+
15
+ namespace cWS {
16
+ namespace zlib {
17
+
18
+ struct Stream {
19
+ ZStream zs;
20
+ bool isDeflate;
21
+ };
22
+
23
+ typedef decltype(ZStream::next_in) InPtr;
24
+ typedef decltype(ZStream::next_out) OutPtr;
25
+
26
+ Stream *createDeflate(int level, int windowBits, int memLevel) {
27
+ Stream *stream = new Stream();
28
+ stream->isDeflate = true;
29
+ ZFN(deflateInit2)(&stream->zs, level, Z_DEFLATED, -windowBits, memLevel, Z_DEFAULT_STRATEGY);
30
+ return stream;
31
+ }
32
+
33
+ Stream *createInflate(int windowBits) {
34
+ Stream *stream = new Stream();
35
+ stream->isDeflate = false;
36
+ ZFN(inflateInit2)(&stream->zs, -windowBits);
37
+ return stream;
38
+ }
39
+
40
+ void reset(Stream *stream) {
41
+ if (stream->isDeflate) {
42
+ ZFN(deflateReset)(&stream->zs);
43
+ } else {
44
+ ZFN(inflateReset)(&stream->zs);
45
+ }
46
+ }
47
+
48
+ void destroy(Stream *stream) {
49
+ if (!stream) {
50
+ return;
51
+ }
52
+ if (stream->isDeflate) {
53
+ ZFN(deflateEnd)(&stream->zs);
54
+ } else {
55
+ ZFN(inflateEnd)(&stream->zs);
56
+ }
57
+ delete stream;
58
+ }
59
+
60
+ char *deflate(Stream *stream, char *data, size_t &length, char *buffer, size_t bufferSize, std::string &dynamic, bool resetAfter) {
61
+ ZStream &zs = stream->zs;
62
+ dynamic.clear();
63
+
64
+ zs.next_in = (InPtr) data;
65
+ zs.avail_in = (unsigned int) length;
66
+
67
+ // note: zlib requires more than 6 bytes of output space with Z_SYNC_FLUSH
68
+ int err;
69
+ do {
70
+ zs.next_out = (OutPtr) buffer;
71
+ zs.avail_out = (unsigned int) bufferSize;
72
+
73
+ err = ZFN(deflate)(&zs, Z_SYNC_FLUSH);
74
+ if (Z_OK == err && zs.avail_out == 0) {
75
+ dynamic.append(buffer, bufferSize - zs.avail_out);
76
+ continue;
77
+ } else {
78
+ break;
79
+ }
80
+ } while (true);
81
+
82
+ // note: must not change avail_out
83
+ if (resetAfter) {
84
+ ZFN(deflateReset)(&zs);
85
+ }
86
+
87
+ if (dynamic.length()) {
88
+ dynamic.append(buffer, bufferSize - zs.avail_out);
89
+ length = dynamic.length() - 4;
90
+ return (char *) dynamic.data();
91
+ }
92
+
93
+ length = bufferSize - zs.avail_out - 4;
94
+ return buffer;
95
+ }
96
+
97
+ char *inflate(Stream *stream, char *data, size_t &length, size_t maxPayload, char *buffer, size_t bufferSize, std::string &dynamic) {
98
+ ZStream &zs = stream->zs;
99
+ dynamic.clear();
100
+
101
+ zs.next_in = (InPtr) data;
102
+ zs.avail_in = (unsigned int) length;
103
+
104
+ int err;
105
+ do {
106
+ zs.next_out = (OutPtr) buffer;
107
+ zs.avail_out = (unsigned int) bufferSize;
108
+ err = ZFN(inflate)(&zs, Z_FINISH);
109
+ if (!zs.avail_in) {
110
+ break;
111
+ }
112
+
113
+ dynamic.append(buffer, bufferSize - zs.avail_out);
114
+ } while (err == Z_BUF_ERROR && dynamic.length() <= maxPayload);
115
+
116
+ ZFN(inflateReset)(&zs);
117
+
118
+ if ((err != Z_BUF_ERROR && err != Z_OK) || dynamic.length() > maxPayload) {
119
+ length = 0;
120
+ return nullptr;
121
+ }
122
+
123
+ if (dynamic.length()) {
124
+ dynamic.append(buffer, bufferSize - zs.avail_out);
125
+ length = dynamic.length();
126
+ return (char *) dynamic.data();
127
+ }
128
+
129
+ length = bufferSize - zs.avail_out;
130
+ return buffer;
131
+ }
132
+
133
+ const char *backend() {
134
+ return CWS_ZLIB_BACKEND;
135
+ }
136
+
137
+ }
138
+ }