http*: add helper methods for fetching packs
[git.git] / http.c
blob381593441e67ac542d9b08e15fea0d81629df119
1 #include "http.h"
2 #include "pack.h"
4 int data_received;
5 int active_requests;
6 int http_is_verbose;
8 #ifdef USE_CURL_MULTI
9 static int max_requests = -1;
10 static CURLM *curlm;
11 #endif
12 #ifndef NO_CURL_EASY_DUPHANDLE
13 static CURL *curl_default;
14 #endif
15 char curl_errorstr[CURL_ERROR_SIZE];
17 static int curl_ssl_verify = -1;
18 static const char *ssl_cert;
19 #if LIBCURL_VERSION_NUM >= 0x070902
20 static const char *ssl_key;
21 #endif
22 #if LIBCURL_VERSION_NUM >= 0x070908
23 static const char *ssl_capath;
24 #endif
25 static const char *ssl_cainfo;
26 static long curl_low_speed_limit = -1;
27 static long curl_low_speed_time = -1;
28 static int curl_ftp_no_epsv;
29 static const char *curl_http_proxy;
30 static char *user_name, *user_pass;
32 static struct curl_slist *pragma_header;
34 struct curl_slist *no_pragma_header;
36 static struct active_request_slot *active_queue_head;
38 size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
40 size_t size = eltsize * nmemb;
41 struct buffer *buffer = buffer_;
43 if (size > buffer->buf.len - buffer->posn)
44 size = buffer->buf.len - buffer->posn;
45 memcpy(ptr, buffer->buf.buf + buffer->posn, size);
46 buffer->posn += size;
48 return size;
51 #ifndef NO_CURL_IOCTL
52 curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp)
54 struct buffer *buffer = clientp;
56 switch (cmd) {
57 case CURLIOCMD_NOP:
58 return CURLIOE_OK;
60 case CURLIOCMD_RESTARTREAD:
61 buffer->posn = 0;
62 return CURLIOE_OK;
64 default:
65 return CURLIOE_UNKNOWNCMD;
68 #endif
70 size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
72 size_t size = eltsize * nmemb;
73 struct strbuf *buffer = buffer_;
75 strbuf_add(buffer, ptr, size);
76 data_received++;
77 return size;
80 size_t fwrite_null(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf)
82 data_received++;
83 return eltsize * nmemb;
86 static void finish_active_slot(struct active_request_slot *slot);
88 #ifdef USE_CURL_MULTI
89 static void process_curl_messages(void)
91 int num_messages;
92 struct active_request_slot *slot;
93 CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);
95 while (curl_message != NULL) {
96 if (curl_message->msg == CURLMSG_DONE) {
97 int curl_result = curl_message->data.result;
98 slot = active_queue_head;
99 while (slot != NULL &&
100 slot->curl != curl_message->easy_handle)
101 slot = slot->next;
102 if (slot != NULL) {
103 curl_multi_remove_handle(curlm, slot->curl);
104 slot->curl_result = curl_result;
105 finish_active_slot(slot);
106 } else {
107 fprintf(stderr, "Received DONE message for unknown request!\n");
109 } else {
110 fprintf(stderr, "Unknown CURL message received: %d\n",
111 (int)curl_message->msg);
113 curl_message = curl_multi_info_read(curlm, &num_messages);
116 #endif
118 static int http_options(const char *var, const char *value, void *cb)
120 if (!strcmp("http.sslverify", var)) {
121 curl_ssl_verify = git_config_bool(var, value);
122 return 0;
124 if (!strcmp("http.sslcert", var))
125 return git_config_string(&ssl_cert, var, value);
126 #if LIBCURL_VERSION_NUM >= 0x070902
127 if (!strcmp("http.sslkey", var))
128 return git_config_string(&ssl_key, var, value);
129 #endif
130 #if LIBCURL_VERSION_NUM >= 0x070908
131 if (!strcmp("http.sslcapath", var))
132 return git_config_string(&ssl_capath, var, value);
133 #endif
134 if (!strcmp("http.sslcainfo", var))
135 return git_config_string(&ssl_cainfo, var, value);
136 #ifdef USE_CURL_MULTI
137 if (!strcmp("http.maxrequests", var)) {
138 max_requests = git_config_int(var, value);
139 return 0;
141 #endif
142 if (!strcmp("http.lowspeedlimit", var)) {
143 curl_low_speed_limit = (long)git_config_int(var, value);
144 return 0;
146 if (!strcmp("http.lowspeedtime", var)) {
147 curl_low_speed_time = (long)git_config_int(var, value);
148 return 0;
151 if (!strcmp("http.noepsv", var)) {
152 curl_ftp_no_epsv = git_config_bool(var, value);
153 return 0;
155 if (!strcmp("http.proxy", var))
156 return git_config_string(&curl_http_proxy, var, value);
158 /* Fall back on the default ones */
159 return git_default_config(var, value, cb);
162 static void init_curl_http_auth(CURL *result)
164 if (user_name) {
165 struct strbuf up = STRBUF_INIT;
166 if (!user_pass)
167 user_pass = xstrdup(getpass("Password: "));
168 strbuf_addf(&up, "%s:%s", user_name, user_pass);
169 curl_easy_setopt(result, CURLOPT_USERPWD,
170 strbuf_detach(&up, NULL));
174 static CURL *get_curl_handle(void)
176 CURL *result = curl_easy_init();
178 if (!curl_ssl_verify) {
179 curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
180 curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0);
181 } else {
182 /* Verify authenticity of the peer's certificate */
183 curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 1);
184 /* The name in the cert must match whom we tried to connect */
185 curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
188 #if LIBCURL_VERSION_NUM >= 0x070907
189 curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
190 #endif
192 init_curl_http_auth(result);
194 if (ssl_cert != NULL)
195 curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
196 #if LIBCURL_VERSION_NUM >= 0x070902
197 if (ssl_key != NULL)
198 curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
199 #endif
200 #if LIBCURL_VERSION_NUM >= 0x070908
201 if (ssl_capath != NULL)
202 curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
203 #endif
204 if (ssl_cainfo != NULL)
205 curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
206 curl_easy_setopt(result, CURLOPT_FAILONERROR, 1);
208 if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
209 curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
210 curl_low_speed_limit);
211 curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,
212 curl_low_speed_time);
215 curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
217 if (getenv("GIT_CURL_VERBOSE"))
218 curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
220 curl_easy_setopt(result, CURLOPT_USERAGENT, GIT_USER_AGENT);
222 if (curl_ftp_no_epsv)
223 curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
225 if (curl_http_proxy)
226 curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
228 return result;
231 static void http_auth_init(const char *url)
233 char *at, *colon, *cp, *slash;
234 int len;
236 cp = strstr(url, "://");
237 if (!cp)
238 return;
241 * Ok, the URL looks like "proto://something". Which one?
242 * "proto://<user>:<pass>@<host>/...",
243 * "proto://<user>@<host>/...", or just
244 * "proto://<host>/..."?
246 cp += 3;
247 at = strchr(cp, '@');
248 colon = strchr(cp, ':');
249 slash = strchrnul(cp, '/');
250 if (!at || slash <= at)
251 return; /* No credentials */
252 if (!colon || at <= colon) {
253 /* Only username */
254 len = at - cp;
255 user_name = xmalloc(len + 1);
256 memcpy(user_name, cp, len);
257 user_name[len] = '\0';
258 user_pass = NULL;
259 } else {
260 len = colon - cp;
261 user_name = xmalloc(len + 1);
262 memcpy(user_name, cp, len);
263 user_name[len] = '\0';
264 len = at - (colon + 1);
265 user_pass = xmalloc(len + 1);
266 memcpy(user_pass, colon + 1, len);
267 user_pass[len] = '\0';
271 static void set_from_env(const char **var, const char *envname)
273 const char *val = getenv(envname);
274 if (val)
275 *var = val;
278 void http_init(struct remote *remote)
280 char *low_speed_limit;
281 char *low_speed_time;
283 http_is_verbose = 0;
285 git_config(http_options, NULL);
287 curl_global_init(CURL_GLOBAL_ALL);
289 if (remote && remote->http_proxy)
290 curl_http_proxy = xstrdup(remote->http_proxy);
292 pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
293 no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
295 #ifdef USE_CURL_MULTI
297 char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
298 if (http_max_requests != NULL)
299 max_requests = atoi(http_max_requests);
302 curlm = curl_multi_init();
303 if (curlm == NULL) {
304 fprintf(stderr, "Error creating curl multi handle.\n");
305 exit(1);
307 #endif
309 if (getenv("GIT_SSL_NO_VERIFY"))
310 curl_ssl_verify = 0;
312 set_from_env(&ssl_cert, "GIT_SSL_CERT");
313 #if LIBCURL_VERSION_NUM >= 0x070902
314 set_from_env(&ssl_key, "GIT_SSL_KEY");
315 #endif
316 #if LIBCURL_VERSION_NUM >= 0x070908
317 set_from_env(&ssl_capath, "GIT_SSL_CAPATH");
318 #endif
319 set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
321 low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
322 if (low_speed_limit != NULL)
323 curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
324 low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME");
325 if (low_speed_time != NULL)
326 curl_low_speed_time = strtol(low_speed_time, NULL, 10);
328 if (curl_ssl_verify == -1)
329 curl_ssl_verify = 1;
331 #ifdef USE_CURL_MULTI
332 if (max_requests < 1)
333 max_requests = DEFAULT_MAX_REQUESTS;
334 #endif
336 if (getenv("GIT_CURL_FTP_NO_EPSV"))
337 curl_ftp_no_epsv = 1;
339 if (remote && remote->url && remote->url[0])
340 http_auth_init(remote->url[0]);
342 #ifndef NO_CURL_EASY_DUPHANDLE
343 curl_default = get_curl_handle();
344 #endif
347 void http_cleanup(void)
349 struct active_request_slot *slot = active_queue_head;
351 while (slot != NULL) {
352 struct active_request_slot *next = slot->next;
353 if (slot->curl != NULL) {
354 #ifdef USE_CURL_MULTI
355 curl_multi_remove_handle(curlm, slot->curl);
356 #endif
357 curl_easy_cleanup(slot->curl);
359 free(slot);
360 slot = next;
362 active_queue_head = NULL;
364 #ifndef NO_CURL_EASY_DUPHANDLE
365 curl_easy_cleanup(curl_default);
366 #endif
368 #ifdef USE_CURL_MULTI
369 curl_multi_cleanup(curlm);
370 #endif
371 curl_global_cleanup();
373 curl_slist_free_all(pragma_header);
374 pragma_header = NULL;
376 curl_slist_free_all(no_pragma_header);
377 no_pragma_header = NULL;
379 if (curl_http_proxy) {
380 free((void *)curl_http_proxy);
381 curl_http_proxy = NULL;
385 struct active_request_slot *get_active_slot(void)
387 struct active_request_slot *slot = active_queue_head;
388 struct active_request_slot *newslot;
390 #ifdef USE_CURL_MULTI
391 int num_transfers;
393 /* Wait for a slot to open up if the queue is full */
394 while (active_requests >= max_requests) {
395 curl_multi_perform(curlm, &num_transfers);
396 if (num_transfers < active_requests)
397 process_curl_messages();
399 #endif
401 while (slot != NULL && slot->in_use)
402 slot = slot->next;
404 if (slot == NULL) {
405 newslot = xmalloc(sizeof(*newslot));
406 newslot->curl = NULL;
407 newslot->in_use = 0;
408 newslot->next = NULL;
410 slot = active_queue_head;
411 if (slot == NULL) {
412 active_queue_head = newslot;
413 } else {
414 while (slot->next != NULL)
415 slot = slot->next;
416 slot->next = newslot;
418 slot = newslot;
421 if (slot->curl == NULL) {
422 #ifdef NO_CURL_EASY_DUPHANDLE
423 slot->curl = get_curl_handle();
424 #else
425 slot->curl = curl_easy_duphandle(curl_default);
426 #endif
429 active_requests++;
430 slot->in_use = 1;
431 slot->local = NULL;
432 slot->results = NULL;
433 slot->finished = NULL;
434 slot->callback_data = NULL;
435 slot->callback_func = NULL;
436 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
437 curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
438 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);
439 curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);
440 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);
441 curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
442 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
444 return slot;
447 int start_active_slot(struct active_request_slot *slot)
449 #ifdef USE_CURL_MULTI
450 CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);
451 int num_transfers;
453 if (curlm_result != CURLM_OK &&
454 curlm_result != CURLM_CALL_MULTI_PERFORM) {
455 active_requests--;
456 slot->in_use = 0;
457 return 0;
461 * We know there must be something to do, since we just added
462 * something.
464 curl_multi_perform(curlm, &num_transfers);
465 #endif
466 return 1;
469 #ifdef USE_CURL_MULTI
470 struct fill_chain {
471 void *data;
472 int (*fill)(void *);
473 struct fill_chain *next;
476 static struct fill_chain *fill_cfg;
478 void add_fill_function(void *data, int (*fill)(void *))
480 struct fill_chain *new = xmalloc(sizeof(*new));
481 struct fill_chain **linkp = &fill_cfg;
482 new->data = data;
483 new->fill = fill;
484 new->next = NULL;
485 while (*linkp)
486 linkp = &(*linkp)->next;
487 *linkp = new;
490 void fill_active_slots(void)
492 struct active_request_slot *slot = active_queue_head;
494 while (active_requests < max_requests) {
495 struct fill_chain *fill;
496 for (fill = fill_cfg; fill; fill = fill->next)
497 if (fill->fill(fill->data))
498 break;
500 if (!fill)
501 break;
504 while (slot != NULL) {
505 if (!slot->in_use && slot->curl != NULL) {
506 curl_easy_cleanup(slot->curl);
507 slot->curl = NULL;
509 slot = slot->next;
513 void step_active_slots(void)
515 int num_transfers;
516 CURLMcode curlm_result;
518 do {
519 curlm_result = curl_multi_perform(curlm, &num_transfers);
520 } while (curlm_result == CURLM_CALL_MULTI_PERFORM);
521 if (num_transfers < active_requests) {
522 process_curl_messages();
523 fill_active_slots();
526 #endif
528 void run_active_slot(struct active_request_slot *slot)
530 #ifdef USE_CURL_MULTI
531 long last_pos = 0;
532 long current_pos;
533 fd_set readfds;
534 fd_set writefds;
535 fd_set excfds;
536 int max_fd;
537 struct timeval select_timeout;
538 int finished = 0;
540 slot->finished = &finished;
541 while (!finished) {
542 data_received = 0;
543 step_active_slots();
545 if (!data_received && slot->local != NULL) {
546 current_pos = ftell(slot->local);
547 if (current_pos > last_pos)
548 data_received++;
549 last_pos = current_pos;
552 if (slot->in_use && !data_received) {
553 max_fd = 0;
554 FD_ZERO(&readfds);
555 FD_ZERO(&writefds);
556 FD_ZERO(&excfds);
557 select_timeout.tv_sec = 0;
558 select_timeout.tv_usec = 50000;
559 select(max_fd, &readfds, &writefds,
560 &excfds, &select_timeout);
563 #else
564 while (slot->in_use) {
565 slot->curl_result = curl_easy_perform(slot->curl);
566 finish_active_slot(slot);
568 #endif
571 static void closedown_active_slot(struct active_request_slot *slot)
573 active_requests--;
574 slot->in_use = 0;
577 void release_active_slot(struct active_request_slot *slot)
579 closedown_active_slot(slot);
580 if (slot->curl) {
581 #ifdef USE_CURL_MULTI
582 curl_multi_remove_handle(curlm, slot->curl);
583 #endif
584 curl_easy_cleanup(slot->curl);
585 slot->curl = NULL;
587 #ifdef USE_CURL_MULTI
588 fill_active_slots();
589 #endif
592 static void finish_active_slot(struct active_request_slot *slot)
594 closedown_active_slot(slot);
595 curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
597 if (slot->finished != NULL)
598 (*slot->finished) = 1;
600 /* Store slot results so they can be read after the slot is reused */
601 if (slot->results != NULL) {
602 slot->results->curl_result = slot->curl_result;
603 slot->results->http_code = slot->http_code;
606 /* Run callback if appropriate */
607 if (slot->callback_func != NULL)
608 slot->callback_func(slot->callback_data);
611 void finish_all_active_slots(void)
613 struct active_request_slot *slot = active_queue_head;
615 while (slot != NULL)
616 if (slot->in_use) {
617 run_active_slot(slot);
618 slot = active_queue_head;
619 } else {
620 slot = slot->next;
624 /* Helpers for modifying and creating URLs */
625 static inline int needs_quote(int ch)
627 if (((ch >= 'A') && (ch <= 'Z'))
628 || ((ch >= 'a') && (ch <= 'z'))
629 || ((ch >= '0') && (ch <= '9'))
630 || (ch == '/')
631 || (ch == '-')
632 || (ch == '.'))
633 return 0;
634 return 1;
637 static inline int hex(int v)
639 if (v < 10)
640 return '0' + v;
641 else
642 return 'A' + v - 10;
645 static void end_url_with_slash(struct strbuf *buf, const char *url)
647 strbuf_addstr(buf, url);
648 if (buf->len && buf->buf[buf->len - 1] != '/')
649 strbuf_addstr(buf, "/");
652 static char *quote_ref_url(const char *base, const char *ref)
654 struct strbuf buf = STRBUF_INIT;
655 const char *cp;
656 int ch;
658 end_url_with_slash(&buf, base);
660 for (cp = ref; (ch = *cp) != 0; cp++)
661 if (needs_quote(ch))
662 strbuf_addf(&buf, "%%%02x", ch);
663 else
664 strbuf_addch(&buf, *cp);
666 return strbuf_detach(&buf, NULL);
669 /* http_request() targets */
670 #define HTTP_REQUEST_STRBUF 0
671 #define HTTP_REQUEST_FILE 1
673 static int http_request(const char *url, void *result, int target, int options)
675 struct active_request_slot *slot;
676 struct slot_results results;
677 struct curl_slist *headers = NULL;
678 struct strbuf buf = STRBUF_INIT;
679 int ret;
681 slot = get_active_slot();
682 slot->results = &results;
683 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
685 if (result == NULL) {
686 curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
687 } else {
688 curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
689 curl_easy_setopt(slot->curl, CURLOPT_FILE, result);
691 if (target == HTTP_REQUEST_FILE) {
692 long posn = ftell(result);
693 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
694 fwrite);
695 if (posn > 0) {
696 strbuf_addf(&buf, "Range: bytes=%ld-", posn);
697 headers = curl_slist_append(headers, buf.buf);
698 strbuf_reset(&buf);
700 slot->local = result;
701 } else
702 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
703 fwrite_buffer);
706 strbuf_addstr(&buf, "Pragma:");
707 if (options & HTTP_NO_CACHE)
708 strbuf_addstr(&buf, " no-cache");
710 headers = curl_slist_append(headers, buf.buf);
712 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
713 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
715 if (start_active_slot(slot)) {
716 run_active_slot(slot);
717 if (results.curl_result == CURLE_OK)
718 ret = HTTP_OK;
719 else if (missing_target(&results))
720 ret = HTTP_MISSING_TARGET;
721 else
722 ret = HTTP_ERROR;
723 } else {
724 error("Unable to start HTTP request for %s", url);
725 ret = HTTP_START_FAILED;
728 slot->local = NULL;
729 curl_slist_free_all(headers);
730 strbuf_release(&buf);
732 return ret;
735 int http_get_strbuf(const char *url, struct strbuf *result, int options)
737 return http_request(url, result, HTTP_REQUEST_STRBUF, options);
740 int http_get_file(const char *url, const char *filename, int options)
742 int ret;
743 struct strbuf tmpfile = STRBUF_INIT;
744 FILE *result;
746 strbuf_addf(&tmpfile, "%s.temp", filename);
747 result = fopen(tmpfile.buf, "a");
748 if (! result) {
749 error("Unable to open local file %s", tmpfile.buf);
750 ret = HTTP_ERROR;
751 goto cleanup;
754 ret = http_request(url, result, HTTP_REQUEST_FILE, options);
755 fclose(result);
757 if ((ret == HTTP_OK) && move_temp_to_file(tmpfile.buf, filename))
758 ret = HTTP_ERROR;
759 cleanup:
760 strbuf_release(&tmpfile);
761 return ret;
764 int http_error(const char *url, int ret)
766 /* http_request has already handled HTTP_START_FAILED. */
767 if (ret != HTTP_START_FAILED)
768 error("%s while accessing %s\n", curl_errorstr, url);
770 return ret;
773 int http_fetch_ref(const char *base, struct ref *ref)
775 char *url;
776 struct strbuf buffer = STRBUF_INIT;
777 int ret = -1;
779 url = quote_ref_url(base, ref->name);
780 if (http_get_strbuf(url, &buffer, HTTP_NO_CACHE) == HTTP_OK) {
781 strbuf_rtrim(&buffer);
782 if (buffer.len == 40)
783 ret = get_sha1_hex(buffer.buf, ref->old_sha1);
784 else if (!prefixcmp(buffer.buf, "ref: ")) {
785 ref->symref = xstrdup(buffer.buf + 5);
786 ret = 0;
790 strbuf_release(&buffer);
791 free(url);
792 return ret;
795 /* Helpers for fetching packs */
796 static int fetch_pack_index(unsigned char *sha1, const char *base_url)
798 int ret = 0;
799 char *hex = xstrdup(sha1_to_hex(sha1));
800 char *filename;
801 char *url;
802 struct strbuf buf = STRBUF_INIT;
804 /* Don't use the index if the pack isn't there */
805 end_url_with_slash(&buf, base_url);
806 strbuf_addf(&buf, "objects/pack/pack-%s.pack", hex);
807 url = strbuf_detach(&buf, 0);
809 if (http_get_strbuf(url, NULL, 0)) {
810 ret = error("Unable to verify pack %s is available",
811 hex);
812 goto cleanup;
815 if (has_pack_index(sha1)) {
816 ret = 0;
817 goto cleanup;
820 if (http_is_verbose)
821 fprintf(stderr, "Getting index for pack %s\n", hex);
823 end_url_with_slash(&buf, base_url);
824 strbuf_addf(&buf, "objects/pack/pack-%s.idx", hex);
825 url = strbuf_detach(&buf, NULL);
827 filename = sha1_pack_index_name(sha1);
828 if (http_get_file(url, filename, 0) != HTTP_OK)
829 ret = error("Unable to get pack index %s\n", url);
831 cleanup:
832 free(hex);
833 free(url);
834 return ret;
837 static int fetch_and_setup_pack_index(struct packed_git **packs_head,
838 unsigned char *sha1, const char *base_url)
840 struct packed_git *new_pack;
842 if (fetch_pack_index(sha1, base_url))
843 return -1;
845 new_pack = parse_pack_index(sha1);
846 if (!new_pack)
847 return -1; /* parse_pack_index() already issued error message */
848 new_pack->next = *packs_head;
849 *packs_head = new_pack;
850 return 0;
853 int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
855 int ret = 0, i = 0;
856 char *url, *data;
857 struct strbuf buf = STRBUF_INIT;
858 unsigned char sha1[20];
860 end_url_with_slash(&buf, base_url);
861 strbuf_addstr(&buf, "objects/info/packs");
862 url = strbuf_detach(&buf, NULL);
864 ret = http_get_strbuf(url, &buf, HTTP_NO_CACHE);
865 if (ret != HTTP_OK)
866 goto cleanup;
868 data = buf.buf;
869 while (i < buf.len) {
870 switch (data[i]) {
871 case 'P':
872 i++;
873 if (i + 52 <= buf.len &&
874 !prefixcmp(data + i, " pack-") &&
875 !prefixcmp(data + i + 46, ".pack\n")) {
876 get_sha1_hex(data + i + 6, sha1);
877 fetch_and_setup_pack_index(packs_head, sha1,
878 base_url);
879 i += 51;
880 break;
882 default:
883 while (i < buf.len && data[i] != '\n')
884 i++;
886 i++;
889 cleanup:
890 free(url);
891 return ret;
894 void release_http_pack_request(struct http_pack_request *preq)
896 if (preq->packfile != NULL) {
897 fclose(preq->packfile);
898 preq->packfile = NULL;
899 preq->slot->local = NULL;
901 if (preq->range_header != NULL) {
902 curl_slist_free_all(preq->range_header);
903 preq->range_header = NULL;
905 preq->slot = NULL;
906 free(preq->url);
909 int finish_http_pack_request(struct http_pack_request *preq)
911 int ret;
912 struct packed_git **lst;
914 preq->target->pack_size = ftell(preq->packfile);
916 if (preq->packfile != NULL) {
917 fclose(preq->packfile);
918 preq->packfile = NULL;
919 preq->slot->local = NULL;
922 ret = move_temp_to_file(preq->tmpfile, preq->filename);
923 if (ret)
924 return ret;
926 lst = preq->lst;
927 while (*lst != preq->target)
928 lst = &((*lst)->next);
929 *lst = (*lst)->next;
931 if (verify_pack(preq->target))
932 return -1;
933 install_packed_git(preq->target);
935 return 0;
938 struct http_pack_request *new_http_pack_request(
939 struct packed_git *target, const char *base_url)
941 char *url;
942 char *filename;
943 long prev_posn = 0;
944 char range[RANGE_HEADER_SIZE];
945 struct strbuf buf = STRBUF_INIT;
946 struct http_pack_request *preq;
948 preq = xmalloc(sizeof(*preq));
949 preq->target = target;
950 preq->range_header = NULL;
952 end_url_with_slash(&buf, base_url);
953 strbuf_addf(&buf, "objects/pack/pack-%s.pack",
954 sha1_to_hex(target->sha1));
955 url = strbuf_detach(&buf, NULL);
956 preq->url = xstrdup(url);
958 filename = sha1_pack_name(target->sha1);
959 snprintf(preq->filename, sizeof(preq->filename), "%s", filename);
960 snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp", filename);
961 preq->packfile = fopen(preq->tmpfile, "a");
962 if (!preq->packfile) {
963 error("Unable to open local file %s for pack",
964 preq->tmpfile);
965 goto abort;
968 preq->slot = get_active_slot();
969 preq->slot->local = preq->packfile;
970 curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile);
971 curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
972 curl_easy_setopt(preq->slot->curl, CURLOPT_URL, url);
973 curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
974 no_pragma_header);
977 * If there is data present from a previous transfer attempt,
978 * resume where it left off
980 prev_posn = ftell(preq->packfile);
981 if (prev_posn>0) {
982 if (http_is_verbose)
983 fprintf(stderr,
984 "Resuming fetch of pack %s at byte %ld\n",
985 sha1_to_hex(target->sha1), prev_posn);
986 sprintf(range, "Range: bytes=%ld-", prev_posn);
987 preq->range_header = curl_slist_append(NULL, range);
988 curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
989 preq->range_header);
992 return preq;
994 abort:
995 free(filename);
996 return NULL;