{"thread":{"id":"66053","subject":"[PATCH] http: add a config to limit the connection time","startedAt":"2026-07-23T09:25:35Z","lastAt":"2026-07-23T16:47:40Z","messageCount":2,"participants":["GalaxySnail via GitGitGadget","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"548805","messageId":"pull.2362.git.git.1784798733557.gitgitgadget@gmail.com","threadId":"66053","inReplyTo":null,"subject":"[PATCH] http: add a config to limit the connection time","fromName":"GalaxySnail via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2026-07-23T09:25:33Z","receivedAt":"2026-07-23T09:25:35Z","isPatch":true,"body":"From: GalaxySnail <me@glxys.nl>\n\nBy default, libcurl uses a 300 seconds timeout for the connection phase,\nwhich is too long for some use cases.\n\nAdd http.connecttimeoutms and GIT_HTTP_CONNECT_TIMEOUT_MS to specify\ntimeout in milliseconds for the connection phase. Both of them call\nCURLOPT_CONNECTTIMEOUT_MS internally.\n\nSigned-off-by: GalaxySnail <me@glxys.nl>\n---\n    http: add a config to limit the connection time\n\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2362%2FGalaxySnail%2Fhttp-connect-timeout-ms-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2362/GalaxySnail/http-connect-timeout-ms-v1\nPull-Request: https://github.com/git/git/pull/2362\n\n Documentation/config/http.adoc  |  7 ++++\n http.c                          | 11 ++++++\n t/meson.build                   |  1 +\n t/t5585-http-connect-timeout.sh | 60 +++++++++++++++++++++++++++++++++\n 4 files changed, 79 insertions(+)\n create mode 100755 t/t5585-http-connect-timeout.sh\n\ndiff --git a/Documentation/config/http.adoc b/Documentation/config/http.adoc\nindex 792a71b413..a4f7afa61e 100644\n--- a/Documentation/config/http.adoc\n+++ b/Documentation/config/http.adoc\n@@ -300,6 +300,13 @@ for most push problems, but can increase memory consumption\n significantly since the entire buffer is allocated even for small\n pushes.\n \n+http.connectTimeoutMS::\n+\tMaximum time in milliseconds that you allow the connection phase\n+\tto take. The connection phase includes DNS lookup and subsequent\n+\tTCP, TLS or QUIC handshakes.\n+\tCan be overridden by the `GIT_HTTP_CONNECT_TIMEOUT_MS`\n+\tenvironment variable.\n+\n http.lowSpeedLimit::\n http.lowSpeedTime::\n \tIf the HTTP transfer speed, in bytes per second, is less than\ndiff --git a/http.c b/http.c\nindex caccf2108e..befe9ea8a0 100644\n--- a/http.c\n+++ b/http.c\n@@ -68,6 +68,7 @@ static char *ssl_capath;\n static char *curl_no_proxy;\n static char *ssl_pinnedkey;\n static char *ssl_cainfo;\n+static long curl_connect_timeout_ms = -1;\n static long curl_low_speed_limit = -1;\n static long curl_low_speed_time = -1;\n static int curl_ftp_no_epsv;\n@@ -450,6 +451,10 @@ static int http_options(const char *var, const char *value,\n \t\tmax_requests = git_config_int(var, value, ctx->kvi);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(\"http.connecttimeoutms\", var)) {\n+\t\tcurl_connect_timeout_ms = git_config_int(var, value, ctx->kvi);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(\"http.lowspeedlimit\", var)) {\n \t\tcurl_low_speed_limit = git_config_int(var, value, ctx->kvi);\n \t\treturn 0;\n@@ -1215,6 +1220,10 @@ static CURL *get_curl_handle(void)\n \t\t\tcurl_easy_setopt(result, CURLOPT_PROXY_CAINFO, http_proxy_ssl_ca_info);\n \t}\n \n+\tif (curl_connect_timeout_ms > 0)\n+\t\tcurl_easy_setopt(result, CURLOPT_CONNECTTIMEOUT_MS,\n+\t\t\t\t curl_connect_timeout_ms);\n+\n \tif (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {\n \t\tcurl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,\n \t\t\t\t curl_low_speed_limit);\n@@ -1474,6 +1483,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)\n \n \tset_from_env(&user_agent, \"GIT_HTTP_USER_AGENT\");\n \n+\tset_long_from_env(&curl_connect_timeout_ms, \"GIT_HTTP_CONNECT_TIMEOUT_MS\");\n+\n \tset_long_from_env(&curl_low_speed_limit, \"GIT_HTTP_LOW_SPEED_LIMIT\");\n \tset_long_from_env(&curl_low_speed_time, \"GIT_HTTP_LOW_SPEED_TIME\");\n \ndiff --git a/t/meson.build b/t/meson.build\nindex 8ae6ab6c5f..6196736cb2 100644\n--- a/t/meson.build\n+++ b/t/meson.build\n@@ -724,6 +724,7 @@ integration_tests = [\n   't5582-fetch-negative-refspec.sh',\n   't5583-push-branches.sh',\n   't5584-http-429-retry.sh',\n+  't5585-http-connect-timeout.sh',\n   't5600-clone-fail-cleanup.sh',\n   't5601-clone.sh',\n   't5602-clone-remote-exec.sh',\ndiff --git a/t/t5585-http-connect-timeout.sh b/t/t5585-http-connect-timeout.sh\nnew file mode 100755\nindex 0000000000..7363e23bfe\n--- /dev/null\n+++ b/t/t5585-http-connect-timeout.sh\n@@ -0,0 +1,60 @@\n+#!/bin/sh\n+\n+test_description='test http.connecttimeoutms and GIT_HTTP_CONNECT_TIMEOUT_MS'\n+\n+. ./test-lib.sh\n+. \"$TEST_DIRECTORY\"/lib-httpd.sh\n+start_httpd\n+\n+test_expect_success 'setup repository' '\n+\ttest_commit initial &&\n+\tgit clone --bare . \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" &&\n+\tgit --git-dir=\"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\" config http.receivepack true\n+'\n+\n+test_expect_success 'http.connecttimeoutms accepts a positive integer via config' '\n+\ttest_config http.connecttimeoutms 5000 &&\n+\tgit ls-remote \"$HTTPD_URL/smart/repo.git\" >output &&\n+\ttest_grep \"refs/heads/\" output\n+'\n+\n+test_expect_success 'http.connecttimeoutms=0 is accepted (disables the option)' '\n+\ttest_config http.connecttimeoutms 0 &&\n+\tgit ls-remote \"$HTTPD_URL/smart/repo.git\" >output &&\n+\ttest_grep \"refs/heads/\" output\n+'\n+\n+test_expect_success 'GIT_HTTP_CONNECT_TIMEOUT_MS env var is accepted' '\n+\tGIT_HTTP_CONNECT_TIMEOUT_MS=5000 \\\n+\t\tgit ls-remote \"$HTTPD_URL/smart/repo.git\" >output 2>err &&\n+\ttest_grep \"refs/heads/\" output &&\n+\ttest_grep ! . err\n+'\n+\n+test_expect_success 'http.connecttimeoutms rejects non-numeric config value' '\n+\ttest_config http.connecttimeoutms not-a-number &&\n+\ttest_must_fail git ls-remote \"$HTTPD_URL/smart/repo.git\" 2>err &&\n+\ttest_grep \"bad numeric config value .not-a-number. for .http\\.connecttimeoutms.\" err\n+'\n+\n+test_expect_success 'http.connecttimeoutms rejects empty config value' '\n+\ttest_config http.connecttimeoutms \"\" &&\n+\ttest_must_fail git ls-remote \"$HTTPD_URL/smart/repo.git\" 2>err &&\n+\ttest_grep \"bad numeric config value\" err\n+'\n+\n+test_expect_success 'GIT_HTTP_CONNECT_TIMEOUT_MS warns on non-numeric value but succeeds' '\n+\tGIT_HTTP_CONNECT_TIMEOUT_MS=not-a-number \\\n+\t\tgit ls-remote \"$HTTPD_URL/smart/repo.git\" >output 2>err &&\n+\ttest_grep \"refs/heads/\" output &&\n+\ttest_grep \"failed to parse GIT_HTTP_CONNECT_TIMEOUT_MS\" err\n+'\n+\n+test_expect_success 'GIT_HTTP_CONNECT_TIMEOUT_MS warns on empty value but succeeds' '\n+\tGIT_HTTP_CONNECT_TIMEOUT_MS= \\\n+\t\tgit ls-remote \"$HTTPD_URL/smart/repo.git\" >output 2>err &&\n+\ttest_grep \"refs/heads/\" output &&\n+\ttest_grep \"failed to parse GIT_HTTP_CONNECT_TIMEOUT_MS\" err\n+'\n+\n+test_done\n\nbase-commit: 9a0c4701dcd5725c4184599322b52933ff5005ca\n-- \ngitgitgadget\n"},{"id":"548817","messageId":"xmqqqzktk5zr.fsf@gitster.g","threadId":"66053","inReplyTo":"pull.2362.git.git.1784798733557.gitgitgadget@gmail.com","subject":"Re: [PATCH] http: add a config to limit the connection time","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-23T16:47:36Z","receivedAt":"2026-07-23T16:47:40Z","isPatch":true,"body":"\"GalaxySnail via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> From: GalaxySnail <me@glxys.nl>\n>\n> By default, libcurl uses a 300 seconds timeout for the connection phase,\n> which is too long for some use cases.\n\nCan you elaborate a bit more on the use cases in which you want to\ntry connecting to an unreachable host yet want to give up on it very\nfast?\n\n> Add http.connecttimeoutms and GIT_HTTP_CONNECT_TIMEOUT_MS to specify\n> timeout in milliseconds for the connection phase. Both of them call\n> CURLOPT_CONNECTTIMEOUT_MS internally.\n>\n> Signed-off-by: GalaxySnail <me@glxys.nl>\n\nDocumentation/SubmittingPatches:[[real-name]] applies here.\n\n>  Documentation/config/http.adoc  |  7 ++++\n>  http.c                          | 11 ++++++\n>  t/meson.build                   |  1 +\n>  t/t5585-http-connect-timeout.sh | 60 +++++++++++++++++++++++++++++++++\n>  4 files changed, 79 insertions(+)\n>  create mode 100755 t/t5585-http-connect-timeout.sh\n>\n> diff --git a/Documentation/config/http.adoc b/Documentation/config/http.adoc\n> index 792a71b413..a4f7afa61e 100644\n> --- a/Documentation/config/http.adoc\n> +++ b/Documentation/config/http.adoc\n> @@ -300,6 +300,13 @@ for most push problems, but can increase memory consumption\n>  significantly since the entire buffer is allocated even for small\n>  pushes.\n>  \n> +http.connectTimeoutMS::\n> +\tMaximum time in milliseconds that you allow the connection phase\n> +\tto take. The connection phase includes DNS lookup and subsequent\n> +\tTCP, TLS or QUIC handshakes.\n> +\tCan be overridden by the `GIT_HTTP_CONNECT_TIMEOUT_MS`\n> +\tenvironment variable.\n\nOnce a knob is provided, users will want to know what value is\nused when unspecified, so they can gauge what a reasonable value to\nset would be.\n\n> diff --git a/http.c b/http.c\n> index caccf2108e..befe9ea8a0 100644\n> --- a/http.c\n> +++ b/http.c\n> @@ -68,6 +68,7 @@ static char *ssl_capath;\n>  static char *curl_no_proxy;\n>  static char *ssl_pinnedkey;\n>  static char *ssl_cainfo;\n> +static long curl_connect_timeout_ms = -1;\n>  static long curl_low_speed_limit = -1;\n>  static long curl_low_speed_time = -1;\n>  static int curl_ftp_no_epsv;\n> @@ -450,6 +451,10 @@ static int http_options(const char *var, const char *value,\n>  \t\tmax_requests = git_config_int(var, value, ctx->kvi);\n>  \t\treturn 0;\n>  \t}\n> +\tif (!strcmp(\"http.connecttimeoutms\", var)) {\n> +\t\tcurl_connect_timeout_ms = git_config_int(var, value, ctx->kvi);\n> +\t\treturn 0;\n> +\t}\n\nWe could set it to -1 if we wanted to, and behave as if no\nconfiguration variable were given.  That may be reasonable, but it\nshould be documented.\n\n> @@ -1215,6 +1220,10 @@ static CURL *get_curl_handle(void)\n>  \t\t\tcurl_easy_setopt(result, CURLOPT_PROXY_CAINFO, http_proxy_ssl_ca_info);\n>  \t}\n>  \n> +\tif (curl_connect_timeout_ms > 0)\n> +\t\tcurl_easy_setopt(result, CURLOPT_CONNECTTIMEOUT_MS,\n> +\t\t\t\t curl_connect_timeout_ms);\n\nThis code silently ignores setting the configuration variable to 0.\nTo the cURL library, however, passing a value of 0 to\nCURLOPT_CONNECTTIMEOUT_MS signals that it should use the default\nvalue (300s).\n\nPerhaps we should tweak the above to\n\n\tif (0 <= curlopt_connecttimeout_ms)\n\t\tcurl_easy_setopt(result, CURLOPT_CONNECTTIMEOUT_MS,\n\t\t\t\t curl_connect_timeout_ms);\n\nand then document what 0 means.\n\n> @@ -1474,6 +1483,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)\n>  \n>  \tset_from_env(&user_agent, \"GIT_HTTP_USER_AGENT\");\n>  \n> +\tset_long_from_env(&curl_connect_timeout_ms, \"GIT_HTTP_CONNECT_TIMEOUT_MS\");\n> +\n>  \tset_long_from_env(&curl_low_speed_limit, \"GIT_HTTP_LOW_SPEED_LIMIT\");\n>  \tset_long_from_env(&curl_low_speed_time, \"GIT_HTTP_LOW_SPEED_TIME\");\n\nThis, along with other environment variables, is processed after\nrepo_config() collects configured values by triggering the\nhttp_options() callback, so the environment overrides the configured\nvalue, as expected.\n\n> diff --git a/t/t5585-http-connect-timeout.sh b/t/t5585-http-connect-timeout.sh\n> new file mode 100755\n> index 0000000000..7363e23bfe\n> --- /dev/null\n> +++ b/t/t5585-http-connect-timeout.sh\n> @@ -0,0 +1,60 @@\n> +#!/bin/sh\n> +\n> +test_description='test http.connecttimeoutms and GIT_HTTP_CONNECT_TIMEOUT_MS'\n> +\n> +. ./test-lib.sh\n> +. \"$TEST_DIRECTORY\"/lib-httpd.sh\n> +start_httpd\n\nWhat are we testing with this new script, really?\n\nAs far as I can see, nobody is sitting next to the running test\nwith a stopwatch to ensure that the client times out as specified.  Should\nwe really consume a limited shared resource, the four-digit test\nnumber, for this instead of adding a few \"not a number (should fail\nto parse)\" tests to existing http tests?\n\nThanks.\n"}]}