http.c: use CURLOPT_RANGE for range requests

A HTTP server is permitted to return a non-range response to a HTTP range request (and Apache httpd in fact does this in some cases). While libcurl knows how to correctly handle this (by skipping bytes before and after the requested range), it only turns on this handling if it is aware that a range request is being made. By manually setting the range header instead of using CURLOPT_RANGE, we were hiding the fact that this was a range request from libcurl. This could cause corruption. Signed-off-by: David Turner <dturner@twopensource.com> Reviewed-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

David Turner committed Nov 2, 2015 at 16:39 UTC 835c4d3689972e616bd109cec3dd8cd4aa4d4c0d
2 files changed +12 -22
http.c
+12 -21
@@ -30,7 +30,6 @@ static CURL *curl_default;
30 #endif
31
32 #define PREV_BUF_SIZE 4096
33 -#define RANGE_HEADER_SIZE 30
33
34 char curl_errorstr[CURL_ERROR_SIZE];
35
@@ -681,6 +680,7 @@ struct active_request_slot *get_active_slot(void)
680 curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
681 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
682 curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
683 + curl_easy_setopt(slot->curl, CURLOPT_RANGE, NULL);
684 #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
685 curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
686 #endif
@@ -1173,6 +1173,13 @@ static const char *get_accept_language(void)
1173 return cached_accept_language;
1174 }
1175
1176 +static void http_opt_request_remainder(CURL *curl, off_t pos)
1177 +{
1178 + char buf[128];
1179 + xsnprintf(buf, sizeof(buf), "%"PRIuMAX"-", (uintmax_t)pos);
1180 + curl_easy_setopt(curl, CURLOPT_RANGE, buf);
1181 +}
1182 +
1183 /* http_request() targets */
1184 #define HTTP_REQUEST_STRBUF 0
1185 #define HTTP_REQUEST_FILE 1
@@ -1201,11 +1208,8 @@ static int http_request(const char *url,
1208 long posn = ftell(result);
1209 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
1210 fwrite);
1204 - if (posn > 0) {
1205 - strbuf_addf(&buf, "Range: bytes=%ld-", posn);
1206 - headers = curl_slist_append(headers, buf.buf);
1207 - strbuf_reset(&buf);
1208 - }
1211 + if (posn > 0)
1212 + http_opt_request_remainder(slot->curl, posn);
1213 } else
1214 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
1215 fwrite_buffer);
@@ -1515,10 +1519,6 @@ void release_http_pack_request(struct http_pack_request *preq)
1519 fclose(preq->packfile);
1520 preq->packfile = NULL;
1521 }
1518 - if (preq->range_header != NULL) {
1519 - curl_slist_free_all(preq->range_header);
1520 - preq->range_header = NULL;
1521 - }
1522 preq->slot = NULL;
1523 free(preq->url);
1524 free(preq);
@@ -1582,7 +1582,6 @@ struct http_pack_request *new_http_pack_request(
1582 struct packed_git *target, const char *base_url)
1583 {
1584 long prev_posn = 0;
1585 - char range[RANGE_HEADER_SIZE];
1585 struct strbuf buf = STRBUF_INIT;
1586 struct http_pack_request *preq;
1587
@@ -1620,10 +1619,7 @@ struct http_pack_request *new_http_pack_request(
1619 fprintf(stderr,
1620 "Resuming fetch of pack %s at byte %ld\n",
1621 sha1_to_hex(target->sha1), prev_posn);
1623 - xsnprintf(range, sizeof(range), "Range: bytes=%ld-", prev_posn);
1624 - preq->range_header = curl_slist_append(NULL, range);
1625 - curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
1626 - preq->range_header);
1622 + http_opt_request_remainder(preq->slot->curl, prev_posn);
1623 }
1624
1625 return preq;
@@ -1673,8 +1669,6 @@ struct http_object_request *new_http_object_request(const char *base_url,
1669 char prev_buf[PREV_BUF_SIZE];
1670 ssize_t prev_read = 0;
1671 long prev_posn = 0;
1676 - char range[RANGE_HEADER_SIZE];
1677 - struct curl_slist *range_header = NULL;
1672 struct http_object_request *freq;
1673
1674 freq = xcalloc(1, sizeof(*freq));
@@ -1780,10 +1774,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
1774 fprintf(stderr,
1775 "Resuming fetch of object %s at byte %ld\n",
1776 hex, prev_posn);
1783 - xsnprintf(range, sizeof(range), "Range: bytes=%ld-", prev_posn);
1784 - range_header = curl_slist_append(range_header, range);
1785 - curl_easy_setopt(freq->slot->curl,
1786 - CURLOPT_HTTPHEADER, range_header);
1777 + http_opt_request_remainder(freq->slot->curl, prev_posn);
1778 }
1779
1780 return freq;
http.h
-1
@@ -190,7 +190,6 @@ struct http_pack_request {
190 struct packed_git **lst;
191 FILE *packfile;
192 char tmpfile[PATH_MAX];
193 - struct curl_slist *range_header;
193 struct active_request_slot *slot;
194 };
195