@fugood/llama.node 0.3.11 → 0.3.13

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 (77) hide show
  1. package/bin/darwin/arm64/llama-node.node +0 -0
  2. package/bin/darwin/x64/llama-node.node +0 -0
  3. package/bin/linux/arm64/llama-node.node +0 -0
  4. package/bin/linux/x64/llama-node.node +0 -0
  5. package/bin/linux-cuda/arm64/llama-node.node +0 -0
  6. package/bin/linux-cuda/x64/llama-node.node +0 -0
  7. package/bin/linux-vulkan/arm64/llama-node.node +0 -0
  8. package/bin/linux-vulkan/x64/llama-node.node +0 -0
  9. package/bin/win32/arm64/llama-node.node +0 -0
  10. package/bin/win32/arm64/node.lib +0 -0
  11. package/bin/win32/x64/llama-node.node +0 -0
  12. package/bin/win32/x64/node.lib +0 -0
  13. package/bin/win32-vulkan/arm64/llama-node.node +0 -0
  14. package/bin/win32-vulkan/arm64/node.lib +0 -0
  15. package/bin/win32-vulkan/x64/llama-node.node +0 -0
  16. package/bin/win32-vulkan/x64/node.lib +0 -0
  17. package/lib/binding.ts +1 -0
  18. package/lib/index.js +26 -20
  19. package/lib/index.ts +32 -28
  20. package/package.json +1 -1
  21. package/src/LlamaCompletionWorker.cpp +14 -0
  22. package/src/LlamaContext.cpp +13 -4
  23. package/src/llama.cpp/.github/workflows/build.yml +35 -3
  24. package/src/llama.cpp/.github/workflows/docker.yml +2 -0
  25. package/src/llama.cpp/.github/workflows/labeler.yml +1 -1
  26. package/src/llama.cpp/common/CMakeLists.txt +20 -3
  27. package/src/llama.cpp/common/arg.cpp +180 -3
  28. package/src/llama.cpp/common/chat-template.hpp +21 -7
  29. package/src/llama.cpp/common/chat.cpp +220 -101
  30. package/src/llama.cpp/common/chat.hpp +3 -0
  31. package/src/llama.cpp/common/common.h +15 -7
  32. package/src/llama.cpp/common/llguidance.cpp +3 -3
  33. package/src/llama.cpp/common/log.cpp +1 -0
  34. package/src/llama.cpp/common/log.h +2 -1
  35. package/src/llama.cpp/common/minja.hpp +24 -9
  36. package/src/llama.cpp/common/sampling.cpp +52 -46
  37. package/src/llama.cpp/common/speculative.h +1 -1
  38. package/src/llama.cpp/docs/build.md +2 -2
  39. package/src/llama.cpp/examples/imatrix/imatrix.cpp +2 -1
  40. package/src/llama.cpp/examples/llama-bench/llama-bench.cpp +6 -5
  41. package/src/llama.cpp/examples/llama.android/llama/src/main/cpp/CMakeLists.txt +1 -1
  42. package/src/llama.cpp/examples/perplexity/perplexity.cpp +1 -0
  43. package/src/llama.cpp/examples/run/run.cpp +5 -12
  44. package/src/llama.cpp/examples/server/CMakeLists.txt +1 -1
  45. package/src/llama.cpp/examples/server/httplib.h +381 -292
  46. package/src/llama.cpp/examples/server/server.cpp +58 -47
  47. package/src/llama.cpp/examples/server/utils.hpp +7 -5
  48. package/src/llama.cpp/ggml/include/ggml-cpu.h +1 -1
  49. package/src/llama.cpp/ggml/include/ggml-metal.h +1 -1
  50. package/src/llama.cpp/ggml/include/ggml-vulkan.h +0 -2
  51. package/src/llama.cpp/ggml/include/ggml.h +1 -1
  52. package/src/llama.cpp/ggml/src/ggml-common.h +0 -2
  53. package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu-impl.h +6 -12
  54. package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu-quants.c +852 -268
  55. package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu.c +200 -107
  56. package/src/llama.cpp/ggml/src/ggml-cpu/ggml-cpu.cpp +2 -5
  57. package/src/llama.cpp/ggml/src/ggml-cpu/llamafile/sgemm.cpp +9 -8
  58. package/src/llama.cpp/ggml/src/ggml-cuda/CMakeLists.txt +2 -2
  59. package/src/llama.cpp/ggml/src/ggml-opencl/ggml-opencl.cpp +26 -4
  60. package/src/llama.cpp/ggml/src/ggml-sycl/ggml-sycl.cpp +6 -7
  61. package/src/llama.cpp/ggml/src/ggml-vulkan/ggml-vulkan.cpp +812 -569
  62. package/src/llama.cpp/ggml/src/ggml-vulkan/vulkan-shaders/vulkan-shaders-gen.cpp +25 -1
  63. package/src/llama.cpp/ggml/src/ggml.c +1 -1
  64. package/src/llama.cpp/include/llama.h +14 -10
  65. package/src/llama.cpp/src/llama-grammar.cpp +1 -1
  66. package/src/llama.cpp/src/llama-grammar.h +1 -1
  67. package/src/llama.cpp/src/llama-impl.h +6 -6
  68. package/src/llama.cpp/src/llama-kv-cache.h +1 -1
  69. package/src/llama.cpp/src/llama-mmap.h +1 -0
  70. package/src/llama.cpp/src/llama-model.cpp +1 -1
  71. package/src/llama.cpp/src/llama-sampling.cpp +131 -57
  72. package/src/llama.cpp/src/llama.cpp +7 -5
  73. package/src/llama.cpp/src/unicode.cpp +9 -2
  74. package/src/llama.cpp/tests/test-backend-ops.cpp +5 -5
  75. package/src/llama.cpp/tests/test-chat.cpp +237 -69
  76. package/src/llama.cpp/tests/test-gguf.cpp +4 -4
  77. package/src/llama.cpp/tests/test-sampling.cpp +15 -0
@@ -1,14 +1,14 @@
1
1
  //
2
2
  // httplib.h
3
3
  //
4
- // Copyright (c) 2024 Yuji Hirose. All rights reserved.
4
+ // Copyright (c) 2025 Yuji Hirose. All rights reserved.
5
5
  // MIT License
6
6
  //
7
7
 
8
8
  #ifndef CPPHTTPLIB_HTTPLIB_H
9
9
  #define CPPHTTPLIB_HTTPLIB_H
10
10
 
11
- #define CPPHTTPLIB_VERSION "0.18.5"
11
+ #define CPPHTTPLIB_VERSION "0.19.0"
12
12
 
13
13
  /*
14
14
  * Configuration
@@ -66,6 +66,10 @@
66
66
  #define CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND 0
67
67
  #endif
68
68
 
69
+ #ifndef CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND
70
+ #define CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND 0
71
+ #endif
72
+
69
73
  #ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
70
74
  #define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
71
75
  #endif
@@ -189,6 +193,7 @@ using ssize_t = long;
189
193
  #endif
190
194
 
191
195
  using socket_t = SOCKET;
196
+ using socklen_t = int;
192
197
  #ifdef CPPHTTPLIB_USE_POLL
193
198
  #define poll(fds, nfds, timeout) WSAPoll(fds, nfds, timeout)
194
199
  #endif
@@ -218,7 +223,9 @@ using socket_t = SOCKET;
218
223
  #include <csignal>
219
224
  #include <pthread.h>
220
225
  #include <sys/mman.h>
226
+ #ifndef __VMS
221
227
  #include <sys/select.h>
228
+ #endif
222
229
  #include <sys/socket.h>
223
230
  #include <sys/un.h>
224
231
  #include <unistd.h>
@@ -662,6 +669,8 @@ struct Request {
662
669
  ContentProvider content_provider_;
663
670
  bool is_chunked_content_provider_ = false;
664
671
  size_t authorization_count_ = 0;
672
+ std::chrono::time_point<std::chrono::steady_clock> start_time_ =
673
+ std::chrono::steady_clock::time_point::min();
665
674
  };
666
675
 
667
676
  struct Response {
@@ -735,6 +744,8 @@ public:
735
744
  virtual void get_local_ip_and_port(std::string &ip, int &port) const = 0;
736
745
  virtual socket_t socket() const = 0;
737
746
 
747
+ virtual time_t duration() const = 0;
748
+
738
749
  ssize_t write(const char *ptr);
739
750
  ssize_t write(const std::string &s);
740
751
  };
@@ -838,6 +849,16 @@ using Logger = std::function<void(const Request &, const Response &)>;
838
849
 
839
850
  using SocketOptions = std::function<void(socket_t sock)>;
840
851
 
852
+ namespace detail {
853
+
854
+ bool set_socket_opt_impl(socket_t sock, int level, int optname,
855
+ const void *optval, socklen_t optlen);
856
+ bool set_socket_opt(socket_t sock, int level, int optname, int opt);
857
+ bool set_socket_opt_time(socket_t sock, int level, int optname, time_t sec,
858
+ time_t usec);
859
+
860
+ } // namespace detail
861
+
841
862
  void default_socket_options(socket_t sock);
842
863
 
843
864
  const char *status_message(int status);
@@ -1421,6 +1442,10 @@ public:
1421
1442
  template <class Rep, class Period>
1422
1443
  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1423
1444
 
1445
+ void set_max_timeout(time_t msec);
1446
+ template <class Rep, class Period>
1447
+ void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
1448
+
1424
1449
  void set_basic_auth(const std::string &username, const std::string &password);
1425
1450
  void set_bearer_token_auth(const std::string &token);
1426
1451
  #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -1529,6 +1554,7 @@ protected:
1529
1554
  time_t read_timeout_usec_ = CPPHTTPLIB_CLIENT_READ_TIMEOUT_USECOND;
1530
1555
  time_t write_timeout_sec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_SECOND;
1531
1556
  time_t write_timeout_usec_ = CPPHTTPLIB_CLIENT_WRITE_TIMEOUT_USECOND;
1557
+ time_t max_timeout_msec_ = CPPHTTPLIB_CLIENT_MAX_TIMEOUT_MSECOND;
1532
1558
 
1533
1559
  std::string basic_auth_username_;
1534
1560
  std::string basic_auth_password_;
@@ -1583,9 +1609,6 @@ private:
1583
1609
  bool send_(Request &req, Response &res, Error &error);
1584
1610
  Result send_(Request &&req);
1585
1611
 
1586
- #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
1587
- bool is_ssl_peer_could_be_closed(SSL *ssl) const;
1588
- #endif
1589
1612
  socket_t create_client_socket(Error &error) const;
1590
1613
  bool read_response_line(Stream &strm, const Request &req,
1591
1614
  Response &res) const;
@@ -1611,8 +1634,10 @@ private:
1611
1634
 
1612
1635
  std::string adjust_host_string(const std::string &host) const;
1613
1636
 
1614
- virtual bool process_socket(const Socket &socket,
1615
- std::function<bool(Stream &strm)> callback);
1637
+ virtual bool
1638
+ process_socket(const Socket &socket,
1639
+ std::chrono::time_point<std::chrono::steady_clock> start_time,
1640
+ std::function<bool(Stream &strm)> callback);
1616
1641
  virtual bool is_ssl() const;
1617
1642
  };
1618
1643
 
@@ -1854,6 +1879,10 @@ public:
1854
1879
  template <class Rep, class Period>
1855
1880
  void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
1856
1881
 
1882
+ void set_max_timeout(time_t msec);
1883
+ template <class Rep, class Period>
1884
+ void set_max_timeout(const std::chrono::duration<Rep, Period> &duration);
1885
+
1857
1886
  void set_basic_auth(const std::string &username, const std::string &password);
1858
1887
  void set_bearer_token_auth(const std::string &token);
1859
1888
  #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -1971,12 +2000,16 @@ private:
1971
2000
  void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
1972
2001
  void shutdown_ssl_impl(Socket &socket, bool shutdown_gracefully);
1973
2002
 
1974
- bool process_socket(const Socket &socket,
1975
- std::function<bool(Stream &strm)> callback) override;
2003
+ bool
2004
+ process_socket(const Socket &socket,
2005
+ std::chrono::time_point<std::chrono::steady_clock> start_time,
2006
+ std::function<bool(Stream &strm)> callback) override;
1976
2007
  bool is_ssl() const override;
1977
2008
 
1978
- bool connect_with_proxy(Socket &sock, Response &res, bool &success,
1979
- Error &error);
2009
+ bool connect_with_proxy(
2010
+ Socket &sock,
2011
+ std::chrono::time_point<std::chrono::steady_clock> start_time,
2012
+ Response &res, bool &success, Error &error);
1980
2013
  bool initialize_ssl(Socket &socket, Error &error);
1981
2014
 
1982
2015
  bool load_certs();
@@ -2053,20 +2086,45 @@ inline uint64_t Response::get_header_value_u64(const std::string &key,
2053
2086
  return detail::get_header_value_u64(headers, key, def, id);
2054
2087
  }
2055
2088
 
2056
- inline void default_socket_options(socket_t sock) {
2057
- int opt = 1;
2089
+ namespace detail {
2090
+
2091
+ inline bool set_socket_opt_impl(socket_t sock, int level, int optname,
2092
+ const void *optval, socklen_t optlen) {
2093
+ return setsockopt(sock, level, optname,
2058
2094
  #ifdef _WIN32
2059
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2060
- reinterpret_cast<const char *>(&opt), sizeof(opt));
2095
+ reinterpret_cast<const char *>(optval),
2061
2096
  #else
2062
- #ifdef SO_REUSEPORT
2063
- setsockopt(sock, SOL_SOCKET, SO_REUSEPORT,
2064
- reinterpret_cast<const void *>(&opt), sizeof(opt));
2097
+ optval,
2098
+ #endif
2099
+ optlen) == 0;
2100
+ }
2101
+
2102
+ inline bool set_socket_opt(socket_t sock, int level, int optname, int optval) {
2103
+ return set_socket_opt_impl(sock, level, optname, &optval, sizeof(optval));
2104
+ }
2105
+
2106
+ inline bool set_socket_opt_time(socket_t sock, int level, int optname,
2107
+ time_t sec, time_t usec) {
2108
+ #ifdef _WIN32
2109
+ auto timeout = static_cast<uint32_t>(sec * 1000 + usec / 1000);
2065
2110
  #else
2066
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
2067
- reinterpret_cast<const void *>(&opt), sizeof(opt));
2111
+ timeval timeout;
2112
+ timeout.tv_sec = static_cast<long>(sec);
2113
+ timeout.tv_usec = static_cast<decltype(timeout.tv_usec)>(usec);
2068
2114
  #endif
2115
+ return set_socket_opt_impl(sock, level, optname, &timeout, sizeof(timeout));
2116
+ }
2117
+
2118
+ } // namespace detail
2119
+
2120
+ inline void default_socket_options(socket_t sock) {
2121
+ detail::set_socket_opt(sock, SOL_SOCKET,
2122
+ #ifdef SO_REUSEPORT
2123
+ SO_REUSEPORT,
2124
+ #else
2125
+ SO_REUSEADDR,
2069
2126
  #endif
2127
+ 1);
2070
2128
  }
2071
2129
 
2072
2130
  inline const char *status_message(int status) {
@@ -2238,6 +2296,14 @@ inline void ClientImpl::set_write_timeout(
2238
2296
  duration, [&](time_t sec, time_t usec) { set_write_timeout(sec, usec); });
2239
2297
  }
2240
2298
 
2299
+ template <class Rep, class Period>
2300
+ inline void ClientImpl::set_max_timeout(
2301
+ const std::chrono::duration<Rep, Period> &duration) {
2302
+ auto msec =
2303
+ std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
2304
+ set_max_timeout(msec);
2305
+ }
2306
+
2241
2307
  template <class Rep, class Period>
2242
2308
  inline void Client::set_connection_timeout(
2243
2309
  const std::chrono::duration<Rep, Period> &duration) {
@@ -2256,6 +2322,12 @@ Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
2256
2322
  cli_->set_write_timeout(duration);
2257
2323
  }
2258
2324
 
2325
+ template <class Rep, class Period>
2326
+ inline void
2327
+ Client::set_max_timeout(const std::chrono::duration<Rep, Period> &duration) {
2328
+ cli_->set_max_timeout(duration);
2329
+ }
2330
+
2259
2331
  /*
2260
2332
  * Forward declarations and types that will be part of the .h file if split into
2261
2333
  * .h + .cc.
@@ -2330,10 +2402,12 @@ void split(const char *b, const char *e, char d,
2330
2402
  void split(const char *b, const char *e, char d, size_t m,
2331
2403
  std::function<void(const char *, const char *)> fn);
2332
2404
 
2333
- bool process_client_socket(socket_t sock, time_t read_timeout_sec,
2334
- time_t read_timeout_usec, time_t write_timeout_sec,
2335
- time_t write_timeout_usec,
2336
- std::function<bool(Stream &)> callback);
2405
+ bool process_client_socket(
2406
+ socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
2407
+ time_t write_timeout_sec, time_t write_timeout_usec,
2408
+ time_t max_timeout_msec,
2409
+ std::chrono::time_point<std::chrono::steady_clock> start_time,
2410
+ std::function<bool(Stream &)> callback);
2337
2411
 
2338
2412
  socket_t create_client_socket(const std::string &host, const std::string &ip,
2339
2413
  int port, int address_family, bool tcp_nodelay,
@@ -2381,6 +2455,7 @@ public:
2381
2455
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
2382
2456
  void get_local_ip_and_port(std::string &ip, int &port) const override;
2383
2457
  socket_t socket() const override;
2458
+ time_t duration() const override;
2384
2459
 
2385
2460
  const std::string &get_buffer() const;
2386
2461
 
@@ -2547,7 +2622,7 @@ inline bool is_obs_text(char c) { return 128 <= static_cast<unsigned char>(c); }
2547
2622
  inline bool is_field_vchar(char c) { return is_vchar(c) || is_obs_text(c); }
2548
2623
 
2549
2624
  inline bool is_field_content(const std::string &s) {
2550
- if (s.empty()) { return false; }
2625
+ if (s.empty()) { return true; }
2551
2626
 
2552
2627
  if (s.size() == 1) {
2553
2628
  return is_field_vchar(s[0]);
@@ -3171,60 +3246,43 @@ inline ssize_t send_socket(socket_t sock, const void *ptr, size_t size,
3171
3246
  });
3172
3247
  }
3173
3248
 
3174
- inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
3249
+ template <bool Read>
3250
+ inline ssize_t select_impl(socket_t sock, time_t sec, time_t usec) {
3175
3251
  #ifdef CPPHTTPLIB_USE_POLL
3176
- struct pollfd pfd_read;
3177
- pfd_read.fd = sock;
3178
- pfd_read.events = POLLIN;
3252
+ struct pollfd pfd;
3253
+ pfd.fd = sock;
3254
+ pfd.events = (Read ? POLLIN : POLLOUT);
3179
3255
 
3180
3256
  auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
3181
3257
 
3182
- return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
3258
+ return handle_EINTR([&]() { return poll(&pfd, 1, timeout); });
3183
3259
  #else
3184
3260
  #ifndef _WIN32
3185
3261
  if (sock >= FD_SETSIZE) { return -1; }
3186
3262
  #endif
3187
3263
 
3188
- fd_set fds;
3264
+ fd_set fds, *rfds, *wfds;
3189
3265
  FD_ZERO(&fds);
3190
3266
  FD_SET(sock, &fds);
3267
+ rfds = (Read ? &fds : nullptr);
3268
+ wfds = (Read ? nullptr : &fds);
3191
3269
 
3192
3270
  timeval tv;
3193
3271
  tv.tv_sec = static_cast<long>(sec);
3194
3272
  tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
3195
3273
 
3196
3274
  return handle_EINTR([&]() {
3197
- return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
3275
+ return select(static_cast<int>(sock + 1), rfds, wfds, nullptr, &tv);
3198
3276
  });
3199
3277
  #endif
3200
3278
  }
3201
3279
 
3202
- inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
3203
- #ifdef CPPHTTPLIB_USE_POLL
3204
- struct pollfd pfd_read;
3205
- pfd_read.fd = sock;
3206
- pfd_read.events = POLLOUT;
3207
-
3208
- auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
3209
-
3210
- return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
3211
- #else
3212
- #ifndef _WIN32
3213
- if (sock >= FD_SETSIZE) { return -1; }
3214
- #endif
3215
-
3216
- fd_set fds;
3217
- FD_ZERO(&fds);
3218
- FD_SET(sock, &fds);
3219
-
3220
- timeval tv;
3221
- tv.tv_sec = static_cast<long>(sec);
3222
- tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
3280
+ inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
3281
+ return select_impl<true>(sock, sec, usec);
3282
+ }
3223
3283
 
3224
- return handle_EINTR([&]() {
3225
- return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);
3226
- });
3227
- #endif
3284
+ inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
3285
+ return select_impl<false>(sock, sec, usec);
3228
3286
  }
3229
3287
 
3230
3288
  inline Error wait_until_socket_is_ready(socket_t sock, time_t sec,
@@ -3298,7 +3356,10 @@ inline bool is_socket_alive(socket_t sock) {
3298
3356
  class SocketStream final : public Stream {
3299
3357
  public:
3300
3358
  SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
3301
- time_t write_timeout_sec, time_t write_timeout_usec);
3359
+ time_t write_timeout_sec, time_t write_timeout_usec,
3360
+ time_t max_timeout_msec = 0,
3361
+ std::chrono::time_point<std::chrono::steady_clock> start_time =
3362
+ std::chrono::steady_clock::time_point::min());
3302
3363
  ~SocketStream() override;
3303
3364
 
3304
3365
  bool is_readable() const override;
@@ -3308,6 +3369,7 @@ public:
3308
3369
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
3309
3370
  void get_local_ip_and_port(std::string &ip, int &port) const override;
3310
3371
  socket_t socket() const override;
3372
+ time_t duration() const override;
3311
3373
 
3312
3374
  private:
3313
3375
  socket_t sock_;
@@ -3315,6 +3377,8 @@ private:
3315
3377
  time_t read_timeout_usec_;
3316
3378
  time_t write_timeout_sec_;
3317
3379
  time_t write_timeout_usec_;
3380
+ time_t max_timeout_msec_;
3381
+ const std::chrono::time_point<std::chrono::steady_clock> start_time;
3318
3382
 
3319
3383
  std::vector<char> read_buff_;
3320
3384
  size_t read_buff_off_ = 0;
@@ -3326,9 +3390,12 @@ private:
3326
3390
  #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
3327
3391
  class SSLSocketStream final : public Stream {
3328
3392
  public:
3329
- SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec,
3330
- time_t read_timeout_usec, time_t write_timeout_sec,
3331
- time_t write_timeout_usec);
3393
+ SSLSocketStream(
3394
+ socket_t sock, SSL *ssl, time_t read_timeout_sec,
3395
+ time_t read_timeout_usec, time_t write_timeout_sec,
3396
+ time_t write_timeout_usec, time_t max_timeout_msec = 0,
3397
+ std::chrono::time_point<std::chrono::steady_clock> start_time =
3398
+ std::chrono::steady_clock::time_point::min());
3332
3399
  ~SSLSocketStream() override;
3333
3400
 
3334
3401
  bool is_readable() const override;
@@ -3338,6 +3405,7 @@ public:
3338
3405
  void get_remote_ip_and_port(std::string &ip, int &port) const override;
3339
3406
  void get_local_ip_and_port(std::string &ip, int &port) const override;
3340
3407
  socket_t socket() const override;
3408
+ time_t duration() const override;
3341
3409
 
3342
3410
  private:
3343
3411
  socket_t sock_;
@@ -3346,6 +3414,8 @@ private:
3346
3414
  time_t read_timeout_usec_;
3347
3415
  time_t write_timeout_sec_;
3348
3416
  time_t write_timeout_usec_;
3417
+ time_t max_timeout_msec_;
3418
+ const std::chrono::time_point<std::chrono::steady_clock> start_time;
3349
3419
  };
3350
3420
  #endif
3351
3421
 
@@ -3416,13 +3486,15 @@ process_server_socket(const std::atomic<socket_t> &svr_sock, socket_t sock,
3416
3486
  });
3417
3487
  }
3418
3488
 
3419
- inline bool process_client_socket(socket_t sock, time_t read_timeout_sec,
3420
- time_t read_timeout_usec,
3421
- time_t write_timeout_sec,
3422
- time_t write_timeout_usec,
3423
- std::function<bool(Stream &)> callback) {
3489
+ inline bool process_client_socket(
3490
+ socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
3491
+ time_t write_timeout_sec, time_t write_timeout_usec,
3492
+ time_t max_timeout_msec,
3493
+ std::chrono::time_point<std::chrono::steady_clock> start_time,
3494
+ std::function<bool(Stream &)> callback) {
3424
3495
  SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
3425
- write_timeout_sec, write_timeout_usec);
3496
+ write_timeout_sec, write_timeout_usec, max_timeout_msec,
3497
+ start_time);
3426
3498
  return callback(strm);
3427
3499
  }
3428
3500
 
@@ -3569,26 +3641,10 @@ socket_t create_socket(const std::string &host, const std::string &ip, int port,
3569
3641
  }
3570
3642
  #endif
3571
3643
 
3572
- if (tcp_nodelay) {
3573
- auto opt = 1;
3574
- #ifdef _WIN32
3575
- setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
3576
- reinterpret_cast<const char *>(&opt), sizeof(opt));
3577
- #else
3578
- setsockopt(sock, IPPROTO_TCP, TCP_NODELAY,
3579
- reinterpret_cast<const void *>(&opt), sizeof(opt));
3580
- #endif
3581
- }
3644
+ if (tcp_nodelay) { set_socket_opt(sock, IPPROTO_TCP, TCP_NODELAY, 1); }
3582
3645
 
3583
3646
  if (rp->ai_family == AF_INET6) {
3584
- auto opt = ipv6_v6only ? 1 : 0;
3585
- #ifdef _WIN32
3586
- setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
3587
- reinterpret_cast<const char *>(&opt), sizeof(opt));
3588
- #else
3589
- setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
3590
- reinterpret_cast<const void *>(&opt), sizeof(opt));
3591
- #endif
3647
+ set_socket_opt(sock, IPPROTO_IPV6, IPV6_V6ONLY, ipv6_v6only ? 1 : 0);
3592
3648
  }
3593
3649
 
3594
3650
  if (socket_options) { socket_options(sock); }
@@ -3731,36 +3787,10 @@ inline socket_t create_client_socket(
3731
3787
  }
3732
3788
 
3733
3789
  set_nonblocking(sock2, false);
3734
-
3735
- {
3736
- #ifdef _WIN32
3737
- auto timeout = static_cast<uint32_t>(read_timeout_sec * 1000 +
3738
- read_timeout_usec / 1000);
3739
- setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,
3740
- reinterpret_cast<const char *>(&timeout), sizeof(timeout));
3741
- #else
3742
- timeval tv;
3743
- tv.tv_sec = static_cast<long>(read_timeout_sec);
3744
- tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec);
3745
- setsockopt(sock2, SOL_SOCKET, SO_RCVTIMEO,
3746
- reinterpret_cast<const void *>(&tv), sizeof(tv));
3747
- #endif
3748
- }
3749
- {
3750
-
3751
- #ifdef _WIN32
3752
- auto timeout = static_cast<uint32_t>(write_timeout_sec * 1000 +
3753
- write_timeout_usec / 1000);
3754
- setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,
3755
- reinterpret_cast<const char *>(&timeout), sizeof(timeout));
3756
- #else
3757
- timeval tv;
3758
- tv.tv_sec = static_cast<long>(write_timeout_sec);
3759
- tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec);
3760
- setsockopt(sock2, SOL_SOCKET, SO_SNDTIMEO,
3761
- reinterpret_cast<const void *>(&tv), sizeof(tv));
3762
- #endif
3763
- }
3790
+ set_socket_opt_time(sock2, SOL_SOCKET, SO_RCVTIMEO, read_timeout_sec,
3791
+ read_timeout_usec);
3792
+ set_socket_opt_time(sock2, SOL_SOCKET, SO_SNDTIMEO, write_timeout_sec,
3793
+ write_timeout_usec);
3764
3794
 
3765
3795
  error = Error::Success;
3766
3796
  return true;
@@ -4212,22 +4242,21 @@ inline bool parse_header(const char *beg, const char *end, T fn) {
4212
4242
  if (!key_len) { return false; }
4213
4243
 
4214
4244
  auto key = std::string(beg, key_end);
4215
- auto val = case_ignore::equal(key, "Location")
4216
- ? std::string(p, end)
4217
- : decode_url(std::string(p, end), false);
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);
4245
+ // auto val = (case_ignore::equal(key, "Location") ||
4246
+ // case_ignore::equal(key, "Referer"))
4247
+ // ? std::string(p, end)
4248
+ // : decode_url(std::string(p, end), false);
4249
+ auto val = std::string(p, end);
4250
+
4251
+ if (!detail::fields::is_field_value(val)) { return false; }
4252
+
4253
+ if (case_ignore::equal(key, "Location") ||
4254
+ case_ignore::equal(key, "Referer")) {
4255
+ fn(key, val);
4256
+ } else {
4257
+ fn(key, decode_url(val, false));
4258
+ }
4259
+
4231
4260
  return true;
4232
4261
  }
4233
4262
 
@@ -4263,7 +4292,7 @@ inline bool read_headers(Stream &strm, Headers &headers) {
4263
4292
  auto end = line_reader.ptr() + line_reader.size() - line_terminator_len;
4264
4293
 
4265
4294
  if (!parse_header(line_reader.ptr(), end,
4266
- [&](const std::string &key, std::string &val) {
4295
+ [&](const std::string &key, const std::string &val) {
4267
4296
  headers.emplace(key, val);
4268
4297
  })) {
4269
4298
  return false;
@@ -4312,7 +4341,7 @@ inline bool read_content_without_length(Stream &strm,
4312
4341
  uint64_t r = 0;
4313
4342
  for (;;) {
4314
4343
  auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
4315
- if (n <= 0) { return true; }
4344
+ if (n <= 0) { return false; }
4316
4345
 
4317
4346
  if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
4318
4347
  r += static_cast<uint64_t>(n);
@@ -4456,9 +4485,9 @@ bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
4456
4485
  ret = read_content_without_length(strm, out);
4457
4486
  } else {
4458
4487
  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);
4488
+ auto len = get_header_value_u64(
4489
+ x.headers, "Content-Length",
4490
+ (std::numeric_limits<uint64_t>::max)(), 0, is_invalid_value);
4462
4491
 
4463
4492
  if (is_invalid_value) {
4464
4493
  ret = false;
@@ -5380,10 +5409,14 @@ write_multipart_ranges_data(Stream &strm, const Request &req, Response &res,
5380
5409
 
5381
5410
  inline bool expect_content(const Request &req) {
5382
5411
  if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
5383
- req.method == "PRI" || req.method == "DELETE") {
5412
+ req.method == "DELETE") {
5413
+ return true;
5414
+ }
5415
+ if (req.has_header("Content-Length") &&
5416
+ req.get_header_value_u64("Content-Length") > 0) {
5384
5417
  return true;
5385
5418
  }
5386
- // TODO: check if Content-Length is set
5419
+ if (is_chunked_transfer_encoding(req.headers)) { return true; }
5387
5420
  return false;
5388
5421
  }
5389
5422
 
@@ -5428,9 +5461,76 @@ inline std::string SHA_256(const std::string &s) {
5428
5461
  inline std::string SHA_512(const std::string &s) {
5429
5462
  return message_digest(s, EVP_sha512());
5430
5463
  }
5431
- #endif
5432
5464
 
5433
- #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5465
+ inline std::pair<std::string, std::string> make_digest_authentication_header(
5466
+ const Request &req, const std::map<std::string, std::string> &auth,
5467
+ size_t cnonce_count, const std::string &cnonce, const std::string &username,
5468
+ const std::string &password, bool is_proxy = false) {
5469
+ std::string nc;
5470
+ {
5471
+ std::stringstream ss;
5472
+ ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;
5473
+ nc = ss.str();
5474
+ }
5475
+
5476
+ std::string qop;
5477
+ if (auth.find("qop") != auth.end()) {
5478
+ qop = auth.at("qop");
5479
+ if (qop.find("auth-int") != std::string::npos) {
5480
+ qop = "auth-int";
5481
+ } else if (qop.find("auth") != std::string::npos) {
5482
+ qop = "auth";
5483
+ } else {
5484
+ qop.clear();
5485
+ }
5486
+ }
5487
+
5488
+ std::string algo = "MD5";
5489
+ if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }
5490
+
5491
+ std::string response;
5492
+ {
5493
+ auto H = algo == "SHA-256" ? detail::SHA_256
5494
+ : algo == "SHA-512" ? detail::SHA_512
5495
+ : detail::MD5;
5496
+
5497
+ auto A1 = username + ":" + auth.at("realm") + ":" + password;
5498
+
5499
+ auto A2 = req.method + ":" + req.path;
5500
+ if (qop == "auth-int") { A2 += ":" + H(req.body); }
5501
+
5502
+ if (qop.empty()) {
5503
+ response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2));
5504
+ } else {
5505
+ response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
5506
+ ":" + qop + ":" + H(A2));
5507
+ }
5508
+ }
5509
+
5510
+ auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : "";
5511
+
5512
+ auto field = "Digest username=\"" + username + "\", realm=\"" +
5513
+ auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
5514
+ "\", uri=\"" + req.path + "\", algorithm=" + algo +
5515
+ (qop.empty() ? ", response=\""
5516
+ : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" +
5517
+ cnonce + "\", response=\"") +
5518
+ response + "\"" +
5519
+ (opaque.empty() ? "" : ", opaque=\"" + opaque + "\"");
5520
+
5521
+ auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
5522
+ return std::make_pair(key, field);
5523
+ }
5524
+
5525
+ inline bool is_ssl_peer_could_be_closed(SSL *ssl, socket_t sock) {
5526
+ detail::set_nonblocking(sock, true);
5527
+ auto se = detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
5528
+
5529
+ char buf[1];
5530
+ return !SSL_peek(ssl, buf, 1) &&
5531
+ SSL_get_error(ssl, 0) == SSL_ERROR_ZERO_RETURN;
5532
+ }
5533
+
5434
5534
  #ifdef _WIN32
5435
5535
  // NOTE: This code came up with the following stackoverflow post:
5436
5536
  // https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
@@ -5569,68 +5669,6 @@ public:
5569
5669
  static WSInit wsinit_;
5570
5670
  #endif
5571
5671
 
5572
- #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
5573
- inline std::pair<std::string, std::string> make_digest_authentication_header(
5574
- const Request &req, const std::map<std::string, std::string> &auth,
5575
- size_t cnonce_count, const std::string &cnonce, const std::string &username,
5576
- const std::string &password, bool is_proxy = false) {
5577
- std::string nc;
5578
- {
5579
- std::stringstream ss;
5580
- ss << std::setfill('0') << std::setw(8) << std::hex << cnonce_count;
5581
- nc = ss.str();
5582
- }
5583
-
5584
- std::string qop;
5585
- if (auth.find("qop") != auth.end()) {
5586
- qop = auth.at("qop");
5587
- if (qop.find("auth-int") != std::string::npos) {
5588
- qop = "auth-int";
5589
- } else if (qop.find("auth") != std::string::npos) {
5590
- qop = "auth";
5591
- } else {
5592
- qop.clear();
5593
- }
5594
- }
5595
-
5596
- std::string algo = "MD5";
5597
- if (auth.find("algorithm") != auth.end()) { algo = auth.at("algorithm"); }
5598
-
5599
- std::string response;
5600
- {
5601
- auto H = algo == "SHA-256" ? detail::SHA_256
5602
- : algo == "SHA-512" ? detail::SHA_512
5603
- : detail::MD5;
5604
-
5605
- auto A1 = username + ":" + auth.at("realm") + ":" + password;
5606
-
5607
- auto A2 = req.method + ":" + req.path;
5608
- if (qop == "auth-int") { A2 += ":" + H(req.body); }
5609
-
5610
- if (qop.empty()) {
5611
- response = H(H(A1) + ":" + auth.at("nonce") + ":" + H(A2));
5612
- } else {
5613
- response = H(H(A1) + ":" + auth.at("nonce") + ":" + nc + ":" + cnonce +
5614
- ":" + qop + ":" + H(A2));
5615
- }
5616
- }
5617
-
5618
- auto opaque = (auth.find("opaque") != auth.end()) ? auth.at("opaque") : "";
5619
-
5620
- auto field = "Digest username=\"" + username + "\", realm=\"" +
5621
- auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
5622
- "\", uri=\"" + req.path + "\", algorithm=" + algo +
5623
- (qop.empty() ? ", response=\""
5624
- : ", qop=" + qop + ", nc=" + nc + ", cnonce=\"" +
5625
- cnonce + "\", response=\"") +
5626
- response + "\"" +
5627
- (opaque.empty() ? "" : ", opaque=\"" + opaque + "\"");
5628
-
5629
- auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
5630
- return std::make_pair(key, field);
5631
- }
5632
- #endif
5633
-
5634
5672
  inline bool parse_www_authenticate(const Response &res,
5635
5673
  std::map<std::string, std::string> &auth,
5636
5674
  bool is_proxy) {
@@ -5949,20 +5987,45 @@ inline ssize_t Stream::write(const std::string &s) {
5949
5987
 
5950
5988
  namespace detail {
5951
5989
 
5990
+ inline void calc_actual_timeout(time_t max_timeout_msec,
5991
+ time_t duration_msec, time_t timeout_sec,
5992
+ time_t timeout_usec, time_t &actual_timeout_sec,
5993
+ time_t &actual_timeout_usec) {
5994
+ auto timeout_msec = (timeout_sec * 1000) + (timeout_usec / 1000);
5995
+
5996
+ auto actual_timeout_msec =
5997
+ std::min(max_timeout_msec - duration_msec, timeout_msec);
5998
+
5999
+ actual_timeout_sec = actual_timeout_msec / 1000;
6000
+ actual_timeout_usec = (actual_timeout_msec % 1000) * 1000;
6001
+ }
6002
+
5952
6003
  // Socket stream implementation
5953
- inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec,
5954
- time_t read_timeout_usec,
5955
- time_t write_timeout_sec,
5956
- time_t write_timeout_usec)
6004
+ inline SocketStream::SocketStream(
6005
+ socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
6006
+ time_t write_timeout_sec, time_t write_timeout_usec,
6007
+ time_t max_timeout_msec,
6008
+ std::chrono::time_point<std::chrono::steady_clock> start_time)
5957
6009
  : sock_(sock), read_timeout_sec_(read_timeout_sec),
5958
6010
  read_timeout_usec_(read_timeout_usec),
5959
6011
  write_timeout_sec_(write_timeout_sec),
5960
- write_timeout_usec_(write_timeout_usec), read_buff_(read_buff_size_, 0) {}
6012
+ write_timeout_usec_(write_timeout_usec),
6013
+ max_timeout_msec_(max_timeout_msec), start_time(start_time),
6014
+ read_buff_(read_buff_size_, 0) {}
5961
6015
 
5962
6016
  inline SocketStream::~SocketStream() = default;
5963
6017
 
5964
6018
  inline bool SocketStream::is_readable() const {
5965
- return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
6019
+ if (max_timeout_msec_ <= 0) {
6020
+ return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
6021
+ }
6022
+
6023
+ time_t read_timeout_sec;
6024
+ time_t read_timeout_usec;
6025
+ calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
6026
+ read_timeout_usec_, read_timeout_sec, read_timeout_usec);
6027
+
6028
+ return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
5966
6029
  }
5967
6030
 
5968
6031
  inline bool SocketStream::is_writable() const {
@@ -6039,6 +6102,12 @@ inline void SocketStream::get_local_ip_and_port(std::string &ip,
6039
6102
 
6040
6103
  inline socket_t SocketStream::socket() const { return sock_; }
6041
6104
 
6105
+ inline time_t SocketStream::duration() const {
6106
+ return std::chrono::duration_cast<std::chrono::milliseconds>(
6107
+ std::chrono::steady_clock::now() - start_time)
6108
+ .count();
6109
+ }
6110
+
6042
6111
  // Buffer stream implementation
6043
6112
  inline bool BufferStream::is_readable() const { return true; }
6044
6113
 
@@ -6067,6 +6136,8 @@ inline void BufferStream::get_local_ip_and_port(std::string & /*ip*/,
6067
6136
 
6068
6137
  inline socket_t BufferStream::socket() const { return 0; }
6069
6138
 
6139
+ inline time_t BufferStream::duration() const { return 0; }
6140
+
6070
6141
  inline const std::string &BufferStream::get_buffer() const { return buffer; }
6071
6142
 
6072
6143
  inline PathParamsMatcher::PathParamsMatcher(const std::string &pattern) {
@@ -6869,35 +6940,10 @@ inline bool Server::listen_internal() {
6869
6940
  break;
6870
6941
  }
6871
6942
 
6872
- {
6873
- #ifdef _WIN32
6874
- auto timeout = static_cast<uint32_t>(read_timeout_sec_ * 1000 +
6875
- read_timeout_usec_ / 1000);
6876
- setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
6877
- reinterpret_cast<const char *>(&timeout), sizeof(timeout));
6878
- #else
6879
- timeval tv;
6880
- tv.tv_sec = static_cast<long>(read_timeout_sec_);
6881
- tv.tv_usec = static_cast<decltype(tv.tv_usec)>(read_timeout_usec_);
6882
- setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,
6883
- reinterpret_cast<const void *>(&tv), sizeof(tv));
6884
- #endif
6885
- }
6886
- {
6887
-
6888
- #ifdef _WIN32
6889
- auto timeout = static_cast<uint32_t>(write_timeout_sec_ * 1000 +
6890
- write_timeout_usec_ / 1000);
6891
- setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
6892
- reinterpret_cast<const char *>(&timeout), sizeof(timeout));
6893
- #else
6894
- timeval tv;
6895
- tv.tv_sec = static_cast<long>(write_timeout_sec_);
6896
- tv.tv_usec = static_cast<decltype(tv.tv_usec)>(write_timeout_usec_);
6897
- setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO,
6898
- reinterpret_cast<const void *>(&tv), sizeof(tv));
6899
- #endif
6900
- }
6943
+ detail::set_socket_opt_time(sock, SOL_SOCKET, SO_RCVTIMEO,
6944
+ read_timeout_sec_, read_timeout_usec_);
6945
+ detail::set_socket_opt_time(sock, SOL_SOCKET, SO_SNDTIMEO,
6946
+ write_timeout_sec_, write_timeout_usec_);
6901
6947
 
6902
6948
  if (!task_queue->enqueue(
6903
6949
  [this, sock]() { process_and_close_socket(sock); })) {
@@ -7157,14 +7203,6 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
7157
7203
  #endif
7158
7204
  #endif
7159
7205
 
7160
- // Check if the request URI doesn't exceed the limit
7161
- if (line_reader.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
7162
- Headers dummy;
7163
- detail::read_headers(strm, dummy);
7164
- res.status = StatusCode::UriTooLong_414;
7165
- return write_response(strm, close_connection, req, res);
7166
- }
7167
-
7168
7206
  // Request line and headers
7169
7207
  if (!parse_request_line(line_reader.ptr(), req) ||
7170
7208
  !detail::read_headers(strm, req.headers)) {
@@ -7172,6 +7210,14 @@ Server::process_request(Stream &strm, const std::string &remote_addr,
7172
7210
  return write_response(strm, close_connection, req, res);
7173
7211
  }
7174
7212
 
7213
+ // Check if the request URI doesn't exceed the limit
7214
+ if (req.target.size() > CPPHTTPLIB_REQUEST_URI_MAX_LENGTH) {
7215
+ Headers dummy;
7216
+ detail::read_headers(strm, dummy);
7217
+ res.status = StatusCode::UriTooLong_414;
7218
+ return write_response(strm, close_connection, req, res);
7219
+ }
7220
+
7175
7221
  if (req.get_header_value("Connection") == "close") {
7176
7222
  connection_closed = true;
7177
7223
  }
@@ -7363,6 +7409,7 @@ inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
7363
7409
  read_timeout_usec_ = rhs.read_timeout_usec_;
7364
7410
  write_timeout_sec_ = rhs.write_timeout_sec_;
7365
7411
  write_timeout_usec_ = rhs.write_timeout_usec_;
7412
+ max_timeout_msec_ = rhs.max_timeout_msec_;
7366
7413
  basic_auth_username_ = rhs.basic_auth_username_;
7367
7414
  basic_auth_password_ = rhs.basic_auth_password_;
7368
7415
  bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
@@ -7509,18 +7556,6 @@ inline bool ClientImpl::send(Request &req, Response &res, Error &error) {
7509
7556
  return ret;
7510
7557
  }
7511
7558
 
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
-
7524
7559
  inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
7525
7560
  {
7526
7561
  std::lock_guard<std::mutex> guard(socket_mutex_);
@@ -7535,7 +7570,9 @@ inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
7535
7570
 
7536
7571
  #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
7537
7572
  if (is_alive && is_ssl()) {
7538
- if (is_ssl_peer_could_be_closed(socket_.ssl)) { is_alive = false; }
7573
+ if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
7574
+ is_alive = false;
7575
+ }
7539
7576
  }
7540
7577
  #endif
7541
7578
 
@@ -7560,7 +7597,8 @@ inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
7560
7597
  auto &scli = static_cast<SSLClient &>(*this);
7561
7598
  if (!proxy_host_.empty() && proxy_port_ != -1) {
7562
7599
  auto success = false;
7563
- if (!scli.connect_with_proxy(socket_, res, success, error)) {
7600
+ if (!scli.connect_with_proxy(socket_, req.start_time_, res, success,
7601
+ error)) {
7564
7602
  return success;
7565
7603
  }
7566
7604
  }
@@ -7606,7 +7644,7 @@ inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
7606
7644
  }
7607
7645
  });
7608
7646
 
7609
- ret = process_socket(socket_, [&](Stream &strm) {
7647
+ ret = process_socket(socket_, req.start_time_, [&](Stream &strm) {
7610
7648
  return handle_request(strm, req, res, close_connection, error);
7611
7649
  });
7612
7650
 
@@ -8015,6 +8053,9 @@ inline Result ClientImpl::send_with_content_provider(
8015
8053
  req.headers = headers;
8016
8054
  req.path = path;
8017
8055
  req.progress = progress;
8056
+ if (max_timeout_msec_ > 0) {
8057
+ req.start_time_ = std::chrono::steady_clock::now();
8058
+ }
8018
8059
 
8019
8060
  auto error = Error::Success;
8020
8061
 
@@ -8041,7 +8082,7 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
8041
8082
  if (is_ssl()) {
8042
8083
  auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
8043
8084
  if (!is_proxy_enabled) {
8044
- if (is_ssl_peer_could_be_closed(socket_.ssl)) {
8085
+ if (detail::is_ssl_peer_could_be_closed(socket_.ssl, socket_.sock)) {
8045
8086
  error = Error::SSLPeerCouldBeClosed_;
8046
8087
  return false;
8047
8088
  }
@@ -8166,12 +8207,14 @@ inline ContentProviderWithoutLength ClientImpl::get_multipart_content_provider(
8166
8207
  };
8167
8208
  }
8168
8209
 
8169
- inline bool
8170
- ClientImpl::process_socket(const Socket &socket,
8171
- std::function<bool(Stream &strm)> callback) {
8210
+ inline bool ClientImpl::process_socket(
8211
+ const Socket &socket,
8212
+ std::chrono::time_point<std::chrono::steady_clock> start_time,
8213
+ std::function<bool(Stream &strm)> callback) {
8172
8214
  return detail::process_client_socket(
8173
8215
  socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
8174
- write_timeout_usec_, std::move(callback));
8216
+ write_timeout_usec_, max_timeout_msec_, start_time,
8217
+ std::move(callback));
8175
8218
  }
8176
8219
 
8177
8220
  inline bool ClientImpl::is_ssl() const { return false; }
@@ -8195,6 +8238,9 @@ inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
8195
8238
  req.path = path;
8196
8239
  req.headers = headers;
8197
8240
  req.progress = std::move(progress);
8241
+ if (max_timeout_msec_ > 0) {
8242
+ req.start_time_ = std::chrono::steady_clock::now();
8243
+ }
8198
8244
 
8199
8245
  return send_(std::move(req));
8200
8246
  }
@@ -8260,6 +8306,9 @@ inline Result ClientImpl::Get(const std::string &path, const Headers &headers,
8260
8306
  return content_receiver(data, data_length);
8261
8307
  };
8262
8308
  req.progress = std::move(progress);
8309
+ if (max_timeout_msec_ > 0) {
8310
+ req.start_time_ = std::chrono::steady_clock::now();
8311
+ }
8263
8312
 
8264
8313
  return send_(std::move(req));
8265
8314
  }
@@ -8305,6 +8354,9 @@ inline Result ClientImpl::Head(const std::string &path,
8305
8354
  req.method = "HEAD";
8306
8355
  req.headers = headers;
8307
8356
  req.path = path;
8357
+ if (max_timeout_msec_ > 0) {
8358
+ req.start_time_ = std::chrono::steady_clock::now();
8359
+ }
8308
8360
 
8309
8361
  return send_(std::move(req));
8310
8362
  }
@@ -8722,6 +8774,9 @@ inline Result ClientImpl::Delete(const std::string &path,
8722
8774
  req.headers = headers;
8723
8775
  req.path = path;
8724
8776
  req.progress = progress;
8777
+ if (max_timeout_msec_ > 0) {
8778
+ req.start_time_ = std::chrono::steady_clock::now();
8779
+ }
8725
8780
 
8726
8781
  if (!content_type.empty()) { req.set_header("Content-Type", content_type); }
8727
8782
  req.body.assign(body, content_length);
@@ -8769,6 +8824,9 @@ inline Result ClientImpl::Options(const std::string &path,
8769
8824
  req.method = "OPTIONS";
8770
8825
  req.headers = headers;
8771
8826
  req.path = path;
8827
+ if (max_timeout_msec_ > 0) {
8828
+ req.start_time_ = std::chrono::steady_clock::now();
8829
+ }
8772
8830
 
8773
8831
  return send_(std::move(req));
8774
8832
  }
@@ -8822,6 +8880,10 @@ inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
8822
8880
  write_timeout_usec_ = usec;
8823
8881
  }
8824
8882
 
8883
+ inline void ClientImpl::set_max_timeout(time_t msec) {
8884
+ max_timeout_msec_ = msec;
8885
+ }
8886
+
8825
8887
  inline void ClientImpl::set_basic_auth(const std::string &username,
8826
8888
  const std::string &password) {
8827
8889
  basic_auth_username_ = username;
@@ -9004,11 +9066,7 @@ inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl, socket_t sock,
9004
9066
  (void)(sock);
9005
9067
  SSL_shutdown(ssl);
9006
9068
  #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));
9069
+ detail::set_socket_opt_time(sock, SOL_SOCKET, SO_RCVTIMEO, 1, 0);
9012
9070
 
9013
9071
  auto ret = SSL_shutdown(ssl);
9014
9072
  while (ret == 0) {
@@ -9060,12 +9118,14 @@ inline bool process_server_socket_ssl(
9060
9118
  }
9061
9119
 
9062
9120
  template <typename T>
9063
- inline bool
9064
- process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec,
9065
- time_t read_timeout_usec, time_t write_timeout_sec,
9066
- time_t write_timeout_usec, T callback) {
9121
+ inline bool process_client_socket_ssl(
9122
+ SSL *ssl, socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
9123
+ time_t write_timeout_sec, time_t write_timeout_usec,
9124
+ time_t max_timeout_msec,
9125
+ std::chrono::time_point<std::chrono::steady_clock> start_time, T callback) {
9067
9126
  SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
9068
- write_timeout_sec, write_timeout_usec);
9127
+ write_timeout_sec, write_timeout_usec,
9128
+ max_timeout_msec, start_time);
9069
9129
  return callback(strm);
9070
9130
  }
9071
9131
 
@@ -9078,27 +9138,37 @@ public:
9078
9138
  };
9079
9139
 
9080
9140
  // SSL socket stream implementation
9081
- inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
9082
- time_t read_timeout_sec,
9083
- time_t read_timeout_usec,
9084
- time_t write_timeout_sec,
9085
- time_t write_timeout_usec)
9141
+ inline SSLSocketStream::SSLSocketStream(
9142
+ socket_t sock, SSL *ssl, time_t read_timeout_sec, time_t read_timeout_usec,
9143
+ time_t write_timeout_sec, time_t write_timeout_usec,
9144
+ time_t max_timeout_msec,
9145
+ std::chrono::time_point<std::chrono::steady_clock> start_time)
9086
9146
  : sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
9087
9147
  read_timeout_usec_(read_timeout_usec),
9088
9148
  write_timeout_sec_(write_timeout_sec),
9089
- write_timeout_usec_(write_timeout_usec) {
9149
+ write_timeout_usec_(write_timeout_usec),
9150
+ max_timeout_msec_(max_timeout_msec), start_time(start_time) {
9090
9151
  SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
9091
9152
  }
9092
9153
 
9093
9154
  inline SSLSocketStream::~SSLSocketStream() = default;
9094
9155
 
9095
9156
  inline bool SSLSocketStream::is_readable() const {
9096
- return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
9157
+ if (max_timeout_msec_ <= 0) {
9158
+ return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
9159
+ }
9160
+
9161
+ time_t read_timeout_sec;
9162
+ time_t read_timeout_usec;
9163
+ calc_actual_timeout(max_timeout_msec_, duration(), read_timeout_sec_,
9164
+ read_timeout_usec_, read_timeout_sec, read_timeout_usec);
9165
+
9166
+ return select_read(sock_, read_timeout_sec, read_timeout_usec) > 0;
9097
9167
  }
9098
9168
 
9099
9169
  inline bool SSLSocketStream::is_writable() const {
9100
9170
  return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0 &&
9101
- is_socket_alive(sock_);
9171
+ is_socket_alive(sock_) && !is_ssl_peer_could_be_closed(ssl_, sock_);
9102
9172
  }
9103
9173
 
9104
9174
  inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
@@ -9129,8 +9199,9 @@ inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
9129
9199
  }
9130
9200
  }
9131
9201
  return ret;
9202
+ } else {
9203
+ return -1;
9132
9204
  }
9133
- return -1;
9134
9205
  }
9135
9206
 
9136
9207
  inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
@@ -9176,6 +9247,12 @@ inline void SSLSocketStream::get_local_ip_and_port(std::string &ip,
9176
9247
 
9177
9248
  inline socket_t SSLSocketStream::socket() const { return sock_; }
9178
9249
 
9250
+ inline time_t SSLSocketStream::duration() const {
9251
+ return std::chrono::duration_cast<std::chrono::milliseconds>(
9252
+ std::chrono::steady_clock::now() - start_time)
9253
+ .count();
9254
+ }
9255
+
9179
9256
  static SSLInit sslinit_;
9180
9257
 
9181
9258
  } // namespace detail
@@ -9416,16 +9493,22 @@ inline bool SSLClient::create_and_connect_socket(Socket &socket, Error &error) {
9416
9493
  }
9417
9494
 
9418
9495
  // Assumes that socket_mutex_ is locked and that there are no requests in flight
9419
- inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
9420
- bool &success, Error &error) {
9496
+ inline bool SSLClient::connect_with_proxy(
9497
+ Socket &socket,
9498
+ std::chrono::time_point<std::chrono::steady_clock> start_time,
9499
+ Response &res, bool &success, Error &error) {
9421
9500
  success = true;
9422
9501
  Response proxy_res;
9423
9502
  if (!detail::process_client_socket(
9424
9503
  socket.sock, read_timeout_sec_, read_timeout_usec_,
9425
- write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
9504
+ write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
9505
+ start_time, [&](Stream &strm) {
9426
9506
  Request req2;
9427
9507
  req2.method = "CONNECT";
9428
9508
  req2.path = host_and_port_;
9509
+ if (max_timeout_msec_ > 0) {
9510
+ req2.start_time_ = std::chrono::steady_clock::now();
9511
+ }
9429
9512
  return process_request(strm, req2, proxy_res, false, error);
9430
9513
  })) {
9431
9514
  // Thread-safe to close everything because we are assuming there are no
@@ -9445,7 +9528,8 @@ inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
9445
9528
  proxy_res = Response();
9446
9529
  if (!detail::process_client_socket(
9447
9530
  socket.sock, read_timeout_sec_, read_timeout_usec_,
9448
- write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
9531
+ write_timeout_sec_, write_timeout_usec_, max_timeout_msec_,
9532
+ start_time, [&](Stream &strm) {
9449
9533
  Request req3;
9450
9534
  req3.method = "CONNECT";
9451
9535
  req3.path = host_and_port_;
@@ -9453,6 +9537,9 @@ inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
9453
9537
  req3, auth, 1, detail::random_string(10),
9454
9538
  proxy_digest_auth_username_, proxy_digest_auth_password_,
9455
9539
  true));
9540
+ if (max_timeout_msec_ > 0) {
9541
+ req3.start_time_ = std::chrono::steady_clock::now();
9542
+ }
9456
9543
  return process_request(strm, req3, proxy_res, false, error);
9457
9544
  })) {
9458
9545
  // Thread-safe to close everything because we are assuming there are
@@ -9608,13 +9695,15 @@ inline void SSLClient::shutdown_ssl_impl(Socket &socket,
9608
9695
  assert(socket.ssl == nullptr);
9609
9696
  }
9610
9697
 
9611
- inline bool
9612
- SSLClient::process_socket(const Socket &socket,
9613
- std::function<bool(Stream &strm)> callback) {
9698
+ inline bool SSLClient::process_socket(
9699
+ const Socket &socket,
9700
+ std::chrono::time_point<std::chrono::steady_clock> start_time,
9701
+ std::function<bool(Stream &strm)> callback) {
9614
9702
  assert(socket.ssl);
9615
9703
  return detail::process_client_socket_ssl(
9616
9704
  socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
9617
- write_timeout_sec_, write_timeout_usec_, std::move(callback));
9705
+ write_timeout_sec_, write_timeout_usec_, max_timeout_msec_, start_time,
9706
+ std::move(callback));
9618
9707
  }
9619
9708
 
9620
9709
  inline bool SSLClient::is_ssl() const { return true; }