From e3a20d1b6589f015102700399553fbc967ec339f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:49:47 +0000 Subject: [PATCH 1/5] Initial plan From ea0d4a6931f685566233b020f5fd5291e397926b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:58:22 +0000 Subject: [PATCH 2/5] Fix connect timeout not accounting for DNS resolution time The ConnectTimeout function previously only applied the timeout to the TCP connect phase (poll/select), but DNS resolution via getaddrinfo() had no timeout applied. On platforms like Android with no network, getaddrinfo() can block for 30-40 seconds, making user-set connect_timeout and timeout values ineffective. Changes: - ConnectTimeout now tracks elapsed time for DNS resolution and subtracts it from the TCP connect timeout. If DNS takes longer than the timeout, it returns ETIMEDOUT immediately. - http_client_connect adjusts SSL handshake timeout by subtracting time already spent on DNS+TCP connect. - http_client_exec checks overall request timeout after connect, handling cases where connect took longer than expected. - Updated build files (CMakeLists.txt, Makefile) to link htime.c with all targets that use hsocket.c. Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- Makefile | 8 ++++---- base/hsocket.c | 11 ++++++++++- http/client/HttpClient.cpp | 14 +++++++++++++- unittest/CMakeLists.txt | 6 +++--- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index cbb8deadf..a8a43b4fb 100644 --- a/Makefile +++ b/Makefile @@ -268,7 +268,7 @@ unittest: prepare $(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -o bin/hthread_test unittest/hthread_test.cpp -pthread $(CC) -g -Wall -O0 -std=c99 -I. -Ibase -o bin/hmutex_test unittest/hmutex_test.c base/htime.c -pthread $(CC) -g -Wall -O0 -std=c99 -I. -Ibase -o bin/connect_test unittest/connect_test.c base/hsocket.c base/htime.c - $(CC) -g -Wall -O0 -std=c99 -I. -Ibase -o bin/socketpair_test unittest/socketpair_test.c base/hsocket.c + $(CC) -g -Wall -O0 -std=c99 -I. -Ibase -o bin/socketpair_test unittest/socketpair_test.c base/hsocket.c base/htime.c $(CC) -g -Wall -O0 -std=c99 -I. -Iutil -o bin/base64 unittest/base64_test.c util/base64.c $(CC) -g -Wall -O0 -std=c99 -I. -Iutil -o bin/md5 unittest/md5_test.c util/md5.c $(CC) -g -Wall -O0 -std=c99 -I. -Iutil -o bin/sha1 unittest/sha1_test.c util/sha1.c @@ -282,10 +282,10 @@ unittest: prepare $(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Icpputil -o bin/threadpool_test unittest/threadpool_test.cpp -pthread $(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Icpputil -o bin/objectpool_test unittest/objectpool_test.cpp -pthread $(CXX) -g -Wall -O0 -std=c++11 -I. -Ibase -Issl -Ievent -Ievpp -Icpputil -Ihttp -Ihttp/client -Ihttp/server -o bin/sizeof_test unittest/sizeof_test.cpp - $(CC) -g -Wall -O0 -std=c99 -I. -Ibase -Iprotocol -o bin/nslookup unittest/nslookup_test.c protocol/dns.c base/hsocket.c + $(CC) -g -Wall -O0 -std=c99 -I. -Ibase -Iprotocol -o bin/nslookup unittest/nslookup_test.c protocol/dns.c base/hsocket.c base/htime.c $(CC) -g -Wall -O0 -std=c99 -I. -Ibase -Iprotocol -o bin/ping unittest/ping_test.c protocol/icmp.c base/hsocket.c base/htime.c -DPRINT_DEBUG - $(CC) -g -Wall -O0 -std=c99 -I. -Ibase -Iprotocol -o bin/ftp unittest/ftp_test.c protocol/ftp.c base/hsocket.c - $(CC) -g -Wall -O0 -std=c99 -I. -Ibase -Iprotocol -Iutil -o bin/sendmail unittest/sendmail_test.c protocol/smtp.c base/hsocket.c util/base64.c + $(CC) -g -Wall -O0 -std=c99 -I. -Ibase -Iprotocol -o bin/ftp unittest/ftp_test.c protocol/ftp.c base/hsocket.c base/htime.c + $(CC) -g -Wall -O0 -std=c99 -I. -Ibase -Iprotocol -Iutil -o bin/sendmail unittest/sendmail_test.c protocol/smtp.c base/hsocket.c base/htime.c util/base64.c run-unittest: unittest bash scripts/unittest.sh diff --git a/base/hsocket.c b/base/hsocket.c index 6131d23d2..ef992e086 100644 --- a/base/hsocket.c +++ b/base/hsocket.c @@ -1,6 +1,7 @@ #include "hsocket.h" #include "hdef.h" +#include "htime.h" #ifdef OS_UNIX #include @@ -339,9 +340,17 @@ int ConnectNonblock(const char* host, int port) { } int ConnectTimeout(const char* host, int port, int ms) { + unsigned int start_time = gettick_ms(); int connfd = Connect(host, port, 1); if (connfd < 0) return connfd; - return ConnectFDTimeout(connfd, ms); + unsigned int elapsed = gettick_ms() - start_time; + int remaining = ms - (int)elapsed; + if (remaining <= 0) { + closesocket(connfd); + errno = ETIMEDOUT; + return -ETIMEDOUT; + } + return ConnectFDTimeout(connfd, remaining); } #ifdef ENABLE_UDS diff --git a/http/client/HttpClient.cpp b/http/client/HttpClient.cpp index 0960b682b..1e35cc8c2 100644 --- a/http/client/HttpClient.cpp +++ b/http/client/HttpClient.cpp @@ -211,6 +211,7 @@ int http_client_connect(http_client_t* cli, const char* host, int port, int http if (timeout > 0) { blocktime = MIN(timeout*1000, blocktime); } + unsigned int start_time = gettick_ms(); int connfd = ConnectTimeout(host, port, blocktime); if (connfd < 0) { hloge("connect %s:%d failed!", host, port); @@ -241,7 +242,10 @@ int http_client_connect(http_client_t* cli, const char* host, int port, int http if (!is_ipaddr(host)) { hssl_set_sni_hostname(cli->ssl, host); } - so_rcvtimeo(connfd, blocktime); + unsigned int elapsed = gettick_ms() - start_time; + int ssl_timeout = blocktime - (int)elapsed; + if (ssl_timeout <= 0) ssl_timeout = 1; + so_rcvtimeo(connfd, ssl_timeout); int ret = hssl_connect(cli->ssl); if (ret != 0) { fprintf(stderr, "* ssl handshake failed: %d\n", ret); @@ -329,6 +333,14 @@ static int http_client_exec(http_client_t* cli, HttpRequest* req, HttpResponse* if (connfd < 0) { return connfd; } + if (timeout_ms > 0) { + cur_time = gettick_ms(); + if (cur_time - start_time >= timeout_ms) { + cli->Close(); + return ERR_TASK_TIMEOUT; + } + left_time = timeout_ms - (cur_time - start_time); + } } cli->parser->SubmitRequest(req); diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index 4e469e602..f59b60211 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -28,7 +28,7 @@ target_link_libraries(hmutex_test -lpthread) add_executable(connect_test connect_test.c ../base/hsocket.c ../base/htime.c) target_include_directories(connect_test PRIVATE .. ../base) -add_executable(socketpair_test socketpair_test.c ../base/hsocket.c) +add_executable(socketpair_test socketpair_test.c ../base/hsocket.c ../base/htime.c) target_include_directories(socketpair_test PRIVATE .. ../base) # ------util------ @@ -80,10 +80,10 @@ add_executable(ping ping_test.c ../protocol/icmp.c ../base/hsocket.c ../base/hti target_compile_definitions(ping PRIVATE -DPRINT_DEBUG) target_include_directories(ping PRIVATE .. ../base ../protocol) -add_executable(ftp ftp_test.c ../protocol/ftp.c ../base/hsocket.c) +add_executable(ftp ftp_test.c ../protocol/ftp.c ../base/hsocket.c ../base/htime.c) target_include_directories(ftp PRIVATE .. ../base ../protocol) -add_executable(sendmail sendmail_test.c ../protocol/smtp.c ../base/hsocket.c ../util/base64.c) +add_executable(sendmail sendmail_test.c ../protocol/smtp.c ../base/hsocket.c ../base/htime.c ../util/base64.c) target_include_directories(sendmail PRIVATE .. ../base ../protocol ../util) if(UNIX) From 1ac34d67719df52f84430d6628e0654db9f484ab Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Mar 2026 13:59:39 +0000 Subject: [PATCH 3/5] Address code review: use consistent error handling and fail immediately on SSL timeout Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- base/hsocket.c | 3 +-- http/client/HttpClient.cpp | 7 ++++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/base/hsocket.c b/base/hsocket.c index ef992e086..a15e6ec8f 100644 --- a/base/hsocket.c +++ b/base/hsocket.c @@ -346,9 +346,8 @@ int ConnectTimeout(const char* host, int port, int ms) { unsigned int elapsed = gettick_ms() - start_time; int remaining = ms - (int)elapsed; if (remaining <= 0) { - closesocket(connfd); errno = ETIMEDOUT; - return -ETIMEDOUT; + return socket_errno_negative(connfd); } return ConnectFDTimeout(connfd, remaining); } diff --git a/http/client/HttpClient.cpp b/http/client/HttpClient.cpp index 1e35cc8c2..0a8f2141d 100644 --- a/http/client/HttpClient.cpp +++ b/http/client/HttpClient.cpp @@ -244,7 +244,12 @@ int http_client_connect(http_client_t* cli, const char* host, int port, int http } unsigned int elapsed = gettick_ms() - start_time; int ssl_timeout = blocktime - (int)elapsed; - if (ssl_timeout <= 0) ssl_timeout = 1; + if (ssl_timeout <= 0) { + hssl_free(cli->ssl); + cli->ssl = NULL; + closesocket(connfd); + return NABS(ETIMEDOUT); + } so_rcvtimeo(connfd, ssl_timeout); int ret = hssl_connect(cli->ssl); if (ret != 0) { From 899eb0d8730d0ca3129617faba3663643740fdaa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 03:30:27 +0000 Subject: [PATCH 4/5] Use CHECK_TIMEOUT after connect and add hsocket.c+htime.c to nslookup target - Replace inline timeout check with CHECK_TIMEOUT macro by moving variable declarations before the connect: label to avoid crossing initializations with goto. - Add hsocket.c and htime.c to nslookup CMake target for Windows link compatibility (dns.c calls WSAInit from hsocket.c). Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- http/client/HttpClient.cpp | 22 +++++++++------------- unittest/CMakeLists.txt | 2 +- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/http/client/HttpClient.cpp b/http/client/HttpClient.cpp index 0a8f2141d..a05336e63 100644 --- a/http/client/HttpClient.cpp +++ b/http/client/HttpClient.cpp @@ -330,6 +330,12 @@ static int http_client_exec(http_client_t* cli, HttpRequest* req, HttpResponse* } } + char recvbuf[1024] = {0}; + char* data = NULL; + size_t len = 0; + int total_nsend, nsend, nrecv; + total_nsend = nsend = nrecv = 0; + if (connfd <= 0 || cli->host != req->host || cli->port != req->port) { cli->host = req->host; cli->port = req->port; @@ -338,23 +344,13 @@ static int http_client_exec(http_client_t* cli, HttpRequest* req, HttpResponse* if (connfd < 0) { return connfd; } - if (timeout_ms > 0) { - cur_time = gettick_ms(); - if (cur_time - start_time >= timeout_ms) { - cli->Close(); - return ERR_TASK_TIMEOUT; - } - left_time = timeout_ms - (cur_time - start_time); - } + CHECK_TIMEOUT } cli->parser->SubmitRequest(req); - char recvbuf[1024] = {0}; - int total_nsend, nsend, nrecv; - total_nsend = nsend = nrecv = 0; send: - char* data = NULL; - size_t len = 0; + data = NULL; + len = 0; while (cli->parser->GetSendData(&data, &len)) { total_nsend = 0; while (total_nsend < len) { diff --git a/unittest/CMakeLists.txt b/unittest/CMakeLists.txt index f59b60211..2eec34790 100644 --- a/unittest/CMakeLists.txt +++ b/unittest/CMakeLists.txt @@ -73,7 +73,7 @@ target_include_directories(objectpool_test PRIVATE .. ../base ../cpputil) target_link_libraries(objectpool_test -lpthread) # ------protocol------ -add_executable(nslookup nslookup_test.c ../protocol/dns.c) +add_executable(nslookup nslookup_test.c ../protocol/dns.c ../base/hsocket.c ../base/htime.c) target_include_directories(nslookup PRIVATE .. ../base ../protocol) add_executable(ping ping_test.c ../protocol/icmp.c ../base/hsocket.c ../base/htime.c) From d19257f7b2feb8b902ca65f2aab6aa827a57b65d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 11 Mar 2026 03:47:23 +0000 Subject: [PATCH 5/5] Add explicit closesocket(connfd) in ConnectTimeout before returning -ETIMEDOUT When DNS resolution exhausts the timeout budget, explicitly close the socket with closesocket() and return -ETIMEDOUT directly, instead of relying on socket_errno_negative() which reads errno indirectly and could return the wrong error code on Windows. Co-authored-by: ithewei <26049660+ithewei@users.noreply.github.com> --- base/hsocket.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/hsocket.c b/base/hsocket.c index a15e6ec8f..c6f023db1 100644 --- a/base/hsocket.c +++ b/base/hsocket.c @@ -346,8 +346,8 @@ int ConnectTimeout(const char* host, int port, int ms) { unsigned int elapsed = gettick_ms() - start_time; int remaining = ms - (int)elapsed; if (remaining <= 0) { - errno = ETIMEDOUT; - return socket_errno_negative(connfd); + closesocket(connfd); + return -ETIMEDOUT; } return ConnectFDTimeout(connfd, remaining); }