@@ -1832,7 +1832,7 @@ int getaddrinfo_with_timeout(const char *node, const char *service,
18321832
18331833#ifdef _WIN32
18341834 // Windows-specific implementation using GetAddrInfoEx with overlapped I/O
1835- OVERLAPPED overlapped = {0 };
1835+ OVERLAPPED overlapped = {};
18361836 HANDLE event = CreateEventW(nullptr, TRUE, FALSE, nullptr);
18371837 if (!event) { return EAI_FAIL; }
18381838
@@ -1841,7 +1841,7 @@ int getaddrinfo_with_timeout(const char *node, const char *service,
18411841 PADDRINFOEXW result_addrinfo = nullptr;
18421842 HANDLE cancel_handle = nullptr;
18431843
1844- ADDRINFOEXW hints_ex = {0 };
1844+ ADDRINFOEXW hints_ex = {};
18451845 if (hints) {
18461846 hints_ex.ai_flags = hints->ai_flags;
18471847 hints_ex.ai_family = hints->ai_family;
@@ -9912,21 +9912,37 @@ bool ClientImpl::process_request(Stream &strm, Request &req,
99129912 }
99139913#endif
99149914
9915- // Handle Expect: 100-continue with timeout
9916- if (expect_100_continue && CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND > 0) {
9917- time_t sec = CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND / 1000;
9918- time_t usec = (CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND % 1000) * 1000;
9919- auto ret = detail::select_read(strm.socket(), sec, usec);
9920- if (ret <= 0) {
9921- // Timeout or error: send body anyway (server didn't respond in time)
9915+ // Handle Expect: 100-continue.
9916+ //
9917+ // Wait for an interim/early response by attempting to read the status line
9918+ // under a short timeout, instead of trusting raw socket readability. Over
9919+ // TLS, post-handshake records (e.g. session tickets) make the socket
9920+ // readable without any HTTP response being available; relying on
9921+ // `select_read` there caused the body to be withheld forever and the
9922+ // request to fail with `Read` (#2458). If no status line arrives within the
9923+ // timeout, send the body anyway (matching curl's behavior).
9924+ auto status_line_read = false;
9925+ if (expect_100_continue && write_request_success) {
9926+ if (CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND > 0) {
9927+ time_t sec = CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND / 1000;
9928+ time_t usec = (CPPHTTPLIB_EXPECT_100_TIMEOUT_MSECOND % 1000) * 1000;
9929+ strm.set_read_timeout(sec, usec);
9930+ status_line_read = read_response_line(strm, req, res, false);
9931+ strm.set_read_timeout(read_timeout_sec_, read_timeout_usec_);
9932+ }
9933+
9934+ if (!status_line_read) {
9935+ // No interim response within the timeout: send the body and handle the
9936+ // response as usual.
99229937 if (!write_request_body(strm, req, error)) { return false; }
99239938 expect_100_continue = false; // Switch to normal response handling
99249939 }
99259940 }
99269941
99279942 // Receive response and headers
99289943 // When using Expect: 100-continue, don't auto-skip `100 Continue` response
9929- if (!read_response_line(strm, req, res, !expect_100_continue) ||
9944+ if ((!status_line_read &&
9945+ !read_response_line(strm, req, res, !expect_100_continue)) ||
99309946 !detail::read_headers(strm, res.headers)) {
99319947 if (write_request_success) { error = Error::Read; }
99329948 output_error_log(error, &req);
0 commit comments