@fugood/llama.node 0.3.6 → 0.3.8

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 (186) hide show
  1. package/README.md +17 -2
  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 +3 -1
  19. package/lib/index.js +16 -1
  20. package/lib/index.ts +16 -0
  21. package/package.json +1 -1
  22. package/src/EmbeddingWorker.cpp +4 -3
  23. package/src/LlamaCompletionWorker.cpp +4 -2
  24. package/src/LlamaContext.cpp +61 -6
  25. package/src/LlamaContext.h +1 -0
  26. package/src/common.hpp +6 -11
  27. package/src/llama.cpp/.github/workflows/build.yml +19 -17
  28. package/src/llama.cpp/.github/workflows/docker.yml +77 -30
  29. package/src/llama.cpp/.github/workflows/editorconfig.yml +3 -1
  30. package/src/llama.cpp/.github/workflows/server.yml +22 -3
  31. package/src/llama.cpp/CMakeLists.txt +49 -24
  32. package/src/llama.cpp/common/arg.cpp +82 -26
  33. package/src/llama.cpp/common/arg.h +3 -0
  34. package/src/llama.cpp/common/common.cpp +192 -72
  35. package/src/llama.cpp/common/common.h +51 -18
  36. package/src/llama.cpp/common/ngram-cache.cpp +12 -12
  37. package/src/llama.cpp/common/ngram-cache.h +2 -2
  38. package/src/llama.cpp/common/sampling.cpp +11 -6
  39. package/src/llama.cpp/common/speculative.cpp +18 -15
  40. package/src/llama.cpp/docs/build.md +2 -0
  41. package/src/llama.cpp/examples/batched/batched.cpp +9 -7
  42. package/src/llama.cpp/examples/batched-bench/batched-bench.cpp +3 -3
  43. package/src/llama.cpp/examples/convert-llama2c-to-ggml/convert-llama2c-to-ggml.cpp +10 -8
  44. package/src/llama.cpp/examples/cvector-generator/cvector-generator.cpp +11 -8
  45. package/src/llama.cpp/examples/cvector-generator/mean.hpp +1 -1
  46. package/src/llama.cpp/examples/cvector-generator/pca.hpp +1 -1
  47. package/src/llama.cpp/examples/embedding/embedding.cpp +8 -7
  48. package/src/llama.cpp/examples/eval-callback/eval-callback.cpp +7 -6
  49. package/src/llama.cpp/examples/export-lora/export-lora.cpp +8 -7
  50. package/src/llama.cpp/examples/gguf/gguf.cpp +10 -6
  51. package/src/llama.cpp/examples/gguf-hash/gguf-hash.cpp +1 -0
  52. package/src/llama.cpp/examples/gguf-split/gguf-split.cpp +8 -7
  53. package/src/llama.cpp/examples/gritlm/gritlm.cpp +13 -10
  54. package/src/llama.cpp/examples/imatrix/imatrix.cpp +13 -12
  55. package/src/llama.cpp/examples/infill/infill.cpp +23 -24
  56. package/src/llama.cpp/examples/llama-bench/llama-bench.cpp +44 -13
  57. package/src/llama.cpp/examples/llama.android/llama/src/main/cpp/llama-android.cpp +11 -6
  58. package/src/llama.cpp/examples/llava/clip.cpp +4 -2
  59. package/src/llama.cpp/examples/llava/llava-cli.cpp +9 -6
  60. package/src/llama.cpp/examples/llava/llava.cpp +2 -2
  61. package/src/llama.cpp/examples/llava/minicpmv-cli.cpp +8 -4
  62. package/src/llama.cpp/examples/llava/qwen2vl-cli.cpp +11 -8
  63. package/src/llama.cpp/examples/lookahead/lookahead.cpp +6 -7
  64. package/src/llama.cpp/examples/lookup/lookup-create.cpp +4 -9
  65. package/src/llama.cpp/examples/lookup/lookup-stats.cpp +3 -7
  66. package/src/llama.cpp/examples/lookup/lookup.cpp +5 -6
  67. package/src/llama.cpp/examples/main/main.cpp +51 -29
  68. package/src/llama.cpp/examples/parallel/parallel.cpp +5 -6
  69. package/src/llama.cpp/examples/passkey/passkey.cpp +7 -5
  70. package/src/llama.cpp/examples/perplexity/perplexity.cpp +37 -23
  71. package/src/llama.cpp/examples/quantize-stats/quantize-stats.cpp +12 -14
  72. package/src/llama.cpp/examples/retrieval/retrieval.cpp +8 -8
  73. package/src/llama.cpp/examples/rpc/rpc-server.cpp +12 -0
  74. package/src/llama.cpp/examples/run/CMakeLists.txt +1 -1
  75. package/src/llama.cpp/examples/run/linenoise.cpp/linenoise.cpp +1351 -0
  76. package/src/llama.cpp/examples/run/linenoise.cpp/linenoise.h +114 -0
  77. package/src/llama.cpp/examples/run/run.cpp +175 -61
  78. package/src/llama.cpp/examples/save-load-state/save-load-state.cpp +4 -25
  79. package/src/llama.cpp/examples/server/CMakeLists.txt +1 -0
  80. package/src/llama.cpp/examples/server/httplib.h +1295 -409
  81. package/src/llama.cpp/examples/server/server.cpp +387 -181
  82. package/src/llama.cpp/examples/server/tests/requirements.txt +1 -0
  83. package/src/llama.cpp/examples/server/utils.hpp +170 -58
  84. package/src/llama.cpp/examples/simple/simple.cpp +9 -8
  85. package/src/llama.cpp/examples/simple-chat/simple-chat.cpp +16 -12
  86. package/src/llama.cpp/examples/speculative/speculative.cpp +22 -23
  87. package/src/llama.cpp/examples/speculative-simple/speculative-simple.cpp +8 -12
  88. package/src/llama.cpp/examples/tokenize/tokenize.cpp +17 -5
  89. package/src/llama.cpp/examples/tts/tts.cpp +64 -23
  90. package/src/llama.cpp/ggml/CMakeLists.txt +5 -21
  91. package/src/llama.cpp/ggml/include/ggml-backend.h +2 -0
  92. package/src/llama.cpp/ggml/include/ggml-cpp.h +1 -0
  93. package/src/llama.cpp/ggml/include/ggml.h +36 -145
  94. package/src/llama.cpp/ggml/include/gguf.h +202 -0
  95. package/src/llama.cpp/ggml/src/CMakeLists.txt +6 -3
  96. package/src/llama.cpp/ggml/src/ggml-alloc.c +5 -0
  97. package/src/llama.cpp/ggml/src/ggml-backend-impl.h +0 -1
  98. package/src/llama.cpp/ggml/src/ggml-backend-reg.cpp +79 -49
  99. package/src/llama.cpp/ggml/src/ggml-backend.cpp +5 -2
  100. package/src/llama.cpp/ggml/src/ggml-cpu/CMakeLists.txt +33 -23
  101. package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu-aarch64.cpp +57 -72
  102. package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu-quants.c +87 -2
  103. package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu.c +335 -66
  104. package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu.cpp +10 -2
  105. package/src/llama.cpp/ggml/src/ggml-cpu/llamafile/sgemm.cpp +1090 -378
  106. package/src/llama.cpp/ggml/src/ggml-cpu/llamafile/sgemm.h +2 -2
  107. package/src/llama.cpp/ggml/src/ggml-cuda/vendors/cuda.h +1 -0
  108. package/src/llama.cpp/ggml/src/ggml-cuda/vendors/hip.h +3 -0
  109. package/src/llama.cpp/ggml/src/ggml-cuda/vendors/musa.h +3 -0
  110. package/src/llama.cpp/ggml/src/ggml-hip/CMakeLists.txt +3 -1
  111. package/src/llama.cpp/ggml/src/ggml-impl.h +11 -16
  112. package/src/llama.cpp/ggml/src/ggml-metal/CMakeLists.txt +16 -0
  113. package/src/llama.cpp/ggml/src/ggml-opencl/ggml-opencl.cpp +6 -6
  114. package/src/llama.cpp/ggml/src/ggml-rpc/ggml-rpc.cpp +154 -35
  115. package/src/llama.cpp/ggml/src/ggml-sycl/backend.hpp +1 -0
  116. package/src/llama.cpp/ggml/src/ggml-sycl/common.cpp +9 -3
  117. package/src/llama.cpp/ggml/src/ggml-sycl/common.hpp +18 -0
  118. package/src/llama.cpp/ggml/src/ggml-sycl/concat.cpp +3 -2
  119. package/src/llama.cpp/ggml/src/ggml-sycl/concat.hpp +1 -2
  120. package/src/llama.cpp/ggml/src/ggml-sycl/conv.cpp +3 -2
  121. package/src/llama.cpp/ggml/src/ggml-sycl/conv.hpp +1 -2
  122. package/src/llama.cpp/ggml/src/ggml-sycl/dpct/helper.hpp +40 -95
  123. package/src/llama.cpp/ggml/src/ggml-sycl/element_wise.cpp +48 -48
  124. package/src/llama.cpp/ggml/src/ggml-sycl/element_wise.hpp +24 -24
  125. package/src/llama.cpp/ggml/src/ggml-sycl/ggml-sycl.cpp +238 -164
  126. package/src/llama.cpp/ggml/src/ggml-sycl/gla.cpp +105 -0
  127. package/src/llama.cpp/ggml/src/ggml-sycl/gla.hpp +8 -0
  128. package/src/llama.cpp/ggml/src/ggml-sycl/outprod.cpp +3 -3
  129. package/src/llama.cpp/ggml/src/ggml-sycl/outprod.hpp +1 -2
  130. package/src/llama.cpp/ggml/src/ggml-sycl/tsembd.cpp +3 -2
  131. package/src/llama.cpp/ggml/src/ggml-sycl/tsembd.hpp +1 -2
  132. package/src/llama.cpp/ggml/src/ggml-sycl/wkv6.cpp +7 -5
  133. package/src/llama.cpp/ggml/src/ggml-sycl/wkv6.hpp +1 -2
  134. package/src/llama.cpp/ggml/src/ggml-vulkan/CMakeLists.txt +74 -4
  135. package/src/llama.cpp/ggml/src/ggml-vulkan/ggml-vulkan.cpp +314 -116
  136. package/src/llama.cpp/ggml/src/ggml-vulkan/vulkan-shaders/CMakeLists.txt +4 -2
  137. package/src/llama.cpp/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp +9 -3
  138. package/src/llama.cpp/ggml/src/ggml.c +117 -1327
  139. package/src/llama.cpp/ggml/src/gguf.cpp +1329 -0
  140. package/src/llama.cpp/include/llama-cpp.h +6 -1
  141. package/src/llama.cpp/include/llama.h +138 -75
  142. package/src/llama.cpp/src/CMakeLists.txt +13 -1
  143. package/src/llama.cpp/src/llama-adapter.cpp +347 -0
  144. package/src/llama.cpp/src/llama-adapter.h +74 -0
  145. package/src/llama.cpp/src/llama-arch.cpp +1487 -0
  146. package/src/llama.cpp/src/llama-arch.h +400 -0
  147. package/src/llama.cpp/src/llama-batch.cpp +368 -0
  148. package/src/llama.cpp/src/llama-batch.h +88 -0
  149. package/src/llama.cpp/src/llama-chat.cpp +578 -0
  150. package/src/llama.cpp/src/llama-chat.h +52 -0
  151. package/src/llama.cpp/src/llama-context.cpp +1775 -0
  152. package/src/llama.cpp/src/llama-context.h +128 -0
  153. package/src/llama.cpp/src/llama-cparams.cpp +1 -0
  154. package/src/llama.cpp/src/llama-cparams.h +37 -0
  155. package/src/llama.cpp/src/llama-grammar.cpp +5 -4
  156. package/src/llama.cpp/src/llama-grammar.h +3 -1
  157. package/src/llama.cpp/src/llama-hparams.cpp +71 -0
  158. package/src/llama.cpp/src/llama-hparams.h +139 -0
  159. package/src/llama.cpp/src/llama-impl.cpp +167 -0
  160. package/src/llama.cpp/src/llama-impl.h +16 -136
  161. package/src/llama.cpp/src/llama-kv-cache.cpp +718 -0
  162. package/src/llama.cpp/src/llama-kv-cache.h +218 -0
  163. package/src/llama.cpp/src/llama-mmap.cpp +589 -0
  164. package/src/llama.cpp/src/llama-mmap.h +67 -0
  165. package/src/llama.cpp/src/llama-model-loader.cpp +1124 -0
  166. package/src/llama.cpp/src/llama-model-loader.h +167 -0
  167. package/src/llama.cpp/src/llama-model.cpp +3953 -0
  168. package/src/llama.cpp/src/llama-model.h +370 -0
  169. package/src/llama.cpp/src/llama-quant.cpp +934 -0
  170. package/src/llama.cpp/src/llama-quant.h +1 -0
  171. package/src/llama.cpp/src/llama-sampling.cpp +147 -32
  172. package/src/llama.cpp/src/llama-sampling.h +3 -19
  173. package/src/llama.cpp/src/llama-vocab.cpp +1832 -575
  174. package/src/llama.cpp/src/llama-vocab.h +97 -142
  175. package/src/llama.cpp/src/llama.cpp +7160 -20314
  176. package/src/llama.cpp/src/unicode.cpp +8 -3
  177. package/src/llama.cpp/tests/CMakeLists.txt +2 -0
  178. package/src/llama.cpp/tests/test-autorelease.cpp +3 -3
  179. package/src/llama.cpp/tests/test-backend-ops.cpp +370 -59
  180. package/src/llama.cpp/tests/test-chat-template.cpp +162 -125
  181. package/src/llama.cpp/tests/test-gguf.cpp +222 -187
  182. package/src/llama.cpp/tests/test-model-load-cancel.cpp +1 -1
  183. package/src/llama.cpp/tests/test-sampling.cpp +0 -1
  184. package/src/llama.cpp/tests/test-tokenizer-0.cpp +4 -4
  185. package/src/llama.cpp/tests/test-tokenizer-1-bpe.cpp +9 -7
  186. package/src/llama.cpp/tests/test-tokenizer-1-spm.cpp +8 -6
@@ -8,7 +8,7 @@
8
8
  #ifndef CPPHTTPLIB_HTTPLIB_H
9
9
  #define CPPHTTPLIB_HTTPLIB_H
10
10
 
11
- #define CPPHTTPLIB_VERSION "0.15.3"
11
+ #define CPPHTTPLIB_VERSION "0.18.5"
12
12
 
13
13
  /*
14
14
  * Configuration
@@ -18,8 +18,12 @@
18
18
  #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
19
19
  #endif
20
20
 
21
+ #ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND
22
+ #define CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND 10000
23
+ #endif
24
+
21
25
  #ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
22
- #define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
26
+ #define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 100
23
27
  #endif
24
28
 
25
29
  #ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
@@ -30,20 +34,36 @@
30
34
  #define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
31
35
  #endif
32
36
 
33
- #ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND
34
- #define CPPHTTPLIB_READ_TIMEOUT_SECOND 5
37
+ #ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND
38
+ #define CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND 5
39
+ #endif
40
+
41
+ #ifndef CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND
42
+ #define CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND 0
43
+ #endif
44
+
45
+ #ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND
46
+ #define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND 5
47
+ #endif
48
+
49
+ #ifndef CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND
50
+ #define CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND 0
51
+ #endif
52
+
53
+ #ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND
54
+ #define CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND 300
35
55
  #endif
36
56
 
37
- #ifndef CPPHTTPLIB_READ_TIMEOUT_USECOND
38
- #define CPPHTTPLIB_READ_TIMEOUT_USECOND 0
57
+ #ifndef CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND
58
+ #define CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND 0
39
59
  #endif
40
60
 
41
- #ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND
42
- #define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5
61
+ #ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND
62
+ #define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND 5
43
63
  #endif
44
64
 
45
- #ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND
46
- #define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0
65
+ #ifndef CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND
66
+ #define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0
47
67
  #endif
48
68
 
49
69
  #ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
@@ -90,8 +110,12 @@
90
110
  #define CPPHTTPLIB_TCP_NODELAY false
91
111
  #endif
92
112
 
113
+ #ifndef CPPHTTPLIB_IPV6_V6ONLY
114
+ #define CPPHTTPLIB_IPV6_V6ONLY false
115
+ #endif
116
+
93
117
  #ifndef CPPHTTPLIB_RECV_BUFSIZ
94
- #define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
118
+ #define CPPHTTPLIB_RECV_BUFSIZ size_t(16384u)
95
119
  #endif
96
120
 
97
121
  #ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
@@ -145,11 +169,11 @@ using ssize_t = long;
145
169
  #endif // _MSC_VER
146
170
 
147
171
  #ifndef S_ISREG
148
- #define S_ISREG(m) (((m)&S_IFREG) == S_IFREG)
172
+ #define S_ISREG(m) (((m) & S_IFREG) == S_IFREG)
149
173
  #endif // S_ISREG
150
174
 
151
175
  #ifndef S_ISDIR
152
- #define S_ISDIR(m) (((m)&S_IFDIR) == S_IFDIR)
176
+ #define S_ISDIR(m) (((m) & S_IFDIR) == S_IFDIR)
153
177
  #endif // S_ISDIR
154
178
 
155
179
  #ifndef NOMINMAX
@@ -269,7 +293,12 @@ using socket_t = int;
269
293
  #include <iostream>
270
294
  #include <sstream>
271
295
 
272
- #if OPENSSL_VERSION_NUMBER < 0x30000000L
296
+ #if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
297
+ #if OPENSSL_VERSION_NUMBER < 0x1010107f
298
+ #error Please use OpenSSL or a current version of BoringSSL
299
+ #endif
300
+ #define SSL_get1_peer_certificate SSL_get_peer_certificate
301
+ #elif OPENSSL_VERSION_NUMBER < 0x30000000L
273
302
  #error Sorry, OpenSSL versions prior to 3.0.0 are not supported
274
303
  #endif
275
304
 
@@ -312,16 +341,63 @@ make_unique(std::size_t n) {
312
341
  return std::unique_ptr<T>(new RT[n]);
313
342
  }
314
343
 
315
- struct ci {
316
- bool operator()(const std::string &s1, const std::string &s2) const {
317
- return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(),
318
- s2.end(),
319
- [](unsigned char c1, unsigned char c2) {
320
- return ::tolower(c1) < ::tolower(c2);
321
- });
344
+ namespace case_ignore {
345
+
346
+ inline unsigned char to_lower(int c) {
347
+ const static unsigned char table[256] = {
348
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
349
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
350
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44,
351
+ 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
352
+ 60, 61, 62, 63, 64, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
353
+ 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
354
+ 122, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104,
355
+ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
356
+ 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
357
+ 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
358
+ 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
359
+ 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
360
+ 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 224, 225, 226,
361
+ 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
362
+ 242, 243, 244, 245, 246, 215, 248, 249, 250, 251, 252, 253, 254, 223, 224,
363
+ 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
364
+ 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
365
+ 255,
366
+ };
367
+ return table[(unsigned char)(char)c];
368
+ }
369
+
370
+ inline bool equal(const std::string &a, const std::string &b) {
371
+ return a.size() == b.size() &&
372
+ std::equal(a.begin(), a.end(), b.begin(), [](char ca, char cb) {
373
+ return to_lower(ca) == to_lower(cb);
374
+ });
375
+ }
376
+
377
+ struct equal_to {
378
+ bool operator()(const std::string &a, const std::string &b) const {
379
+ return equal(a, b);
322
380
  }
323
381
  };
324
382
 
383
+ struct hash {
384
+ size_t operator()(const std::string &key) const {
385
+ return hash_core(key.data(), key.size(), 0);
386
+ }
387
+
388
+ size_t hash_core(const char *s, size_t l, size_t h) const {
389
+ return (l == 0) ? h
390
+ : hash_core(s + 1, l - 1,
391
+ // Unsets the 6 high bits of h, therefore no
392
+ // overflow happens
393
+ (((std::numeric_limits<size_t>::max)() >> 6) &
394
+ h * 33) ^
395
+ static_cast<unsigned char>(to_lower(*s)));
396
+ }
397
+ };
398
+
399
+ } // namespace case_ignore
400
+
325
401
  // This is based on
326
402
  // "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189".
327
403
 
@@ -427,7 +503,9 @@ enum StatusCode {
427
503
  NetworkAuthenticationRequired_511 = 511,
428
504
  };
429
505
 
430
- using Headers = std::multimap<std::string, std::string, detail::ci>;
506
+ using Headers =
507
+ std::unordered_multimap<std::string, std::string, detail::case_ignore::hash,
508
+ detail::case_ignore::equal_to>;
431
509
 
432
510
  using Params = std::multimap<std::string, std::string>;
433
511
  using Match = std::smatch;
@@ -534,6 +612,7 @@ using Ranges = std::vector<Range>;
534
612
  struct Request {
535
613
  std::string method;
536
614
  std::string path;
615
+ Params params;
537
616
  Headers headers;
538
617
  std::string body;
539
618
 
@@ -545,11 +624,11 @@ struct Request {
545
624
  // for server
546
625
  std::string version;
547
626
  std::string target;
548
- Params params;
549
627
  MultipartFormDataMap files;
550
628
  Ranges ranges;
551
629
  Match matches;
552
630
  std::unordered_map<std::string, std::string> path_params;
631
+ std::function<bool()> is_connection_closed = []() { return true; };
553
632
 
554
633
  // for client
555
634
  ResponseHandler response_handler;
@@ -560,8 +639,10 @@ struct Request {
560
639
  #endif
561
640
 
562
641
  bool has_header(const std::string &key) const;
563
- std::string get_header_value(const std::string &key, size_t id = 0) const;
564
- uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const;
642
+ std::string get_header_value(const std::string &key, const char *def = "",
643
+ size_t id = 0) const;
644
+ uint64_t get_header_value_u64(const std::string &key, uint64_t def = 0,
645
+ size_t id = 0) const;
565
646
  size_t get_header_value_count(const std::string &key) const;
566
647
  void set_header(const std::string &key, const std::string &val);
567
648
 
@@ -592,8 +673,10 @@ struct Response {
592
673
  std::string location; // Redirect location
593
674
 
594
675
  bool has_header(const std::string &key) const;
595
- std::string get_header_value(const std::string &key, size_t id = 0) const;
596
- uint64_t get_header_value_u64(const std::string &key, size_t id = 0) const;
676
+ std::string get_header_value(const std::string &key, const char *def = "",
677
+ size_t id = 0) const;
678
+ uint64_t get_header_value_u64(const std::string &key, uint64_t def = 0,
679
+ size_t id = 0) const;
597
680
  size_t get_header_value_count(const std::string &key) const;
598
681
  void set_header(const std::string &key, const std::string &val);
599
682
 
@@ -614,6 +697,10 @@ struct Response {
614
697
  const std::string &content_type, ContentProviderWithoutLength provider,
615
698
  ContentProviderResourceReleaser resource_releaser = nullptr);
616
699
 
700
+ void set_file_content(const std::string &path,
701
+ const std::string &content_type);
702
+ void set_file_content(const std::string &path);
703
+
617
704
  Response() = default;
618
705
  Response(const Response &) = default;
619
706
  Response &operator=(const Response &) = default;
@@ -631,6 +718,8 @@ struct Response {
631
718
  ContentProviderResourceReleaser content_provider_resource_releaser_;
632
719
  bool is_chunked_content_provider_ = false;
633
720
  bool content_provider_success_ = false;
721
+ std::string file_content_path_;
722
+ std::string file_content_content_type_;
634
723
  };
635
724
 
636
725
  class Stream {
@@ -646,8 +735,6 @@ public:
646
735
  virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;
647
736
  virtual socket_t socket() const = 0;
648
737
 
649
- template <typename... Args>
650
- ssize_t write_format(const char *fmt, const Args &...args);
651
738
  ssize_t write(const char *ptr);
652
739
  ssize_t write(const std::string &s);
653
740
  };
@@ -719,13 +806,18 @@ private:
719
806
 
720
807
  if (pool_.shutdown_ && pool_.jobs_.empty()) { break; }
721
808
 
722
- fn = std::move(pool_.jobs_.front());
809
+ fn = pool_.jobs_.front();
723
810
  pool_.jobs_.pop_front();
724
811
  }
725
812
 
726
813
  assert(true == static_cast<bool>(fn));
727
814
  fn();
728
815
  }
816
+
817
+ #if defined(CPPHTTPLIB_OPENSSL_SUPPORT) && !defined(OPENSSL_IS_BORINGSSL) && \
818
+ !defined(LIBRESSL_VERSION_NUMBER)
819
+ OPENSSL_thread_stop();
820
+ #endif
729
821
  }
730
822
 
731
823
  ThreadPool &pool_;
@@ -787,7 +879,6 @@ public:
787
879
  bool match(Request &request) const override;
788
880
 
789
881
  private:
790
- static constexpr char marker = ':';
791
882
  // Treat segment separators as the end of path parameter capture
792
883
  // Does not need to handle query parameters as they are parsed before path
793
884
  // matching
@@ -871,8 +962,13 @@ public:
871
962
  Server &set_default_file_mimetype(const std::string &mime);
872
963
  Server &set_file_request_handler(Handler handler);
873
964
 
874
- Server &set_error_handler(HandlerWithResponse handler);
875
- Server &set_error_handler(Handler handler);
965
+ template <class ErrorHandlerFunc>
966
+ Server &set_error_handler(ErrorHandlerFunc &&handler) {
967
+ return set_error_handler_core(
968
+ std::forward<ErrorHandlerFunc>(handler),
969
+ std::is_convertible<ErrorHandlerFunc, HandlerWithResponse>{});
970
+ }
971
+
876
972
  Server &set_exception_handler(ExceptionHandler handler);
877
973
  Server &set_pre_routing_handler(HandlerWithResponse handler);
878
974
  Server &set_post_routing_handler(Handler handler);
@@ -882,6 +978,7 @@ public:
882
978
 
883
979
  Server &set_address_family(int family);
884
980
  Server &set_tcp_nodelay(bool on);
981
+ Server &set_ipv6_v6only(bool on);
885
982
  Server &set_socket_options(SocketOptions socket_options);
886
983
 
887
984
  Server &set_default_headers(Headers headers);
@@ -914,21 +1011,24 @@ public:
914
1011
  bool is_running() const;
915
1012
  void wait_until_ready() const;
916
1013
  void stop();
1014
+ void decommission();
917
1015
 
918
1016
  std::function<TaskQueue *(void)> new_task_queue;
919
1017
 
920
1018
  protected:
921
- bool process_request(Stream &strm, bool close_connection,
1019
+ bool process_request(Stream &strm, const std::string &remote_addr,
1020
+ int remote_port, const std::string &local_addr,
1021
+ int local_port, bool close_connection,
922
1022
  bool &connection_closed,
923
1023
  const std::function<void(Request &)> &setup_request);
924
1024
 
925
1025
  std::atomic<socket_t> svr_sock_{INVALID_SOCKET};
926
1026
  size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
927
1027
  time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
928
- time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
929
- time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
930
- time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
931
- time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;
1028
+ time_t read_timeout_sec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_SECOND;
1029
+ time_t read_timeout_usec_ = CPPHTTPLIB_SERVER_READ_TIMEOUT_USECOND;
1030
+ time_t write_timeout_sec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_SECOND;
1031
+ time_t write_timeout_usec_ = CPPHTTPLIB_SERVER_WRITE_TIMEOUT_USECOND;
932
1032
  time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
933
1033
  time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
934
1034
  size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
@@ -943,6 +1043,9 @@ private:
943
1043
  static std::unique_ptr<detail::MatcherBase>
944
1044
  make_matcher(const std::string &pattern);
945
1045
 
1046
+ Server &set_error_handler_core(HandlerWithResponse handler, std::true_type);
1047
+ Server &set_error_handler_core(Handler handler, std::false_type);
1048
+
946
1049
  socket_t create_server_socket(const std::string &host, int port,
947
1050
  int socket_flags,
948
1051
  SocketOptions socket_options) const;
@@ -985,7 +1088,7 @@ private:
985
1088
  virtual bool process_and_close_socket(socket_t sock);
986
1089
 
987
1090
  std::atomic<bool> is_running_{false};
988
- std::atomic<bool> done_{false};
1091
+ std::atomic<bool> is_decommisioned{false};
989
1092
 
990
1093
  struct MountPointEntry {
991
1094
  std::string mount_point;
@@ -1018,6 +1121,7 @@ private:
1018
1121
 
1019
1122
  int address_family_ = AF_UNSPEC;
1020
1123
  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1124
+ bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1021
1125
  SocketOptions socket_options_ = default_socket_options;
1022
1126
 
1023
1127
  Headers default_headers_;
@@ -1037,6 +1141,7 @@ enum class Error {
1037
1141
  SSLConnection,
1038
1142
  SSLLoadingCerts,
1039
1143
  SSLServerVerification,
1144
+ SSLServerHostnameVerification,
1040
1145
  UnsupportedMultipartBoundaryChars,
1041
1146
  Compression,
1042
1147
  ConnectionTimeout,
@@ -1074,9 +1179,10 @@ public:
1074
1179
  // Request Headers
1075
1180
  bool has_request_header(const std::string &key) const;
1076
1181
  std::string get_request_header_value(const std::string &key,
1182
+ const char *def = "",
1077
1183
  size_t id = 0) const;
1078
1184
  uint64_t get_request_header_value_u64(const std::string &key,
1079
- size_t id = 0) const;
1185
+ uint64_t def = 0, size_t id = 0) const;
1080
1186
  size_t get_request_header_value_count(const std::string &key) const;
1081
1187
 
1082
1188
  private:
@@ -1140,10 +1246,18 @@ public:
1140
1246
  const std::string &content_type);
1141
1247
  Result Post(const std::string &path, const Headers &headers, const char *body,
1142
1248
  size_t content_length, const std::string &content_type);
1249
+ Result Post(const std::string &path, const Headers &headers, const char *body,
1250
+ size_t content_length, const std::string &content_type,
1251
+ Progress progress);
1143
1252
  Result Post(const std::string &path, const std::string &body,
1144
1253
  const std::string &content_type);
1254
+ Result Post(const std::string &path, const std::string &body,
1255
+ const std::string &content_type, Progress progress);
1145
1256
  Result Post(const std::string &path, const Headers &headers,
1146
1257
  const std::string &body, const std::string &content_type);
1258
+ Result Post(const std::string &path, const Headers &headers,
1259
+ const std::string &body, const std::string &content_type,
1260
+ Progress progress);
1147
1261
  Result Post(const std::string &path, size_t content_length,
1148
1262
  ContentProvider content_provider,
1149
1263
  const std::string &content_type);
@@ -1159,6 +1273,8 @@ public:
1159
1273
  Result Post(const std::string &path, const Params &params);
1160
1274
  Result Post(const std::string &path, const Headers &headers,
1161
1275
  const Params &params);
1276
+ Result Post(const std::string &path, const Headers &headers,
1277
+ const Params &params, Progress progress);
1162
1278
  Result Post(const std::string &path, const MultipartFormDataItems &items);
1163
1279
  Result Post(const std::string &path, const Headers &headers,
1164
1280
  const MultipartFormDataItems &items);
@@ -1173,10 +1289,18 @@ public:
1173
1289
  const std::string &content_type);
1174
1290
  Result Put(const std::string &path, const Headers &headers, const char *body,
1175
1291
  size_t content_length, const std::string &content_type);
1292
+ Result Put(const std::string &path, const Headers &headers, const char *body,
1293
+ size_t content_length, const std::string &content_type,
1294
+ Progress progress);
1176
1295
  Result Put(const std::string &path, const std::string &body,
1177
1296
  const std::string &content_type);
1297
+ Result Put(const std::string &path, const std::string &body,
1298
+ const std::string &content_type, Progress progress);
1178
1299
  Result Put(const std::string &path, const Headers &headers,
1179
1300
  const std::string &body, const std::string &content_type);
1301
+ Result Put(const std::string &path, const Headers &headers,
1302
+ const std::string &body, const std::string &content_type,
1303
+ Progress progress);
1180
1304
  Result Put(const std::string &path, size_t content_length,
1181
1305
  ContentProvider content_provider, const std::string &content_type);
1182
1306
  Result Put(const std::string &path,
@@ -1191,6 +1315,8 @@ public:
1191
1315
  Result Put(const std::string &path, const Params &params);
1192
1316
  Result Put(const std::string &path, const Headers &headers,
1193
1317
  const Params &params);
1318
+ Result Put(const std::string &path, const Headers &headers,
1319
+ const Params &params, Progress progress);
1194
1320
  Result Put(const std::string &path, const MultipartFormDataItems &items);
1195
1321
  Result Put(const std::string &path, const Headers &headers,
1196
1322
  const MultipartFormDataItems &items);
@@ -1203,13 +1329,23 @@ public:
1203
1329
  Result Patch(const std::string &path);
1204
1330
  Result Patch(const std::string &path, const char *body, size_t content_length,
1205
1331
  const std::string &content_type);
1332
+ Result Patch(const std::string &path, const char *body, size_t content_length,
1333
+ const std::string &content_type, Progress progress);
1206
1334
  Result Patch(const std::string &path, const Headers &headers,
1207
1335
  const char *body, size_t content_length,
1208
1336
  const std::string &content_type);
1337
+ Result Patch(const std::string &path, const Headers &headers,
1338
+ const char *body, size_t content_length,
1339
+ const std::string &content_type, Progress progress);
1209
1340
  Result Patch(const std::string &path, const std::string &body,
1210
1341
  const std::string &content_type);
1342
+ Result Patch(const std::string &path, const std::string &body,
1343
+ const std::string &content_type, Progress progress);
1211
1344
  Result Patch(const std::string &path, const Headers &headers,
1212
1345
  const std::string &body, const std::string &content_type);
1346
+ Result Patch(const std::string &path, const Headers &headers,
1347
+ const std::string &body, const std::string &content_type,
1348
+ Progress progress);
1213
1349
  Result Patch(const std::string &path, size_t content_length,
1214
1350
  ContentProvider content_provider,
1215
1351
  const std::string &content_type);
@@ -1227,13 +1363,24 @@ public:
1227
1363
  Result Delete(const std::string &path, const Headers &headers);
1228
1364
  Result Delete(const std::string &path, const char *body,
1229
1365
  size_t content_length, const std::string &content_type);
1366
+ Result Delete(const std::string &path, const char *body,
1367
+ size_t content_length, const std::string &content_type,
1368
+ Progress progress);
1230
1369
  Result Delete(const std::string &path, const Headers &headers,
1231
1370
  const char *body, size_t content_length,
1232
1371
  const std::string &content_type);
1372
+ Result Delete(const std::string &path, const Headers &headers,
1373
+ const char *body, size_t content_length,
1374
+ const std::string &content_type, Progress progress);
1233
1375
  Result Delete(const std::string &path, const std::string &body,
1234
1376
  const std::string &content_type);
1377
+ Result Delete(const std::string &path, const std::string &body,
1378
+ const std::string &content_type, Progress progress);
1235
1379
  Result Delete(const std::string &path, const Headers &headers,
1236
1380
  const std::string &body, const std::string &content_type);
1381
+ Result Delete(const std::string &path, const Headers &headers,
1382
+ const std::string &body, const std::string &content_type,
1383
+ Progress progress);
1237
1384
 
1238
1385
  Result Options(const std::string &path);
1239
1386
  Result Options(const std::string &path, const Headers &headers);
@@ -1258,6 +1405,7 @@ public:
1258
1405
 
1259
1406
  void set_address_family(int family);
1260
1407
  void set_tcp_nodelay(bool on);
1408
+ void set_ipv6_v6only(bool on);
1261
1409
  void set_socket_options(SocketOptions socket_options);
1262
1410
 
1263
1411
  void set_connection_timeout(time_t sec, time_t usec = 0);
@@ -1309,6 +1457,8 @@ public:
1309
1457
 
1310
1458
  #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1311
1459
  void enable_server_certificate_verification(bool enabled);
1460
+ void enable_server_hostname_verification(bool enabled);
1461
+ void set_server_certificate_verifier(std::function<bool(SSL *ssl)> verifier);
1312
1462
  #endif
1313
1463
 
1314
1464
  void set_logger(Logger logger);
@@ -1375,10 +1525,10 @@ protected:
1375
1525
 
1376
1526
  time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
1377
1527
  time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
1378
- time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
1379
- time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
1380
- time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
1381
- time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;
1528
+ time_t read_timeout_sec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_SECOND;
1529
+ time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;
1530
+ time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
1531
+ time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
1382
1532
 
1383
1533
  std::string basic_auth_username_;
1384
1534
  std::string basic_auth_password_;
@@ -1395,6 +1545,7 @@ protected:
1395
1545
 
1396
1546
  int address_family_ = AF_UNSPEC;
1397
1547
  bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
1548
+ bool ipv6_v6only_ = CPPHTTPLIB_IPV6_V6ONLY;
1398
1549
  SocketOptions socket_options_ = nullptr;
1399
1550
 
1400
1551
  bool compress_ = false;
@@ -1422,6 +1573,8 @@ protected:
1422
1573
 
1423
1574
  #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1424
1575
  bool server_certificate_verification_ = true;
1576
+ bool server_hostname_verification_ = true;
1577
+ std::function<bool(SSL *ssl)> server_certificate_verifier_;
1425
1578
  #endif
1426
1579
 
1427
1580
  Logger logger_;
@@ -1430,6 +1583,9 @@ private:
1430
1583
  bool send_(Request &req, Response &res, Error &error);
1431
1584
  Result send_(Request &&req);
1432
1585
 
1586
+ #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1587
+ bool is_ssl_peer_could_be_closed(SSL *ssl) const;
1588
+ #endif
1433
1589
  socket_t create_client_socket(Error &error) const;
1434
1590
  bool read_response_line(Stream &strm, const Request &req,
1435
1591
  Response &res) const;
@@ -1448,7 +1604,7 @@ private:
1448
1604
  const Headers &headers, const char *body, size_t content_length,
1449
1605
  ContentProvider content_provider,
1450
1606
  ContentProviderWithoutLength content_provider_without_length,
1451
- const std::string &content_type);
1607
+ const std::string &content_type, Progress progress);
1452
1608
  ContentProviderWithoutLength get_multipart_content_provider(
1453
1609
  const std::string &boundary, const MultipartFormDataItems &items,
1454
1610
  const MultipartFormDataProviderItems &provider_items) const;
@@ -1477,6 +1633,7 @@ public:
1477
1633
  const std::string &client_key_path);
1478
1634
 
1479
1635
  Client(Client &&) = default;
1636
+ Client &operator=(Client &&) = default;
1480
1637
 
1481
1638
  ~Client();
1482
1639
 
@@ -1523,10 +1680,18 @@ public:
1523
1680
  const std::string &content_type);
1524
1681
  Result Post(const std::string &path, const Headers &headers, const char *body,
1525
1682
  size_t content_length, const std::string &content_type);
1683
+ Result Post(const std::string &path, const Headers &headers, const char *body,
1684
+ size_t content_length, const std::string &content_type,
1685
+ Progress progress);
1526
1686
  Result Post(const std::string &path, const std::string &body,
1527
1687
  const std::string &content_type);
1688
+ Result Post(const std::string &path, const std::string &body,
1689
+ const std::string &content_type, Progress progress);
1528
1690
  Result Post(const std::string &path, const Headers &headers,
1529
1691
  const std::string &body, const std::string &content_type);
1692
+ Result Post(const std::string &path, const Headers &headers,
1693
+ const std::string &body, const std::string &content_type,
1694
+ Progress progress);
1530
1695
  Result Post(const std::string &path, size_t content_length,
1531
1696
  ContentProvider content_provider,
1532
1697
  const std::string &content_type);
@@ -1542,6 +1707,8 @@ public:
1542
1707
  Result Post(const std::string &path, const Params &params);
1543
1708
  Result Post(const std::string &path, const Headers &headers,
1544
1709
  const Params &params);
1710
+ Result Post(const std::string &path, const Headers &headers,
1711
+ const Params &params, Progress progress);
1545
1712
  Result Post(const std::string &path, const MultipartFormDataItems &items);
1546
1713
  Result Post(const std::string &path, const Headers &headers,
1547
1714
  const MultipartFormDataItems &items);
@@ -1556,10 +1723,18 @@ public:
1556
1723
  const std::string &content_type);
1557
1724
  Result Put(const std::string &path, const Headers &headers, const char *body,
1558
1725
  size_t content_length, const std::string &content_type);
1726
+ Result Put(const std::string &path, const Headers &headers, const char *body,
1727
+ size_t content_length, const std::string &content_type,
1728
+ Progress progress);
1559
1729
  Result Put(const std::string &path, const std::string &body,
1560
1730
  const std::string &content_type);
1731
+ Result Put(const std::string &path, const std::string &body,
1732
+ const std::string &content_type, Progress progress);
1561
1733
  Result Put(const std::string &path, const Headers &headers,
1562
1734
  const std::string &body, const std::string &content_type);
1735
+ Result Put(const std::string &path, const Headers &headers,
1736
+ const std::string &body, const std::string &content_type,
1737
+ Progress progress);
1563
1738
  Result Put(const std::string &path, size_t content_length,
1564
1739
  ContentProvider content_provider, const std::string &content_type);
1565
1740
  Result Put(const std::string &path,
@@ -1574,6 +1749,8 @@ public:
1574
1749
  Result Put(const std::string &path, const Params &params);
1575
1750
  Result Put(const std::string &path, const Headers &headers,
1576
1751
  const Params &params);
1752
+ Result Put(const std::string &path, const Headers &headers,
1753
+ const Params &params, Progress progress);
1577
1754
  Result Put(const std::string &path, const MultipartFormDataItems &items);
1578
1755
  Result Put(const std::string &path, const Headers &headers,
1579
1756
  const MultipartFormDataItems &items);
@@ -1586,13 +1763,23 @@ public:
1586
1763
  Result Patch(const std::string &path);
1587
1764
  Result Patch(const std::string &path, const char *body, size_t content_length,
1588
1765
  const std::string &content_type);
1766
+ Result Patch(const std::string &path, const char *body, size_t content_length,
1767
+ const std::string &content_type, Progress progress);
1589
1768
  Result Patch(const std::string &path, const Headers &headers,
1590
1769
  const char *body, size_t content_length,
1591
1770
  const std::string &content_type);
1771
+ Result Patch(const std::string &path, const Headers &headers,
1772
+ const char *body, size_t content_length,
1773
+ const std::string &content_type, Progress progress);
1592
1774
  Result Patch(const std::string &path, const std::string &body,
1593
1775
  const std::string &content_type);
1776
+ Result Patch(const std::string &path, const std::string &body,
1777
+ const std::string &content_type, Progress progress);
1594
1778
  Result Patch(const std::string &path, const Headers &headers,
1595
1779
  const std::string &body, const std::string &content_type);
1780
+ Result Patch(const std::string &path, const Headers &headers,
1781
+ const std::string &body, const std::string &content_type,
1782
+ Progress progress);
1596
1783
  Result Patch(const std::string &path, size_t content_length,
1597
1784
  ContentProvider content_provider,
1598
1785
  const std::string &content_type);
@@ -1610,13 +1797,24 @@ public:
1610
1797
  Result Delete(const std::string &path, const Headers &headers);
1611
1798
  Result Delete(const std::string &path, const char *body,
1612
1799
  size_t content_length, const std::string &content_type);
1800
+ Result Delete(const std::string &path, const char *body,
1801
+ size_t content_length, const std::string &content_type,
1802
+ Progress progress);
1613
1803
  Result Delete(const std::string &path, const Headers &headers,
1614
1804
  const char *body, size_t content_length,
1615
1805
  const std::string &content_type);
1806
+ Result Delete(const std::string &path, const Headers &headers,
1807
+ const char *body, size_t content_length,
1808
+ const std::string &content_type, Progress progress);
1616
1809
  Result Delete(const std::string &path, const std::string &body,
1617
1810
  const std::string &content_type);
1811
+ Result Delete(const std::string &path, const std::string &body,
1812
+ const std::string &content_type, Progress progress);
1618
1813
  Result Delete(const std::string &path, const Headers &headers,
1619
1814
  const std::string &body, const std::string &content_type);
1815
+ Result Delete(const std::string &path, const Headers &headers,
1816
+ const std::string &body, const std::string &content_type,
1817
+ Progress progress);
1620
1818
 
1621
1819
  Result Options(const std::string &path);
1622
1820
  Result Options(const std::string &path, const Headers &headers);
@@ -1685,6 +1883,8 @@ public:
1685
1883
 
1686
1884
  #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1687
1885
  void enable_server_certificate_verification(bool enabled);
1886
+ void enable_server_hostname_verification(bool enabled);
1887
+ void set_server_certificate_verifier(std::function<bool(SSL *ssl)> verifier);
1688
1888
  #endif
1689
1889
 
1690
1890
  void set_logger(Logger logger);
@@ -1730,6 +1930,9 @@ public:
1730
1930
 
1731
1931
  SSL_CTX *ssl_context() const;
1732
1932
 
1933
+ void update_certs(X509 *cert, EVP_PKEY *private_key,
1934
+ X509_STORE *client_ca_cert_store = nullptr);
1935
+
1733
1936
  private:
1734
1937
  bool process_and_close_socket(socket_t sock) override;
1735
1938
 
@@ -1810,68 +2013,58 @@ inline void duration_to_sec_and_usec(const T &duration, U callback) {
1810
2013
  callback(static_cast<time_t>(sec), static_cast<time_t>(usec));
1811
2014
  }
1812
2015
 
2016
+ inline bool is_numeric(const std::string &str) {
2017
+ return !str.empty() && std::all_of(str.begin(), str.end(), ::isdigit);
2018
+ }
2019
+
1813
2020
  inline uint64_t get_header_value_u64(const Headers &headers,
1814
- const std::string &key, size_t id,
1815
- uint64_t def) {
2021
+ const std::string &key, uint64_t def,
2022
+ size_t id, bool &is_invalid_value) {
2023
+ is_invalid_value = false;
1816
2024
  auto rng = headers.equal_range(key);
1817
2025
  auto it = rng.first;
1818
2026
  std::advance(it, static_cast<ssize_t>(id));
1819
2027
  if (it != rng.second) {
1820
- return std::strtoull(it->second.data(), nullptr, 10);
2028
+ if (is_numeric(it->second)) {
2029
+ return std::strtoull(it->second.data(), nullptr, 10);
2030
+ } else {
2031
+ is_invalid_value = true;
2032
+ }
1821
2033
  }
1822
2034
  return def;
1823
2035
  }
1824
2036
 
2037
+ inline uint64_t get_header_value_u64(const Headers &headers,
2038
+ const std::string &key, uint64_t def,
2039
+ size_t id) {
2040
+ bool dummy = false;
2041
+ return get_header_value_u64(headers, key, def, id, dummy);
2042
+ }
2043
+
1825
2044
  } // namespace detail
1826
2045
 
1827
2046
  inline uint64_t Request::get_header_value_u64(const std::string &key,
1828
- size_t id) const {
1829
- return detail::get_header_value_u64(headers, key, id, 0);
2047
+ uint64_t def, size_t id) const {
2048
+ return detail::get_header_value_u64(headers, key, def, id);
1830
2049
  }
1831
2050
 
1832
2051
  inline uint64_t Response::get_header_value_u64(const std::string &key,
1833
- size_t id) const {
1834
- return detail::get_header_value_u64(headers, key, id, 0);
1835
- }
1836
-
1837
- template <typename... Args>
1838
- inline ssize_t Stream::write_format(const char *fmt, const Args &...args) {
1839
- const auto bufsiz = 2048;
1840
- std::array<char, bufsiz> buf{};
1841
-
1842
- auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...);
1843
- if (sn <= 0) { return sn; }
1844
-
1845
- auto n = static_cast<size_t>(sn);
1846
-
1847
- if (n >= buf.size() - 1) {
1848
- std::vector<char> glowable_buf(buf.size());
1849
-
1850
- while (n >= glowable_buf.size() - 1) {
1851
- glowable_buf.resize(glowable_buf.size() * 2);
1852
- n = static_cast<size_t>(
1853
- snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...));
1854
- }
1855
- return write(&glowable_buf[0], n);
1856
- } else {
1857
- return write(buf.data(), n);
1858
- }
2052
+ uint64_t def, size_t id) const {
2053
+ return detail::get_header_value_u64(headers, key, def, id);
1859
2054
  }
1860
2055
 
1861
2056
  inline void default_socket_options(socket_t sock) {
1862
- int yes = 1;
2057
+ int opt = 1;
1863
2058
  #ifdef _WIN32
1864
2059
  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
1865
- reinterpret_cast<const char *>(&yes), sizeof(yes));
1866
- setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
1867
- reinterpret_cast<const char *>(&yes), sizeof(yes));
2060
+ reinterpret_cast<const char *>(&opt), sizeof(opt));
1868
2061
  #else
1869
2062
  #ifdef SO_REUSEPORT
1870
2063
  setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
1871
- reinterpret_cast<const void *>(&yes), sizeof(yes));
2064
+ reinterpret_cast<const void *>(&opt), sizeof(opt));
1872
2065
  #else
1873
2066
  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
1874
- reinterpret_cast<const void *>(&yes), sizeof(yes));
2067
+ reinterpret_cast<const void *>(&opt), sizeof(opt));
1875
2068
  #endif
1876
2069
  #endif
1877
2070
  }
@@ -1997,6 +2190,8 @@ inline std::string to_string(const Error error) {
1997
2190
  case Error::SSLConnection: return "SSL connection failed";
1998
2191
  case Error::SSLLoadingCerts: return "SSL certificate loading failed";
1999
2192
  case Error::SSLServerVerification: return "SSL server verification failed";
2193
+ case Error::SSLServerHostnameVerification:
2194
+ return "SSL server hostname verification failed";
2000
2195
  case Error::UnsupportedMultipartBoundaryChars:
2001
2196
  return "Unsupported HTTP multipart boundary characters";
2002
2197
  case Error::Compression: return "Compression failed";
@@ -2016,8 +2211,9 @@ inline std::ostream &operator<<(std::ostream &os, const Error &obj) {
2016
2211
  }
2017
2212
 
2018
2213
  inline uint64_t Result::get_request_header_value_u64(const std::string &key,
2214
+ uint64_t def,
2019
2215
  size_t id) const {
2020
- return detail::get_header_value_u64(request_headers_, key, id, 0);
2216
+ return detail::get_header_value_u64(request_headers_, key, def, id);
2021
2217
  }
2022
2218
 
2023
2219
  template <class Rep, class Period>
@@ -2080,6 +2276,36 @@ make_basic_authentication_header(const std::string &username,
2080
2276
 
2081
2277
  namespace detail {
2082
2278
 
2279
+ #if defined(_WIN32)
2280
+ inline std::wstring u8string_to_wstring(const char *s) {
2281
+ std::wstring ws;
2282
+ auto len = static_cast<int>(strlen(s));
2283
+ auto wlen = ::MultiByteToWideChar(CP_UTF8, 0, s, len, nullptr, 0);
2284
+ if (wlen > 0) {
2285
+ ws.resize(wlen);
2286
+ wlen = ::MultiByteToWideChar(
2287
+ CP_UTF8, 0, s, len,
2288
+ const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(ws.data())), wlen);
2289
+ if (wlen != static_cast<int>(ws.size())) { ws.clear(); }
2290
+ }
2291
+ return ws;
2292
+ }
2293
+ #endif
2294
+
2295
+ struct FileStat {
2296
+ FileStat(const std::string &path);
2297
+ bool is_file() const;
2298
+ bool is_dir() const;
2299
+
2300
+ private:
2301
+ #if defined(_WIN32)
2302
+ struct _stat st_;
2303
+ #else
2304
+ struct stat st_;
2305
+ #endif
2306
+ int ret_ = -1;
2307
+ };
2308
+
2083
2309
  std::string encode_query_param(const std::string &value);
2084
2310
 
2085
2311
  std::string decode_url(const std::string &s, bool convert_plus_to_space);
@@ -2088,6 +2314,16 @@ void read_file(const std::string &path, std::string &out);
2088
2314
 
2089
2315
  std::string trim_copy(const std::string &s);
2090
2316
 
2317
+ void divide(
2318
+ const char *data, std::size_t size, char d,
2319
+ std::function<void(const char *, std::size_t, const char *, std::size_t)>
2320
+ fn);
2321
+
2322
+ void divide(
2323
+ const std::string &str, char d,
2324
+ std::function<void(const char *, std::size_t, const char *, std::size_t)>
2325
+ fn);
2326
+
2091
2327
  void split(const char *b, const char *e, char d,
2092
2328
  std::function<void(const char *, const char *)> fn);
2093
2329
 
@@ -2099,18 +2335,23 @@ bool process_client_socket(socket_t sock, time_t read_timeout_sec,
2099
2335
  time_t write_timeout_usec,
2100
2336
  std::function<bool(Stream &)> callback);
2101
2337
 
2102
- socket_t create_client_socket(
2103
- const std::string &host, const std::string &ip, int port,
2104
- int address_family, bool tcp_nodelay, SocketOptions socket_options,
2105
- time_t connection_timeout_sec, time_t connection_timeout_usec,
2106
- time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
2107
- time_t write_timeout_usec, const std::string &intf, Error &error);
2338
+ socket_t create_client_socket(const std::string &host, const std::string &ip,
2339
+ int port, int address_family, bool tcp_nodelay,
2340
+ bool ipv6_v6only, SocketOptions socket_options,
2341
+ time_t connection_timeout_sec,
2342
+ time_t connection_timeout_usec,
2343
+ time_t read_timeout_sec, time_t read_timeout_usec,
2344
+ time_t write_timeout_sec,
2345
+ time_t write_timeout_usec,
2346
+ const std::string &intf, Error &error);
2108
2347
 
2109
2348
  const char *get_header_value(const Headers &headers, const std::string &key,
2110
- size_t id = 0, const char *def = nullptr);
2349
+ const char *def, size_t id);
2111
2350
 
2112
2351
  std::string params_to_query_str(const Params &params);
2113
2352
 
2353
+ void parse_query_text(const char *data, std::size_t size, Params &params);
2354
+
2114
2355
  void parse_query_text(const std::string &s, Params &params);
2115
2356
 
2116
2357
  bool parse_multipart_boundary(const std::string &content_type,
@@ -2270,15 +2511,70 @@ public:
2270
2511
 
2271
2512
  private:
2272
2513
  #if defined(_WIN32)
2273
- HANDLE hFile_;
2274
- HANDLE hMapping_;
2514
+ HANDLE hFile_ = NULL;
2515
+ HANDLE hMapping_ = NULL;
2275
2516
  #else
2276
- int fd_;
2517
+ int fd_ = -1;
2277
2518
  #endif
2278
- size_t size_;
2279
- void *addr_;
2519
+ size_t size_ = 0;
2520
+ void *addr_ = nullptr;
2521
+ bool is_open_empty_file = false;
2280
2522
  };
2281
2523
 
2524
+ // NOTE: https://www.rfc-editor.org/rfc/rfc9110#section-5
2525
+ namespace fields {
2526
+
2527
+ inline bool is_token_char(char c) {
2528
+ return std::isalnum(c) || c == '!' || c == '#' || c == '$' || c == '%' ||
2529
+ c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' ||
2530
+ c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~';
2531
+ }
2532
+
2533
+ inline bool is_token(const std::string &s) {
2534
+ if (s.empty()) { return false; }
2535
+ for (auto c : s) {
2536
+ if (!is_token_char(c)) { return false; }
2537
+ }
2538
+ return true;
2539
+ }
2540
+
2541
+ inline bool is_field_name(const std::string &s) { return is_token(s); }
2542
+
2543
+ inline bool is_vchar(char c) { return c >= 33 && c <= 126; }
2544
+
2545
+ inline bool is_obs_text(char c) { return 128 <= static_cast<unsigned char>(c); }
2546
+
2547
+ inline bool is_field_vchar(char c) { return is_vchar(c) || is_obs_text(c); }
2548
+
2549
+ inline bool is_field_content(const std::string &s) {
2550
+ if (s.empty()) { return false; }
2551
+
2552
+ if (s.size() == 1) {
2553
+ return is_field_vchar(s[0]);
2554
+ } else if (s.size() == 2) {
2555
+ return is_field_vchar(s[0]) && is_field_vchar(s[1]);
2556
+ } else {
2557
+ size_t i = 0;
2558
+
2559
+ if (!is_field_vchar(s[i])) { return false; }
2560
+ i++;
2561
+
2562
+ while (i < s.size() - 1) {
2563
+ auto c = s[i++];
2564
+ if (c == ' ' || c == '\t' || is_field_vchar(c)) {
2565
+ } else {
2566
+ return false;
2567
+ }
2568
+ }
2569
+
2570
+ return is_field_vchar(s[i]);
2571
+ }
2572
+ }
2573
+
2574
+ inline bool is_field_value(const std::string &s) { return is_field_content(s); }
2575
+
2576
+ } // namespace fields
2577
+
2282
2578
  } // namespace detail
2283
2579
 
2284
2580
  // ----------------------------------------------------------------------------
@@ -2392,20 +2688,6 @@ inline std::string base64_encode(const std::string &in) {
2392
2688
  return out;
2393
2689
  }
2394
2690
 
2395
- inline bool is_file(const std::string &path) {
2396
- #ifdef _WIN32
2397
- return _access_s(path.c_str(), 0) == 0;
2398
- #else
2399
- struct stat st;
2400
- return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode);
2401
- #endif
2402
- }
2403
-
2404
- inline bool is_dir(const std::string &path) {
2405
- struct stat st;
2406
- return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode);
2407
- }
2408
-
2409
2691
  inline bool is_valid_path(const std::string &path) {
2410
2692
  size_t level = 0;
2411
2693
  size_t i = 0;
@@ -2448,6 +2730,21 @@ inline bool is_valid_path(const std::string &path) {
2448
2730
  return true;
2449
2731
  }
2450
2732
 
2733
+ inline FileStat::FileStat(const std::string &path) {
2734
+ #if defined(_WIN32)
2735
+ auto wpath = u8string_to_wstring(path.c_str());
2736
+ ret_ = _wstat(wpath.c_str(), &st_);
2737
+ #else
2738
+ ret_ = stat(path.c_str(), &st_);
2739
+ #endif
2740
+ }
2741
+ inline bool FileStat::is_file() const {
2742
+ return ret_ >= 0 && S_ISREG(st_.st_mode);
2743
+ }
2744
+ inline bool FileStat::is_dir() const {
2745
+ return ret_ >= 0 && S_ISDIR(st_.st_mode);
2746
+ }
2747
+
2451
2748
  inline std::string encode_query_param(const std::string &value) {
2452
2749
  std::ostringstream escaped;
2453
2750
  escaped.fill('0');
@@ -2579,6 +2876,27 @@ inline std::string trim_double_quotes_copy(const std::string &s) {
2579
2876
  return s;
2580
2877
  }
2581
2878
 
2879
+ inline void
2880
+ divide(const char *data, std::size_t size, char d,
2881
+ std::function<void(const char *, std::size_t, const char *, std::size_t)>
2882
+ fn) {
2883
+ const auto it = std::find(data, data + size, d);
2884
+ const auto found = static_cast<std::size_t>(it != data + size);
2885
+ const auto lhs_data = data;
2886
+ const auto lhs_size = static_cast<std::size_t>(it - data);
2887
+ const auto rhs_data = it + found;
2888
+ const auto rhs_size = size - lhs_size - found;
2889
+
2890
+ fn(lhs_data, lhs_size, rhs_data, rhs_size);
2891
+ }
2892
+
2893
+ inline void
2894
+ divide(const std::string &str, char d,
2895
+ std::function<void(const char *, std::size_t, const char *, std::size_t)>
2896
+ fn) {
2897
+ divide(str.data(), str.size(), d, std::move(fn));
2898
+ }
2899
+
2582
2900
  inline void split(const char *b, const char *e, char d,
2583
2901
  std::function<void(const char *, const char *)> fn) {
2584
2902
  return split(b, e, d, (std::numeric_limits<size_t>::max)(), std::move(fn));
@@ -2636,6 +2954,10 @@ inline bool stream_line_reader::getline() {
2636
2954
  fixed_buffer_used_size_ = 0;
2637
2955
  glowable_buffer_.clear();
2638
2956
 
2957
+ #ifndef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
2958
+ char prev_byte = 0;
2959
+ #endif
2960
+
2639
2961
  for (size_t i = 0;; i++) {
2640
2962
  char byte;
2641
2963
  auto n = strm_.read(&byte, 1);
@@ -2652,7 +2974,12 @@ inline bool stream_line_reader::getline() {
2652
2974
 
2653
2975
  append(byte);
2654
2976
 
2977
+ #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
2655
2978
  if (byte == '\n') { break; }
2979
+ #else
2980
+ if (prev_byte == '\r' && byte == '\n') { break; }
2981
+ prev_byte = byte;
2982
+ #endif
2656
2983
  }
2657
2984
 
2658
2985
  return true;
@@ -2671,16 +2998,7 @@ inline void stream_line_reader::append(char c) {
2671
2998
  }
2672
2999
  }
2673
3000
 
2674
- inline mmap::mmap(const char *path)
2675
- #if defined(_WIN32)
2676
- : hFile_(NULL), hMapping_(NULL)
2677
- #else
2678
- : fd_(-1)
2679
- #endif
2680
- ,
2681
- size_(0), addr_(nullptr) {
2682
- open(path);
2683
- }
3001
+ inline mmap::mmap(const char *path) { open(path); }
2684
3002
 
2685
3003
  inline mmap::~mmap() { close(); }
2686
3004
 
@@ -2688,29 +3006,60 @@ inline bool mmap::open(const char *path) {
2688
3006
  close();
2689
3007
 
2690
3008
  #if defined(_WIN32)
2691
- std::wstring wpath;
2692
- for (size_t i = 0; i < strlen(path); i++) {
2693
- wpath += path[i];
2694
- }
3009
+ auto wpath = u8string_to_wstring(path);
3010
+ if (wpath.empty()) { return false; }
2695
3011
 
3012
+ #if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2696
3013
  hFile_ = ::CreateFile2(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ,
2697
3014
  OPEN_EXISTING, NULL);
3015
+ #else
3016
+ hFile_ = ::CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
3017
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
3018
+ #endif
2698
3019
 
2699
3020
  if (hFile_ == INVALID_HANDLE_VALUE) { return false; }
2700
3021
 
2701
3022
  LARGE_INTEGER size{};
2702
3023
  if (!::GetFileSizeEx(hFile_, &size)) { return false; }
3024
+ // If the following line doesn't compile due to QuadPart, update Windows SDK.
3025
+ // See:
3026
+ // https://github.com/yhirose/cpp-httplib/issues/1903#issuecomment-2316520721
3027
+ if (static_cast<ULONGLONG>(size.QuadPart) >
3028
+ (std::numeric_limits<decltype(size_)>::max)()) {
3029
+ // `size_t` might be 32-bits, on 32-bits Windows.
3030
+ return false;
3031
+ }
2703
3032
  size_ = static_cast<size_t>(size.QuadPart);
2704
3033
 
3034
+ #if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2705
3035
  hMapping_ =
2706
3036
  ::CreateFileMappingFromApp(hFile_, NULL, PAGE_READONLY, size_, NULL);
3037
+ #else
3038
+ hMapping_ = ::CreateFileMappingW(hFile_, NULL, PAGE_READONLY, 0, 0, NULL);
3039
+ #endif
3040
+
3041
+ // Special treatment for an empty file...
3042
+ if (hMapping_ == NULL && size_ == 0) {
3043
+ close();
3044
+ is_open_empty_file = true;
3045
+ return true;
3046
+ }
2707
3047
 
2708
3048
  if (hMapping_ == NULL) {
2709
3049
  close();
2710
3050
  return false;
2711
3051
  }
2712
3052
 
3053
+ #if _WIN32_WINNT >= _WIN32_WINNT_WIN8
2713
3054
  addr_ = ::MapViewOfFileFromApp(hMapping_, FILE_MAP_READ, 0, 0);
3055
+ #else
3056
+ addr_ = ::MapViewOfFile(hMapping_, FILE_MAP_READ, 0, 0, 0);
3057
+ #endif
3058
+
3059
+ if (addr_ == nullptr) {
3060
+ close();
3061
+ return false;
3062
+ }
2714
3063
  #else
2715
3064
  fd_ = ::open(path, O_RDONLY);
2716
3065
  if (fd_ == -1) { return false; }
@@ -2723,22 +3072,26 @@ inline bool mmap::open(const char *path) {
2723
3072
  size_ = static_cast<size_t>(sb.st_size);
2724
3073
 
2725
3074
  addr_ = ::mmap(NULL, size_, PROT_READ, MAP_PRIVATE, fd_, 0);
2726
- #endif
2727
3075
 
2728
- if (addr_ == nullptr) {
3076
+ // Special treatment for an empty file...
3077
+ if (addr_ == MAP_FAILED && size_ == 0) {
2729
3078
  close();
3079
+ is_open_empty_file = true;
2730
3080
  return false;
2731
3081
  }
3082
+ #endif
2732
3083
 
2733
3084
  return true;
2734
3085
  }
2735
3086
 
2736
- inline bool mmap::is_open() const { return addr_ != nullptr; }
3087
+ inline bool mmap::is_open() const {
3088
+ return is_open_empty_file ? true : addr_ != nullptr;
3089
+ }
2737
3090
 
2738
3091
  inline size_t mmap::size() const { return size_; }
2739
3092
 
2740
3093
  inline const char *mmap::data() const {
2741
- return static_cast<const char *>(addr_);
3094
+ return is_open_empty_file ? "" : static_cast<const char *>(addr_);
2742
3095
  }
2743
3096
 
2744
3097
  inline void mmap::close() {
@@ -2757,6 +3110,8 @@ inline void mmap::close() {
2757
3110
  ::CloseHandle(hFile_);
2758
3111
  hFile_ = INVALID_HANDLE_VALUE;
2759
3112
  }
3113
+
3114
+ is_open_empty_file = false;
2760
3115
  #else
2761
3116
  if (addr_ != nullptr) {
2762
3117
  munmap(addr_, size_);
@@ -2782,7 +3137,10 @@ template <typename T> inline ssize_t handle_EINTR(T fn) {
2782
3137
  ssize_t res = 0;
2783
3138
  while (true) {
2784
3139
  res = fn();
2785
- if (res < 0 && errno == EINTR) { continue; }
3140
+ if (res < 0 && errno == EINTR) {
3141
+ std::this_thread::sleep_for(std::chrono::microseconds{1});
3142
+ continue;
3143
+ }
2786
3144
  break;
2787
3145
  }
2788
3146
  return res;
@@ -2991,23 +3349,37 @@ private:
2991
3349
  };
2992
3350
  #endif
2993
3351
 
2994
- inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) {
3352
+ inline bool keep_alive(const std::atomic<socket_t> &svr_sock, socket_t sock,
3353
+ time_t keep_alive_timeout_sec) {
2995
3354
  using namespace std::chrono;
2996
- auto start = steady_clock::now();
3355
+
3356
+ const auto interval_usec =
3357
+ CPPHTTPLIB_KEEPALIVE_TIMEOUT_CHECK_INTERVAL_USECOND;
3358
+
3359
+ // Avoid expensive `steady_clock::now()` call for the first time
3360
+ if (select_read(sock, 0, interval_usec) > 0) { return true; }
3361
+
3362
+ const auto start = steady_clock::now() - microseconds{interval_usec};
3363
+ const auto timeout = seconds{keep_alive_timeout_sec};
3364
+
2997
3365
  while (true) {
2998
- auto val = select_read(sock, 0, 10000);
3366
+ if (svr_sock == INVALID_SOCKET) {
3367
+ break; // Server socket is closed
3368
+ }
3369
+
3370
+ auto val = select_read(sock, 0, interval_usec);
2999
3371
  if (val < 0) {
3000
- return false;
3372
+ break; // Ssocket error
3001
3373
  } else if (val == 0) {
3002
- auto current = steady_clock::now();
3003
- auto duration = duration_cast<milliseconds>(current - start);
3004
- auto timeout = keep_alive_timeout_sec * 1000;
3005
- if (duration.count() > timeout) { return false; }
3006
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
3374
+ if (steady_clock::now() - start > timeout) {
3375
+ break; // Timeout
3376
+ }
3007
3377
  } else {
3008
- return true;
3378
+ return true; // Ready for read
3009
3379
  }
3010
3380
  }
3381
+
3382
+ return false;
3011
3383
  }
3012
3384
 
3013
3385
  template <typename T>
@@ -3018,8 +3390,7 @@ process_server_socket_core(const std::atomic<socket_t> &svr_sock, socket_t sock,
3018
3390
  assert(keep_alive_max_count > 0);
3019
3391
  auto ret = false;
3020
3392
  auto count = keep_alive_max_count;
3021
- while (svr_sock != INVALID_SOCKET && count > 0 &&
3022
- keep_alive(sock, keep_alive_timeout_sec)) {
3393
+ while (count > 0 && keep_alive(svr_sock, sock, keep_alive_timeout_sec)) {
3023
3394
  auto close_connection = count == 1;
3024
3395
  auto connection_closed = false;
3025
3396
  ret = callback(close_connection, connection_closed);
@@ -3063,10 +3434,29 @@ inline int shutdown_socket(socket_t sock) {
3063
3434
  #endif
3064
3435
  }
3065
3436
 
3437
+ inline std::string escape_abstract_namespace_unix_domain(const std::string &s) {
3438
+ if (s.size() > 1 && s[0] == '\0') {
3439
+ auto ret = s;
3440
+ ret[0] = '@';
3441
+ return ret;
3442
+ }
3443
+ return s;
3444
+ }
3445
+
3446
+ inline std::string
3447
+ unescape_abstract_namespace_unix_domain(const std::string &s) {
3448
+ if (s.size() > 1 && s[0] == '@') {
3449
+ auto ret = s;
3450
+ ret[0] = '\0';
3451
+ return ret;
3452
+ }
3453
+ return s;
3454
+ }
3455
+
3066
3456
  template <typename BindOrConnect>
3067
3457
  socket_t create_socket(const std::string &host, const std::string &ip, int port,
3068
3458
  int address_family, int socket_flags, bool tcp_nodelay,
3069
- SocketOptions socket_options,
3459
+ bool ipv6_v6only, SocketOptions socket_options,
3070
3460
  BindOrConnect bind_or_connect) {
3071
3461
  // Get address info
3072
3462
  const char *node = nullptr;
@@ -3075,7 +3465,7 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
3075
3465
 
3076
3466
  memset(&hints, 0, sizeof(struct addrinfo));
3077
3467
  hints.ai_socktype = SOCK_STREAM;
3078
- hints.ai_protocol = 0;
3468
+ hints.ai_protocol = IPPROTO_IP;
3079
3469
 
3080
3470
  if (!ip.empty()) {
3081
3471
  node = ip.c_str();
@@ -3093,20 +3483,32 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
3093
3483
  const auto addrlen = host.length();
3094
3484
  if (addrlen > sizeof(sockaddr_un::sun_path)) { return INVALID_SOCKET; }
3095
3485
 
3486
+ #ifdef SOCK_CLOEXEC
3487
+ auto sock = socket(hints.ai_family, hints.ai_socktype | SOCK_CLOEXEC,
3488
+ hints.ai_protocol);
3489
+ #else
3096
3490
  auto sock = socket(hints.ai_family, hints.ai_socktype, hints.ai_protocol);
3491
+ #endif
3492
+
3097
3493
  if (sock != INVALID_SOCKET) {
3098
3494
  sockaddr_un addr{};
3099
3495
  addr.sun_family = AF_UNIX;
3100
- std::copy(host.begin(), host.end(), addr.sun_path);
3496
+
3497
+ auto unescaped_host = unescape_abstract_namespace_unix_domain(host);
3498
+ std::copy(unescaped_host.begin(), unescaped_host.end(), addr.sun_path);
3101
3499
 
3102
3500
  hints.ai_addr = reinterpret_cast<sockaddr *>(&addr);
3103
3501
  hints.ai_addrlen = static_cast<socklen_t>(
3104
3502
  sizeof(addr) - sizeof(addr.sun_path) + addrlen);
3105
3503
 
3504
+ #ifndef SOCK_CLOEXEC
3106
3505
  fcntl(sock, F_SETFD, FD_CLOEXEC);
3506
+ #endif
3507
+
3107
3508
  if (socket_options) { socket_options(sock); }
3108
3509
 
3109
- if (!bind_or_connect(sock, hints)) {
3510
+ bool dummy;
3511
+ if (!bind_or_connect(sock, hints, dummy)) {
3110
3512
  close_socket(sock);
3111
3513
  sock = INVALID_SOCKET;
3112
3514
  }
@@ -3123,6 +3525,7 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
3123
3525
  #endif
3124
3526
  return INVALID_SOCKET;
3125
3527
  }
3528
+ auto se = detail::scope_exit([&] { freeaddrinfo(result); });
3126
3529
 
3127
3530
  for (auto rp = result; rp; rp = rp->ai_next) {
3128
3531
  // Create a socket
@@ -3148,11 +3551,18 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
3148
3551
  sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3149
3552
  }
3150
3553
  #else
3554
+
3555
+ #ifdef SOCK_CLOEXEC
3556
+ auto sock =
3557
+ socket(rp->ai_family, rp->ai_socktype | SOCK_CLOEXEC, rp->ai_protocol);
3558
+ #else
3151
3559
  auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
3560
+ #endif
3561
+
3152
3562
  #endif
3153
3563
  if (sock == INVALID_SOCKET) { continue; }
3154
3564
 
3155
- #ifndef _WIN32
3565
+ #if !defined _WIN32 && !defined SOCK_CLOEXEC
3156
3566
  if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
3157
3567
  close_socket(sock);
3158
3568
  continue;
@@ -3160,39 +3570,38 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
3160
3570
  #endif
3161
3571
 
3162
3572
  if (tcp_nodelay) {
3163
- auto yes = 1;
3573
+ auto opt = 1;
3164
3574
  #ifdef _WIN32
3165
3575
  setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
3166
- reinterpret_cast<const char *>(&yes), sizeof(yes));
3576
+ reinterpret_cast<const char *>(&opt), sizeof(opt));
3167
3577
  #else
3168
3578
  setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
3169
- reinterpret_cast<const void *>(&yes), sizeof(yes));
3579
+ reinterpret_cast<const void *>(&opt), sizeof(opt));
3170
3580
  #endif
3171
3581
  }
3172
3582
 
3173
- if (socket_options) { socket_options(sock); }
3174
-
3175
3583
  if (rp->ai_family == AF_INET6) {
3176
- auto no = 0;
3584
+ auto opt = ipv6_v6only ? 1 : 0;
3177
3585
  #ifdef _WIN32
3178
3586
  setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
3179
- reinterpret_cast<const char *>(&no), sizeof(no));
3587
+ reinterpret_cast<const char *>(&opt), sizeof(opt));
3180
3588
  #else
3181
3589
  setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
3182
- reinterpret_cast<const void *>(&no), sizeof(no));
3590
+ reinterpret_cast<const void *>(&opt), sizeof(opt));
3183
3591
  #endif
3184
3592
  }
3185
3593
 
3594
+ if (socket_options) { socket_options(sock); }
3595
+
3186
3596
  // bind or connect
3187
- if (bind_or_connect(sock, *rp)) {
3188
- freeaddrinfo(result);
3189
- return sock;
3190
- }
3597
+ auto quit = false;
3598
+ if (bind_or_connect(sock, *rp, quit)) { return sock; }
3191
3599
 
3192
3600
  close_socket(sock);
3601
+
3602
+ if (quit) { break; }
3193
3603
  }
3194
3604
 
3195
- freeaddrinfo(result);
3196
3605
  return INVALID_SOCKET;
3197
3606
  }
3198
3607
 
@@ -3225,6 +3634,7 @@ inline bool bind_ip_address(socket_t sock, const std::string &host) {
3225
3634
  hints.ai_protocol = 0;
3226
3635
 
3227
3636
  if (getaddrinfo(host.c_str(), "0", &hints, &result)) { return false; }
3637
+ auto se = detail::scope_exit([&] { freeaddrinfo(result); });
3228
3638
 
3229
3639
  auto ret = false;
3230
3640
  for (auto rp = result; rp; rp = rp->ai_next) {
@@ -3235,7 +3645,6 @@ inline bool bind_ip_address(socket_t sock, const std::string &host) {
3235
3645
  }
3236
3646
  }
3237
3647
 
3238
- freeaddrinfo(result);
3239
3648
  return ret;
3240
3649
  }
3241
3650
 
@@ -3247,6 +3656,8 @@ inline bool bind_ip_address(socket_t sock, const std::string &host) {
3247
3656
  inline std::string if2ip(int address_family, const std::string &ifn) {
3248
3657
  struct ifaddrs *ifap;
3249
3658
  getifaddrs(&ifap);
3659
+ auto se = detail::scope_exit([&] { freeifaddrs(ifap); });
3660
+
3250
3661
  std::string addr_candidate;
3251
3662
  for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
3252
3663
  if (ifa->ifa_addr && ifn == ifa->ifa_name &&
@@ -3256,7 +3667,6 @@ inline std::string if2ip(int address_family, const std::string &ifn) {
3256
3667
  auto sa = reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr);
3257
3668
  char buf[INET_ADDRSTRLEN];
3258
3669
  if (inet_ntop(AF_INET, &sa->sin_addr, buf, INET_ADDRSTRLEN)) {
3259
- freeifaddrs(ifap);
3260
3670
  return std::string(buf, INET_ADDRSTRLEN);
3261
3671
  }
3262
3672
  } else if (ifa->ifa_addr->sa_family == AF_INET6) {
@@ -3269,7 +3679,6 @@ inline std::string if2ip(int address_family, const std::string &ifn) {
3269
3679
  if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) {
3270
3680
  addr_candidate = std::string(buf, INET6_ADDRSTRLEN);
3271
3681
  } else {
3272
- freeifaddrs(ifap);
3273
3682
  return std::string(buf, INET6_ADDRSTRLEN);
3274
3683
  }
3275
3684
  }
@@ -3277,20 +3686,21 @@ inline std::string if2ip(int address_family, const std::string &ifn) {
3277
3686
  }
3278
3687
  }
3279
3688
  }
3280
- freeifaddrs(ifap);
3281
3689
  return addr_candidate;
3282
3690
  }
3283
3691
  #endif
3284
3692
 
3285
3693
  inline socket_t create_client_socket(
3286
3694
  const std::string &host, const std::string &ip, int port,
3287
- int address_family, bool tcp_nodelay, SocketOptions socket_options,
3288
- time_t connection_timeout_sec, time_t connection_timeout_usec,
3289
- time_t read_timeout_sec, time_t read_timeout_usec, time_t write_timeout_sec,
3695
+ int address_family, bool tcp_nodelay, bool ipv6_v6only,
3696
+ SocketOptions socket_options, time_t connection_timeout_sec,
3697
+ time_t connection_timeout_usec, time_t read_timeout_sec,
3698
+ time_t read_timeout_usec, time_t write_timeout_sec,
3290
3699
  time_t write_timeout_usec, const std::string &intf, Error &error) {
3291
3700
  auto sock = create_socket(
3292
- host, ip, port, address_family, 0, tcp_nodelay, std::move(socket_options),
3293
- [&](socket_t sock2, struct addrinfo &ai) -> bool {
3701
+ host, ip, port, address_family, 0, tcp_nodelay, ipv6_v6only,
3702
+ std::move(socket_options),
3703
+ [&](socket_t sock2, struct addrinfo &ai, bool &quit) -> bool {
3294
3704
  if (!intf.empty()) {
3295
3705
  #ifdef USE_IF2IP
3296
3706
  auto ip_from_if = if2ip(address_family, intf);
@@ -3314,7 +3724,10 @@ inline socket_t create_client_socket(
3314
3724
  }
3315
3725
  error = wait_until_socket_is_ready(sock2, connection_timeout_sec,
3316
3726
  connection_timeout_usec);
3317
- if (error != Error::Success) { return false; }
3727
+ if (error != Error::Success) {
3728
+ if (error == Error::ConnectionTimeout) { quit = true; }
3729
+ return false;
3730
+ }
3318
3731
  }
3319
3732
 
3320
3733
  set_nonblocking(sock2, false);
@@ -3439,7 +3852,7 @@ inline unsigned int str2tag(const std::string &s) {
3439
3852
 
3440
3853
  namespace udl {
3441
3854
 
3442
- inline constexpr unsigned int operator"" _t(const char *s, size_t l) {
3855
+ inline constexpr unsigned int operator""_t(const char *s, size_t l) {
3443
3856
  return str2tag_core(s, l, 0);
3444
3857
  }
3445
3858
 
@@ -3524,8 +3937,9 @@ inline bool can_compress_content_type(const std::string &content_type) {
3524
3937
  case "application/protobuf"_t:
3525
3938
  case "application/xhtml+xml"_t: return true;
3526
3939
 
3527
- default:
3528
- return !content_type.rfind("text/", 0) && tag != "text/event-stream"_t;
3940
+ case "text/event-stream"_t: return false;
3941
+
3942
+ default: return !content_type.rfind("text/", 0);
3529
3943
  }
3530
3944
  }
3531
3945
 
@@ -3762,8 +4176,8 @@ inline bool has_header(const Headers &headers, const std::string &key) {
3762
4176
  }
3763
4177
 
3764
4178
  inline const char *get_header_value(const Headers &headers,
3765
- const std::string &key, size_t id,
3766
- const char *def) {
4179
+ const std::string &key, const char *def,
4180
+ size_t id) {
3767
4181
  auto rng = headers.equal_range(key);
3768
4182
  auto it = rng.first;
3769
4183
  std::advance(it, static_cast<ssize_t>(id));
@@ -3771,14 +4185,6 @@ inline const char *get_header_value(const Headers &headers,
3771
4185
  return def;
3772
4186
  }
3773
4187
 
3774
- inline bool compare_case_ignore(const std::string &a, const std::string &b) {
3775
- if (a.size() != b.size()) { return false; }
3776
- for (size_t i = 0; i < b.size(); i++) {
3777
- if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
3778
- }
3779
- return true;
3780
- }
3781
-
3782
4188
  template <typename T>
3783
4189
  inline bool parse_header(const char *beg, const char *end, T fn) {
3784
4190
  // Skip trailing spaces and tabs.
@@ -3801,15 +4207,27 @@ inline bool parse_header(const char *beg, const char *end, T fn) {
3801
4207
  p++;
3802
4208
  }
3803
4209
 
3804
- if (p < end) {
4210
+ if (p <= end) {
3805
4211
  auto key_len = key_end - beg;
3806
4212
  if (!key_len) { return false; }
3807
4213
 
3808
4214
  auto key = std::string(beg, key_end);
3809
- auto val = compare_case_ignore(key, "Location")
4215
+ auto val = case_ignore::equal(key, "Location")
3810
4216
  ? std::string(p, end)
3811
4217
  : decode_url(std::string(p, end), false);
3812
- fn(std::move(key), std::move(val));
4218
+
4219
+ // NOTE: From RFC 9110:
4220
+ // Field values containing CR, LF, or NUL characters are
4221
+ // invalid and dangerous, due to the varying ways that
4222
+ // implementations might parse and interpret those
4223
+ // characters; a recipient of CR, LF, or NUL within a field
4224
+ // value MUST either reject the message or replace each of
4225
+ // those characters with SP before further processing or
4226
+ // forwarding of that message.
4227
+ static const std::string CR_LF_NUL("\r\n\0", 3);
4228
+ if (val.find_first_of(CR_LF_NUL) != std::string::npos) { return false; }
4229
+
4230
+ fn(key, val);
3813
4231
  return true;
3814
4232
  }
3815
4233
 
@@ -3829,27 +4247,27 @@ inline bool read_headers(Stream &strm, Headers &headers) {
3829
4247
  if (line_reader.end_with_crlf()) {
3830
4248
  // Blank line indicates end of headers.
3831
4249
  if (line_reader.size() == 2) { break; }
3832
- #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
3833
4250
  } else {
4251
+ #ifdef CPPHTTPLIB_ALLOW_LF_AS_LINE_TERMINATOR
3834
4252
  // Blank line indicates end of headers.
3835
4253
  if (line_reader.size() == 1) { break; }
3836
4254
  line_terminator_len = 1;
3837
- }
3838
4255
  #else
3839
- } else {
3840
4256
  continue; // Skip invalid line.
3841
- }
3842
4257
  #endif
4258
+ }
3843
4259
 
3844
4260
  if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
3845
4261
 
3846
4262
  // Exclude line terminator
3847
4263
  auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
3848
4264
 
3849
- parse_header(line_reader.ptr(), end,
3850
- [&](std::string &&key, std::string &&val) {
3851
- headers.emplace(std::move(key), std::move(val));
3852
- });
4265
+ if (!parse_header(line_reader.ptr(), end,
4266
+ [&](const std::string &key, std::string &val) {
4267
+ headers.emplace(key, val);
4268
+ })) {
4269
+ return false;
4270
+ }
3853
4271
  }
3854
4272
 
3855
4273
  return true;
@@ -3937,8 +4355,19 @@ inline bool read_content_chunked(Stream &strm, T &x,
3937
4355
 
3938
4356
  assert(chunk_len == 0);
3939
4357
 
3940
- // Trailer
3941
- if (!line_reader.getline()) { return false; }
4358
+ // NOTE: In RFC 9112, '7.1 Chunked Transfer Coding' mentiones "The chunked
4359
+ // transfer coding is complete when a chunk with a chunk-size of zero is
4360
+ // received, possibly followed by a trailer section, and finally terminated by
4361
+ // an empty line". https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1
4362
+ //
4363
+ // In '7.1.3. Decoding Chunked', however, the pseudo-code in the section
4364
+ // does't care for the existence of the final CRLF. In other words, it seems
4365
+ // to be ok whether the final CRLF exists or not in the chunked data.
4366
+ // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1.3
4367
+ //
4368
+ // According to the reference code in RFC 9112, cpp-htpplib now allows
4369
+ // chuncked transfer coding data without the final CRLF.
4370
+ if (!line_reader.getline()) { return true; }
3942
4371
 
3943
4372
  while (strcmp(line_reader.ptr(), "\r\n") != 0) {
3944
4373
  if (line_reader.size() > CPPHTTPLIB_HEADER_MAX_LENGTH) { return false; }
@@ -3948,8 +4377,8 @@ inline bool read_content_chunked(Stream &strm, T &x,
3948
4377
  auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
3949
4378
 
3950
4379
  parse_header(line_reader.ptr(), end,
3951
- [&](std::string &&key, std::string &&val) {
3952
- x.headers.emplace(std::move(key), std::move(val));
4380
+ [&](const std::string &key, const std::string &val) {
4381
+ x.headers.emplace(key, val);
3953
4382
  });
3954
4383
 
3955
4384
  if (!line_reader.getline()) { return false; }
@@ -3959,8 +4388,8 @@ inline bool read_content_chunked(Stream &strm, T &x,
3959
4388
  }
3960
4389
 
3961
4390
  inline bool is_chunked_transfer_encoding(const Headers &headers) {
3962
- return compare_case_ignore(
3963
- get_header_value(headers, "Transfer-Encoding", 0, ""), "chunked");
4391
+ return case_ignore::equal(
4392
+ get_header_value(headers, "Transfer-Encoding", "", 0), "chunked");
3964
4393
  }
3965
4394
 
3966
4395
  template <typename T, typename U>
@@ -4026,8 +4455,14 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
4026
4455
  } else if (!has_header(x.headers, "Content-Length")) {
4027
4456
  ret = read_content_without_length(strm, out);
4028
4457
  } else {
4029
- auto len = get_header_value_u64(x.headers, "Content-Length", 0, 0);
4030
- if (len > payload_max_length) {
4458
+ auto is_invalid_value = false;
4459
+ auto len = get_header_value_u64(x.headers, "Content-Length",
4460
+ std::numeric_limits<uint64_t>::max(),
4461
+ 0, is_invalid_value);
4462
+
4463
+ if (is_invalid_value) {
4464
+ ret = false;
4465
+ } else if (len > payload_max_length) {
4031
4466
  exceed_payload_max_length = true;
4032
4467
  skip_content_with_length(strm, len);
4033
4468
  ret = false;
@@ -4042,13 +4477,36 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
4042
4477
  }
4043
4478
  return ret;
4044
4479
  });
4045
- } // namespace detail
4480
+ }
4481
+
4482
+ inline ssize_t write_request_line(Stream &strm, const std::string &method,
4483
+ const std::string &path) {
4484
+ std::string s = method;
4485
+ s += " ";
4486
+ s += path;
4487
+ s += " HTTP/1.1\r\n";
4488
+ return strm.write(s.data(), s.size());
4489
+ }
4490
+
4491
+ inline ssize_t write_response_line(Stream &strm, int status) {
4492
+ std::string s = "HTTP/1.1 ";
4493
+ s += std::to_string(status);
4494
+ s += " ";
4495
+ s += httplib::status_message(status);
4496
+ s += "\r\n";
4497
+ return strm.write(s.data(), s.size());
4498
+ }
4046
4499
 
4047
4500
  inline ssize_t write_headers(Stream &strm, const Headers &headers) {
4048
4501
  ssize_t write_len = 0;
4049
4502
  for (const auto &x : headers) {
4050
- auto len =
4051
- strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
4503
+ std::string s;
4504
+ s = x.first;
4505
+ s += ": ";
4506
+ s += x.second;
4507
+ s += "\r\n";
4508
+
4509
+ auto len = strm.write(s.data(), s.size());
4052
4510
  if (len < 0) { return len; }
4053
4511
  write_len += len;
4054
4512
  }
@@ -4302,22 +4760,22 @@ inline std::string params_to_query_str(const Params &params) {
4302
4760
  return query;
4303
4761
  }
4304
4762
 
4305
- inline void parse_query_text(const std::string &s, Params &params) {
4763
+ inline void parse_query_text(const char *data, std::size_t size,
4764
+ Params &params) {
4306
4765
  std::set<std::string> cache;
4307
- split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) {
4766
+ split(data, data + size, '&', [&](const char *b, const char *e) {
4308
4767
  std::string kv(b, e);
4309
4768
  if (cache.find(kv) != cache.end()) { return; }
4310
- cache.insert(kv);
4769
+ cache.insert(std::move(kv));
4311
4770
 
4312
4771
  std::string key;
4313
4772
  std::string val;
4314
- split(b, e, '=', [&](const char *b2, const char *e2) {
4315
- if (key.empty()) {
4316
- key.assign(b2, e2);
4317
- } else {
4318
- val.assign(b2, e2);
4319
- }
4320
- });
4773
+ divide(b, static_cast<std::size_t>(e - b), '=',
4774
+ [&](const char *lhs_data, std::size_t lhs_size, const char *rhs_data,
4775
+ std::size_t rhs_size) {
4776
+ key.assign(lhs_data, lhs_size);
4777
+ val.assign(rhs_data, rhs_size);
4778
+ });
4321
4779
 
4322
4780
  if (!key.empty()) {
4323
4781
  params.emplace(decode_url(key, true), decode_url(val, true));
@@ -4325,6 +4783,10 @@ inline void parse_query_text(const std::string &s, Params &params) {
4325
4783
  });
4326
4784
  }
4327
4785
 
4786
+ inline void parse_query_text(const std::string &s, Params &params) {
4787
+ parse_query_text(s.data(), s.size(), params);
4788
+ }
4789
+
4328
4790
  inline bool parse_multipart_boundary(const std::string &content_type,
4329
4791
  std::string &boundary) {
4330
4792
  auto boundary_keyword = "boundary=";
@@ -4365,35 +4827,44 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
4365
4827
  #else
4366
4828
  inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
4367
4829
  #endif
4368
- static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
4369
- std::smatch m;
4370
- if (std::regex_match(s, m, re_first_range)) {
4371
- auto pos = static_cast<size_t>(m.position(1));
4372
- auto len = static_cast<size_t>(m.length(1));
4830
+ auto is_valid = [](const std::string &str) {
4831
+ return std::all_of(str.cbegin(), str.cend(),
4832
+ [](unsigned char c) { return std::isdigit(c); });
4833
+ };
4834
+
4835
+ if (s.size() > 7 && s.compare(0, 6, "bytes=") == 0) {
4836
+ const auto pos = static_cast<size_t>(6);
4837
+ const auto len = static_cast<size_t>(s.size() - 6);
4373
4838
  auto all_valid_ranges = true;
4374
4839
  split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
4375
4840
  if (!all_valid_ranges) { return; }
4376
- static auto re_another_range = std::regex(R"(\s*(\d*)-(\d*))");
4377
- std::cmatch cm;
4378
- if (std::regex_match(b, e, cm, re_another_range)) {
4379
- ssize_t first = -1;
4380
- if (!cm.str(1).empty()) {
4381
- first = static_cast<ssize_t>(std::stoll(cm.str(1)));
4382
- }
4383
4841
 
4384
- ssize_t last = -1;
4385
- if (!cm.str(2).empty()) {
4386
- last = static_cast<ssize_t>(std::stoll(cm.str(2)));
4387
- }
4842
+ const auto it = std::find(b, e, '-');
4843
+ if (it == e) {
4844
+ all_valid_ranges = false;
4845
+ return;
4846
+ }
4847
+
4848
+ const auto lhs = std::string(b, it);
4849
+ const auto rhs = std::string(it + 1, e);
4850
+ if (!is_valid(lhs) || !is_valid(rhs)) {
4851
+ all_valid_ranges = false;
4852
+ return;
4853
+ }
4388
4854
 
4389
- if (first != -1 && last != -1 && first > last) {
4390
- all_valid_ranges = false;
4391
- return;
4392
- }
4393
- ranges.emplace_back(std::make_pair(first, last));
4855
+ const auto first =
4856
+ static_cast<ssize_t>(lhs.empty() ? -1 : std::stoll(lhs));
4857
+ const auto last =
4858
+ static_cast<ssize_t>(rhs.empty() ? -1 : std::stoll(rhs));
4859
+ if ((first == -1 && last == -1) ||
4860
+ (first != -1 && last != -1 && first > last)) {
4861
+ all_valid_ranges = false;
4862
+ return;
4394
4863
  }
4864
+
4865
+ ranges.emplace_back(first, last);
4395
4866
  });
4396
- return all_valid_ranges;
4867
+ return all_valid_ranges && !ranges.empty();
4397
4868
  }
4398
4869
  return false;
4399
4870
  #ifdef CPPHTTPLIB_NO_EXCEPTIONS
@@ -4452,7 +4923,7 @@ public:
4452
4923
  const auto header = buf_head(pos);
4453
4924
 
4454
4925
  if (!parse_header(header.data(), header.data() + header.size(),
4455
- [&](std::string &&, std::string &&) {})) {
4926
+ [&](const std::string &, const std::string &) {})) {
4456
4927
  is_valid_ = false;
4457
4928
  return false;
4458
4929
  }
@@ -4562,7 +5033,9 @@ private:
4562
5033
  const std::string &b) const {
4563
5034
  if (a.size() < b.size()) { return false; }
4564
5035
  for (size_t i = 0; i < b.size(); i++) {
4565
- if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
5036
+ if (case_ignore::to_lower(a[i]) != case_ignore::to_lower(b[i])) {
5037
+ return false;
5038
+ }
4566
5039
  }
4567
5040
  return true;
4568
5041
  }
@@ -4645,16 +5118,6 @@ private:
4645
5118
  size_t buf_epos_ = 0;
4646
5119
  };
4647
5120
 
4648
- inline std::string to_lower(const char *beg, const char *end) {
4649
- std::string out;
4650
- auto it = beg;
4651
- while (it != end) {
4652
- out += static_cast<char>(::tolower(*it));
4653
- it++;
4654
- }
4655
- return out;
4656
- }
4657
-
4658
5121
  inline std::string random_string(size_t length) {
4659
5122
  static const char data[] =
4660
5123
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
@@ -4768,7 +5231,18 @@ inline bool range_error(Request &req, Response &res) {
4768
5231
  last_pos = contant_len - 1;
4769
5232
  }
4770
5233
 
4771
- if (last_pos == -1) { last_pos = contant_len - 1; }
5234
+ // NOTE: RFC-9110 '14.1.2. Byte Ranges':
5235
+ // A client can limit the number of bytes requested without knowing the
5236
+ // size of the selected representation. If the last-pos value is absent,
5237
+ // or if the value is greater than or equal to the current length of the
5238
+ // representation data, the byte range is interpreted as the remainder of
5239
+ // the representation (i.e., the server replaces the value of last-pos
5240
+ // with a value that is one less than the current length of the selected
5241
+ // representation).
5242
+ // https://www.rfc-editor.org/rfc/rfc9110.html#section-14.1.2-6
5243
+ if (last_pos == -1 || last_pos >= contant_len) {
5244
+ last_pos = contant_len - 1;
5245
+ }
4772
5246
 
4773
5247
  // Range must be within content length
4774
5248
  if (!(0 <= first_pos && first_pos <= last_pos &&
@@ -4795,12 +5269,11 @@ inline bool range_error(Request &req, Response &res) {
4795
5269
 
4796
5270
  inline std::pair<size_t, size_t>
4797
5271
  get_range_offset_and_length(Range r, size_t content_length) {
4798
- (void)(content_length); // patch to get rid of "unused parameter" on release build
4799
5272
  assert(r.first != -1 && r.second != -1);
4800
5273
  assert(0 <= r.first && r.first < static_cast<ssize_t>(content_length));
4801
5274
  assert(r.first <= r.second &&
4802
5275
  r.second < static_cast<ssize_t>(content_length));
4803
-
5276
+ (void)(content_length);
4804
5277
  return std::make_pair(r.first, static_cast<size_t>(r.second - r.first) + 1);
4805
5278
  }
4806
5279
 
@@ -5230,6 +5703,7 @@ inline void hosted_at(const std::string &hostname,
5230
5703
  #endif
5231
5704
  return;
5232
5705
  }
5706
+ auto se = detail::scope_exit([&] { freeaddrinfo(result); });
5233
5707
 
5234
5708
  for (auto rp = result; rp; rp = rp->ai_next) {
5235
5709
  const auto &addr =
@@ -5241,8 +5715,6 @@ inline void hosted_at(const std::string &hostname,
5241
5715
  addrs.push_back(ip);
5242
5716
  }
5243
5717
  }
5244
-
5245
- freeaddrinfo(result);
5246
5718
  }
5247
5719
 
5248
5720
  inline std::string append_query_params(const std::string &path,
@@ -5291,8 +5763,8 @@ inline bool Request::has_header(const std::string &key) const {
5291
5763
  }
5292
5764
 
5293
5765
  inline std::string Request::get_header_value(const std::string &key,
5294
- size_t id) const {
5295
- return detail::get_header_value(headers, key, id, "");
5766
+ const char *def, size_t id) const {
5767
+ return detail::get_header_value(headers, key, def, id);
5296
5768
  }
5297
5769
 
5298
5770
  inline size_t Request::get_header_value_count(const std::string &key) const {
@@ -5302,7 +5774,8 @@ inline size_t Request::get_header_value_count(const std::string &key) const {
5302
5774
 
5303
5775
  inline void Request::set_header(const std::string &key,
5304
5776
  const std::string &val) {
5305
- if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
5777
+ if (detail::fields::is_field_name(key) &&
5778
+ detail::fields::is_field_value(val)) {
5306
5779
  headers.emplace(key, val);
5307
5780
  }
5308
5781
  }
@@ -5356,8 +5829,9 @@ inline bool Response::has_header(const std::string &key) const {
5356
5829
  }
5357
5830
 
5358
5831
  inline std::string Response::get_header_value(const std::string &key,
5832
+ const char *def,
5359
5833
  size_t id) const {
5360
- return detail::get_header_value(headers, key, id, "");
5834
+ return detail::get_header_value(headers, key, def, id);
5361
5835
  }
5362
5836
 
5363
5837
  inline size_t Response::get_header_value_count(const std::string &key) const {
@@ -5367,13 +5841,14 @@ inline size_t Response::get_header_value_count(const std::string &key) const {
5367
5841
 
5368
5842
  inline void Response::set_header(const std::string &key,
5369
5843
  const std::string &val) {
5370
- if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
5844
+ if (detail::fields::is_field_name(key) &&
5845
+ detail::fields::is_field_value(val)) {
5371
5846
  headers.emplace(key, val);
5372
5847
  }
5373
5848
  }
5374
5849
 
5375
5850
  inline void Response::set_redirect(const std::string &url, int stat) {
5376
- if (!detail::has_crlf(url)) {
5851
+ if (detail::fields::is_field_value(url)) {
5377
5852
  set_header("Location", url);
5378
5853
  if (300 <= stat && stat < 400) {
5379
5854
  this->status = stat;
@@ -5436,14 +5911,25 @@ inline void Response::set_chunked_content_provider(
5436
5911
  is_chunked_content_provider_ = true;
5437
5912
  }
5438
5913
 
5914
+ inline void Response::set_file_content(const std::string &path,
5915
+ const std::string &content_type) {
5916
+ file_content_path_ = path;
5917
+ file_content_content_type_ = content_type;
5918
+ }
5919
+
5920
+ inline void Response::set_file_content(const std::string &path) {
5921
+ file_content_path_ = path;
5922
+ }
5923
+
5439
5924
  // Result implementation
5440
5925
  inline bool Result::has_request_header(const std::string &key) const {
5441
5926
  return request_headers_.find(key) != request_headers_.end();
5442
5927
  }
5443
5928
 
5444
5929
  inline std::string Result::get_request_header_value(const std::string &key,
5930
+ const char *def,
5445
5931
  size_t id) const {
5446
- return detail::get_header_value(request_headers_, key, id, "");
5932
+ return detail::get_header_value(request_headers_, key, def, id);
5447
5933
  }
5448
5934
 
5449
5935
  inline size_t
@@ -5584,6 +6070,8 @@ inline socket_t BufferStream::socket() const { return 0; }
5584
6070
  inline const std::string &BufferStream::get_buffer() const { return buffer; }
5585
6071
 
5586
6072
  inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) {
6073
+ static constexpr char marker[] = "/:";
6074
+
5587
6075
  // One past the last ending position of a path param substring
5588
6076
  std::size_t last_param_end = 0;
5589
6077
 
@@ -5596,13 +6084,14 @@ inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) {
5596
6084
  #endif
5597
6085
 
5598
6086
  while (true) {
5599
- const auto marker_pos = pattern.find(marker, last_param_end);
6087
+ const auto marker_pos = pattern.find(
6088
+ marker, last_param_end == 0 ? last_param_end : last_param_end - 1);
5600
6089
  if (marker_pos == std::string::npos) { break; }
5601
6090
 
5602
6091
  static_fragments_.push_back(
5603
- pattern.substr(last_param_end, marker_pos - last_param_end));
6092
+ pattern.substr(last_param_end, marker_pos - last_param_end + 1));
5604
6093
 
5605
- const auto param_name_start = marker_pos + 1;
6094
+ const auto param_name_start = marker_pos + 2;
5606
6095
 
5607
6096
  auto sep_pos = pattern.find(separator, param_name_start);
5608
6097
  if (sep_pos == std::string::npos) { sep_pos = pattern.length(); }
@@ -5664,7 +6153,7 @@ inline bool PathParamsMatcher::match(Request &request) const {
5664
6153
  request.path_params.emplace(
5665
6154
  param_name, request.path.substr(starting_pos, sep_pos - starting_pos));
5666
6155
 
5667
- // Mark everythin up to '/' as matched
6156
+ // Mark everything up to '/' as matched
5668
6157
  starting_pos = sep_pos + 1;
5669
6158
  }
5670
6159
  // Returns false if the path is longer than the pattern
@@ -5763,7 +6252,8 @@ inline bool Server::set_base_dir(const std::string &dir,
5763
6252
 
5764
6253
  inline bool Server::set_mount_point(const std::string &mount_point,
5765
6254
  const std::string &dir, Headers headers) {
5766
- if (detail::is_dir(dir)) {
6255
+ detail::FileStat stat(dir);
6256
+ if (stat.is_dir()) {
5767
6257
  std::string mnt = !mount_point.empty() ? mount_point : "/";
5768
6258
  if (!mnt.empty() && mnt[0] == '/') {
5769
6259
  base_dirs_.push_back({mnt, dir, std::move(headers)});
@@ -5800,12 +6290,14 @@ inline Server &Server::set_file_request_handler(Handler handler) {
5800
6290
  return *this;
5801
6291
  }
5802
6292
 
5803
- inline Server &Server::set_error_handler(HandlerWithResponse handler) {
6293
+ inline Server &Server::set_error_handler_core(HandlerWithResponse handler,
6294
+ std::true_type) {
5804
6295
  error_handler_ = std::move(handler);
5805
6296
  return *this;
5806
6297
  }
5807
6298
 
5808
- inline Server &Server::set_error_handler(Handler handler) {
6299
+ inline Server &Server::set_error_handler_core(Handler handler,
6300
+ std::false_type) {
5809
6301
  error_handler_ = [handler](const Request &req, Response &res) {
5810
6302
  handler(req, res);
5811
6303
  return HandlerResponse::Handled;
@@ -5849,6 +6341,11 @@ inline Server &Server::set_tcp_nodelay(bool on) {
5849
6341
  return *this;
5850
6342
  }
5851
6343
 
6344
+ inline Server &Server::set_ipv6_v6only(bool on) {
6345
+ ipv6_v6only_ = on;
6346
+ return *this;
6347
+ }
6348
+
5852
6349
  inline Server &Server::set_socket_options(SocketOptions socket_options) {
5853
6350
  socket_options_ = std::move(socket_options);
5854
6351
  return *this;
@@ -5900,27 +6397,27 @@ inline Server &Server::set_payload_max_length(size_t length) {
5900
6397
 
5901
6398
  inline bool Server::bind_to_port(const std::string &host, int port,
5902
6399
  int socket_flags) {
5903
- return bind_internal(host, port, socket_flags) >= 0;
6400
+ auto ret = bind_internal(host, port, socket_flags);
6401
+ if (ret == -1) { is_decommisioned = true; }
6402
+ return ret >= 0;
5904
6403
  }
5905
6404
  inline int Server::bind_to_any_port(const std::string &host, int socket_flags) {
5906
- return bind_internal(host, 0, socket_flags);
6405
+ auto ret = bind_internal(host, 0, socket_flags);
6406
+ if (ret == -1) { is_decommisioned = true; }
6407
+ return ret;
5907
6408
  }
5908
6409
 
5909
- inline bool Server::listen_after_bind() {
5910
- auto se = detail::scope_exit([&]() { done_ = true; });
5911
- return listen_internal();
5912
- }
6410
+ inline bool Server::listen_after_bind() { return listen_internal(); }
5913
6411
 
5914
6412
  inline bool Server::listen(const std::string &host, int port,
5915
6413
  int socket_flags) {
5916
- auto se = detail::scope_exit([&]() { done_ = true; });
5917
6414
  return bind_to_port(host, port, socket_flags) && listen_internal();
5918
6415
  }
5919
6416
 
5920
6417
  inline bool Server::is_running() const { return is_running_; }
5921
6418
 
5922
6419
  inline void Server::wait_until_ready() const {
5923
- while (!is_running() && !done_) {
6420
+ while (!is_running_ && !is_decommisioned) {
5924
6421
  std::this_thread::sleep_for(std::chrono::milliseconds{1});
5925
6422
  }
5926
6423
  }
@@ -5932,8 +6429,11 @@ inline void Server::stop() {
5932
6429
  detail::shutdown_socket(sock);
5933
6430
  detail::close_socket(sock);
5934
6431
  }
6432
+ is_decommisioned = false;
5935
6433
  }
5936
6434
 
6435
+ inline void Server::decommission() { is_decommisioned = true; }
6436
+
5937
6437
  inline bool Server::parse_request_line(const char *s, Request &req) const {
5938
6438
  auto len = strlen(s);
5939
6439
  if (len < 2 || s[len - 2] != '\r' || s[len - 1] != '\n') { return false; }
@@ -5972,26 +6472,13 @@ inline bool Server::parse_request_line(const char *s, Request &req) const {
5972
6472
  }
5973
6473
  }
5974
6474
 
5975
- size_t count = 0;
5976
-
5977
- detail::split(req.target.data(), req.target.data() + req.target.size(), '?',
5978
- 2, [&](const char *b, const char *e) {
5979
- switch (count) {
5980
- case 0:
5981
- req.path = detail::decode_url(std::string(b, e), false);
5982
- break;
5983
- case 1: {
5984
- if (e - b > 0) {
5985
- detail::parse_query_text(std::string(b, e), req.params);
5986
- }
5987
- break;
5988
- }
5989
- default: break;
5990
- }
5991
- count++;
5992
- });
5993
-
5994
- if (count > 2) { return false; }
6475
+ detail::divide(req.target, '?',
6476
+ [&](const char *lhs_data, std::size_t lhs_size,
6477
+ const char *rhs_data, std::size_t rhs_size) {
6478
+ req.path = detail::decode_url(
6479
+ std::string(lhs_data, lhs_size), false);
6480
+ detail::parse_query_text(rhs_data, rhs_size, req.params);
6481
+ });
5995
6482
  }
5996
6483
 
5997
6484
  return true;
@@ -6030,23 +6517,24 @@ inline bool Server::write_response_core(Stream &strm, bool close_connection,
6030
6517
  if (close_connection || req.get_header_value("Connection") == "close") {
6031
6518
  res.set_header("Connection", "close");
6032
6519
  } else {
6033
- std::stringstream ss;
6034
- ss << "timeout=" << keep_alive_timeout_sec_
6035
- << ", max=" << keep_alive_max_count_;
6036
- res.set_header("Keep-Alive", ss.str());
6520
+ std::string s = "timeout=";
6521
+ s += std::to_string(keep_alive_timeout_sec_);
6522
+ s += ", max=";
6523
+ s += std::to_string(keep_alive_max_count_);
6524
+ res.set_header("Keep-Alive", s);
6037
6525
  }
6038
6526
 
6039
- if (!res.has_header("Content-Type") &&
6040
- (!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) {
6527
+ if ((!res.body.empty() || res.content_length_ > 0 || res.content_provider_) &&
6528
+ !res.has_header("Content-Type")) {
6041
6529
  res.set_header("Content-Type", "text/plain");
6042
6530
  }
6043
6531
 
6044
- if (!res.has_header("Content-Length") && res.body.empty() &&
6045
- !res.content_length_ && !res.content_provider_) {
6532
+ if (res.body.empty() && !res.content_length_ && !res.content_provider_ &&
6533
+ !res.has_header("Content-Length")) {
6046
6534
  res.set_header("Content-Length", "0");
6047
6535
  }
6048
6536
 
6049
- if (!res.has_header("Accept-Ranges") && req.method == "HEAD") {
6537
+ if (req.method == "HEAD" && !res.has_header("Accept-Ranges")) {
6050
6538
  res.set_header("Accept-Ranges", "bytes");
6051
6539
  }
6052
6540
 
@@ -6055,12 +6543,7 @@ inline bool Server::write_response_core(Stream &strm, bool close_connection,
6055
6543
  // Response line and headers
6056
6544
  {
6057
6545
  detail::BufferStream bstrm;
6058
-
6059
- if (!bstrm.write_format("HTTP/1.1 %d %s\r\n", res.status,
6060
- status_message(res.status))) {
6061
- return false;
6062
- }
6063
-
6546
+ if (!detail::write_response_line(bstrm, res.status)) { return false; }
6064
6547
  if (!header_writer_(bstrm, res.headers)) { return false; }
6065
6548
 
6066
6549
  // Flush buffer
@@ -6254,7 +6737,14 @@ inline bool Server::handle_file_request(const Request &req, Response &res,
6254
6737
  auto path = entry.base_dir + sub_path;
6255
6738
  if (path.back() == '/') { path += "index.html"; }
6256
6739
 
6257
- if (detail::is_file(path)) {
6740
+ detail::FileStat stat(path);
6741
+
6742
+ if (stat.is_dir()) {
6743
+ res.set_redirect(sub_path + "/", StatusCode::MovedPermanently_301);
6744
+ return true;
6745
+ }
6746
+
6747
+ if (stat.is_file()) {
6258
6748
  for (const auto &kv : entry.headers) {
6259
6749
  res.set_header(kv.first, kv.second);
6260
6750
  }
@@ -6289,8 +6779,8 @@ Server::create_server_socket(const std::string &host, int port,
6289
6779
  SocketOptions socket_options) const {
6290
6780
  return detail::create_socket(
6291
6781
  host, std::string(), port, address_family_, socket_flags, tcp_nodelay_,
6292
- std::move(socket_options),
6293
- [](socket_t sock, struct addrinfo &ai) -> bool {
6782
+ ipv6_v6only_, std::move(socket_options),
6783
+ [](socket_t sock, struct addrinfo &ai, bool & /*quit*/) -> bool {
6294
6784
  if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
6295
6785
  return false;
6296
6786
  }
@@ -6301,6 +6791,8 @@ Server::create_server_socket(const std::string &host, int port,
6301
6791
 
6302
6792
  inline int Server::bind_internal(const std::string &host, int port,
6303
6793
  int socket_flags) {
6794
+ if (is_decommisioned) { return -1; }
6795
+
6304
6796
  if (!is_valid()) { return -1; }
6305
6797
 
6306
6798
  svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
@@ -6326,6 +6818,8 @@ inline int Server::bind_internal(const std::string &host, int port,
6326
6818
  }
6327
6819
 
6328
6820
  inline bool Server::listen_internal() {
6821
+ if (is_decommisioned) { return false; }
6822
+
6329
6823
  auto ret = true;
6330
6824
  is_running_ = true;
6331
6825
  auto se = detail::scope_exit([&]() { is_running_ = false; });
@@ -6346,13 +6840,22 @@ inline bool Server::listen_internal() {
6346
6840
  #ifndef _WIN32
6347
6841
  }
6348
6842
  #endif
6843
+
6844
+ #if defined _WIN32
6845
+ // sockets conneced via WASAccept inherit flags NO_HANDLE_INHERIT,
6846
+ // OVERLAPPED
6847
+ socket_t sock = WSAAccept(svr_sock_, nullptr, nullptr, nullptr, 0);
6848
+ #elif defined SOCK_CLOEXEC
6849
+ socket_t sock = accept4(svr_sock_, nullptr, nullptr, SOCK_CLOEXEC);
6850
+ #else
6349
6851
  socket_t sock = accept(svr_sock_, nullptr, nullptr);
6852
+ #endif
6350
6853
 
6351
6854
  if (sock == INVALID_SOCKET) {
6352
6855
  if (errno == EMFILE) {
6353
6856
  // The per-process limit of open file descriptors has been reached.
6354
6857
  // Try to accept new connections after a short sleep.
6355
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
6858
+ std::this_thread::sleep_for(std::chrono::microseconds{1});
6356
6859
  continue;
6357
6860
  } else if (errno == EINTR || errno == EAGAIN) {
6358
6861
  continue;
@@ -6406,6 +6909,7 @@ inline bool Server::listen_internal() {
6406
6909
  task_queue->shutdown();
6407
6910
  }
6408
6911
 
6912
+ is_decommisioned = !ret;
6409
6913
  return ret;
6410
6914
  }
6411
6915
 
@@ -6503,7 +7007,7 @@ inline bool Server::dispatch_request(Request &req, Response &res,
6503
7007
  inline void Server::apply_ranges(const Request &req, Response &res,
6504
7008
  std::string &content_type,
6505
7009
  std::string &boundary) const {
6506
- if (req.ranges.size() > 1) {
7010
+ if (req.ranges.size() > 1 && res.status == StatusCode::PartialContent_206) {
6507
7011
  auto it = res.headers.find("Content-Type");
6508
7012
  if (it != res.headers.end()) {
6509
7013
  content_type = it->second;
@@ -6521,7 +7025,7 @@ inline void Server::apply_ranges(const Request &req, Response &res,
6521
7025
  if (res.body.empty()) {
6522
7026
  if (res.content_length_ > 0) {
6523
7027
  size_t length = 0;
6524
- if (req.ranges.empty()) {
7028
+ if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
6525
7029
  length = res.content_length_;
6526
7030
  } else if (req.ranges.size() == 1) {
6527
7031
  auto offset_and_length = detail::get_range_offset_and_length(
@@ -6550,7 +7054,7 @@ inline void Server::apply_ranges(const Request &req, Response &res,
6550
7054
  }
6551
7055
  }
6552
7056
  } else {
6553
- if (req.ranges.empty()) {
7057
+ if (req.ranges.empty() || res.status != StatusCode::PartialContent_206) {
6554
7058
  ;
6555
7059
  } else if (req.ranges.size() == 1) {
6556
7060
  auto offset_and_length =
@@ -6621,7 +7125,9 @@ inline bool Server::dispatch_request_for_content_reader(
6621
7125
  }
6622
7126
 
6623
7127
  inline bool
6624
- Server::process_request(Stream &strm, bool close_connection,
7128
+ Server::process_request(Stream &strm, const std::string &remote_addr,
7129
+ int remote_port, const std::string &local_addr,
7130
+ int local_port, bool close_connection,
6625
7131
  bool &connection_closed,
6626
7132
  const std::function<void(Request &)> &setup_request) {
6627
7133
  std::array<char, 2048> buf{};
@@ -6675,11 +7181,13 @@ Server::process_request(Stream &strm, bool close_connection,
6675
7181
  connection_closed = true;
6676
7182
  }
6677
7183
 
6678
- strm.get_remote_ip_and_port(req.remote_addr, req.remote_port);
7184
+ req.remote_addr = remote_addr;
7185
+ req.remote_port = remote_port;
6679
7186
  req.set_header("REMOTE_ADDR", req.remote_addr);
6680
7187
  req.set_header("REMOTE_PORT", std::to_string(req.remote_port));
6681
7188
 
6682
- strm.get_local_ip_and_port(req.local_addr, req.local_port);
7189
+ req.local_addr = local_addr;
7190
+ req.local_port = local_port;
6683
7191
  req.set_header("LOCAL_ADDR", req.local_addr);
6684
7192
  req.set_header("LOCAL_PORT", std::to_string(req.local_port));
6685
7193
 
@@ -6701,13 +7209,20 @@ Server::process_request(Stream &strm, bool close_connection,
6701
7209
  switch (status) {
6702
7210
  case StatusCode::Continue_100:
6703
7211
  case StatusCode::ExpectationFailed_417:
6704
- strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status,
6705
- status_message(status));
7212
+ detail::write_response_line(strm, status);
7213
+ strm.write("\r\n");
6706
7214
  break;
6707
- default: return write_response(strm, close_connection, req, res);
7215
+ default:
7216
+ connection_closed = true;
7217
+ return write_response(strm, true, req, res);
6708
7218
  }
6709
7219
  }
6710
7220
 
7221
+ // Setup `is_connection_closed` method
7222
+ req.is_connection_closed = [&]() {
7223
+ return !detail::is_socket_alive(strm.socket());
7224
+ };
7225
+
6711
7226
  // Routing
6712
7227
  auto routed = false;
6713
7228
  #ifdef CPPHTTPLIB_NO_EXCEPTIONS
@@ -6750,6 +7265,32 @@ Server::process_request(Stream &strm, bool close_connection,
6750
7265
  : StatusCode::PartialContent_206;
6751
7266
  }
6752
7267
 
7268
+ // Serve file content by using a content provider
7269
+ if (!res.file_content_path_.empty()) {
7270
+ const auto &path = res.file_content_path_;
7271
+ auto mm = std::make_shared<detail::mmap>(path.c_str());
7272
+ if (!mm->is_open()) {
7273
+ res.body.clear();
7274
+ res.content_length_ = 0;
7275
+ res.content_provider_ = nullptr;
7276
+ res.status = StatusCode::NotFound_404;
7277
+ return write_response(strm, close_connection, req, res);
7278
+ }
7279
+
7280
+ auto content_type = res.file_content_content_type_;
7281
+ if (content_type.empty()) {
7282
+ content_type = detail::find_content_type(
7283
+ path, file_extension_and_mimetype_map_, default_file_mimetype_);
7284
+ }
7285
+
7286
+ res.set_content_provider(
7287
+ mm->size(), content_type,
7288
+ [mm](size_t offset, size_t length, DataSink &sink) -> bool {
7289
+ sink.write(mm->data() + offset, length);
7290
+ return true;
7291
+ });
7292
+ }
7293
+
6753
7294
  if (detail::range_error(req, res)) {
6754
7295
  res.body.clear();
6755
7296
  res.content_length_ = 0;
@@ -6769,12 +7310,21 @@ Server::process_request(Stream &strm, bool close_connection,
6769
7310
  inline bool Server::is_valid() const { return true; }
6770
7311
 
6771
7312
  inline bool Server::process_and_close_socket(socket_t sock) {
7313
+ std::string remote_addr;
7314
+ int remote_port = 0;
7315
+ detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
7316
+
7317
+ std::string local_addr;
7318
+ int local_port = 0;
7319
+ detail::get_local_ip_and_port(sock, local_addr, local_port);
7320
+
6772
7321
  auto ret = detail::process_server_socket(
6773
7322
  svr_sock_, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
6774
7323
  read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
6775
7324
  write_timeout_usec_,
6776
- [this](Stream &strm, bool close_connection, bool &connection_closed) {
6777
- return process_request(strm, close_connection, connection_closed,
7325
+ [&](Stream &strm, bool close_connection, bool &connection_closed) {
7326
+ return process_request(strm, remote_addr, remote_port, local_addr,
7327
+ local_port, close_connection, connection_closed,
6778
7328
  nullptr);
6779
7329
  });
6780
7330
 
@@ -6793,8 +7343,8 @@ inline ClientImpl::ClientImpl(const std::string &host, int port)
6793
7343
  inline ClientImpl::ClientImpl(const std::string &host, int port,
6794
7344
  const std::string &client_cert_path,
6795
7345
  const std::string &client_key_path)
6796
- : host_(host), port_(port),
6797
- host_and_port_(adjust_host_string(host) + ":" + std::to_string(port)),
7346
+ : host_(detail::escape_abstract_namespace_unix_domain(host)), port_(port),
7347
+ host_and_port_(adjust_host_string(host_) + ":" + std::to_string(port)),
6798
7348
  client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
6799
7349
 
6800
7350
  inline ClientImpl::~ClientImpl() {
@@ -6825,6 +7375,7 @@ inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
6825
7375
  url_encode_ = rhs.url_encode_;
6826
7376
  address_family_ = rhs.address_family_;
6827
7377
  tcp_nodelay_ = rhs.tcp_nodelay_;
7378
+ ipv6_v6only_ = rhs.ipv6_v6only_;
6828
7379
  socket_options_ = rhs.socket_options_;
6829
7380
  compress_ = rhs.compress_;
6830
7381
  decompress_ = rhs.decompress_;
@@ -6845,6 +7396,8 @@ inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
6845
7396
  #endif
6846
7397
  #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
6847
7398
  server_certificate_verification_ = rhs.server_certificate_verification_;
7399
+ server_hostname_verification_ = rhs.server_hostname_verification_;
7400
+ server_certificate_verifier_ = rhs.server_certificate_verifier_;
6848
7401
  #endif
6849
7402
  logger_ = rhs.logger_;
6850
7403
  }
@@ -6853,9 +7406,9 @@ inline socket_t ClientImpl::create_client_socket(Error &error) const {
6853
7406
  if (!proxy_host_.empty() && proxy_port_ != -1) {
6854
7407
  return detail::create_client_socket(
6855
7408
  proxy_host_, std::string(), proxy_port_, address_family_, tcp_nodelay_,
6856
- socket_options_, connection_timeout_sec_, connection_timeout_usec_,
6857
- read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
6858
- write_timeout_usec_, interface_, error);
7409
+ ipv6_v6only_, socket_options_, connection_timeout_sec_,
7410
+ connection_timeout_usec_, read_timeout_sec_, read_timeout_usec_,
7411
+ write_timeout_sec_, write_timeout_usec_, interface_, error);
6859
7412
  }
6860
7413
 
6861
7414
  // Check is custom IP specified for host_
@@ -6864,10 +7417,10 @@ inline socket_t ClientImpl::create_client_socket(Error &error) const {
6864
7417
  if (it != addr_map_.end()) { ip = it->second; }
6865
7418
 
6866
7419
  return detail::create_client_socket(
6867
- host_, ip, port_, address_family_, tcp_nodelay_, socket_options_,
6868
- connection_timeout_sec_, connection_timeout_usec_, read_timeout_sec_,
6869
- read_timeout_usec_, write_timeout_sec_, write_timeout_usec_, interface_,
6870
- error);
7420
+ host_, ip, port_, address_family_, tcp_nodelay_, ipv6_v6only_,
7421
+ socket_options_, connection_timeout_sec_, connection_timeout_usec_,
7422
+ read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
7423
+ write_timeout_usec_, interface_, error);
6871
7424
  }
6872
7425
 
6873
7426
  inline bool ClientImpl::create_and_connect_socket(Socket &socket,
@@ -6956,6 +7509,18 @@ inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
6956
7509
  return ret;
6957
7510
  }
6958
7511
 
7512
+ #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7513
+ inline bool ClientImpl::is_ssl_peer_could_be_closed(SSL *ssl) const {
7514
+ detail::set_nonblocking(socket_.sock, true);
7515
+ auto se = detail::scope_exit(
7516
+ [&]() { detail::set_nonblocking(socket_.sock, false); });
7517
+
7518
+ char buf[1];
7519
+ return !SSL_peek(ssl, buf, 1) &&
7520
+ SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
7521
+ }
7522
+ #endif
7523
+
6959
7524
  inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
6960
7525
  {
6961
7526
  std::lock_guard<std::mutex> guard(socket_mutex_);
@@ -6967,6 +7532,13 @@ inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
6967
7532
  auto is_alive = false;
6968
7533
  if (socket_.is_open()) {
6969
7534
  is_alive = detail::is_socket_alive(socket_.sock);
7535
+
7536
+ #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7537
+ if (is_alive && is_ssl()) {
7538
+ if (is_ssl_peer_could_be_closed(socket_.ssl)) { is_alive = false; }
7539
+ }
7540
+ #endif
7541
+
6970
7542
  if (!is_alive) {
6971
7543
  // Attempt to avoid sigpipe by shutting down nongracefully if it seems
6972
7544
  // like the other side has already closed the connection Also, there
@@ -7144,7 +7716,7 @@ inline bool ClientImpl::redirect(Request &req, Response &res, Error &error) {
7144
7716
  if (location.empty()) { return false; }
7145
7717
 
7146
7718
  const static std::regex re(
7147
- R"((?:(https?):)?(?://(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
7719
+ R"((?:(https?):)?(?://(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)?([^?#]*)(\?[^#]*)?(?:#.*)?)");
7148
7720
 
7149
7721
  std::smatch m;
7150
7722
  if (!std::regex_match(location, m, re)) { return false; }
@@ -7243,12 +7815,26 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
7243
7815
 
7244
7816
  if (!req.has_header("Accept")) { req.set_header("Accept", "*/*"); }
7245
7817
 
7818
+ if (!req.content_receiver) {
7819
+ if (!req.has_header("Accept-Encoding")) {
7820
+ std::string accept_encoding;
7821
+ #ifdef CPPHTTPLIB_BROTLI_SUPPORT
7822
+ accept_encoding = "br";
7823
+ #endif
7824
+ #ifdef CPPHTTPLIB_ZLIB_SUPPORT
7825
+ if (!accept_encoding.empty()) { accept_encoding += ", "; }
7826
+ accept_encoding += "gzip, deflate";
7827
+ #endif
7828
+ req.set_header("Accept-Encoding", accept_encoding);
7829
+ }
7830
+
7246
7831
  #ifndef CPPHTTPLIB_NO_DEFAULT_USER_AGENT
7247
- if (!req.has_header("User-Agent")) {
7248
- auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
7249
- req.set_header("User-Agent", agent);
7250
- }
7832
+ if (!req.has_header("User-Agent")) {
7833
+ auto agent = std::string("cpp-httplib/") + CPPHTTPLIB_VERSION;
7834
+ req.set_header("User-Agent", agent);
7835
+ }
7251
7836
  #endif
7837
+ };
7252
7838
 
7253
7839
  if (req.body.empty()) {
7254
7840
  if (req.content_provider_) {
@@ -7308,8 +7894,14 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
7308
7894
  {
7309
7895
  detail::BufferStream bstrm;
7310
7896
 
7311
- const auto &path = url_encode_ ? detail::encode_url(req.path) : req.path;
7312
- bstrm.write_format("%s %s HTTP/1.1\r\n", req.method.c_str(), path.c_str());
7897
+ const auto &path_with_query =
7898
+ req.params.empty() ? req.path
7899
+ : append_query_params(req.path, req.params);
7900
+
7901
+ const auto &path =
7902
+ url_encode_ ? detail::encode_url(path_with_query) : path_with_query;
7903
+
7904
+ detail::write_request_line(bstrm, req.method, path);
7313
7905
 
7314
7906
  header_writer_(bstrm, req.headers);
7315
7907
 
@@ -7417,11 +8009,12 @@ inline Result ClientImpl::send_with_content_provider(
7417
8009
  const std::string &method, const std::string &path, const Headers &headers,
7418
8010
  const char *body, size_t content_length, ContentProvider content_provider,
7419
8011
  ContentProviderWithoutLength content_provider_without_length,
7420
- const std::string &content_type) {
8012
+ const std::string &content_type, Progress progress) {
7421
8013
  Request req;
7422
8014
  req.method = method;
7423
8015
  req.headers = headers;
7424
8016
  req.path = path;
8017
+ req.progress = progress;
7425
8018
 
7426
8019
  auto error = Error::Success;
7427
8020
 
@@ -7448,9 +8041,7 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
7448
8041
  if (is_ssl()) {
7449
8042
  auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
7450
8043
  if (!is_proxy_enabled) {
7451
- char buf[1];
7452
- if (SSL_peek(socket_.ssl, buf, 1) == 0 &&
7453
- SSL_get_error(socket_.ssl, 0) == SSL_ERROR_ZERO_RETURN) {
8044
+ if (is_ssl_peer_could_be_closed(socket_.ssl)) {
7454
8045
  error = Error::SSLPeerCouldBeClosed_;
7455
8046
  return false;
7456
8047
  }
@@ -7468,7 +8059,9 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
7468
8059
  // Body
7469
8060
  if ((res.status != StatusCode::NoContent_204) && req.method != "HEAD" &&
7470
8061
  req.method != "CONNECT") {
7471
- auto redirect = 300 < res.status && res.status < 400 && follow_location_;
8062
+ auto redirect = 300 < res.status && res.status < 400 &&
8063
+ res.status != StatusCode::NotModified_304 &&
8064
+ follow_location_;
7472
8065
 
7473
8066
  if (req.response_handler && !redirect) {
7474
8067
  if (!req.response_handler(res)) {
@@ -7489,9 +8082,7 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
7489
8082
  : static_cast<ContentReceiverWithProgress>(
7490
8083
  [&](const char *buf, size_t n, uint64_t /*off*/,
7491
8084
  uint64_t /*len*/) {
7492
- if (res.body.size() + n > res.body.max_size()) {
7493
- return false;
7494
- }
8085
+ assert(res.body.size() + n <= res.body.max_size());
7495
8086
  res.body.append(buf, n);
7496
8087
  return true;
7497
8088
  });
@@ -7503,12 +8094,25 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
7503
8094
  return ret;
7504
8095
  };
7505
8096
 
7506
- int dummy_status;
7507
- if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
7508
- dummy_status, std::move(progress), std::move(out),
7509
- decompress_)) {
7510
- if (error != Error::Canceled) { error = Error::Read; }
7511
- return false;
8097
+ if (res.has_header("Content-Length")) {
8098
+ if (!req.content_receiver) {
8099
+ auto len = res.get_header_value_u64("Content-Length");
8100
+ if (len > res.body.max_size()) {
8101
+ error = Error::Read;
8102
+ return false;
8103
+ }
8104
+ res.body.reserve(static_cast<size_t>(len));
8105
+ }
8106
+ }
8107
+
8108
+ if (res.status != StatusCode::NotModified_304) {
8109
+ int dummy_status;
8110
+ if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
8111
+ dummy_status, std::move(progress),
8112
+ std::move(out), decompress_)) {
8113
+ if (error != Error::Canceled) { error = Error::Read; }
8114
+ return false;
8115
+ }
7512
8116
  }
7513
8117
  }
7514
8118
 
@@ -7717,14 +8321,22 @@ inline Result ClientImpl::Post(const std::string &path,
7717
8321
  inline Result ClientImpl::Post(const std::string &path, const char *body,
7718
8322
  size_t content_length,
7719
8323
  const std::string &content_type) {
7720
- return Post(path, Headers(), body, content_length, content_type);
8324
+ return Post(path, Headers(), body, content_length, content_type, nullptr);
7721
8325
  }
7722
8326
 
7723
8327
  inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
7724
8328
  const char *body, size_t content_length,
7725
8329
  const std::string &content_type) {
7726
8330
  return send_with_content_provider("POST", path, headers, body, content_length,
7727
- nullptr, nullptr, content_type);
8331
+ nullptr, nullptr, content_type, nullptr);
8332
+ }
8333
+
8334
+ inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
8335
+ const char *body, size_t content_length,
8336
+ const std::string &content_type,
8337
+ Progress progress) {
8338
+ return send_with_content_provider("POST", path, headers, body, content_length,
8339
+ nullptr, nullptr, content_type, progress);
7728
8340
  }
7729
8341
 
7730
8342
  inline Result ClientImpl::Post(const std::string &path, const std::string &body,
@@ -7732,12 +8344,27 @@ inline Result ClientImpl::Post(const std::string &path, const std::string &body,
7732
8344
  return Post(path, Headers(), body, content_type);
7733
8345
  }
7734
8346
 
8347
+ inline Result ClientImpl::Post(const std::string &path, const std::string &body,
8348
+ const std::string &content_type,
8349
+ Progress progress) {
8350
+ return Post(path, Headers(), body, content_type, progress);
8351
+ }
8352
+
7735
8353
  inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
7736
8354
  const std::string &body,
7737
8355
  const std::string &content_type) {
7738
8356
  return send_with_content_provider("POST", path, headers, body.data(),
7739
- body.size(), nullptr, nullptr,
7740
- content_type);
8357
+ body.size(), nullptr, nullptr, content_type,
8358
+ nullptr);
8359
+ }
8360
+
8361
+ inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
8362
+ const std::string &body,
8363
+ const std::string &content_type,
8364
+ Progress progress) {
8365
+ return send_with_content_provider("POST", path, headers, body.data(),
8366
+ body.size(), nullptr, nullptr, content_type,
8367
+ progress);
7741
8368
  }
7742
8369
 
7743
8370
  inline Result ClientImpl::Post(const std::string &path, const Params &params) {
@@ -7763,14 +8390,15 @@ inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
7763
8390
  const std::string &content_type) {
7764
8391
  return send_with_content_provider("POST", path, headers, nullptr,
7765
8392
  content_length, std::move(content_provider),
7766
- nullptr, content_type);
8393
+ nullptr, content_type, nullptr);
7767
8394
  }
7768
8395
 
7769
8396
  inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
7770
8397
  ContentProviderWithoutLength content_provider,
7771
8398
  const std::string &content_type) {
7772
8399
  return send_with_content_provider("POST", path, headers, nullptr, 0, nullptr,
7773
- std::move(content_provider), content_type);
8400
+ std::move(content_provider), content_type,
8401
+ nullptr);
7774
8402
  }
7775
8403
 
7776
8404
  inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
@@ -7779,6 +8407,13 @@ inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
7779
8407
  return Post(path, headers, query, "application/x-www-form-urlencoded");
7780
8408
  }
7781
8409
 
8410
+ inline Result ClientImpl::Post(const std::string &path, const Headers &headers,
8411
+ const Params &params, Progress progress) {
8412
+ auto query = detail::params_to_query_str(params);
8413
+ return Post(path, headers, query, "application/x-www-form-urlencoded",
8414
+ progress);
8415
+ }
8416
+
7782
8417
  inline Result ClientImpl::Post(const std::string &path,
7783
8418
  const MultipartFormDataItems &items) {
7784
8419
  return Post(path, Headers(), items);
@@ -7816,7 +8451,7 @@ ClientImpl::Post(const std::string &path, const Headers &headers,
7816
8451
  return send_with_content_provider(
7817
8452
  "POST", path, headers, nullptr, 0, nullptr,
7818
8453
  get_multipart_content_provider(boundary, items, provider_items),
7819
- content_type);
8454
+ content_type, nullptr);
7820
8455
  }
7821
8456
 
7822
8457
  inline Result ClientImpl::Put(const std::string &path) {
@@ -7833,7 +8468,15 @@ inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
7833
8468
  const char *body, size_t content_length,
7834
8469
  const std::string &content_type) {
7835
8470
  return send_with_content_provider("PUT", path, headers, body, content_length,
7836
- nullptr, nullptr, content_type);
8471
+ nullptr, nullptr, content_type, nullptr);
8472
+ }
8473
+
8474
+ inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
8475
+ const char *body, size_t content_length,
8476
+ const std::string &content_type,
8477
+ Progress progress) {
8478
+ return send_with_content_provider("PUT", path, headers, body, content_length,
8479
+ nullptr, nullptr, content_type, progress);
7837
8480
  }
7838
8481
 
7839
8482
  inline Result ClientImpl::Put(const std::string &path, const std::string &body,
@@ -7841,12 +8484,27 @@ inline Result ClientImpl::Put(const std::string &path, const std::string &body,
7841
8484
  return Put(path, Headers(), body, content_type);
7842
8485
  }
7843
8486
 
8487
+ inline Result ClientImpl::Put(const std::string &path, const std::string &body,
8488
+ const std::string &content_type,
8489
+ Progress progress) {
8490
+ return Put(path, Headers(), body, content_type, progress);
8491
+ }
8492
+
7844
8493
  inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
7845
8494
  const std::string &body,
7846
8495
  const std::string &content_type) {
7847
8496
  return send_with_content_provider("PUT", path, headers, body.data(),
7848
- body.size(), nullptr, nullptr,
7849
- content_type);
8497
+ body.size(), nullptr, nullptr, content_type,
8498
+ nullptr);
8499
+ }
8500
+
8501
+ inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
8502
+ const std::string &body,
8503
+ const std::string &content_type,
8504
+ Progress progress) {
8505
+ return send_with_content_provider("PUT", path, headers, body.data(),
8506
+ body.size(), nullptr, nullptr, content_type,
8507
+ progress);
7850
8508
  }
7851
8509
 
7852
8510
  inline Result ClientImpl::Put(const std::string &path, size_t content_length,
@@ -7868,14 +8526,15 @@ inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
7868
8526
  const std::string &content_type) {
7869
8527
  return send_with_content_provider("PUT", path, headers, nullptr,
7870
8528
  content_length, std::move(content_provider),
7871
- nullptr, content_type);
8529
+ nullptr, content_type, nullptr);
7872
8530
  }
7873
8531
 
7874
8532
  inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
7875
8533
  ContentProviderWithoutLength content_provider,
7876
8534
  const std::string &content_type) {
7877
8535
  return send_with_content_provider("PUT", path, headers, nullptr, 0, nullptr,
7878
- std::move(content_provider), content_type);
8536
+ std::move(content_provider), content_type,
8537
+ nullptr);
7879
8538
  }
7880
8539
 
7881
8540
  inline Result ClientImpl::Put(const std::string &path, const Params &params) {
@@ -7888,6 +8547,13 @@ inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
7888
8547
  return Put(path, headers, query, "application/x-www-form-urlencoded");
7889
8548
  }
7890
8549
 
8550
+ inline Result ClientImpl::Put(const std::string &path, const Headers &headers,
8551
+ const Params &params, Progress progress) {
8552
+ auto query = detail::params_to_query_str(params);
8553
+ return Put(path, headers, query, "application/x-www-form-urlencoded",
8554
+ progress);
8555
+ }
8556
+
7891
8557
  inline Result ClientImpl::Put(const std::string &path,
7892
8558
  const MultipartFormDataItems &items) {
7893
8559
  return Put(path, Headers(), items);
@@ -7925,7 +8591,7 @@ ClientImpl::Put(const std::string &path, const Headers &headers,
7925
8591
  return send_with_content_provider(
7926
8592
  "PUT", path, headers, nullptr, 0, nullptr,
7927
8593
  get_multipart_content_provider(boundary, items, provider_items),
7928
- content_type);
8594
+ content_type, nullptr);
7929
8595
  }
7930
8596
  inline Result ClientImpl::Patch(const std::string &path) {
7931
8597
  return Patch(path, std::string(), std::string());
@@ -7937,12 +8603,26 @@ inline Result ClientImpl::Patch(const std::string &path, const char *body,
7937
8603
  return Patch(path, Headers(), body, content_length, content_type);
7938
8604
  }
7939
8605
 
8606
+ inline Result ClientImpl::Patch(const std::string &path, const char *body,
8607
+ size_t content_length,
8608
+ const std::string &content_type,
8609
+ Progress progress) {
8610
+ return Patch(path, Headers(), body, content_length, content_type, progress);
8611
+ }
8612
+
7940
8613
  inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
7941
8614
  const char *body, size_t content_length,
7942
8615
  const std::string &content_type) {
8616
+ return Patch(path, headers, body, content_length, content_type, nullptr);
8617
+ }
8618
+
8619
+ inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
8620
+ const char *body, size_t content_length,
8621
+ const std::string &content_type,
8622
+ Progress progress) {
7943
8623
  return send_with_content_provider("PATCH", path, headers, body,
7944
8624
  content_length, nullptr, nullptr,
7945
- content_type);
8625
+ content_type, progress);
7946
8626
  }
7947
8627
 
7948
8628
  inline Result ClientImpl::Patch(const std::string &path,
@@ -7951,12 +8631,26 @@ inline Result ClientImpl::Patch(const std::string &path,
7951
8631
  return Patch(path, Headers(), body, content_type);
7952
8632
  }
7953
8633
 
8634
+ inline Result ClientImpl::Patch(const std::string &path,
8635
+ const std::string &body,
8636
+ const std::string &content_type,
8637
+ Progress progress) {
8638
+ return Patch(path, Headers(), body, content_type, progress);
8639
+ }
8640
+
7954
8641
  inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
7955
8642
  const std::string &body,
7956
8643
  const std::string &content_type) {
8644
+ return Patch(path, headers, body, content_type, nullptr);
8645
+ }
8646
+
8647
+ inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
8648
+ const std::string &body,
8649
+ const std::string &content_type,
8650
+ Progress progress) {
7957
8651
  return send_with_content_provider("PATCH", path, headers, body.data(),
7958
- body.size(), nullptr, nullptr,
7959
- content_type);
8652
+ body.size(), nullptr, nullptr, content_type,
8653
+ progress);
7960
8654
  }
7961
8655
 
7962
8656
  inline Result ClientImpl::Patch(const std::string &path, size_t content_length,
@@ -7978,14 +8672,15 @@ inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
7978
8672
  const std::string &content_type) {
7979
8673
  return send_with_content_provider("PATCH", path, headers, nullptr,
7980
8674
  content_length, std::move(content_provider),
7981
- nullptr, content_type);
8675
+ nullptr, content_type, nullptr);
7982
8676
  }
7983
8677
 
7984
8678
  inline Result ClientImpl::Patch(const std::string &path, const Headers &headers,
7985
8679
  ContentProviderWithoutLength content_provider,
7986
8680
  const std::string &content_type) {
7987
8681
  return send_with_content_provider("PATCH", path, headers, nullptr, 0, nullptr,
7988
- std::move(content_provider), content_type);
8682
+ std::move(content_provider), content_type,
8683
+ nullptr);
7989
8684
  }
7990
8685
 
7991
8686
  inline Result ClientImpl::Delete(const std::string &path) {
@@ -8003,14 +8698,30 @@ inline Result ClientImpl::Delete(const std::string &path, const char *body,
8003
8698
  return Delete(path, Headers(), body, content_length, content_type);
8004
8699
  }
8005
8700
 
8701
+ inline Result ClientImpl::Delete(const std::string &path, const char *body,
8702
+ size_t content_length,
8703
+ const std::string &content_type,
8704
+ Progress progress) {
8705
+ return Delete(path, Headers(), body, content_length, content_type, progress);
8706
+ }
8707
+
8006
8708
  inline Result ClientImpl::Delete(const std::string &path,
8007
8709
  const Headers &headers, const char *body,
8008
8710
  size_t content_length,
8009
8711
  const std::string &content_type) {
8712
+ return Delete(path, headers, body, content_length, content_type, nullptr);
8713
+ }
8714
+
8715
+ inline Result ClientImpl::Delete(const std::string &path,
8716
+ const Headers &headers, const char *body,
8717
+ size_t content_length,
8718
+ const std::string &content_type,
8719
+ Progress progress) {
8010
8720
  Request req;
8011
8721
  req.method = "DELETE";
8012
8722
  req.headers = headers;
8013
8723
  req.path = path;
8724
+ req.progress = progress;
8014
8725
 
8015
8726
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
8016
8727
  req.body.assign(body, content_length);
@@ -8024,6 +8735,14 @@ inline Result ClientImpl::Delete(const std::string &path,
8024
8735
  return Delete(path, Headers(), body.data(), body.size(), content_type);
8025
8736
  }
8026
8737
 
8738
+ inline Result ClientImpl::Delete(const std::string &path,
8739
+ const std::string &body,
8740
+ const std::string &content_type,
8741
+ Progress progress) {
8742
+ return Delete(path, Headers(), body.data(), body.size(), content_type,
8743
+ progress);
8744
+ }
8745
+
8027
8746
  inline Result ClientImpl::Delete(const std::string &path,
8028
8747
  const Headers &headers,
8029
8748
  const std::string &body,
@@ -8031,6 +8750,15 @@ inline Result ClientImpl::Delete(const std::string &path,
8031
8750
  return Delete(path, headers, body.data(), body.size(), content_type);
8032
8751
  }
8033
8752
 
8753
+ inline Result ClientImpl::Delete(const std::string &path,
8754
+ const Headers &headers,
8755
+ const std::string &body,
8756
+ const std::string &content_type,
8757
+ Progress progress) {
8758
+ return Delete(path, headers, body.data(), body.size(), content_type,
8759
+ progress);
8760
+ }
8761
+
8034
8762
  inline Result ClientImpl::Options(const std::string &path) {
8035
8763
  return Options(path, Headers());
8036
8764
  }
@@ -8138,6 +8866,8 @@ inline void ClientImpl::set_address_family(int family) {
8138
8866
 
8139
8867
  inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
8140
8868
 
8869
+ inline void ClientImpl::set_ipv6_v6only(bool on) { ipv6_v6only_ = on; }
8870
+
8141
8871
  inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
8142
8872
  socket_options_ = std::move(socket_options);
8143
8873
  }
@@ -8187,13 +8917,11 @@ inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
8187
8917
  inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert,
8188
8918
  std::size_t size) const {
8189
8919
  auto mem = BIO_new_mem_buf(ca_cert, static_cast<int>(size));
8920
+ auto se = detail::scope_exit([&] { BIO_free_all(mem); });
8190
8921
  if (!mem) { return nullptr; }
8191
8922
 
8192
8923
  auto inf = PEM_X509_INFO_read_bio(mem, nullptr, nullptr, nullptr);
8193
- if (!inf) {
8194
- BIO_free_all(mem);
8195
- return nullptr;
8196
- }
8924
+ if (!inf) { return nullptr; }
8197
8925
 
8198
8926
  auto cts = X509_STORE_new();
8199
8927
  if (cts) {
@@ -8207,13 +8935,21 @@ inline X509_STORE *ClientImpl::create_ca_cert_store(const char *ca_cert,
8207
8935
  }
8208
8936
 
8209
8937
  sk_X509_INFO_pop_free(inf, X509_INFO_free);
8210
- BIO_free_all(mem);
8211
8938
  return cts;
8212
8939
  }
8213
8940
 
8214
8941
  inline void ClientImpl::enable_server_certificate_verification(bool enabled) {
8215
8942
  server_certificate_verification_ = enabled;
8216
8943
  }
8944
+
8945
+ inline void ClientImpl::enable_server_hostname_verification(bool enabled) {
8946
+ server_hostname_verification_ = enabled;
8947
+ }
8948
+
8949
+ inline void ClientImpl::set_server_certificate_verifier(
8950
+ std::function<bool(SSL *ssl)> verifier) {
8951
+ server_certificate_verifier_ = verifier;
8952
+ }
8217
8953
  #endif
8218
8954
 
8219
8955
  inline void ClientImpl::set_logger(Logger logger) {
@@ -8257,13 +8993,30 @@ inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
8257
8993
  return ssl;
8258
8994
  }
8259
8995
 
8260
- inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
8996
+ inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, socket_t sock,
8261
8997
  bool shutdown_gracefully) {
8262
8998
  // sometimes we may want to skip this to try to avoid SIGPIPE if we know
8263
8999
  // the remote has closed the network connection
8264
9000
  // Note that it is not always possible to avoid SIGPIPE, this is merely a
8265
9001
  // best-efforts.
8266
- if (shutdown_gracefully) { SSL_shutdown(ssl); }
9002
+ if (shutdown_gracefully) {
9003
+ #ifdef _WIN32
9004
+ (void)(sock);
9005
+ SSL_shutdown(ssl);
9006
+ #else
9007
+ timeval tv;
9008
+ tv.tv_sec = 1;
9009
+ tv.tv_usec = 0;
9010
+ setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
9011
+ reinterpret_cast<const void *>(&tv), sizeof(tv));
9012
+
9013
+ auto ret = SSL_shutdown(ssl);
9014
+ while (ret == 0) {
9015
+ std::this_thread::sleep_for(std::chrono::milliseconds{100});
9016
+ ret = SSL_shutdown(ssl);
9017
+ }
9018
+ #endif
9019
+ }
8267
9020
 
8268
9021
  std::lock_guard<std::mutex> guard(ctx_mutex);
8269
9022
  SSL_free(ssl);
@@ -8366,7 +9119,7 @@ inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
8366
9119
  if (SSL_pending(ssl_) > 0) {
8367
9120
  return SSL_read(ssl_, ptr, static_cast<int>(size));
8368
9121
  } else if (is_readable()) {
8369
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
9122
+ std::this_thread::sleep_for(std::chrono::microseconds{10});
8370
9123
  ret = SSL_read(ssl_, ptr, static_cast<int>(size));
8371
9124
  if (ret >= 0) { return ret; }
8372
9125
  err = SSL_get_error(ssl_, ret);
@@ -8397,7 +9150,7 @@ inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
8397
9150
  while (--n >= 0 && err == SSL_ERROR_WANT_WRITE) {
8398
9151
  #endif
8399
9152
  if (is_writable()) {
8400
- std::this_thread::sleep_for(std::chrono::milliseconds(1));
9153
+ std::this_thread::sleep_for(std::chrono::microseconds{10});
8401
9154
  ret = SSL_write(ssl_, ptr, static_cast<int>(handle_size));
8402
9155
  if (ret >= 0) { return ret; }
8403
9156
  err = SSL_get_error(ssl_, ret);
@@ -8439,7 +9192,7 @@ inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
8439
9192
  SSL_OP_NO_COMPRESSION |
8440
9193
  SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
8441
9194
 
8442
- SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION);
9195
+ SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
8443
9196
 
8444
9197
  if (private_key_password != nullptr && (private_key_password[0] != '\0')) {
8445
9198
  SSL_CTX_set_default_passwd_cb_userdata(
@@ -8449,7 +9202,8 @@ inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
8449
9202
 
8450
9203
  if (SSL_CTX_use_certificate_chain_file(ctx_, cert_path) != 1 ||
8451
9204
  SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) !=
8452
- 1) {
9205
+ 1 ||
9206
+ SSL_CTX_check_private_key(ctx_) != 1) {
8453
9207
  SSL_CTX_free(ctx_);
8454
9208
  ctx_ = nullptr;
8455
9209
  } else if (client_ca_cert_file_path || client_ca_cert_dir_path) {
@@ -8471,7 +9225,7 @@ inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
8471
9225
  SSL_OP_NO_COMPRESSION |
8472
9226
  SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
8473
9227
 
8474
- SSL_CTX_set_min_proto_version(ctx_, TLS1_1_VERSION);
9228
+ SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
8475
9229
 
8476
9230
  if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
8477
9231
  SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
@@ -8505,6 +9259,19 @@ inline bool SSLServer::is_valid() const { return ctx_; }
8505
9259
 
8506
9260
  inline SSL_CTX *SSLServer::ssl_context() const { return ctx_; }
8507
9261
 
9262
+ inline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key,
9263
+ X509_STORE *client_ca_cert_store) {
9264
+
9265
+ std::lock_guard<std::mutex> guard(ctx_mutex_);
9266
+
9267
+ SSL_CTX_use_certificate(ctx_, cert);
9268
+ SSL_CTX_use_PrivateKey(ctx_, private_key);
9269
+
9270
+ if (client_ca_cert_store != nullptr) {
9271
+ SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
9272
+ }
9273
+ }
9274
+
8508
9275
  inline bool SSLServer::process_and_close_socket(socket_t sock) {
8509
9276
  auto ssl = detail::ssl_new(
8510
9277
  sock, ctx_, ctx_mutex_,
@@ -8516,20 +9283,29 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
8516
9283
 
8517
9284
  auto ret = false;
8518
9285
  if (ssl) {
9286
+ std::string remote_addr;
9287
+ int remote_port = 0;
9288
+ detail::get_remote_ip_and_port(sock, remote_addr, remote_port);
9289
+
9290
+ std::string local_addr;
9291
+ int local_port = 0;
9292
+ detail::get_local_ip_and_port(sock, local_addr, local_port);
9293
+
8519
9294
  ret = detail::process_server_socket_ssl(
8520
9295
  svr_sock_, ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
8521
9296
  read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
8522
9297
  write_timeout_usec_,
8523
- [this, ssl](Stream &strm, bool close_connection,
8524
- bool &connection_closed) {
8525
- return process_request(strm, close_connection, connection_closed,
9298
+ [&](Stream &strm, bool close_connection, bool &connection_closed) {
9299
+ return process_request(strm, remote_addr, remote_port, local_addr,
9300
+ local_port, close_connection,
9301
+ connection_closed,
8526
9302
  [&](Request &req) { req.ssl = ssl; });
8527
9303
  });
8528
9304
 
8529
9305
  // Shutdown gracefully if the result seemed successful, non-gracefully if
8530
9306
  // the connection appeared to be closed.
8531
9307
  const bool shutdown_gracefully = ret;
8532
- detail::ssl_delete(ctx_mutex_, ssl, shutdown_gracefully);
9308
+ detail::ssl_delete(ctx_mutex_, ssl, sock, shutdown_gracefully);
8533
9309
  }
8534
9310
 
8535
9311
  detail::shutdown_socket(sock);
@@ -8551,6 +9327,8 @@ inline SSLClient::SSLClient(const std::string &host, int port,
8551
9327
  : ClientImpl(host, port, client_cert_path, client_key_path) {
8552
9328
  ctx_ = SSL_CTX_new(TLS_client_method());
8553
9329
 
9330
+ SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
9331
+
8554
9332
  detail::split(&host_[0], &host_[host_.size()], '.',
8555
9333
  [&](const char *b, const char *e) {
8556
9334
  host_components_.emplace_back(b, e);
@@ -8758,36 +9536,47 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
8758
9536
  }
8759
9537
 
8760
9538
  if (server_certificate_verification_) {
8761
- verify_result_ = SSL_get_verify_result(ssl2);
9539
+ if (server_certificate_verifier_) {
9540
+ if (!server_certificate_verifier_(ssl2)) {
9541
+ error = Error::SSLServerVerification;
9542
+ return false;
9543
+ }
9544
+ } else {
9545
+ verify_result_ = SSL_get_verify_result(ssl2);
8762
9546
 
8763
- if (verify_result_ != X509_V_OK) {
8764
- error = Error::SSLServerVerification;
8765
- return false;
8766
- }
9547
+ if (verify_result_ != X509_V_OK) {
9548
+ error = Error::SSLServerVerification;
9549
+ return false;
9550
+ }
8767
9551
 
8768
- auto server_cert = SSL_get1_peer_certificate(ssl2);
9552
+ auto server_cert = SSL_get1_peer_certificate(ssl2);
9553
+ auto se = detail::scope_exit([&] { X509_free(server_cert); });
8769
9554
 
8770
- if (server_cert == nullptr) {
8771
- error = Error::SSLServerVerification;
8772
- return false;
8773
- }
9555
+ if (server_cert == nullptr) {
9556
+ error = Error::SSLServerVerification;
9557
+ return false;
9558
+ }
8774
9559
 
8775
- if (!verify_host(server_cert)) {
8776
- X509_free(server_cert);
8777
- error = Error::SSLServerVerification;
8778
- return false;
9560
+ if (server_hostname_verification_) {
9561
+ if (!verify_host(server_cert)) {
9562
+ error = Error::SSLServerHostnameVerification;
9563
+ return false;
9564
+ }
9565
+ }
8779
9566
  }
8780
- X509_free(server_cert);
8781
9567
  }
8782
9568
 
8783
9569
  return true;
8784
9570
  },
8785
9571
  [&](SSL *ssl2) {
9572
+ #if defined(OPENSSL_IS_BORINGSSL)
9573
+ SSL_set_tlsext_host_name(ssl2, host_.c_str());
9574
+ #else
8786
9575
  // NOTE: Direct call instead of using the OpenSSL macro to suppress
8787
9576
  // -Wold-style-cast warning
8788
- // SSL_set_tlsext_host_name(ssl2, host_.c_str());
8789
9577
  SSL_ctrl(ssl2, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name,
8790
9578
  static_cast<void *>(const_cast<char *>(host_.c_str())));
9579
+ #endif
8791
9580
  return true;
8792
9581
  });
8793
9582
 
@@ -8812,7 +9601,8 @@ inline void SSLClient::shutdown_ssl_impl(Socket &socket,
8812
9601
  return;
8813
9602
  }
8814
9603
  if (socket.ssl) {
8815
- detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully);
9604
+ detail::ssl_delete(ctx_mutex_, socket.ssl, socket.sock,
9605
+ shutdown_gracefully);
8816
9606
  socket.ssl = nullptr;
8817
9607
  }
8818
9608
  assert(socket.ssl == nullptr);
@@ -8861,8 +9651,8 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
8861
9651
 
8862
9652
  auto type = GEN_DNS;
8863
9653
 
8864
- struct in6_addr addr6 {};
8865
- struct in_addr addr {};
9654
+ struct in6_addr addr6{};
9655
+ struct in_addr addr{};
8866
9656
  size_t addr_len = 0;
8867
9657
 
8868
9658
  #ifndef __MINGW32__
@@ -8965,7 +9755,7 @@ inline Client::Client(const std::string &scheme_host_port,
8965
9755
  const std::string &client_cert_path,
8966
9756
  const std::string &client_key_path) {
8967
9757
  const static std::regex re(
8968
- R"((?:([a-z]+):\/\/)?(?:\[([\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
9758
+ R"((?:([a-z]+):\/\/)?(?:\[([a-fA-F\d:]+)\]|([^:/?#]+))(?::(\d+))?)");
8969
9759
 
8970
9760
  std::smatch m;
8971
9761
  if (std::regex_match(scheme_host_port, m, re)) {
@@ -9002,10 +9792,12 @@ inline Client::Client(const std::string &scheme_host_port,
9002
9792
  client_key_path);
9003
9793
  }
9004
9794
  } else {
9795
+ // NOTE: Update TEST(UniversalClientImplTest, Ipv6LiteralAddress)
9796
+ // if port param below changes.
9005
9797
  cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
9006
9798
  client_cert_path, client_key_path);
9007
9799
  }
9008
- }
9800
+ } // namespace detail
9009
9801
 
9010
9802
  inline Client::Client(const std::string &host, int port)
9011
9803
  : cli_(detail::make_unique<ClientImpl>(host, port)) {}
@@ -9111,15 +9903,30 @@ inline Result Client::Post(const std::string &path, const Headers &headers,
9111
9903
  const std::string &content_type) {
9112
9904
  return cli_->Post(path, headers, body, content_length, content_type);
9113
9905
  }
9906
+ inline Result Client::Post(const std::string &path, const Headers &headers,
9907
+ const char *body, size_t content_length,
9908
+ const std::string &content_type, Progress progress) {
9909
+ return cli_->Post(path, headers, body, content_length, content_type,
9910
+ progress);
9911
+ }
9114
9912
  inline Result Client::Post(const std::string &path, const std::string &body,
9115
9913
  const std::string &content_type) {
9116
9914
  return cli_->Post(path, body, content_type);
9117
9915
  }
9916
+ inline Result Client::Post(const std::string &path, const std::string &body,
9917
+ const std::string &content_type, Progress progress) {
9918
+ return cli_->Post(path, body, content_type, progress);
9919
+ }
9118
9920
  inline Result Client::Post(const std::string &path, const Headers &headers,
9119
9921
  const std::string &body,
9120
9922
  const std::string &content_type) {
9121
9923
  return cli_->Post(path, headers, body, content_type);
9122
9924
  }
9925
+ inline Result Client::Post(const std::string &path, const Headers &headers,
9926
+ const std::string &body,
9927
+ const std::string &content_type, Progress progress) {
9928
+ return cli_->Post(path, headers, body, content_type, progress);
9929
+ }
9123
9930
  inline Result Client::Post(const std::string &path, size_t content_length,
9124
9931
  ContentProvider content_provider,
9125
9932
  const std::string &content_type) {
@@ -9150,6 +9957,10 @@ inline Result Client::Post(const std::string &path, const Headers &headers,
9150
9957
  const Params &params) {
9151
9958
  return cli_->Post(path, headers, params);
9152
9959
  }
9960
+ inline Result Client::Post(const std::string &path, const Headers &headers,
9961
+ const Params &params, Progress progress) {
9962
+ return cli_->Post(path, headers, params, progress);
9963
+ }
9153
9964
  inline Result Client::Post(const std::string &path,
9154
9965
  const MultipartFormDataItems &items) {
9155
9966
  return cli_->Post(path, items);
@@ -9180,15 +9991,29 @@ inline Result Client::Put(const std::string &path, const Headers &headers,
9180
9991
  const std::string &content_type) {
9181
9992
  return cli_->Put(path, headers, body, content_length, content_type);
9182
9993
  }
9994
+ inline Result Client::Put(const std::string &path, const Headers &headers,
9995
+ const char *body, size_t content_length,
9996
+ const std::string &content_type, Progress progress) {
9997
+ return cli_->Put(path, headers, body, content_length, content_type, progress);
9998
+ }
9183
9999
  inline Result Client::Put(const std::string &path, const std::string &body,
9184
10000
  const std::string &content_type) {
9185
10001
  return cli_->Put(path, body, content_type);
9186
10002
  }
10003
+ inline Result Client::Put(const std::string &path, const std::string &body,
10004
+ const std::string &content_type, Progress progress) {
10005
+ return cli_->Put(path, body, content_type, progress);
10006
+ }
9187
10007
  inline Result Client::Put(const std::string &path, const Headers &headers,
9188
10008
  const std::string &body,
9189
10009
  const std::string &content_type) {
9190
10010
  return cli_->Put(path, headers, body, content_type);
9191
10011
  }
10012
+ inline Result Client::Put(const std::string &path, const Headers &headers,
10013
+ const std::string &body,
10014
+ const std::string &content_type, Progress progress) {
10015
+ return cli_->Put(path, headers, body, content_type, progress);
10016
+ }
9192
10017
  inline Result Client::Put(const std::string &path, size_t content_length,
9193
10018
  ContentProvider content_provider,
9194
10019
  const std::string &content_type) {
@@ -9219,6 +10044,10 @@ inline Result Client::Put(const std::string &path, const Headers &headers,
9219
10044
  const Params &params) {
9220
10045
  return cli_->Put(path, headers, params);
9221
10046
  }
10047
+ inline Result Client::Put(const std::string &path, const Headers &headers,
10048
+ const Params &params, Progress progress) {
10049
+ return cli_->Put(path, headers, params, progress);
10050
+ }
9222
10051
  inline Result Client::Put(const std::string &path,
9223
10052
  const MultipartFormDataItems &items) {
9224
10053
  return cli_->Put(path, items);
@@ -9246,20 +10075,44 @@ inline Result Client::Patch(const std::string &path, const char *body,
9246
10075
  const std::string &content_type) {
9247
10076
  return cli_->Patch(path, body, content_length, content_type);
9248
10077
  }
10078
+ inline Result Client::Patch(const std::string &path, const char *body,
10079
+ size_t content_length,
10080
+ const std::string &content_type,
10081
+ Progress progress) {
10082
+ return cli_->Patch(path, body, content_length, content_type, progress);
10083
+ }
9249
10084
  inline Result Client::Patch(const std::string &path, const Headers &headers,
9250
10085
  const char *body, size_t content_length,
9251
10086
  const std::string &content_type) {
9252
10087
  return cli_->Patch(path, headers, body, content_length, content_type);
9253
10088
  }
10089
+ inline Result Client::Patch(const std::string &path, const Headers &headers,
10090
+ const char *body, size_t content_length,
10091
+ const std::string &content_type,
10092
+ Progress progress) {
10093
+ return cli_->Patch(path, headers, body, content_length, content_type,
10094
+ progress);
10095
+ }
9254
10096
  inline Result Client::Patch(const std::string &path, const std::string &body,
9255
10097
  const std::string &content_type) {
9256
10098
  return cli_->Patch(path, body, content_type);
9257
10099
  }
10100
+ inline Result Client::Patch(const std::string &path, const std::string &body,
10101
+ const std::string &content_type,
10102
+ Progress progress) {
10103
+ return cli_->Patch(path, body, content_type, progress);
10104
+ }
9258
10105
  inline Result Client::Patch(const std::string &path, const Headers &headers,
9259
10106
  const std::string &body,
9260
10107
  const std::string &content_type) {
9261
10108
  return cli_->Patch(path, headers, body, content_type);
9262
10109
  }
10110
+ inline Result Client::Patch(const std::string &path, const Headers &headers,
10111
+ const std::string &body,
10112
+ const std::string &content_type,
10113
+ Progress progress) {
10114
+ return cli_->Patch(path, headers, body, content_type, progress);
10115
+ }
9263
10116
  inline Result Client::Patch(const std::string &path, size_t content_length,
9264
10117
  ContentProvider content_provider,
9265
10118
  const std::string &content_type) {
@@ -9294,20 +10147,44 @@ inline Result Client::Delete(const std::string &path, const char *body,
9294
10147
  const std::string &content_type) {
9295
10148
  return cli_->Delete(path, body, content_length, content_type);
9296
10149
  }
10150
+ inline Result Client::Delete(const std::string &path, const char *body,
10151
+ size_t content_length,
10152
+ const std::string &content_type,
10153
+ Progress progress) {
10154
+ return cli_->Delete(path, body, content_length, content_type, progress);
10155
+ }
9297
10156
  inline Result Client::Delete(const std::string &path, const Headers &headers,
9298
10157
  const char *body, size_t content_length,
9299
10158
  const std::string &content_type) {
9300
10159
  return cli_->Delete(path, headers, body, content_length, content_type);
9301
10160
  }
10161
+ inline Result Client::Delete(const std::string &path, const Headers &headers,
10162
+ const char *body, size_t content_length,
10163
+ const std::string &content_type,
10164
+ Progress progress) {
10165
+ return cli_->Delete(path, headers, body, content_length, content_type,
10166
+ progress);
10167
+ }
9302
10168
  inline Result Client::Delete(const std::string &path, const std::string &body,
9303
10169
  const std::string &content_type) {
9304
10170
  return cli_->Delete(path, body, content_type);
9305
10171
  }
10172
+ inline Result Client::Delete(const std::string &path, const std::string &body,
10173
+ const std::string &content_type,
10174
+ Progress progress) {
10175
+ return cli_->Delete(path, body, content_type, progress);
10176
+ }
9306
10177
  inline Result Client::Delete(const std::string &path, const Headers &headers,
9307
10178
  const std::string &body,
9308
10179
  const std::string &content_type) {
9309
10180
  return cli_->Delete(path, headers, body, content_type);
9310
10181
  }
10182
+ inline Result Client::Delete(const std::string &path, const Headers &headers,
10183
+ const std::string &body,
10184
+ const std::string &content_type,
10185
+ Progress progress) {
10186
+ return cli_->Delete(path, headers, body, content_type, progress);
10187
+ }
9311
10188
  inline Result Client::Options(const std::string &path) {
9312
10189
  return cli_->Options(path);
9313
10190
  }
@@ -9417,6 +10294,15 @@ inline void Client::set_proxy_digest_auth(const std::string &username,
9417
10294
  inline void Client::enable_server_certificate_verification(bool enabled) {
9418
10295
  cli_->enable_server_certificate_verification(enabled);
9419
10296
  }
10297
+
10298
+ inline void Client::enable_server_hostname_verification(bool enabled) {
10299
+ cli_->enable_server_hostname_verification(enabled);
10300
+ }
10301
+
10302
+ inline void Client::set_server_certificate_verifier(
10303
+ std::function<bool(SSL *ssl)> verifier) {
10304
+ cli_->set_server_certificate_verifier(verifier);
10305
+ }
9420
10306
  #endif
9421
10307
 
9422
10308
  inline void Client::set_logger(Logger logger) {