diff --git a/http-walker.c b/http-walker.c index 8ae5d76c6a..48b1b3a193 100644 --- a/http-walker.c +++ b/http-walker.c @@ -98,6 +98,11 @@ static void process_object_response(void *callback_data) process_http_object_request(obj_req->req); obj_req->state = COMPLETE; + normalize_curl_result(&obj_req->req->curl_result, + obj_req->req->http_code, + obj_req->req->errorstr, + sizeof(obj_req->req->errorstr)); + /* Use alternates if necessary */ if (missing_target(obj_req->req)) { fetch_alternates(walker, alt->base); @@ -208,6 +213,9 @@ static void process_alternates_response(void *callback_data) char *data; int i = 0; + normalize_curl_result(&slot->curl_result, slot->http_code, + curl_errorstr, sizeof(curl_errorstr)); + if (alt_req->http_specific) { if (slot->curl_result != CURLE_OK || !alt_req->buffer->len) { @@ -518,17 +526,8 @@ static int fetch_object(struct walker *walker, unsigned char *sha1) req->localfile = -1; } - /* - * we turned off CURLOPT_FAILONERROR to avoid losing a - * persistent connection and got CURLE_OK. - */ - if (req->http_code >= 300 && req->curl_result == CURLE_OK && - (starts_with(req->url, "http://") || - starts_with(req->url, "https://"))) { - req->curl_result = CURLE_HTTP_RETURNED_ERROR; - xsnprintf(req->errorstr, sizeof(req->errorstr), - "HTTP request failed"); - } + normalize_curl_result(&req->curl_result, req->http_code, + req->errorstr, sizeof(req->errorstr)); if (obj_req->state == ABORTED) { ret = error("Request for %s aborted", hex); diff --git a/http.c b/http.c index a32ad36ddf..89fcd36a80 100644 --- a/http.c +++ b/http.c @@ -1544,7 +1544,8 @@ char *get_remote_object_url(const char *url, const char *hex, return strbuf_detach(&buf, NULL); } -static int handle_curl_result(struct slot_results *results) +void normalize_curl_result(CURLcode *result, long http_code, + char *errorstr, size_t errorlen) { /* * If we see a failing http code with CURLE_OK, we have turned off @@ -1554,19 +1555,24 @@ static int handle_curl_result(struct slot_results *results) * Likewise, if we see a redirect (30x code), that means we turned off * redirect-following, and we should treat the result as an error. */ - if (results->curl_result == CURLE_OK && - results->http_code >= 300) { - results->curl_result = CURLE_HTTP_RETURNED_ERROR; + if (*result == CURLE_OK && http_code >= 300) { + *result = CURLE_HTTP_RETURNED_ERROR; /* * Normally curl will already have put the "reason phrase" * from the server into curl_errorstr; unfortunately without * FAILONERROR it is lost, so we can give only the numeric * status code. */ - xsnprintf(curl_errorstr, sizeof(curl_errorstr), + xsnprintf(errorstr, errorlen, "The requested URL returned error: %ld", - results->http_code); + http_code); } +} + +static int handle_curl_result(struct slot_results *results) +{ + normalize_curl_result(&results->curl_result, results->http_code, + curl_errorstr, sizeof(curl_errorstr)); if (results->curl_result == CURLE_OK) { credential_approve(&http_auth); diff --git a/http.h b/http.h index 4eb4e808e5..f0d271bb7b 100644 --- a/http.h +++ b/http.h @@ -136,6 +136,15 @@ static inline int missing__target(int code, int result) #define missing_target(a) missing__target((a)->http_code, (a)->curl_result) +/* + * Normalize curl results to handle CURL_FAILONERROR (or lack thereof). Failing + * http codes have their "result" converted to CURLE_HTTP_RETURNED_ERROR, and + * an appropriate string placed in the errorstr buffer (pass curl_errorstr if + * you don't have a custom buffer). + */ +void normalize_curl_result(CURLcode *result, long http_code, char *errorstr, + size_t errorlen); + /* Helpers for modifying and creating URLs */ extern void append_remote_object_url(struct strbuf *buf, const char *url, const char *hex, diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh index 6d7d88ccc9..694b77c855 100755 --- a/t/t5550-http-fetch-dumb.sh +++ b/t/t5550-http-fetch-dumb.sh @@ -408,5 +408,21 @@ test_expect_success 'print HTTP error when any intermediate redirect throws erro test_i18ngrep "unable to access.*/redir-to/502" stderr ' +test_expect_success 'fetching via http alternates works' ' + parent=$HTTPD_DOCUMENT_ROOT_PATH/alt-parent.git && + git init --bare "$parent" && + git -C "$parent" --work-tree=. commit --allow-empty -m foo && + git -C "$parent" update-server-info && + commit=$(git -C "$parent" rev-parse HEAD) && + + child=$HTTPD_DOCUMENT_ROOT_PATH/alt-child.git && + git init --bare "$child" && + echo "../../alt-parent.git/objects" >"$child/objects/info/alternates" && + git -C "$child" update-ref HEAD $commit && + git -C "$child" update-server-info && + + git -c http.followredirects=true clone "$HTTPD_URL/dumb/alt-child.git" +' + stop_httpd test_done