@fugood/llama.node 0.3.16 → 0.3.17

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 (202) hide show
  1. package/CMakeLists.txt +3 -0
  2. package/bin/darwin/arm64/llama-node.node +0 -0
  3. package/bin/darwin/x64/llama-node.node +0 -0
  4. package/bin/linux/arm64/llama-node.node +0 -0
  5. package/bin/linux/x64/llama-node.node +0 -0
  6. package/bin/linux-cuda/arm64/llama-node.node +0 -0
  7. package/bin/linux-cuda/x64/llama-node.node +0 -0
  8. package/bin/linux-vulkan/arm64/llama-node.node +0 -0
  9. package/bin/linux-vulkan/x64/llama-node.node +0 -0
  10. package/bin/win32/arm64/llama-node.node +0 -0
  11. package/bin/win32/arm64/node.lib +0 -0
  12. package/bin/win32/x64/llama-node.node +0 -0
  13. package/bin/win32/x64/node.lib +0 -0
  14. package/bin/win32-vulkan/arm64/llama-node.node +0 -0
  15. package/bin/win32-vulkan/arm64/node.lib +0 -0
  16. package/bin/win32-vulkan/x64/llama-node.node +0 -0
  17. package/bin/win32-vulkan/x64/node.lib +0 -0
  18. package/lib/binding.ts +5 -0
  19. package/package.json +1 -1
  20. package/src/LlamaCompletionWorker.cpp +8 -0
  21. package/src/LlamaCompletionWorker.h +1 -0
  22. package/src/LlamaContext.cpp +3 -2
  23. package/src/llama.cpp/.github/workflows/build-linux-cross.yml +124 -0
  24. package/src/llama.cpp/.github/workflows/build.yml +70 -27
  25. package/src/llama.cpp/.github/workflows/docker.yml +6 -6
  26. package/src/llama.cpp/.github/workflows/server.yml +7 -11
  27. package/src/llama.cpp/CMakeLists.txt +23 -1
  28. package/src/llama.cpp/common/CMakeLists.txt +6 -3
  29. package/src/llama.cpp/common/arg.cpp +809 -105
  30. package/src/llama.cpp/common/arg.h +9 -0
  31. package/src/llama.cpp/common/chat.cpp +1 -1
  32. package/src/llama.cpp/common/common.cpp +31 -521
  33. package/src/llama.cpp/common/common.h +17 -36
  34. package/src/llama.cpp/common/json-schema-to-grammar.cpp +3 -0
  35. package/src/llama.cpp/common/llguidance.cpp +30 -47
  36. package/src/llama.cpp/common/minja/chat-template.hpp +15 -7
  37. package/src/llama.cpp/common/minja/minja.hpp +119 -93
  38. package/src/llama.cpp/common/sampling.cpp +3 -0
  39. package/src/llama.cpp/docs/build.md +122 -7
  40. package/src/llama.cpp/examples/CMakeLists.txt +0 -9
  41. package/src/llama.cpp/examples/batched/batched.cpp +1 -1
  42. package/src/llama.cpp/examples/batched-bench/batched-bench.cpp +1 -1
  43. package/src/llama.cpp/examples/embedding/embedding.cpp +7 -1
  44. package/src/llama.cpp/examples/export-lora/export-lora.cpp +1 -1
  45. package/src/llama.cpp/examples/gguf-split/gguf-split.cpp +15 -16
  46. package/src/llama.cpp/examples/gritlm/gritlm.cpp +1 -1
  47. package/src/llama.cpp/examples/llama-bench/llama-bench.cpp +210 -8
  48. package/src/llama.cpp/examples/llama.android/llama/build.gradle.kts +1 -0
  49. package/src/llama.cpp/examples/llava/CMakeLists.txt +39 -24
  50. package/src/llama.cpp/examples/llava/clip-impl.h +345 -0
  51. package/src/llama.cpp/examples/llava/clip.cpp +2152 -1803
  52. package/src/llama.cpp/examples/llava/clip.h +39 -22
  53. package/src/llama.cpp/examples/llava/deprecation-warning.cpp +22 -0
  54. package/src/llama.cpp/examples/llava/llava.cpp +64 -52
  55. package/src/llama.cpp/examples/llava/mtmd-cli.cpp +344 -0
  56. package/src/llama.cpp/examples/llava/mtmd.cpp +708 -0
  57. package/src/llama.cpp/examples/llava/mtmd.h +168 -0
  58. package/src/llama.cpp/examples/llava/{qwen2vl-cli.cpp → qwen2vl-test.cpp} +83 -31
  59. package/src/llama.cpp/examples/main/main.cpp +16 -5
  60. package/src/llama.cpp/examples/parallel/parallel.cpp +3 -1
  61. package/src/llama.cpp/examples/passkey/passkey.cpp +1 -1
  62. package/src/llama.cpp/examples/perplexity/perplexity.cpp +17 -3
  63. package/src/llama.cpp/examples/quantize/quantize.cpp +115 -2
  64. package/src/llama.cpp/examples/rpc/CMakeLists.txt +4 -2
  65. package/src/llama.cpp/examples/rpc/rpc-server.cpp +163 -8
  66. package/src/llama.cpp/examples/run/CMakeLists.txt +12 -1
  67. package/src/llama.cpp/examples/run/run.cpp +14 -28
  68. package/src/llama.cpp/examples/server/httplib.h +313 -247
  69. package/src/llama.cpp/examples/server/server.cpp +238 -139
  70. package/src/llama.cpp/examples/server/utils.hpp +51 -2
  71. package/src/llama.cpp/examples/speculative/speculative.cpp +1 -1
  72. package/src/llama.cpp/examples/speculative-simple/speculative-simple.cpp +1 -1
  73. package/src/llama.cpp/examples/sycl/build.sh +2 -2
  74. package/src/llama.cpp/examples/sycl/win-build-sycl.bat +2 -2
  75. package/src/llama.cpp/examples/tts/tts.cpp +6 -9
  76. package/src/llama.cpp/ggml/CMakeLists.txt +8 -2
  77. package/src/llama.cpp/ggml/cmake/GitVars.cmake +22 -0
  78. package/src/llama.cpp/ggml/include/ggml-cpu.h +5 -0
  79. package/src/llama.cpp/ggml/include/ggml-rpc.h +6 -1
  80. package/src/llama.cpp/ggml/include/ggml.h +66 -99
  81. package/src/llama.cpp/ggml/src/CMakeLists.txt +10 -7
  82. package/src/llama.cpp/ggml/src/ggml-cann/CMakeLists.txt +0 -2
  83. package/src/llama.cpp/ggml/src/ggml-cann/acl_tensor.cpp +8 -4
  84. package/src/llama.cpp/ggml/src/ggml-cann/acl_tensor.h +5 -5
  85. package/src/llama.cpp/ggml/src/ggml-cann/aclnn_ops.cpp +692 -1534
  86. package/src/llama.cpp/ggml/src/ggml-cann/aclnn_ops.h +613 -122
  87. package/src/llama.cpp/ggml/src/ggml-cann/common.h +135 -1
  88. package/src/llama.cpp/ggml/src/ggml-cann/ggml-cann.cpp +507 -137
  89. package/src/llama.cpp/ggml/src/ggml-common.h +12 -6
  90. package/src/llama.cpp/ggml/src/ggml-cpu/CMakeLists.txt +48 -22
  91. package/src/llama.cpp/ggml/src/ggml-cpu/binary-ops.cpp +158 -0
  92. package/src/llama.cpp/ggml/src/ggml-cpu/binary-ops.h +16 -0
  93. package/src/llama.cpp/ggml/src/ggml-cpu/common.h +72 -0
  94. package/src/llama.cpp/ggml/src/ggml-cpu/cpu-feats-x86.cpp +1 -1
  95. package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu-aarch64.cpp +896 -192
  96. package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu-impl.h +2 -21
  97. package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu-quants.c +754 -404
  98. package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu.c +1003 -13519
  99. package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu.cpp +2 -0
  100. package/src/llama.cpp/ggml/src/ggml-cpu/kleidiai/kernels.cpp +2 -7
  101. package/src/llama.cpp/ggml/src/ggml-cpu/kleidiai/kernels.h +0 -1
  102. package/src/llama.cpp/ggml/src/ggml-cpu/kleidiai/kleidiai.cpp +3 -4
  103. package/src/llama.cpp/ggml/src/ggml-cpu/llamafile/sgemm.cpp +533 -88
  104. package/src/llama.cpp/ggml/src/ggml-cpu/ops.cpp +8809 -0
  105. package/src/llama.cpp/ggml/src/ggml-cpu/ops.h +110 -0
  106. package/src/llama.cpp/ggml/src/ggml-cpu/simd-mappings.h +892 -0
  107. package/src/llama.cpp/ggml/src/ggml-cpu/unary-ops.cpp +186 -0
  108. package/src/llama.cpp/ggml/src/ggml-cpu/unary-ops.h +28 -0
  109. package/src/llama.cpp/ggml/src/ggml-cpu/vec.cpp +258 -0
  110. package/src/llama.cpp/ggml/src/ggml-cpu/vec.h +802 -0
  111. package/src/llama.cpp/ggml/src/ggml-cuda/vendors/hip.h +7 -0
  112. package/src/llama.cpp/ggml/src/ggml-cuda/vendors/musa.h +1 -0
  113. package/src/llama.cpp/ggml/src/ggml-hip/CMakeLists.txt +0 -4
  114. package/src/llama.cpp/ggml/src/ggml-impl.h +52 -18
  115. package/src/llama.cpp/ggml/src/ggml-metal/ggml-metal-impl.h +70 -3
  116. package/src/llama.cpp/ggml/src/ggml-opencl/CMakeLists.txt +67 -119
  117. package/src/llama.cpp/ggml/src/ggml-opencl/ggml-opencl.cpp +1023 -260
  118. package/src/llama.cpp/ggml/src/ggml-rpc/ggml-rpc.cpp +293 -40
  119. package/src/llama.cpp/ggml/src/ggml-sycl/CMakeLists.txt +96 -22
  120. package/src/llama.cpp/ggml/src/ggml-sycl/backend.hpp +1 -0
  121. package/src/llama.cpp/ggml/src/ggml-sycl/binbcast.cpp +350 -0
  122. package/src/llama.cpp/ggml/src/ggml-sycl/binbcast.hpp +39 -0
  123. package/src/llama.cpp/ggml/src/ggml-sycl/common.cpp +0 -35
  124. package/src/llama.cpp/ggml/src/ggml-sycl/common.hpp +2 -292
  125. package/src/llama.cpp/ggml/src/ggml-sycl/dpct/helper.hpp +79 -90
  126. package/src/llama.cpp/ggml/src/ggml-sycl/element_wise.cpp +967 -438
  127. package/src/llama.cpp/ggml/src/ggml-sycl/element_wise.hpp +22 -23
  128. package/src/llama.cpp/ggml/src/ggml-sycl/getrows.cpp +24 -20
  129. package/src/llama.cpp/ggml/src/ggml-sycl/getrows.hpp +1 -4
  130. package/src/llama.cpp/ggml/src/ggml-sycl/ggml-sycl.cpp +204 -280
  131. package/src/llama.cpp/ggml/src/ggml-sycl/im2col.cpp +84 -74
  132. package/src/llama.cpp/ggml/src/ggml-sycl/im2col.hpp +1 -3
  133. package/src/llama.cpp/ggml/src/ggml-sycl/norm.cpp +37 -49
  134. package/src/llama.cpp/ggml/src/ggml-sycl/norm.hpp +7 -22
  135. package/src/llama.cpp/ggml/src/ggml-sycl/outprod.cpp +4 -14
  136. package/src/llama.cpp/ggml/src/ggml-sycl/rope.cpp +204 -118
  137. package/src/llama.cpp/ggml/src/ggml-sycl/rope.hpp +1 -3
  138. package/src/llama.cpp/ggml/src/ggml-vulkan/CMakeLists.txt +23 -0
  139. package/src/llama.cpp/ggml/src/ggml-vulkan/ggml-vulkan.cpp +646 -114
  140. package/src/llama.cpp/ggml/src/ggml-vulkan/vulkan-shaders/CMakeLists.txt +12 -0
  141. package/src/llama.cpp/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp +17 -8
  142. package/src/llama.cpp/ggml/src/ggml.c +141 -245
  143. package/src/llama.cpp/ggml/src/gguf.cpp +1 -0
  144. package/src/llama.cpp/include/llama.h +30 -11
  145. package/src/llama.cpp/models/ggml-vocab-llama4.gguf.inp +112 -0
  146. package/src/llama.cpp/models/ggml-vocab-llama4.gguf.out +46 -0
  147. package/src/llama.cpp/models/ggml-vocab-pixtral.gguf.inp +112 -0
  148. package/src/llama.cpp/models/ggml-vocab-pixtral.gguf.out +46 -0
  149. package/src/llama.cpp/requirements/requirements-all.txt +2 -0
  150. package/src/llama.cpp/requirements/requirements-gguf_editor_gui.txt +3 -0
  151. package/src/llama.cpp/src/CMakeLists.txt +3 -2
  152. package/src/llama.cpp/src/llama-adapter.cpp +37 -1
  153. package/src/llama.cpp/src/llama-arch.cpp +160 -17
  154. package/src/llama.cpp/src/llama-arch.h +16 -0
  155. package/src/llama.cpp/src/llama-chat.cpp +82 -17
  156. package/src/llama.cpp/src/llama-chat.h +6 -2
  157. package/src/llama.cpp/src/llama-context.cpp +108 -92
  158. package/src/llama.cpp/src/llama-context.h +1 -2
  159. package/src/llama.cpp/src/llama-graph.cpp +189 -119
  160. package/src/llama.cpp/src/llama-graph.h +26 -6
  161. package/src/llama.cpp/src/llama-hparams.h +13 -0
  162. package/src/llama.cpp/src/llama-kv-cache.cpp +70 -123
  163. package/src/llama.cpp/src/llama-kv-cache.h +41 -115
  164. package/src/llama.cpp/src/llama-memory.h +1 -1
  165. package/src/llama.cpp/src/llama-mmap.cpp +1 -1
  166. package/src/llama.cpp/src/llama-model-loader.cpp +10 -5
  167. package/src/llama.cpp/src/llama-model-loader.h +5 -3
  168. package/src/llama.cpp/src/llama-model.cpp +1760 -534
  169. package/src/llama.cpp/src/llama-model.h +13 -1
  170. package/src/llama.cpp/src/llama-quant.cpp +29 -8
  171. package/src/llama.cpp/src/llama-sampling.cpp +7 -1
  172. package/src/llama.cpp/src/llama-vocab.cpp +44 -6
  173. package/src/llama.cpp/src/llama.cpp +1 -1
  174. package/src/llama.cpp/tests/CMakeLists.txt +43 -30
  175. package/src/llama.cpp/tests/test-arg-parser.cpp +51 -4
  176. package/src/llama.cpp/tests/test-backend-ops.cpp +82 -43
  177. package/src/llama.cpp/tests/test-chat-template.cpp +34 -13
  178. package/src/llama.cpp/tests/test-chat.cpp +12 -2
  179. package/src/llama.cpp/{examples/gbnf-validator/gbnf-validator.cpp → tests/test-gbnf-validator.cpp} +2 -2
  180. package/src/llama.cpp/tests/test-grammar-integration.cpp +3 -2
  181. package/src/llama.cpp/tests/test-grammar-llguidance.cpp +63 -2
  182. package/src/llama.cpp/tests/test-grammar-parser.cpp +3 -1
  183. package/src/llama.cpp/tests/test-json-schema-to-grammar.cpp +17 -1
  184. package/src/llama.cpp/tests/test-llama-grammar.cpp +2 -1
  185. package/src/llama.cpp/{examples/quantize-stats/quantize-stats.cpp → tests/test-quantize-stats.cpp} +3 -1
  186. package/src/llama.cpp/tests/test-tokenizer-1-bpe.cpp +2 -1
  187. package/src/llama.cpp/tests/test-tokenizer-1-spm.cpp +2 -1
  188. package/src/llama.cpp/examples/gbnf-validator/CMakeLists.txt +0 -5
  189. package/src/llama.cpp/examples/llava/gemma3-cli.cpp +0 -341
  190. package/src/llama.cpp/examples/llava/llava-cli.cpp +0 -332
  191. package/src/llama.cpp/examples/llava/minicpmv-cli.cpp +0 -354
  192. package/src/llama.cpp/examples/quantize-stats/CMakeLists.txt +0 -6
  193. package/src/llama.cpp/ggml/src/ggml-cann/kernels/CMakeLists.txt +0 -30
  194. package/src/llama.cpp/ggml/src/ggml-cann/kernels/ascendc_kernels.h +0 -19
  195. package/src/llama.cpp/ggml/src/ggml-cann/kernels/dup.cpp +0 -234
  196. package/src/llama.cpp/ggml/src/ggml-cann/kernels/get_row_f16.cpp +0 -197
  197. package/src/llama.cpp/ggml/src/ggml-cann/kernels/get_row_f32.cpp +0 -190
  198. package/src/llama.cpp/ggml/src/ggml-cann/kernels/get_row_q4_0.cpp +0 -204
  199. package/src/llama.cpp/ggml/src/ggml-cann/kernels/get_row_q8_0.cpp +0 -191
  200. package/src/llama.cpp/ggml/src/ggml-cann/kernels/quantize_f16_q8_0.cpp +0 -218
  201. package/src/llama.cpp/ggml/src/ggml-cann/kernels/quantize_f32_q8_0.cpp +0 -216
  202. package/src/llama.cpp/ggml/src/ggml-cann/kernels/quantize_float_to_q4_0.cpp +0 -295
@@ -8,7 +8,7 @@
8
8
  #ifndef CPPHTTPLIB_HTTPLIB_H
9
9
  #define CPPHTTPLIB_HTTPLIB_H
10
10
 
11
- #define CPPHTTPLIB_VERSION "0.19.0"
11
+ #define CPPHTTPLIB_VERSION "0.20.0"
12
12
 
13
13
  /*
14
14
  * Configuration
@@ -188,15 +188,16 @@ using ssize_t = long;
188
188
  #include <winsock2.h>
189
189
  #include <ws2tcpip.h>
190
190
 
191
+ // afunix.h uses types declared in winsock2.h, so has to be included after it.
192
+ #include <afunix.h>
193
+
191
194
  #ifndef WSA_FLAG_NO_HANDLE_INHERIT
192
195
  #define WSA_FLAG_NO_HANDLE_INHERIT 0x80
193
196
  #endif
194
197
 
198
+ using nfds_t = unsigned long;
195
199
  using socket_t = SOCKET;
196
200
  using socklen_t = int;
197
- #ifdef CPPHTTPLIB_USE_POLL
198
- #define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
199
- #endif
200
201
 
201
202
  #else // not _WIN32
202
203
 
@@ -216,16 +217,11 @@ using socklen_t = int;
216
217
  #ifdef __linux__
217
218
  #include <resolv.h>
218
219
  #endif
220
+ #include <csignal>
219
221
  #include <netinet/tcp.h>
220
- #ifdef CPPHTTPLIB_USE_POLL
221
222
  #include <poll.h>
222
- #endif
223
- #include <csignal>
224
223
  #include <pthread.h>
225
224
  #include <sys/mman.h>
226
- #ifndef __VMS
227
- #include <sys/select.h>
228
- #endif
229
225
  #include <sys/socket.h>
230
226
  #include <sys/un.h>
231
227
  #include <unistd.h>
@@ -247,7 +243,6 @@ using socket_t = int;
247
243
  #include <errno.h>
248
244
  #include <exception>
249
245
  #include <fcntl.h>
250
- #include <fstream>
251
246
  #include <functional>
252
247
  #include <iomanip>
253
248
  #include <iostream>
@@ -320,6 +315,10 @@ using socket_t = int;
320
315
  #include <brotli/encode.h>
321
316
  #endif
322
317
 
318
+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
319
+ #include <zstd.h>
320
+ #endif
321
+
323
322
  /*
324
323
  * Declaration
325
324
  */
@@ -435,6 +434,15 @@ private:
435
434
 
436
435
  } // namespace detail
437
436
 
437
+ enum SSLVerifierResponse {
438
+ // no decision has been made, use the built-in certificate verifier
439
+ NoDecisionMade,
440
+ // connection certificate is verified and accepted
441
+ CertificateAccepted,
442
+ // connection certificate was processed but is rejected
443
+ CertificateRejected
444
+ };
445
+
438
446
  enum StatusCode {
439
447
  // Information responses
440
448
  Continue_100 = 100,
@@ -670,7 +678,7 @@ struct Request {
670
678
  bool is_chunked_content_provider_ = false;
671
679
  size_t authorization_count_ = 0;
672
680
  std::chrono::time_point<std::chrono::steady_clock> start_time_ =
673
- std::chrono::steady_clock::time_point::min();
681
+ (std::chrono::steady_clock::time_point::min)();
674
682
  };
675
683
 
676
684
  struct Response {
@@ -736,7 +744,8 @@ public:
736
744
  virtual ~Stream() = default;
737
745
 
738
746
  virtual bool is_readable() const = 0;
739
- virtual bool is_writable() const = 0;
747
+ virtual bool wait_readable() const = 0;
748
+ virtual bool wait_writable() const = 0;
740
749
 
741
750
  virtual ssize_t read(char *ptr, size_t size) = 0;
742
751
  virtual ssize_t write(const char *ptr, size_t size) = 0;
@@ -879,7 +888,7 @@ public:
879
888
  * Captures parameters in request path and stores them in Request::path_params
880
889
  *
881
890
  * Capture name is a substring of a pattern from : to /.
882
- * The rest of the pattern is matched agains the request path directly
891
+ * The rest of the pattern is matched against the request path directly
883
892
  * Parameters are captured starting from the next character after
884
893
  * the end of the last matched static pattern fragment until the next /.
885
894
  *
@@ -1109,7 +1118,7 @@ private:
1109
1118
  virtual bool process_and_close_socket(socket_t sock);
1110
1119
 
1111
1120
  std::atomic<bool> is_running_{false};
1112
- std::atomic<bool> is_decommisioned{false};
1121
+ std::atomic<bool> is_decommissioned{false};
1113
1122
 
1114
1123
  struct MountPointEntry {
1115
1124
  std::string mount_point;
@@ -1483,7 +1492,8 @@ public:
1483
1492
  #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1484
1493
  void enable_server_certificate_verification(bool enabled);
1485
1494
  void enable_server_hostname_verification(bool enabled);
1486
- void set_server_certificate_verifier(std::function<bool(SSL *ssl)> verifier);
1495
+ void set_server_certificate_verifier(
1496
+ std::function<SSLVerifierResponse(SSL *ssl)> verifier);
1487
1497
  #endif
1488
1498
 
1489
1499
  void set_logger(Logger logger);
@@ -1600,7 +1610,7 @@ protected:
1600
1610
  #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1601
1611
  bool server_certificate_verification_ = true;
1602
1612
  bool server_hostname_verification_ = true;
1603
- std::function<bool(SSL *ssl)> server_certificate_verifier_;
1613
+ std::function<SSLVerifierResponse(SSL *ssl)> server_certificate_verifier_;
1604
1614
  #endif
1605
1615
 
1606
1616
  Logger logger_;
@@ -1913,7 +1923,8 @@ public:
1913
1923
  #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1914
1924
  void enable_server_certificate_verification(bool enabled);
1915
1925
  void enable_server_hostname_verification(bool enabled);
1916
- void set_server_certificate_verifier(std::function<bool(SSL *ssl)> verifier);
1926
+ void set_server_certificate_verifier(
1927
+ std::function<SSLVerifierResponse(SSL *ssl)> verifier);
1917
1928
  #endif
1918
1929
 
1919
1930
  void set_logger(Logger logger);
@@ -2046,6 +2057,10 @@ inline void duration_to_sec_and_usec(const T &duration, U callback) {
2046
2057
  callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
2047
2058
  }
2048
2059
 
2060
+ template <size_t N> inline constexpr size_t str_len(const char (&)[N]) {
2061
+ return N - 1;
2062
+ }
2063
+
2049
2064
  inline bool is_numeric(const std::string &str) {
2050
2065
  return !str.empty() && std::all_of(str.begin(), str.end(), ::isdigit);
2051
2066
  }
@@ -2205,9 +2220,9 @@ inline const char *status_message(int status) {
2205
2220
 
2206
2221
  inline std::string get_bearer_token_auth(const Request &req) {
2207
2222
  if (req.has_header("Authorization")) {
2208
- static std::string BearerHeaderPrefix = "Bearer ";
2223
+ constexpr auto bearer_header_prefix_len = detail::str_len("Bearer ");
2209
2224
  return req.get_header_value("Authorization")
2210
- .substr(BearerHeaderPrefix.length());
2225
+ .substr(bearer_header_prefix_len);
2211
2226
  }
2212
2227
  return "";
2213
2228
  }
@@ -2382,8 +2397,6 @@ std::string encode_query_param(const std::string &value);
2382
2397
 
2383
2398
  std::string decode_url(const std::string &s, bool convert_plus_to_space);
2384
2399
 
2385
- void read_file(const std::string &path, std::string &out);
2386
-
2387
2400
  std::string trim_copy(const std::string &s);
2388
2401
 
2389
2402
  void divide(
@@ -2439,7 +2452,7 @@ ssize_t send_socket(socket_t sock, const void *ptr, size_t size, int flags);
2439
2452
 
2440
2453
  ssize_t read_socket(socket_t sock, void *ptr, size_t size, int flags);
2441
2454
 
2442
- enum class EncodingType { None = 0, Gzip, Brotli };
2455
+ enum class EncodingType { None = 0, Gzip, Brotli, Zstd };
2443
2456
 
2444
2457
  EncodingType encoding_type(const Request &req, const Response &res);
2445
2458
 
@@ -2449,7 +2462,8 @@ public:
2449
2462
  ~BufferStream() override = default;
2450
2463
 
2451
2464
  bool is_readable() const override;
2452
- bool is_writable() const override;
2465
+ bool wait_readable() const override;
2466
+ bool wait_writable() const override;
2453
2467
  ssize_t read(char *ptr, size_t size) override;
2454
2468
  ssize_t write(const char *ptr, size_t size) override;
2455
2469
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
@@ -2551,6 +2565,34 @@ private:
2551
2565
  };
2552
2566
  #endif
2553
2567
 
2568
+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
2569
+ class zstd_compressor : public compressor {
2570
+ public:
2571
+ zstd_compressor();
2572
+ ~zstd_compressor();
2573
+
2574
+ bool compress(const char *data, size_t data_length, bool last,
2575
+ Callback callback) override;
2576
+
2577
+ private:
2578
+ ZSTD_CCtx *ctx_ = nullptr;
2579
+ };
2580
+
2581
+ class zstd_decompressor : public decompressor {
2582
+ public:
2583
+ zstd_decompressor();
2584
+ ~zstd_decompressor();
2585
+
2586
+ bool is_valid() const override;
2587
+
2588
+ bool decompress(const char *data, size_t data_length,
2589
+ Callback callback) override;
2590
+
2591
+ private:
2592
+ ZSTD_DCtx *ctx_ = nullptr;
2593
+ };
2594
+ #endif
2595
+
2554
2596
  // NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
2555
2597
  // to store data. The call can set memory on stack for performance.
2556
2598
  class stream_line_reader {
@@ -2569,7 +2611,7 @@ private:
2569
2611
  char *fixed_buffer_;
2570
2612
  const size_t fixed_buffer_size_;
2571
2613
  size_t fixed_buffer_used_size_ = 0;
2572
- std::string glowable_buffer_;
2614
+ std::string growable_buffer_;
2573
2615
  };
2574
2616
 
2575
2617
  class mmap {
@@ -2910,18 +2952,9 @@ inline std::string decode_url(const std::string &s,
2910
2952
  return result;
2911
2953
  }
2912
2954
 
2913
- inline void read_file(const std::string &path, std::string &out) {
2914
- std::ifstream fs(path, std::ios_base::binary);
2915
- fs.seekg(0, std::ios_base::end);
2916
- auto size = fs.tellg();
2917
- fs.seekg(0);
2918
- out.resize(static_cast<size_t>(size));
2919
- fs.read(&out[0], static_cast<std::streamsize>(size));
2920
- }
2921
-
2922
2955
  inline std::string file_extension(const std::string &path) {
2923
2956
  std::smatch m;
2924
- static auto re = std::regex("\\.([a-zA-Z0-9]+)$");
2957
+ thread_local auto re = std::regex("\\.([a-zA-Z0-9]+)$");
2925
2958
  if (std::regex_search(path, m, re)) { return m[1].str(); }
2926
2959
  return std::string();
2927
2960
  }
@@ -3005,18 +3038,18 @@ inline stream_line_reader::stream_line_reader(Stream &strm, char *fixed_buffer,
3005
3038
  fixed_buffer_size_(fixed_buffer_size) {}
3006
3039
 
3007
3040
  inline const char *stream_line_reader::ptr() const {
3008
- if (glowable_buffer_.empty()) {
3041
+ if (growable_buffer_.empty()) {
3009
3042
  return fixed_buffer_;
3010
3043
  } else {
3011
- return glowable_buffer_.data();
3044
+ return growable_buffer_.data();
3012
3045
  }
3013
3046
  }
3014
3047
 
3015
3048
  inline size_t stream_line_reader::size() const {
3016
- if (glowable_buffer_.empty()) {
3049
+ if (growable_buffer_.empty()) {
3017
3050
  return fixed_buffer_used_size_;
3018
3051
  } else {
3019
- return glowable_buffer_.size();
3052
+ return growable_buffer_.size();
3020
3053
  }
3021
3054
  }
3022
3055
 
@@ -3027,7 +3060,7 @@ inline bool stream_line_reader::end_with_crlf() const {
3027
3060
 
3028
3061
  inline bool stream_line_reader::getline() {
3029
3062
  fixed_buffer_used_size_ = 0;
3030
- glowable_buffer_.clear();
3063
+ growable_buffer_.clear();
3031
3064
 
3032
3065
  #ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
3033
3066
  char prev_byte = 0;
@@ -3065,11 +3098,11 @@ inline void stream_line_reader::append(char c) {
3065
3098
  fixed_buffer_[fixed_buffer_used_size_++] = c;
3066
3099
  fixed_buffer_[fixed_buffer_used_size_] = '\0';
3067
3100
  } else {
3068
- if (glowable_buffer_.empty()) {
3101
+ if (growable_buffer_.empty()) {
3069
3102
  assert(fixed_buffer_[fixed_buffer_used_size_] == '\0');
3070
- glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
3103
+ growable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_);
3071
3104
  }
3072
- glowable_buffer_ += c;
3105
+ growable_buffer_ += c;
3073
3106
  }
3074
3107
  }
3075
3108
 
@@ -3246,35 +3279,23 @@ inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size,
3246
3279
  });
3247
3280
  }
3248
3281
 
3282
+ inline int poll_wrapper(struct pollfd *fds, nfds_t nfds, int timeout) {
3283
+ #ifdef _WIN32
3284
+ return ::WSAPoll(fds, nfds, timeout);
3285
+ #else
3286
+ return ::poll(fds, nfds, timeout);
3287
+ #endif
3288
+ }
3289
+
3249
3290
  template <bool Read>
3250
3291
  inline ssize_t select_impl(socket_t sock, time_t sec, time_t usec) {
3251
- #ifdef CPPHTTPLIB_USE_POLL
3252
3292
  struct pollfd pfd;
3253
3293
  pfd.fd = sock;
3254
3294
  pfd.events = (Read ? POLLIN : POLLOUT);
3255
3295
 
3256
3296
  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
3257
3297
 
3258
- return handle_EINTR([&]() { return poll(&pfd, 1, timeout); });
3259
- #else
3260
- #ifndef _WIN32
3261
- if (sock >= FD_SETSIZE) { return -1; }
3262
- #endif
3263
-
3264
- fd_set fds, *rfds, *wfds;
3265
- FD_ZERO(&fds);
3266
- FD_SET(sock, &fds);
3267
- rfds = (Read ? &fds : nullptr);
3268
- wfds = (Read ? nullptr : &fds);
3269
-
3270
- timeval tv;
3271
- tv.tv_sec = static_cast<long>(sec);
3272
- tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
3273
-
3274
- return handle_EINTR([&]() {
3275
- return select(static_cast<int>(sock + 1), rfds, wfds, nullptr, &tv);
3276
- });
3277
- #endif
3298
+ return handle_EINTR([&]() { return poll_wrapper(&pfd, 1, timeout); });
3278
3299
  }
3279
3300
 
3280
3301
  inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
@@ -3287,14 +3308,14 @@ inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
3287
3308
 
3288
3309
  inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
3289
3310
  time_t usec) {
3290
- #ifdef CPPHTTPLIB_USE_POLL
3291
3311
  struct pollfd pfd_read;
3292
3312
  pfd_read.fd = sock;
3293
3313
  pfd_read.events = POLLIN | POLLOUT;
3294
3314
 
3295
3315
  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
3296
3316
 
3297
- auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
3317
+ auto poll_res =
3318
+ handle_EINTR([&]() { return poll_wrapper(&pfd_read, 1, timeout); });
3298
3319
 
3299
3320
  if (poll_res == 0) { return Error::ConnectionTimeout; }
3300
3321
 
@@ -3308,38 +3329,6 @@ inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
3308
3329
  }
3309
3330
 
3310
3331
  return Error::Connection;
3311
- #else
3312
- #ifndef _WIN32
3313
- if (sock >= FD_SETSIZE) { return Error::Connection; }
3314
- #endif
3315
-
3316
- fd_set fdsr;
3317
- FD_ZERO(&fdsr);
3318
- FD_SET(sock, &fdsr);
3319
-
3320
- auto fdsw = fdsr;
3321
- auto fdse = fdsr;
3322
-
3323
- timeval tv;
3324
- tv.tv_sec = static_cast<long>(sec);
3325
- tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
3326
-
3327
- auto ret = handle_EINTR([&]() {
3328
- return select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);
3329
- });
3330
-
3331
- if (ret == 0) { return Error::ConnectionTimeout; }
3332
-
3333
- if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
3334
- auto error = 0;
3335
- socklen_t len = sizeof(error);
3336
- auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
3337
- reinterpret_cast<char *>(&error), &len);
3338
- auto successful = res >= 0 && !error;
3339
- return successful ? Error::Success : Error::Connection;
3340
- }
3341
- return Error::Connection;
3342
- #endif
3343
3332
  }
3344
3333
 
3345
3334
  inline bool is_socket_alive(socket_t sock) {
@@ -3359,11 +3348,12 @@ public:
3359
3348
  time_t write_timeout_sec, time_t write_timeout_usec,
3360
3349
  time_t max_timeout_msec = 0,
3361
3350
  std::chrono::time_point<std::chrono::steady_clock> start_time =
3362
- std::chrono::steady_clock::time_point::min());
3351
+ (std::chrono::steady_clock::time_point::min)());
3363
3352
  ~SocketStream() override;
3364
3353
 
3365
3354
  bool is_readable() const override;
3366
- bool is_writable() const override;
3355
+ bool wait_readable() const override;
3356
+ bool wait_writable() const override;
3367
3357
  ssize_t read(char *ptr, size_t size) override;
3368
3358
  ssize_t write(const char *ptr, size_t size) override;
3369
3359
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
@@ -3378,7 +3368,7 @@ private:
3378
3368
  time_t write_timeout_sec_;
3379
3369
  time_t write_timeout_usec_;
3380
3370
  time_t max_timeout_msec_;
3381
- const std::chrono::time_point<std::chrono::steady_clock> start_time;
3371
+ const std::chrono::time_point<std::chrono::steady_clock> start_time_;
3382
3372
 
3383
3373
  std::vector<char> read_buff_;
3384
3374
  size_t read_buff_off_ = 0;
@@ -3395,11 +3385,12 @@ public:
3395
3385
  time_t read_timeout_usec, time_t write_timeout_sec,
3396
3386
  time_t write_timeout_usec, time_t max_timeout_msec = 0,
3397
3387
  std::chrono::time_point<std::chrono::steady_clock> start_time =
3398
- std::chrono::steady_clock::time_point::min());
3388
+ (std::chrono::steady_clock::time_point::min)());
3399
3389
  ~SSLSocketStream() override;
3400
3390
 
3401
3391
  bool is_readable() const override;
3402
- bool is_writable() const override;
3392
+ bool wait_readable() const override;
3393
+ bool wait_writable() const override;
3403
3394
  ssize_t read(char *ptr, size_t size) override;
3404
3395
  ssize_t write(const char *ptr, size_t size) override;
3405
3396
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
@@ -3415,7 +3406,7 @@ private:
3415
3406
  time_t write_timeout_sec_;
3416
3407
  time_t write_timeout_usec_;
3417
3408
  time_t max_timeout_msec_;
3418
- const std::chrono::time_point<std::chrono::steady_clock> start_time;
3409
+ const std::chrono::time_point<std::chrono::steady_clock> start_time_;
3419
3410
  };
3420
3411
  #endif
3421
3412
 
@@ -3550,7 +3541,6 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
3550
3541
  hints.ai_flags = socket_flags;
3551
3542
  }
3552
3543
 
3553
- #ifndef _WIN32
3554
3544
  if (hints.ai_family == AF_UNIX) {
3555
3545
  const auto addrlen = host.length();
3556
3546
  if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; }
@@ -3574,11 +3564,19 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
3574
3564
  sizeof(addr) - sizeof(addr.sun_path) + addrlen);
3575
3565
 
3576
3566
  #ifndef SOCK_CLOEXEC
3567
+ #ifndef _WIN32
3577
3568
  fcntl(sock, F_SETFD, FD_CLOEXEC);
3569
+ #endif
3578
3570
  #endif
3579
3571
 
3580
3572
  if (socket_options) { socket_options(sock); }
3581
3573
 
3574
+ #ifdef _WIN32
3575
+ // Setting SO_REUSEADDR seems not to work well with AF_UNIX on windows, so
3576
+ // remove the option.
3577
+ detail::set_socket_opt(sock, SOL_SOCKET, SO_REUSEADDR, 0);
3578
+ #endif
3579
+
3582
3580
  bool dummy;
3583
3581
  if (!bind_or_connect(sock, hints, dummy)) {
3584
3582
  close_socket(sock);
@@ -3587,7 +3585,6 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
3587
3585
  }
3588
3586
  return sock;
3589
3587
  }
3590
- #endif
3591
3588
 
3592
3589
  auto service = std::to_string(port);
3593
3590
 
@@ -3993,6 +3990,12 @@ inline EncodingType encoding_type(const Request &req, const Response &res) {
3993
3990
  if (ret) { return EncodingType::Gzip; }
3994
3991
  #endif
3995
3992
 
3993
+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
3994
+ // TODO: 'Accept-Encoding' has zstd, not zstd;q=0
3995
+ ret = s.find("zstd") != std::string::npos;
3996
+ if (ret) { return EncodingType::Zstd; }
3997
+ #endif
3998
+
3996
3999
  return EncodingType::None;
3997
4000
  }
3998
4001
 
@@ -4201,6 +4204,61 @@ inline bool brotli_decompressor::decompress(const char *data,
4201
4204
  }
4202
4205
  #endif
4203
4206
 
4207
+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
4208
+ inline zstd_compressor::zstd_compressor() {
4209
+ ctx_ = ZSTD_createCCtx();
4210
+ ZSTD_CCtx_setParameter(ctx_, ZSTD_c_compressionLevel, ZSTD_fast);
4211
+ }
4212
+
4213
+ inline zstd_compressor::~zstd_compressor() { ZSTD_freeCCtx(ctx_); }
4214
+
4215
+ inline bool zstd_compressor::compress(const char *data, size_t data_length,
4216
+ bool last, Callback callback) {
4217
+ std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4218
+
4219
+ ZSTD_EndDirective mode = last ? ZSTD_e_end : ZSTD_e_continue;
4220
+ ZSTD_inBuffer input = {data, data_length, 0};
4221
+
4222
+ bool finished;
4223
+ do {
4224
+ ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
4225
+ size_t const remaining = ZSTD_compressStream2(ctx_, &output, &input, mode);
4226
+
4227
+ if (ZSTD_isError(remaining)) { return false; }
4228
+
4229
+ if (!callback(buff.data(), output.pos)) { return false; }
4230
+
4231
+ finished = last ? (remaining == 0) : (input.pos == input.size);
4232
+
4233
+ } while (!finished);
4234
+
4235
+ return true;
4236
+ }
4237
+
4238
+ inline zstd_decompressor::zstd_decompressor() { ctx_ = ZSTD_createDCtx(); }
4239
+
4240
+ inline zstd_decompressor::~zstd_decompressor() { ZSTD_freeDCtx(ctx_); }
4241
+
4242
+ inline bool zstd_decompressor::is_valid() const { return ctx_ != nullptr; }
4243
+
4244
+ inline bool zstd_decompressor::decompress(const char *data, size_t data_length,
4245
+ Callback callback) {
4246
+ std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
4247
+ ZSTD_inBuffer input = {data, data_length, 0};
4248
+
4249
+ while (input.pos < input.size) {
4250
+ ZSTD_outBuffer output = {buff.data(), CPPHTTPLIB_COMPRESSION_BUFSIZ, 0};
4251
+ size_t const remaining = ZSTD_decompressStream(ctx_, &output, &input);
4252
+
4253
+ if (ZSTD_isError(remaining)) { return false; }
4254
+
4255
+ if (!callback(buff.data(), output.pos)) { return false; }
4256
+ }
4257
+
4258
+ return true;
4259
+ }
4260
+ #endif
4261
+
4204
4262
  inline bool has_header(const Headers &headers, const std::string &key) {
4205
4263
  return headers.find(key) != headers.end();
4206
4264
  }
@@ -4227,6 +4285,9 @@ inline bool parse_header(const char *beg, const char *end, T fn) {
4227
4285
  p++;
4228
4286
  }
4229
4287
 
4288
+ auto name = std::string(beg, p);
4289
+ if (!detail::fields::is_field_name(name)) { return false; }
4290
+
4230
4291
  if (p == end) { return false; }
4231
4292
 
4232
4293
  auto key_end = p;
@@ -4242,10 +4303,6 @@ inline bool parse_header(const char *beg, const char *end, T fn) {
4242
4303
  if (!key_len) { return false; }
4243
4304
 
4244
4305
  auto key = std::string(beg, key_end);
4245
- // auto val = (case_ignore::equal(key, "Location") ||
4246
- // case_ignore::equal(key, "Referer"))
4247
- // ? std::string(p, end)
4248
- // : decode_url(std::string(p, end), false);
4249
4306
  auto val = std::string(p, end);
4250
4307
 
4251
4308
  if (!detail::fields::is_field_value(val)) { return false; }
@@ -4341,7 +4398,8 @@ inline bool read_content_without_length(Stream &strm,
4341
4398
  uint64_t r = 0;
4342
4399
  for (;;) {
4343
4400
  auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
4344
- if (n <= 0) { return false; }
4401
+ if (n == 0) { return true; }
4402
+ if (n < 0) { return false; }
4345
4403
 
4346
4404
  if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
4347
4405
  r += static_cast<uint64_t>(n);
@@ -4384,7 +4442,7 @@ inline bool read_content_chunked(Stream &strm, T &x,
4384
4442
 
4385
4443
  assert(chunk_len == 0);
4386
4444
 
4387
- // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentiones "The chunked
4445
+ // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentions "The chunked
4388
4446
  // transfer coding is complete when a chunk with a chunk-size of zero is
4389
4447
  // received, possibly followed by a trailer section, and finally terminated by
4390
4448
  // an empty line". https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1
@@ -4394,8 +4452,8 @@ inline bool read_content_chunked(Stream &strm, T &x,
4394
4452
  // to be ok whether the final CRLF exists or not in the chunked data.
4395
4453
  // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.3
4396
4454
  //
4397
- // According to the reference code in RFC 9112, cpp-htpplib now allows
4398
- // chuncked transfer coding data without the final CRLF.
4455
+ // According to the reference code in RFC 9112, cpp-httplib now allows
4456
+ // chunked transfer coding data without the final CRLF.
4399
4457
  if (!line_reader.getline()) { return true; }
4400
4458
 
4401
4459
  while (strcmp(line_reader.ptr(), "\r\n") != 0) {
@@ -4442,6 +4500,13 @@ bool prepare_content_receiver(T &x, int &status,
4442
4500
  #else
4443
4501
  status = StatusCode::UnsupportedMediaType_415;
4444
4502
  return false;
4503
+ #endif
4504
+ } else if (encoding == "zstd") {
4505
+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
4506
+ decompressor = detail::make_unique<zstd_decompressor>();
4507
+ #else
4508
+ status = StatusCode::UnsupportedMediaType_415;
4509
+ return false;
4445
4510
  #endif
4446
4511
  }
4447
4512
 
@@ -4565,7 +4630,7 @@ inline bool write_content(Stream &strm, const ContentProvider &content_provider,
4565
4630
 
4566
4631
  data_sink.write = [&](const char *d, size_t l) -> bool {
4567
4632
  if (ok) {
4568
- if (strm.is_writable() && write_data(strm, d, l)) {
4633
+ if (write_data(strm, d, l)) {
4569
4634
  offset += l;
4570
4635
  } else {
4571
4636
  ok = false;
@@ -4574,10 +4639,10 @@ inline bool write_content(Stream &strm, const ContentProvider &content_provider,
4574
4639
  return ok;
4575
4640
  };
4576
4641
 
4577
- data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };
4642
+ data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
4578
4643
 
4579
4644
  while (offset < end_offset && !is_shutting_down()) {
4580
- if (!strm.is_writable()) {
4645
+ if (!strm.wait_writable()) {
4581
4646
  error = Error::Write;
4582
4647
  return false;
4583
4648
  } else if (!content_provider(offset, end_offset - offset, data_sink)) {
@@ -4615,17 +4680,17 @@ write_content_without_length(Stream &strm,
4615
4680
  data_sink.write = [&](const char *d, size_t l) -> bool {
4616
4681
  if (ok) {
4617
4682
  offset += l;
4618
- if (!strm.is_writable() || !write_data(strm, d, l)) { ok = false; }
4683
+ if (!write_data(strm, d, l)) { ok = false; }
4619
4684
  }
4620
4685
  return ok;
4621
4686
  };
4622
4687
 
4623
- data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };
4688
+ data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
4624
4689
 
4625
4690
  data_sink.done = [&](void) { data_available = false; };
4626
4691
 
4627
4692
  while (data_available && !is_shutting_down()) {
4628
- if (!strm.is_writable()) {
4693
+ if (!strm.wait_writable()) {
4629
4694
  return false;
4630
4695
  } else if (!content_provider(offset, 0, data_sink)) {
4631
4696
  return false;
@@ -4660,10 +4725,7 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
4660
4725
  // Emit chunked response header and footer for each chunk
4661
4726
  auto chunk =
4662
4727
  from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
4663
- if (!strm.is_writable() ||
4664
- !write_data(strm, chunk.data(), chunk.size())) {
4665
- ok = false;
4666
- }
4728
+ if (!write_data(strm, chunk.data(), chunk.size())) { ok = false; }
4667
4729
  }
4668
4730
  } else {
4669
4731
  ok = false;
@@ -4672,7 +4734,7 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
4672
4734
  return ok;
4673
4735
  };
4674
4736
 
4675
- data_sink.is_writable = [&]() -> bool { return strm.is_writable(); };
4737
+ data_sink.is_writable = [&]() -> bool { return strm.wait_writable(); };
4676
4738
 
4677
4739
  auto done_with_trailer = [&](const Headers *trailer) {
4678
4740
  if (!ok) { return; }
@@ -4692,17 +4754,14 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
4692
4754
  if (!payload.empty()) {
4693
4755
  // Emit chunked response header and footer for each chunk
4694
4756
  auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
4695
- if (!strm.is_writable() ||
4696
- !write_data(strm, chunk.data(), chunk.size())) {
4757
+ if (!write_data(strm, chunk.data(), chunk.size())) {
4697
4758
  ok = false;
4698
4759
  return;
4699
4760
  }
4700
4761
  }
4701
4762
 
4702
- static const std::string done_marker("0\r\n");
4703
- if (!write_data(strm, done_marker.data(), done_marker.size())) {
4704
- ok = false;
4705
- }
4763
+ constexpr const char done_marker[] = "0\r\n";
4764
+ if (!write_data(strm, done_marker, str_len(done_marker))) { ok = false; }
4706
4765
 
4707
4766
  // Trailer
4708
4767
  if (trailer) {
@@ -4714,8 +4773,8 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
4714
4773
  }
4715
4774
  }
4716
4775
 
4717
- static const std::string crlf("\r\n");
4718
- if (!write_data(strm, crlf.data(), crlf.size())) { ok = false; }
4776
+ constexpr const char crlf[] = "\r\n";
4777
+ if (!write_data(strm, crlf, str_len(crlf))) { ok = false; }
4719
4778
  };
4720
4779
 
4721
4780
  data_sink.done = [&](void) { done_with_trailer(nullptr); };
@@ -4725,7 +4784,7 @@ write_content_chunked(Stream &strm, const ContentProvider &content_provider,
4725
4784
  };
4726
4785
 
4727
4786
  while (data_available && !is_shutting_down()) {
4728
- if (!strm.is_writable()) {
4787
+ if (!strm.wait_writable()) {
4729
4788
  error = Error::Write;
4730
4789
  return false;
4731
4790
  } else if (!content_provider(offset, 0, data_sink)) {
@@ -4957,13 +5016,13 @@ public:
4957
5016
  return false;
4958
5017
  }
4959
5018
 
4960
- static const std::string header_content_type = "Content-Type:";
5019
+ constexpr const char header_content_type[] = "Content-Type:";
4961
5020
 
4962
5021
  if (start_with_case_ignore(header, header_content_type)) {
4963
5022
  file_.content_type =
4964
- trim_copy(header.substr(header_content_type.size()));
5023
+ trim_copy(header.substr(str_len(header_content_type)));
4965
5024
  } else {
4966
- static const std::regex re_content_disposition(
5025
+ thread_local const std::regex re_content_disposition(
4967
5026
  R"~(^Content-Disposition:\s*form-data;\s*(.*)$)~",
4968
5027
  std::regex_constants::icase);
4969
5028
 
@@ -4985,8 +5044,8 @@ public:
4985
5044
 
4986
5045
  it = params.find("filename*");
4987
5046
  if (it != params.end()) {
4988
- // Only allow UTF-8 enconnding...
4989
- static const std::regex re_rfc5987_encoding(
5047
+ // Only allow UTF-8 encoding...
5048
+ thread_local const std::regex re_rfc5987_encoding(
4990
5049
  R"~(^UTF-8''(.+?)$)~", std::regex_constants::icase);
4991
5050
 
4992
5051
  std::smatch m2;
@@ -5058,10 +5117,10 @@ private:
5058
5117
  file_.content_type.clear();
5059
5118
  }
5060
5119
 
5061
- bool start_with_case_ignore(const std::string &a,
5062
- const std::string &b) const {
5063
- if (a.size() < b.size()) { return false; }
5064
- for (size_t i = 0; i < b.size(); i++) {
5120
+ bool start_with_case_ignore(const std::string &a, const char *b) const {
5121
+ const auto b_len = strlen(b);
5122
+ if (a.size() < b_len) { return false; }
5123
+ for (size_t i = 0; i < b_len; i++) {
5065
5124
  if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {
5066
5125
  return false;
5067
5126
  }
@@ -5148,19 +5207,18 @@ private:
5148
5207
  };
5149
5208
 
5150
5209
  inline std::string random_string(size_t length) {
5151
- static const char data[] =
5210
+ constexpr const char data[] =
5152
5211
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
5153
5212
 
5154
- // std::random_device might actually be deterministic on some
5155
- // platforms, but due to lack of support in the c++ standard library,
5156
- // doing better requires either some ugly hacks or breaking portability.
5157
- static std::random_device seed_gen;
5158
-
5159
- // Request 128 bits of entropy for initialization
5160
- static std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(),
5161
- seed_gen()};
5162
-
5163
- static std::mt19937 engine(seed_sequence);
5213
+ thread_local auto engine([]() {
5214
+ // std::random_device might actually be deterministic on some
5215
+ // platforms, but due to lack of support in the c++ standard library,
5216
+ // doing better requires either some ugly hacks or breaking portability.
5217
+ std::random_device seed_gen;
5218
+ // Request 128 bits of entropy for initialization
5219
+ std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
5220
+ return std::mt19937(seed_sequence);
5221
+ }());
5164
5222
 
5165
5223
  std::string result;
5166
5224
  for (size_t i = 0; i < length; i++) {
@@ -5232,7 +5290,7 @@ serialize_multipart_formdata(const MultipartFormDataItems &items,
5232
5290
 
5233
5291
  inline bool range_error(Request &req, Response &res) {
5234
5292
  if (!req.ranges.empty() && 200 <= res.status && res.status < 300) {
5235
- ssize_t contant_len = static_cast<ssize_t>(
5293
+ ssize_t content_len = static_cast<ssize_t>(
5236
5294
  res.content_length_ ? res.content_length_ : res.body.size());
5237
5295
 
5238
5296
  ssize_t prev_first_pos = -1;
@@ -5252,12 +5310,12 @@ inline bool range_error(Request &req, Response &res) {
5252
5310
 
5253
5311
  if (first_pos == -1 && last_pos == -1) {
5254
5312
  first_pos = 0;
5255
- last_pos = contant_len;
5313
+ last_pos = content_len;
5256
5314
  }
5257
5315
 
5258
5316
  if (first_pos == -1) {
5259
- first_pos = contant_len - last_pos;
5260
- last_pos = contant_len - 1;
5317
+ first_pos = content_len - last_pos;
5318
+ last_pos = content_len - 1;
5261
5319
  }
5262
5320
 
5263
5321
  // NOTE: RFC-9110 '14.1.2. Byte Ranges':
@@ -5269,13 +5327,13 @@ inline bool range_error(Request &req, Response &res) {
5269
5327
  // with a value that is one less than the current length of the selected
5270
5328
  // representation).
5271
5329
  // https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2-6
5272
- if (last_pos == -1 || last_pos >= contant_len) {
5273
- last_pos = contant_len - 1;
5330
+ if (last_pos == -1 || last_pos >= content_len) {
5331
+ last_pos = content_len - 1;
5274
5332
  }
5275
5333
 
5276
5334
  // Range must be within content length
5277
5335
  if (!(0 <= first_pos && first_pos <= last_pos &&
5278
- last_pos <= contant_len - 1)) {
5336
+ last_pos <= content_len - 1)) {
5279
5337
  return true;
5280
5338
  }
5281
5339
 
@@ -5674,7 +5732,8 @@ inline bool parse_www_authenticate(const Response &res,
5674
5732
  bool is_proxy) {
5675
5733
  auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
5676
5734
  if (res.has_header(auth_key)) {
5677
- static auto re = std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
5735
+ thread_local auto re =
5736
+ std::regex(R"~((?:(?:,\s*)?(.+?)=(?:"(.*?)"|([^,]*))))~");
5678
5737
  auto s = res.get_header_value(auth_key);
5679
5738
  auto pos = s.find(' ');
5680
5739
  if (pos != std::string::npos) {
@@ -5758,7 +5817,7 @@ inline void hosted_at(const std::string &hostname,
5758
5817
  inline std::string append_query_params(const std::string &path,
5759
5818
  const Params &params) {
5760
5819
  std::string path_with_query = path;
5761
- const static std::regex re("[^?]+\\?.*");
5820
+ thread_local const std::regex re("[^?]+\\?.*");
5762
5821
  auto delm = std::regex_match(path, re) ? '&' : '?';
5763
5822
  path_with_query += delm + detail::params_to_query_str(params);
5764
5823
  return path_with_query;
@@ -5987,14 +6046,14 @@ inline ssize_t Stream::write(const std::string &s) {
5987
6046
 
5988
6047
  namespace detail {
5989
6048
 
5990
- inline void calc_actual_timeout(time_t max_timeout_msec,
5991
- time_t duration_msec, time_t timeout_sec,
5992
- time_t timeout_usec, time_t &actual_timeout_sec,
6049
+ inline void calc_actual_timeout(time_t max_timeout_msec, time_t duration_msec,
6050
+ time_t timeout_sec, time_t timeout_usec,
6051
+ time_t &actual_timeout_sec,
5993
6052
  time_t &actual_timeout_usec) {
5994
6053
  auto timeout_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
5995
6054
 
5996
6055
  auto actual_timeout_msec =
5997
- std::min(max_timeout_msec - duration_msec, timeout_msec);
6056
+ (std::min)(max_timeout_msec - duration_msec, timeout_msec);
5998
6057
 
5999
6058
  actual_timeout_sec = actual_timeout_msec / 1000;
6000
6059
  actual_timeout_usec = (actual_timeout_msec % 1000) * 1000;
@@ -6010,12 +6069,16 @@ inline SocketStream::SocketStream(
6010
6069
  read_timeout_usec_(read_timeout_usec),
6011
6070
  write_timeout_sec_(write_timeout_sec),
6012
6071
  write_timeout_usec_(write_timeout_usec),
6013
- max_timeout_msec_(max_timeout_msec), start_time(start_time),
6072
+ max_timeout_msec_(max_timeout_msec), start_time_(start_time),
6014
6073
  read_buff_(read_buff_size_, 0) {}
6015
6074
 
6016
6075
  inline SocketStream::~SocketStream() = default;
6017
6076
 
6018
6077
  inline bool SocketStream::is_readable() const {
6078
+ return read_buff_off_ < read_buff_content_size_;
6079
+ }
6080
+
6081
+ inline bool SocketStream::wait_readable() const {
6019
6082
  if (max_timeout_msec_ <= 0) {
6020
6083
  return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
6021
6084
  }
@@ -6028,7 +6091,7 @@ inline bool SocketStream::is_readable() const {
6028
6091
  return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
6029
6092
  }
6030
6093
 
6031
- inline bool SocketStream::is_writable() const {
6094
+ inline bool SocketStream::wait_writable() const {
6032
6095
  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
6033
6096
  is_socket_alive(sock_);
6034
6097
  }
@@ -6055,7 +6118,7 @@ inline ssize_t SocketStream::read(char *ptr, size_t size) {
6055
6118
  }
6056
6119
  }
6057
6120
 
6058
- if (!is_readable()) { return -1; }
6121
+ if (!wait_readable()) { return -1; }
6059
6122
 
6060
6123
  read_buff_off_ = 0;
6061
6124
  read_buff_content_size_ = 0;
@@ -6080,7 +6143,7 @@ inline ssize_t SocketStream::read(char *ptr, size_t size) {
6080
6143
  }
6081
6144
 
6082
6145
  inline ssize_t SocketStream::write(const char *ptr, size_t size) {
6083
- if (!is_writable()) { return -1; }
6146
+ if (!wait_writable()) { return -1; }
6084
6147
 
6085
6148
  #if defined(_WIN32) && !defined(_WIN64)
6086
6149
  size =
@@ -6104,14 +6167,16 @@ inline socket_t SocketStream::socket() const { return sock_; }
6104
6167
 
6105
6168
  inline time_t SocketStream::duration() const {
6106
6169
  return std::chrono::duration_cast<std::chrono::milliseconds>(
6107
- std::chrono::steady_clock::now() - start_time)
6170
+ std::chrono::steady_clock::now() - start_time_)
6108
6171
  .count();
6109
6172
  }
6110
6173
 
6111
6174
  // Buffer stream implementation
6112
6175
  inline bool BufferStream::is_readable() const { return true; }
6113
6176
 
6114
- inline bool BufferStream::is_writable() const { return true; }
6177
+ inline bool BufferStream::wait_readable() const { return true; }
6178
+
6179
+ inline bool BufferStream::wait_writable() const { return true; }
6115
6180
 
6116
6181
  inline ssize_t BufferStream::read(char *ptr, size_t size) {
6117
6182
  #if defined(_MSC_VER) && _MSC_VER < 1910
@@ -6141,7 +6206,7 @@ inline time_t BufferStream::duration() const { return 0; }
6141
6206
  inline const std::string &BufferStream::get_buffer() const { return buffer; }
6142
6207
 
6143
6208
  inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) {
6144
- static constexpr char marker[] = "/:";
6209
+ constexpr const char marker[] = "/:";
6145
6210
 
6146
6211
  // One past the last ending position of a path param substring
6147
6212
  std::size_t last_param_end = 0;
@@ -6162,7 +6227,7 @@ inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) {
6162
6227
  static_fragments_.push_back(
6163
6228
  pattern.substr(last_param_end, marker_pos - last_param_end + 1));
6164
6229
 
6165
- const auto param_name_start = marker_pos + 2;
6230
+ const auto param_name_start = marker_pos + str_len(marker);
6166
6231
 
6167
6232
  auto sep_pos = pattern.find(separator, param_name_start);
6168
6233
  if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }
@@ -6469,12 +6534,12 @@ inline Server &Server::set_payload_max_length(size_t length) {
6469
6534
  inline bool Server::bind_to_port(const std::string &host, int port,
6470
6535
  int socket_flags) {
6471
6536
  auto ret = bind_internal(host, port, socket_flags);
6472
- if (ret == -1) { is_decommisioned = true; }
6537
+ if (ret == -1) { is_decommissioned = true; }
6473
6538
  return ret >= 0;
6474
6539
  }
6475
6540
  inline int Server::bind_to_any_port(const std::string &host, int socket_flags) {
6476
6541
  auto ret = bind_internal(host, 0, socket_flags);
6477
- if (ret == -1) { is_decommisioned = true; }
6542
+ if (ret == -1) { is_decommissioned = true; }
6478
6543
  return ret;
6479
6544
  }
6480
6545
 
@@ -6488,7 +6553,7 @@ inline bool Server::listen(const std::string &host, int port,
6488
6553
  inline bool Server::is_running() const { return is_running_; }
6489
6554
 
6490
6555
  inline void Server::wait_until_ready() const {
6491
- while (!is_running_ && !is_decommisioned) {
6556
+ while (!is_running_ && !is_decommissioned) {
6492
6557
  std::this_thread::sleep_for(std::chrono::milliseconds{1});
6493
6558
  }
6494
6559
  }
@@ -6500,10 +6565,10 @@ inline void Server::stop() {
6500
6565
  detail::shutdown_socket(sock);
6501
6566
  detail::close_socket(sock);
6502
6567
  }
6503
- is_decommisioned = false;
6568
+ is_decommissioned = false;
6504
6569
  }
6505
6570
 
6506
- inline void Server::decommission() { is_decommisioned = true; }
6571
+ inline void Server::decommission() { is_decommissioned = true; }
6507
6572
 
6508
6573
  inline bool Server::parse_request_line(const char *s, Request &req) const {
6509
6574
  auto len = strlen(s);
@@ -6526,7 +6591,7 @@ inline bool Server::parse_request_line(const char *s, Request &req) const {
6526
6591
  if (count != 3) { return false; }
6527
6592
  }
6528
6593
 
6529
- static const std::set<std::string> methods{
6594
+ thread_local const std::set<std::string> methods{
6530
6595
  "GET", "HEAD", "POST", "PUT", "DELETE",
6531
6596
  "CONNECT", "OPTIONS", "TRACE", "PATCH", "PRI"};
6532
6597
 
@@ -6680,6 +6745,10 @@ Server::write_content_with_provider(Stream &strm, const Request &req,
6680
6745
  } else if (type == detail::EncodingType::Brotli) {
6681
6746
  #ifdef CPPHTTPLIB_BROTLI_SUPPORT
6682
6747
  compressor = detail::make_unique<detail::brotli_compressor>();
6748
+ #endif
6749
+ } else if (type == detail::EncodingType::Zstd) {
6750
+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
6751
+ compressor = detail::make_unique<detail::zstd_compressor>();
6683
6752
  #endif
6684
6753
  } else {
6685
6754
  compressor = detail::make_unique<detail::nocompressor>();
@@ -6862,7 +6931,7 @@ Server::create_server_socket(const std::string &host, int port,
6862
6931
 
6863
6932
  inline int Server::bind_internal(const std::string &host, int port,
6864
6933
  int socket_flags) {
6865
- if (is_decommisioned) { return -1; }
6934
+ if (is_decommissioned) { return -1; }
6866
6935
 
6867
6936
  if (!is_valid()) { return -1; }
6868
6937
 
@@ -6889,7 +6958,7 @@ inline int Server::bind_internal(const std::string &host, int port,
6889
6958
  }
6890
6959
 
6891
6960
  inline bool Server::listen_internal() {
6892
- if (is_decommisioned) { return false; }
6961
+ if (is_decommissioned) { return false; }
6893
6962
 
6894
6963
  auto ret = true;
6895
6964
  is_running_ = true;
@@ -6913,7 +6982,7 @@ inline bool Server::listen_internal() {
6913
6982
  #endif
6914
6983
 
6915
6984
  #if defined _WIN32
6916
- // sockets conneced via WASAccept inherit flags NO_HANDLE_INHERIT,
6985
+ // sockets connected via WASAccept inherit flags NO_HANDLE_INHERIT,
6917
6986
  // OVERLAPPED
6918
6987
  socket_t sock = WSAAccept(svr_sock_, nullptr, nullptr, nullptr, 0);
6919
6988
  #elif defined SOCK_CLOEXEC
@@ -6955,7 +7024,7 @@ inline bool Server::listen_internal() {
6955
7024
  task_queue->shutdown();
6956
7025
  }
6957
7026
 
6958
- is_decommisioned = !ret;
7027
+ is_decommissioned = !ret;
6959
7028
  return ret;
6960
7029
  }
6961
7030
 
@@ -7095,6 +7164,8 @@ inline void Server::apply_ranges(const Request &req, Response &res,
7095
7164
  res.set_header("Content-Encoding", "gzip");
7096
7165
  } else if (type == detail::EncodingType::Brotli) {
7097
7166
  res.set_header("Content-Encoding", "br");
7167
+ } else if (type == detail::EncodingType::Zstd) {
7168
+ res.set_header("Content-Encoding", "zstd");
7098
7169
  }
7099
7170
  }
7100
7171
  }
@@ -7134,6 +7205,11 @@ inline void Server::apply_ranges(const Request &req, Response &res,
7134
7205
  #ifdef CPPHTTPLIB_BROTLI_SUPPORT
7135
7206
  compressor = detail::make_unique<detail::brotli_compressor>();
7136
7207
  content_encoding = "br";
7208
+ #endif
7209
+ } else if (type == detail::EncodingType::Zstd) {
7210
+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
7211
+ compressor = detail::make_unique<detail::zstd_compressor>();
7212
+ content_encoding = "zstd";
7137
7213
  #endif
7138
7214
  }
7139
7215
 
@@ -7189,20 +7265,6 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
7189
7265
  res.version = "HTTP/1.1";
7190
7266
  res.headers = default_headers_;
7191
7267
 
7192
- #ifdef _WIN32
7193
- // TODO: Increase FD_SETSIZE statically (libzmq), dynamically (MySQL).
7194
- #else
7195
- #ifndef CPPHTTPLIB_USE_POLL
7196
- // Socket file descriptor exceeded FD_SETSIZE...
7197
- if (strm.socket() >= FD_SETSIZE) {
7198
- Headers dummy;
7199
- detail::read_headers(strm, dummy);
7200
- res.status = StatusCode::InternalServerError_500;
7201
- return write_response(strm, close_connection, req, res);
7202
- }
7203
- #endif
7204
- #endif
7205
-
7206
7268
  // Request line and headers
7207
7269
  if (!parse_request_line(line_reader.ptr(), req) ||
7208
7270
  !detail::read_headers(strm, req.headers)) {
@@ -7394,6 +7456,16 @@ inline ClientImpl::ClientImpl(const std::string &host, int port,
7394
7456
  client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
7395
7457
 
7396
7458
  inline ClientImpl::~ClientImpl() {
7459
+ // Wait until all the requests in flight are handled.
7460
+ size_t retry_count = 10;
7461
+ while (retry_count-- > 0) {
7462
+ {
7463
+ std::lock_guard<std::mutex> guard(socket_mutex_);
7464
+ if (socket_requests_in_flight_ == 0) { break; }
7465
+ }
7466
+ std::this_thread::sleep_for(std::chrono::milliseconds{1});
7467
+ }
7468
+
7397
7469
  std::lock_guard<std::mutex> guard(socket_mutex_);
7398
7470
  shutdown_socket(socket_);
7399
7471
  close_socket(socket_);
@@ -7519,9 +7591,9 @@ inline bool ClientImpl::read_response_line(Stream &strm, const Request &req,
7519
7591
  if (!line_reader.getline()) { return false; }
7520
7592
 
7521
7593
  #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
7522
- const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
7594
+ thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r?\n");
7523
7595
  #else
7524
- const static std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
7596
+ thread_local const std::regex re("(HTTP/1\\.[01]) (\\d{3})(?: (.*?))?\r\n");
7525
7597
  #endif
7526
7598
 
7527
7599
  std::cmatch m;
@@ -7577,7 +7649,7 @@ inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
7577
7649
  #endif
7578
7650
 
7579
7651
  if (!is_alive) {
7580
- // Attempt to avoid sigpipe by shutting down nongracefully if it seems
7652
+ // Attempt to avoid sigpipe by shutting down non-gracefully if it seems
7581
7653
  // like the other side has already closed the connection Also, there
7582
7654
  // cannot be any requests in flight from other threads since we locked
7583
7655
  // request_mutex_, so safe to close everything immediately
@@ -7753,7 +7825,7 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
7753
7825
  auto location = res.get_header_value("location");
7754
7826
  if (location.empty()) { return false; }
7755
7827
 
7756
- const static std::regex re(
7828
+ thread_local const std::regex re(
7757
7829
  R"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
7758
7830
 
7759
7831
  std::smatch m;
@@ -7862,6 +7934,10 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
7862
7934
  #ifdef CPPHTTPLIB_ZLIB_SUPPORT
7863
7935
  if (!accept_encoding.empty()) { accept_encoding += ", "; }
7864
7936
  accept_encoding += "gzip, deflate";
7937
+ #endif
7938
+ #ifdef CPPHTTPLIB_ZSTD_SUPPORT
7939
+ if (!accept_encoding.empty()) { accept_encoding += ", "; }
7940
+ accept_encoding += "zstd";
7865
7941
  #endif
7866
7942
  req.set_header("Accept-Encoding", accept_encoding);
7867
7943
  }
@@ -8213,8 +8289,7 @@ inline bool ClientImpl::process_socket(
8213
8289
  std::function<bool(Stream &strm)> callback) {
8214
8290
  return detail::process_client_socket(
8215
8291
  socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
8216
- write_timeout_usec_, max_timeout_msec_, start_time,
8217
- std::move(callback));
8292
+ write_timeout_usec_, max_timeout_msec_, start_time, std::move(callback));
8218
8293
  }
8219
8294
 
8220
8295
  inline bool ClientImpl::is_ssl() const { return false; }
@@ -9009,7 +9084,7 @@ inline void ClientImpl::enable_server_hostname_verification(bool enabled) {
9009
9084
  }
9010
9085
 
9011
9086
  inline void ClientImpl::set_server_certificate_verifier(
9012
- std::function<bool(SSL *ssl)> verifier) {
9087
+ std::function<SSLVerifierResponse(SSL *ssl)> verifier) {
9013
9088
  server_certificate_verifier_ = verifier;
9014
9089
  }
9015
9090
  #endif
@@ -9062,18 +9137,13 @@ inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, socket_t sock,
9062
9137
  // Note that it is not always possible to avoid SIGPIPE, this is merely a
9063
9138
  // best-efforts.
9064
9139
  if (shutdown_gracefully) {
9065
- #ifdef _WIN32
9066
9140
  (void)(sock);
9067
- SSL_shutdown(ssl);
9068
- #else
9069
- detail::set_socket_opt_time(sock, SOL_SOCKET, SO_RCVTIMEO, 1, 0);
9070
-
9071
- auto ret = SSL_shutdown(ssl);
9072
- while (ret == 0) {
9073
- std::this_thread::sleep_for(std::chrono::milliseconds{100});
9074
- ret = SSL_shutdown(ssl);
9141
+ // SSL_shutdown() returns 0 on first call (indicating close_notify alert
9142
+ // sent) and 1 on subsequent call (indicating close_notify alert received)
9143
+ if (SSL_shutdown(ssl) == 0) {
9144
+ // Expected to return 1, but even if it doesn't, we free ssl
9145
+ SSL_shutdown(ssl);
9075
9146
  }
9076
- #endif
9077
9147
  }
9078
9148
 
9079
9149
  std::lock_guard<std::mutex> guard(ctx_mutex);
@@ -9124,19 +9194,11 @@ inline bool process_client_socket_ssl(
9124
9194
  time_t max_timeout_msec,
9125
9195
  std::chrono::time_point<std::chrono::steady_clock> start_time, T callback) {
9126
9196
  SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
9127
- write_timeout_sec, write_timeout_usec,
9128
- max_timeout_msec, start_time);
9197
+ write_timeout_sec, write_timeout_usec, max_timeout_msec,
9198
+ start_time);
9129
9199
  return callback(strm);
9130
9200
  }
9131
9201
 
9132
- class SSLInit {
9133
- public:
9134
- SSLInit() {
9135
- OPENSSL_init_ssl(
9136
- OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
9137
- }
9138
- };
9139
-
9140
9202
  // SSL socket stream implementation
9141
9203
  inline SSLSocketStream::SSLSocketStream(
9142
9204
  socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec,
@@ -9147,13 +9209,17 @@ inline SSLSocketStream::SSLSocketStream(
9147
9209
  read_timeout_usec_(read_timeout_usec),
9148
9210
  write_timeout_sec_(write_timeout_sec),
9149
9211
  write_timeout_usec_(write_timeout_usec),
9150
- max_timeout_msec_(max_timeout_msec), start_time(start_time) {
9212
+ max_timeout_msec_(max_timeout_msec), start_time_(start_time) {
9151
9213
  SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
9152
9214
  }
9153
9215
 
9154
9216
  inline SSLSocketStream::~SSLSocketStream() = default;
9155
9217
 
9156
9218
  inline bool SSLSocketStream::is_readable() const {
9219
+ return SSL_pending(ssl_) > 0;
9220
+ }
9221
+
9222
+ inline bool SSLSocketStream::wait_readable() const {
9157
9223
  if (max_timeout_msec_ <= 0) {
9158
9224
  return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
9159
9225
  }
@@ -9166,7 +9232,7 @@ inline bool SSLSocketStream::is_readable() const {
9166
9232
  return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
9167
9233
  }
9168
9234
 
9169
- inline bool SSLSocketStream::is_writable() const {
9235
+ inline bool SSLSocketStream::wait_writable() const {
9170
9236
  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
9171
9237
  is_socket_alive(sock_) && !is_ssl_peer_could_be_closed(ssl_, sock_);
9172
9238
  }
@@ -9174,7 +9240,7 @@ inline bool SSLSocketStream::is_writable() const {
9174
9240
  inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
9175
9241
  if (SSL_pending(ssl_) > 0) {
9176
9242
  return SSL_read(ssl_, ptr, static_cast<int>(size));
9177
- } else if (is_readable()) {
9243
+ } else if (wait_readable()) {
9178
9244
  auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
9179
9245
  if (ret < 0) {
9180
9246
  auto err = SSL_get_error(ssl_, ret);
@@ -9188,7 +9254,7 @@ inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
9188
9254
  #endif
9189
9255
  if (SSL_pending(ssl_) > 0) {
9190
9256
  return SSL_read(ssl_, ptr, static_cast<int>(size));
9191
- } else if (is_readable()) {
9257
+ } else if (wait_readable()) {
9192
9258
  std::this_thread::sleep_for(std::chrono::microseconds{10});
9193
9259
  ret = SSL_read(ssl_, ptr, static_cast<int>(size));
9194
9260
  if (ret >= 0) { return ret; }
@@ -9205,7 +9271,7 @@ inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
9205
9271
  }
9206
9272
 
9207
9273
  inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
9208
- if (is_writable()) {
9274
+ if (wait_writable()) {
9209
9275
  auto handle_size = static_cast<int>(
9210
9276
  std::min<size_t>(size, (std::numeric_limits<int>::max)()));
9211
9277
 
@@ -9220,7 +9286,7 @@ inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
9220
9286
  #else
9221
9287
  while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
9222
9288
  #endif
9223
- if (is_writable()) {
9289
+ if (wait_writable()) {
9224
9290
  std::this_thread::sleep_for(std::chrono::microseconds{10});
9225
9291
  ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
9226
9292
  if (ret >= 0) { return ret; }
@@ -9249,12 +9315,10 @@ inline socket_t SSLSocketStream::socket() const { return sock_; }
9249
9315
 
9250
9316
  inline time_t SSLSocketStream::duration() const {
9251
9317
  return std::chrono::duration_cast<std::chrono::milliseconds>(
9252
- std::chrono::steady_clock::now() - start_time)
9318
+ std::chrono::steady_clock::now() - start_time_)
9253
9319
  .count();
9254
9320
  }
9255
9321
 
9256
- static SSLInit sslinit_;
9257
-
9258
9322
  } // namespace detail
9259
9323
 
9260
9324
  // SSL HTTP server implementation
@@ -9623,12 +9687,18 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
9623
9687
  }
9624
9688
 
9625
9689
  if (server_certificate_verification_) {
9690
+ auto verification_status = SSLVerifierResponse::NoDecisionMade;
9691
+
9626
9692
  if (server_certificate_verifier_) {
9627
- if (!server_certificate_verifier_(ssl2)) {
9628
- error = Error::SSLServerVerification;
9629
- return false;
9630
- }
9631
- } else {
9693
+ verification_status = server_certificate_verifier_(ssl2);
9694
+ }
9695
+
9696
+ if (verification_status == SSLVerifierResponse::CertificateRejected) {
9697
+ error = Error::SSLServerVerification;
9698
+ return false;
9699
+ }
9700
+
9701
+ if (verification_status == SSLVerifierResponse::NoDecisionMade) {
9632
9702
  verify_result_ = SSL_get_verify_result(ssl2);
9633
9703
 
9634
9704
  if (verify_result_ != X509_V_OK) {
@@ -9740,8 +9810,8 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
9740
9810
 
9741
9811
  auto type = GEN_DNS;
9742
9812
 
9743
- struct in6_addr addr6{};
9744
- struct in_addr addr{};
9813
+ struct in6_addr addr6 = {};
9814
+ struct in_addr addr = {};
9745
9815
  size_t addr_len = 0;
9746
9816
 
9747
9817
  #ifndef __MINGW32__
@@ -10389,7 +10459,7 @@ inline void Client::enable_server_hostname_verification(bool enabled) {
10389
10459
  }
10390
10460
 
10391
10461
  inline void Client::set_server_certificate_verifier(
10392
- std::function<bool(SSL *ssl)> verifier) {
10462
+ std::function<SSLVerifierResponse(SSL *ssl)> verifier) {
10393
10463
  cli_->set_server_certificate_verifier(verifier);
10394
10464
  }
10395
10465
  #endif
@@ -10433,8 +10503,4 @@ inline SSL_CTX *Client::ssl_context() const {
10433
10503
 
10434
10504
  } // namespace httplib
10435
10505
 
10436
- #if defined(_WIN32) && defined(CPPHTTPLIB_USE_POLL)
10437
- #undef poll
10438
- #endif
10439
-
10440
10506
  #endif // CPPHTTPLIB_HTTPLIB_H