Code

Merge branch 'jn/maint-sequencer-fixes'
[git.git] / http.c
1 #include "http.h"
2 #include "pack.h"
3 #include "sideband.h"
4 #include "run-command.h"
5 #include "url.h"
6 #include "credential.h"
8 int active_requests;
9 int http_is_verbose;
10 size_t http_post_buffer = 16 * LARGE_PACKET_MAX;
12 #if LIBCURL_VERSION_NUM >= 0x070a06
13 #define LIBCURL_CAN_HANDLE_AUTH_ANY
14 #endif
16 static int min_curl_sessions = 1;
17 static int curl_session_count;
18 #ifdef USE_CURL_MULTI
19 static int max_requests = -1;
20 static CURLM *curlm;
21 #endif
22 #ifndef NO_CURL_EASY_DUPHANDLE
23 static CURL *curl_default;
24 #endif
26 #define PREV_BUF_SIZE 4096
27 #define RANGE_HEADER_SIZE 30
29 char curl_errorstr[CURL_ERROR_SIZE];
31 static int curl_ssl_verify = -1;
32 static const char *ssl_cert;
33 #if LIBCURL_VERSION_NUM >= 0x070903
34 static const char *ssl_key;
35 #endif
36 #if LIBCURL_VERSION_NUM >= 0x070908
37 static const char *ssl_capath;
38 #endif
39 static const char *ssl_cainfo;
40 static long curl_low_speed_limit = -1;
41 static long curl_low_speed_time = -1;
42 static int curl_ftp_no_epsv;
43 static const char *curl_http_proxy;
44 static const char *curl_cookie_file;
45 static struct credential http_auth = CREDENTIAL_INIT;
46 static const char *user_agent;
48 #if LIBCURL_VERSION_NUM >= 0x071700
49 /* Use CURLOPT_KEYPASSWD as is */
50 #elif LIBCURL_VERSION_NUM >= 0x070903
51 #define CURLOPT_KEYPASSWD CURLOPT_SSLKEYPASSWD
52 #else
53 #define CURLOPT_KEYPASSWD CURLOPT_SSLCERTPASSWD
54 #endif
56 static struct credential cert_auth = CREDENTIAL_INIT;
57 static int ssl_cert_password_required;
59 static struct curl_slist *pragma_header;
60 static struct curl_slist *no_pragma_header;
62 static struct active_request_slot *active_queue_head;
64 size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
65 {
66         size_t size = eltsize * nmemb;
67         struct buffer *buffer = buffer_;
69         if (size > buffer->buf.len - buffer->posn)
70                 size = buffer->buf.len - buffer->posn;
71         memcpy(ptr, buffer->buf.buf + buffer->posn, size);
72         buffer->posn += size;
74         return size;
75 }
77 #ifndef NO_CURL_IOCTL
78 curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp)
79 {
80         struct buffer *buffer = clientp;
82         switch (cmd) {
83         case CURLIOCMD_NOP:
84                 return CURLIOE_OK;
86         case CURLIOCMD_RESTARTREAD:
87                 buffer->posn = 0;
88                 return CURLIOE_OK;
90         default:
91                 return CURLIOE_UNKNOWNCMD;
92         }
93 }
94 #endif
96 size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
97 {
98         size_t size = eltsize * nmemb;
99         struct strbuf *buffer = buffer_;
101         strbuf_add(buffer, ptr, size);
102         return size;
105 size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf)
107         return eltsize * nmemb;
110 #ifdef USE_CURL_MULTI
111 static void process_curl_messages(void)
113         int num_messages;
114         struct active_request_slot *slot;
115         CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);
117         while (curl_message != NULL) {
118                 if (curl_message->msg == CURLMSG_DONE) {
119                         int curl_result = curl_message->data.result;
120                         slot = active_queue_head;
121                         while (slot != NULL &&
122                                slot->curl != curl_message->easy_handle)
123                                 slot = slot->next;
124                         if (slot != NULL) {
125                                 curl_multi_remove_handle(curlm, slot->curl);
126                                 slot->curl_result = curl_result;
127                                 finish_active_slot(slot);
128                         } else {
129                                 fprintf(stderr, "Received DONE message for unknown request!\n");
130                         }
131                 } else {
132                         fprintf(stderr, "Unknown CURL message received: %d\n",
133                                 (int)curl_message->msg);
134                 }
135                 curl_message = curl_multi_info_read(curlm, &num_messages);
136         }
138 #endif
140 static int http_options(const char *var, const char *value, void *cb)
142         if (!strcmp("http.sslverify", var)) {
143                 curl_ssl_verify = git_config_bool(var, value);
144                 return 0;
145         }
146         if (!strcmp("http.sslcert", var))
147                 return git_config_string(&ssl_cert, var, value);
148 #if LIBCURL_VERSION_NUM >= 0x070903
149         if (!strcmp("http.sslkey", var))
150                 return git_config_string(&ssl_key, var, value);
151 #endif
152 #if LIBCURL_VERSION_NUM >= 0x070908
153         if (!strcmp("http.sslcapath", var))
154                 return git_config_string(&ssl_capath, var, value);
155 #endif
156         if (!strcmp("http.sslcainfo", var))
157                 return git_config_string(&ssl_cainfo, var, value);
158         if (!strcmp("http.sslcertpasswordprotected", var)) {
159                 if (git_config_bool(var, value))
160                         ssl_cert_password_required = 1;
161                 return 0;
162         }
163         if (!strcmp("http.minsessions", var)) {
164                 min_curl_sessions = git_config_int(var, value);
165 #ifndef USE_CURL_MULTI
166                 if (min_curl_sessions > 1)
167                         min_curl_sessions = 1;
168 #endif
169                 return 0;
170         }
171 #ifdef USE_CURL_MULTI
172         if (!strcmp("http.maxrequests", var)) {
173                 max_requests = git_config_int(var, value);
174                 return 0;
175         }
176 #endif
177         if (!strcmp("http.lowspeedlimit", var)) {
178                 curl_low_speed_limit = (long)git_config_int(var, value);
179                 return 0;
180         }
181         if (!strcmp("http.lowspeedtime", var)) {
182                 curl_low_speed_time = (long)git_config_int(var, value);
183                 return 0;
184         }
186         if (!strcmp("http.noepsv", var)) {
187                 curl_ftp_no_epsv = git_config_bool(var, value);
188                 return 0;
189         }
190         if (!strcmp("http.proxy", var))
191                 return git_config_string(&curl_http_proxy, var, value);
193         if (!strcmp("http.cookiefile", var))
194                 return git_config_string(&curl_cookie_file, var, value);
196         if (!strcmp("http.postbuffer", var)) {
197                 http_post_buffer = git_config_int(var, value);
198                 if (http_post_buffer < LARGE_PACKET_MAX)
199                         http_post_buffer = LARGE_PACKET_MAX;
200                 return 0;
201         }
203         if (!strcmp("http.useragent", var))
204                 return git_config_string(&user_agent, var, value);
206         /* Fall back on the default ones */
207         return git_default_config(var, value, cb);
210 static void init_curl_http_auth(CURL *result)
212         if (http_auth.username) {
213                 struct strbuf up = STRBUF_INIT;
214                 credential_fill(&http_auth);
215                 strbuf_addf(&up, "%s:%s",
216                             http_auth.username, http_auth.password);
217                 curl_easy_setopt(result, CURLOPT_USERPWD,
218                                  strbuf_detach(&up, NULL));
219         }
222 static int has_cert_password(void)
224         if (ssl_cert == NULL || ssl_cert_password_required != 1)
225                 return 0;
226         if (!cert_auth.password) {
227                 cert_auth.protocol = xstrdup("cert");
228                 cert_auth.path = xstrdup(ssl_cert);
229                 credential_fill(&cert_auth);
230         }
231         return 1;
234 static CURL *get_curl_handle(void)
236         CURL *result = curl_easy_init();
238         if (!curl_ssl_verify) {
239                 curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
240                 curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0);
241         } else {
242                 /* Verify authenticity of the peer's certificate */
243                 curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 1);
244                 /* The name in the cert must match whom we tried to connect */
245                 curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
246         }
248 #if LIBCURL_VERSION_NUM >= 0x070907
249         curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
250 #endif
251 #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
252         curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
253 #endif
255         if (ssl_cert != NULL)
256                 curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
257         if (has_cert_password())
258                 curl_easy_setopt(result, CURLOPT_KEYPASSWD, cert_auth.password);
259 #if LIBCURL_VERSION_NUM >= 0x070903
260         if (ssl_key != NULL)
261                 curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
262 #endif
263 #if LIBCURL_VERSION_NUM >= 0x070908
264         if (ssl_capath != NULL)
265                 curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
266 #endif
267         if (ssl_cainfo != NULL)
268                 curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
269         curl_easy_setopt(result, CURLOPT_FAILONERROR, 1);
271         if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
272                 curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
273                                  curl_low_speed_limit);
274                 curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,
275                                  curl_low_speed_time);
276         }
278         curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
279 #if LIBCURL_VERSION_NUM >= 0x071301
280         curl_easy_setopt(result, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
281 #elif LIBCURL_VERSION_NUM >= 0x071101
282         curl_easy_setopt(result, CURLOPT_POST301, 1);
283 #endif
285         if (getenv("GIT_CURL_VERBOSE"))
286                 curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
288         curl_easy_setopt(result, CURLOPT_USERAGENT,
289                 user_agent ? user_agent : GIT_HTTP_USER_AGENT);
291         if (curl_ftp_no_epsv)
292                 curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
294         if (curl_http_proxy)
295                 curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
297         return result;
300 static void set_from_env(const char **var, const char *envname)
302         const char *val = getenv(envname);
303         if (val)
304                 *var = val;
307 void http_init(struct remote *remote, const char *url)
309         char *low_speed_limit;
310         char *low_speed_time;
312         http_is_verbose = 0;
314         git_config(http_options, NULL);
316         curl_global_init(CURL_GLOBAL_ALL);
318         if (remote && remote->http_proxy)
319                 curl_http_proxy = xstrdup(remote->http_proxy);
321         pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
322         no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
324 #ifdef USE_CURL_MULTI
325         {
326                 char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
327                 if (http_max_requests != NULL)
328                         max_requests = atoi(http_max_requests);
329         }
331         curlm = curl_multi_init();
332         if (curlm == NULL) {
333                 fprintf(stderr, "Error creating curl multi handle.\n");
334                 exit(1);
335         }
336 #endif
338         if (getenv("GIT_SSL_NO_VERIFY"))
339                 curl_ssl_verify = 0;
341         set_from_env(&ssl_cert, "GIT_SSL_CERT");
342 #if LIBCURL_VERSION_NUM >= 0x070903
343         set_from_env(&ssl_key, "GIT_SSL_KEY");
344 #endif
345 #if LIBCURL_VERSION_NUM >= 0x070908
346         set_from_env(&ssl_capath, "GIT_SSL_CAPATH");
347 #endif
348         set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
350         set_from_env(&user_agent, "GIT_HTTP_USER_AGENT");
352         low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
353         if (low_speed_limit != NULL)
354                 curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
355         low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME");
356         if (low_speed_time != NULL)
357                 curl_low_speed_time = strtol(low_speed_time, NULL, 10);
359         if (curl_ssl_verify == -1)
360                 curl_ssl_verify = 1;
362         curl_session_count = 0;
363 #ifdef USE_CURL_MULTI
364         if (max_requests < 1)
365                 max_requests = DEFAULT_MAX_REQUESTS;
366 #endif
368         if (getenv("GIT_CURL_FTP_NO_EPSV"))
369                 curl_ftp_no_epsv = 1;
371         if (url) {
372                 credential_from_url(&http_auth, url);
373                 if (!ssl_cert_password_required &&
374                     getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") &&
375                     !prefixcmp(url, "https://"))
376                         ssl_cert_password_required = 1;
377         }
379 #ifndef NO_CURL_EASY_DUPHANDLE
380         curl_default = get_curl_handle();
381 #endif
384 void http_cleanup(void)
386         struct active_request_slot *slot = active_queue_head;
388         while (slot != NULL) {
389                 struct active_request_slot *next = slot->next;
390                 if (slot->curl != NULL) {
391 #ifdef USE_CURL_MULTI
392                         curl_multi_remove_handle(curlm, slot->curl);
393 #endif
394                         curl_easy_cleanup(slot->curl);
395                 }
396                 free(slot);
397                 slot = next;
398         }
399         active_queue_head = NULL;
401 #ifndef NO_CURL_EASY_DUPHANDLE
402         curl_easy_cleanup(curl_default);
403 #endif
405 #ifdef USE_CURL_MULTI
406         curl_multi_cleanup(curlm);
407 #endif
408         curl_global_cleanup();
410         curl_slist_free_all(pragma_header);
411         pragma_header = NULL;
413         curl_slist_free_all(no_pragma_header);
414         no_pragma_header = NULL;
416         if (curl_http_proxy) {
417                 free((void *)curl_http_proxy);
418                 curl_http_proxy = NULL;
419         }
421         if (cert_auth.password != NULL) {
422                 memset(cert_auth.password, 0, strlen(cert_auth.password));
423                 free(cert_auth.password);
424                 cert_auth.password = NULL;
425         }
426         ssl_cert_password_required = 0;
429 struct active_request_slot *get_active_slot(void)
431         struct active_request_slot *slot = active_queue_head;
432         struct active_request_slot *newslot;
434 #ifdef USE_CURL_MULTI
435         int num_transfers;
437         /* Wait for a slot to open up if the queue is full */
438         while (active_requests >= max_requests) {
439                 curl_multi_perform(curlm, &num_transfers);
440                 if (num_transfers < active_requests)
441                         process_curl_messages();
442         }
443 #endif
445         while (slot != NULL && slot->in_use)
446                 slot = slot->next;
448         if (slot == NULL) {
449                 newslot = xmalloc(sizeof(*newslot));
450                 newslot->curl = NULL;
451                 newslot->in_use = 0;
452                 newslot->next = NULL;
454                 slot = active_queue_head;
455                 if (slot == NULL) {
456                         active_queue_head = newslot;
457                 } else {
458                         while (slot->next != NULL)
459                                 slot = slot->next;
460                         slot->next = newslot;
461                 }
462                 slot = newslot;
463         }
465         if (slot->curl == NULL) {
466 #ifdef NO_CURL_EASY_DUPHANDLE
467                 slot->curl = get_curl_handle();
468 #else
469                 slot->curl = curl_easy_duphandle(curl_default);
470 #endif
471                 curl_session_count++;
472         }
474         active_requests++;
475         slot->in_use = 1;
476         slot->results = NULL;
477         slot->finished = NULL;
478         slot->callback_data = NULL;
479         slot->callback_func = NULL;
480         curl_easy_setopt(slot->curl, CURLOPT_COOKIEFILE, curl_cookie_file);
481         curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
482         curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
483         curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);
484         curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);
485         curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);
486         curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, NULL);
487         curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
488         curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
490         return slot;
493 int start_active_slot(struct active_request_slot *slot)
495 #ifdef USE_CURL_MULTI
496         CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);
497         int num_transfers;
499         if (curlm_result != CURLM_OK &&
500             curlm_result != CURLM_CALL_MULTI_PERFORM) {
501                 active_requests--;
502                 slot->in_use = 0;
503                 return 0;
504         }
506         /*
507          * We know there must be something to do, since we just added
508          * something.
509          */
510         curl_multi_perform(curlm, &num_transfers);
511 #endif
512         return 1;
515 #ifdef USE_CURL_MULTI
516 struct fill_chain {
517         void *data;
518         int (*fill)(void *);
519         struct fill_chain *next;
520 };
522 static struct fill_chain *fill_cfg;
524 void add_fill_function(void *data, int (*fill)(void *))
526         struct fill_chain *new = xmalloc(sizeof(*new));
527         struct fill_chain **linkp = &fill_cfg;
528         new->data = data;
529         new->fill = fill;
530         new->next = NULL;
531         while (*linkp)
532                 linkp = &(*linkp)->next;
533         *linkp = new;
536 void fill_active_slots(void)
538         struct active_request_slot *slot = active_queue_head;
540         while (active_requests < max_requests) {
541                 struct fill_chain *fill;
542                 for (fill = fill_cfg; fill; fill = fill->next)
543                         if (fill->fill(fill->data))
544                                 break;
546                 if (!fill)
547                         break;
548         }
550         while (slot != NULL) {
551                 if (!slot->in_use && slot->curl != NULL
552                         && curl_session_count > min_curl_sessions) {
553                         curl_easy_cleanup(slot->curl);
554                         slot->curl = NULL;
555                         curl_session_count--;
556                 }
557                 slot = slot->next;
558         }
561 void step_active_slots(void)
563         int num_transfers;
564         CURLMcode curlm_result;
566         do {
567                 curlm_result = curl_multi_perform(curlm, &num_transfers);
568         } while (curlm_result == CURLM_CALL_MULTI_PERFORM);
569         if (num_transfers < active_requests) {
570                 process_curl_messages();
571                 fill_active_slots();
572         }
574 #endif
576 void run_active_slot(struct active_request_slot *slot)
578 #ifdef USE_CURL_MULTI
579         fd_set readfds;
580         fd_set writefds;
581         fd_set excfds;
582         int max_fd;
583         struct timeval select_timeout;
584         int finished = 0;
586         slot->finished = &finished;
587         while (!finished) {
588                 step_active_slots();
590                 if (slot->in_use) {
591 #if LIBCURL_VERSION_NUM >= 0x070f04
592                         long curl_timeout;
593                         curl_multi_timeout(curlm, &curl_timeout);
594                         if (curl_timeout == 0) {
595                                 continue;
596                         } else if (curl_timeout == -1) {
597                                 select_timeout.tv_sec  = 0;
598                                 select_timeout.tv_usec = 50000;
599                         } else {
600                                 select_timeout.tv_sec  =  curl_timeout / 1000;
601                                 select_timeout.tv_usec = (curl_timeout % 1000) * 1000;
602                         }
603 #else
604                         select_timeout.tv_sec  = 0;
605                         select_timeout.tv_usec = 50000;
606 #endif
608                         max_fd = -1;
609                         FD_ZERO(&readfds);
610                         FD_ZERO(&writefds);
611                         FD_ZERO(&excfds);
612                         curl_multi_fdset(curlm, &readfds, &writefds, &excfds, &max_fd);
614                         select(max_fd+1, &readfds, &writefds, &excfds, &select_timeout);
615                 }
616         }
617 #else
618         while (slot->in_use) {
619                 slot->curl_result = curl_easy_perform(slot->curl);
620                 finish_active_slot(slot);
621         }
622 #endif
625 static void closedown_active_slot(struct active_request_slot *slot)
627         active_requests--;
628         slot->in_use = 0;
631 static void release_active_slot(struct active_request_slot *slot)
633         closedown_active_slot(slot);
634         if (slot->curl && curl_session_count > min_curl_sessions) {
635 #ifdef USE_CURL_MULTI
636                 curl_multi_remove_handle(curlm, slot->curl);
637 #endif
638                 curl_easy_cleanup(slot->curl);
639                 slot->curl = NULL;
640                 curl_session_count--;
641         }
642 #ifdef USE_CURL_MULTI
643         fill_active_slots();
644 #endif
647 void finish_active_slot(struct active_request_slot *slot)
649         closedown_active_slot(slot);
650         curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
652         if (slot->finished != NULL)
653                 (*slot->finished) = 1;
655         /* Store slot results so they can be read after the slot is reused */
656         if (slot->results != NULL) {
657                 slot->results->curl_result = slot->curl_result;
658                 slot->results->http_code = slot->http_code;
659         }
661         /* Run callback if appropriate */
662         if (slot->callback_func != NULL)
663                 slot->callback_func(slot->callback_data);
666 void finish_all_active_slots(void)
668         struct active_request_slot *slot = active_queue_head;
670         while (slot != NULL)
671                 if (slot->in_use) {
672                         run_active_slot(slot);
673                         slot = active_queue_head;
674                 } else {
675                         slot = slot->next;
676                 }
679 /* Helpers for modifying and creating URLs */
680 static inline int needs_quote(int ch)
682         if (((ch >= 'A') && (ch <= 'Z'))
683                         || ((ch >= 'a') && (ch <= 'z'))
684                         || ((ch >= '0') && (ch <= '9'))
685                         || (ch == '/')
686                         || (ch == '-')
687                         || (ch == '.'))
688                 return 0;
689         return 1;
692 static char *quote_ref_url(const char *base, const char *ref)
694         struct strbuf buf = STRBUF_INIT;
695         const char *cp;
696         int ch;
698         end_url_with_slash(&buf, base);
700         for (cp = ref; (ch = *cp) != 0; cp++)
701                 if (needs_quote(ch))
702                         strbuf_addf(&buf, "%%%02x", ch);
703                 else
704                         strbuf_addch(&buf, *cp);
706         return strbuf_detach(&buf, NULL);
709 void append_remote_object_url(struct strbuf *buf, const char *url,
710                               const char *hex,
711                               int only_two_digit_prefix)
713         end_url_with_slash(buf, url);
715         strbuf_addf(buf, "objects/%.*s/", 2, hex);
716         if (!only_two_digit_prefix)
717                 strbuf_addf(buf, "%s", hex+2);
720 char *get_remote_object_url(const char *url, const char *hex,
721                             int only_two_digit_prefix)
723         struct strbuf buf = STRBUF_INIT;
724         append_remote_object_url(&buf, url, hex, only_two_digit_prefix);
725         return strbuf_detach(&buf, NULL);
728 /* http_request() targets */
729 #define HTTP_REQUEST_STRBUF     0
730 #define HTTP_REQUEST_FILE       1
732 static int http_request(const char *url, void *result, int target, int options)
734         struct active_request_slot *slot;
735         struct slot_results results;
736         struct curl_slist *headers = NULL;
737         struct strbuf buf = STRBUF_INIT;
738         int ret;
740         slot = get_active_slot();
741         slot->results = &results;
742         curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
744         if (result == NULL) {
745                 curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
746         } else {
747                 curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
748                 curl_easy_setopt(slot->curl, CURLOPT_FILE, result);
750                 if (target == HTTP_REQUEST_FILE) {
751                         long posn = ftell(result);
752                         curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
753                                          fwrite);
754                         if (posn > 0) {
755                                 strbuf_addf(&buf, "Range: bytes=%ld-", posn);
756                                 headers = curl_slist_append(headers, buf.buf);
757                                 strbuf_reset(&buf);
758                         }
759                 } else
760                         curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
761                                          fwrite_buffer);
762         }
764         strbuf_addstr(&buf, "Pragma:");
765         if (options & HTTP_NO_CACHE)
766                 strbuf_addstr(&buf, " no-cache");
768         headers = curl_slist_append(headers, buf.buf);
770         curl_easy_setopt(slot->curl, CURLOPT_URL, url);
771         curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
773         if (start_active_slot(slot)) {
774                 run_active_slot(slot);
775                 if (results.curl_result == CURLE_OK)
776                         ret = HTTP_OK;
777                 else if (missing_target(&results))
778                         ret = HTTP_MISSING_TARGET;
779                 else if (results.http_code == 401) {
780                         if (http_auth.username && http_auth.password) {
781                                 credential_reject(&http_auth);
782                                 ret = HTTP_NOAUTH;
783                         } else {
784                                 credential_fill(&http_auth);
785                                 init_curl_http_auth(slot->curl);
786                                 ret = HTTP_REAUTH;
787                         }
788                 } else {
789                         if (!curl_errorstr[0])
790                                 strlcpy(curl_errorstr,
791                                         curl_easy_strerror(results.curl_result),
792                                         sizeof(curl_errorstr));
793                         ret = HTTP_ERROR;
794                 }
795         } else {
796                 error("Unable to start HTTP request for %s", url);
797                 ret = HTTP_START_FAILED;
798         }
800         curl_slist_free_all(headers);
801         strbuf_release(&buf);
803         if (ret == HTTP_OK)
804                 credential_approve(&http_auth);
806         return ret;
809 static int http_request_reauth(const char *url, void *result, int target,
810                                int options)
812         int ret = http_request(url, result, target, options);
813         if (ret != HTTP_REAUTH)
814                 return ret;
815         return http_request(url, result, target, options);
818 int http_get_strbuf(const char *url, struct strbuf *result, int options)
820         return http_request_reauth(url, result, HTTP_REQUEST_STRBUF, options);
823 /*
824  * Downloads an url and stores the result in the given file.
825  *
826  * If a previous interrupted download is detected (i.e. a previous temporary
827  * file is still around) the download is resumed.
828  */
829 static int http_get_file(const char *url, const char *filename, int options)
831         int ret;
832         struct strbuf tmpfile = STRBUF_INIT;
833         FILE *result;
835         strbuf_addf(&tmpfile, "%s.temp", filename);
836         result = fopen(tmpfile.buf, "a");
837         if (! result) {
838                 error("Unable to open local file %s", tmpfile.buf);
839                 ret = HTTP_ERROR;
840                 goto cleanup;
841         }
843         ret = http_request_reauth(url, result, HTTP_REQUEST_FILE, options);
844         fclose(result);
846         if ((ret == HTTP_OK) && move_temp_to_file(tmpfile.buf, filename))
847                 ret = HTTP_ERROR;
848 cleanup:
849         strbuf_release(&tmpfile);
850         return ret;
853 int http_error(const char *url, int ret)
855         /* http_request has already handled HTTP_START_FAILED. */
856         if (ret != HTTP_START_FAILED)
857                 error("%s while accessing %s", curl_errorstr, url);
859         return ret;
862 int http_fetch_ref(const char *base, struct ref *ref)
864         char *url;
865         struct strbuf buffer = STRBUF_INIT;
866         int ret = -1;
868         url = quote_ref_url(base, ref->name);
869         if (http_get_strbuf(url, &buffer, HTTP_NO_CACHE) == HTTP_OK) {
870                 strbuf_rtrim(&buffer);
871                 if (buffer.len == 40)
872                         ret = get_sha1_hex(buffer.buf, ref->old_sha1);
873                 else if (!prefixcmp(buffer.buf, "ref: ")) {
874                         ref->symref = xstrdup(buffer.buf + 5);
875                         ret = 0;
876                 }
877         }
879         strbuf_release(&buffer);
880         free(url);
881         return ret;
884 /* Helpers for fetching packs */
885 static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
887         char *url, *tmp;
888         struct strbuf buf = STRBUF_INIT;
890         if (http_is_verbose)
891                 fprintf(stderr, "Getting index for pack %s\n", sha1_to_hex(sha1));
893         end_url_with_slash(&buf, base_url);
894         strbuf_addf(&buf, "objects/pack/pack-%s.idx", sha1_to_hex(sha1));
895         url = strbuf_detach(&buf, NULL);
897         strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1));
898         tmp = strbuf_detach(&buf, NULL);
900         if (http_get_file(url, tmp, 0) != HTTP_OK) {
901                 error("Unable to get pack index %s\n", url);
902                 free(tmp);
903                 tmp = NULL;
904         }
906         free(url);
907         return tmp;
910 static int fetch_and_setup_pack_index(struct packed_git **packs_head,
911         unsigned char *sha1, const char *base_url)
913         struct packed_git *new_pack;
914         char *tmp_idx = NULL;
915         int ret;
917         if (has_pack_index(sha1)) {
918                 new_pack = parse_pack_index(sha1, NULL);
919                 if (!new_pack)
920                         return -1; /* parse_pack_index() already issued error message */
921                 goto add_pack;
922         }
924         tmp_idx = fetch_pack_index(sha1, base_url);
925         if (!tmp_idx)
926                 return -1;
928         new_pack = parse_pack_index(sha1, tmp_idx);
929         if (!new_pack) {
930                 unlink(tmp_idx);
931                 free(tmp_idx);
933                 return -1; /* parse_pack_index() already issued error message */
934         }
936         ret = verify_pack_index(new_pack);
937         if (!ret) {
938                 close_pack_index(new_pack);
939                 ret = move_temp_to_file(tmp_idx, sha1_pack_index_name(sha1));
940         }
941         free(tmp_idx);
942         if (ret)
943                 return -1;
945 add_pack:
946         new_pack->next = *packs_head;
947         *packs_head = new_pack;
948         return 0;
951 int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
953         int ret = 0, i = 0;
954         char *url, *data;
955         struct strbuf buf = STRBUF_INIT;
956         unsigned char sha1[20];
958         end_url_with_slash(&buf, base_url);
959         strbuf_addstr(&buf, "objects/info/packs");
960         url = strbuf_detach(&buf, NULL);
962         ret = http_get_strbuf(url, &buf, HTTP_NO_CACHE);
963         if (ret != HTTP_OK)
964                 goto cleanup;
966         data = buf.buf;
967         while (i < buf.len) {
968                 switch (data[i]) {
969                 case 'P':
970                         i++;
971                         if (i + 52 <= buf.len &&
972                             !prefixcmp(data + i, " pack-") &&
973                             !prefixcmp(data + i + 46, ".pack\n")) {
974                                 get_sha1_hex(data + i + 6, sha1);
975                                 fetch_and_setup_pack_index(packs_head, sha1,
976                                                       base_url);
977                                 i += 51;
978                                 break;
979                         }
980                 default:
981                         while (i < buf.len && data[i] != '\n')
982                                 i++;
983                 }
984                 i++;
985         }
987 cleanup:
988         free(url);
989         return ret;
992 void release_http_pack_request(struct http_pack_request *preq)
994         if (preq->packfile != NULL) {
995                 fclose(preq->packfile);
996                 preq->packfile = NULL;
997         }
998         if (preq->range_header != NULL) {
999                 curl_slist_free_all(preq->range_header);
1000                 preq->range_header = NULL;
1001         }
1002         preq->slot = NULL;
1003         free(preq->url);
1006 int finish_http_pack_request(struct http_pack_request *preq)
1008         struct packed_git **lst;
1009         struct packed_git *p = preq->target;
1010         char *tmp_idx;
1011         struct child_process ip;
1012         const char *ip_argv[8];
1014         close_pack_index(p);
1016         fclose(preq->packfile);
1017         preq->packfile = NULL;
1019         lst = preq->lst;
1020         while (*lst != p)
1021                 lst = &((*lst)->next);
1022         *lst = (*lst)->next;
1024         tmp_idx = xstrdup(preq->tmpfile);
1025         strcpy(tmp_idx + strlen(tmp_idx) - strlen(".pack.temp"),
1026                ".idx.temp");
1028         ip_argv[0] = "index-pack";
1029         ip_argv[1] = "-o";
1030         ip_argv[2] = tmp_idx;
1031         ip_argv[3] = preq->tmpfile;
1032         ip_argv[4] = NULL;
1034         memset(&ip, 0, sizeof(ip));
1035         ip.argv = ip_argv;
1036         ip.git_cmd = 1;
1037         ip.no_stdin = 1;
1038         ip.no_stdout = 1;
1040         if (run_command(&ip)) {
1041                 unlink(preq->tmpfile);
1042                 unlink(tmp_idx);
1043                 free(tmp_idx);
1044                 return -1;
1045         }
1047         unlink(sha1_pack_index_name(p->sha1));
1049         if (move_temp_to_file(preq->tmpfile, sha1_pack_name(p->sha1))
1050          || move_temp_to_file(tmp_idx, sha1_pack_index_name(p->sha1))) {
1051                 free(tmp_idx);
1052                 return -1;
1053         }
1055         install_packed_git(p);
1056         free(tmp_idx);
1057         return 0;
1060 struct http_pack_request *new_http_pack_request(
1061         struct packed_git *target, const char *base_url)
1063         long prev_posn = 0;
1064         char range[RANGE_HEADER_SIZE];
1065         struct strbuf buf = STRBUF_INIT;
1066         struct http_pack_request *preq;
1068         preq = xcalloc(1, sizeof(*preq));
1069         preq->target = target;
1071         end_url_with_slash(&buf, base_url);
1072         strbuf_addf(&buf, "objects/pack/pack-%s.pack",
1073                 sha1_to_hex(target->sha1));
1074         preq->url = strbuf_detach(&buf, NULL);
1076         snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp",
1077                 sha1_pack_name(target->sha1));
1078         preq->packfile = fopen(preq->tmpfile, "a");
1079         if (!preq->packfile) {
1080                 error("Unable to open local file %s for pack",
1081                       preq->tmpfile);
1082                 goto abort;
1083         }
1085         preq->slot = get_active_slot();
1086         curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile);
1087         curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
1088         curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
1089         curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
1090                 no_pragma_header);
1092         /*
1093          * If there is data present from a previous transfer attempt,
1094          * resume where it left off
1095          */
1096         prev_posn = ftell(preq->packfile);
1097         if (prev_posn>0) {
1098                 if (http_is_verbose)
1099                         fprintf(stderr,
1100                                 "Resuming fetch of pack %s at byte %ld\n",
1101                                 sha1_to_hex(target->sha1), prev_posn);
1102                 sprintf(range, "Range: bytes=%ld-", prev_posn);
1103                 preq->range_header = curl_slist_append(NULL, range);
1104                 curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
1105                         preq->range_header);
1106         }
1108         return preq;
1110 abort:
1111         free(preq->url);
1112         free(preq);
1113         return NULL;
1116 /* Helpers for fetching objects (loose) */
1117 static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,
1118                                void *data)
1120         unsigned char expn[4096];
1121         size_t size = eltsize * nmemb;
1122         int posn = 0;
1123         struct http_object_request *freq =
1124                 (struct http_object_request *)data;
1125         do {
1126                 ssize_t retval = xwrite(freq->localfile,
1127                                         (char *) ptr + posn, size - posn);
1128                 if (retval < 0)
1129                         return posn;
1130                 posn += retval;
1131         } while (posn < size);
1133         freq->stream.avail_in = size;
1134         freq->stream.next_in = (void *)ptr;
1135         do {
1136                 freq->stream.next_out = expn;
1137                 freq->stream.avail_out = sizeof(expn);
1138                 freq->zret = git_inflate(&freq->stream, Z_SYNC_FLUSH);
1139                 git_SHA1_Update(&freq->c, expn,
1140                                 sizeof(expn) - freq->stream.avail_out);
1141         } while (freq->stream.avail_in && freq->zret == Z_OK);
1142         return size;
1145 struct http_object_request *new_http_object_request(const char *base_url,
1146         unsigned char *sha1)
1148         char *hex = sha1_to_hex(sha1);
1149         char *filename;
1150         char prevfile[PATH_MAX];
1151         int prevlocal;
1152         char prev_buf[PREV_BUF_SIZE];
1153         ssize_t prev_read = 0;
1154         long prev_posn = 0;
1155         char range[RANGE_HEADER_SIZE];
1156         struct curl_slist *range_header = NULL;
1157         struct http_object_request *freq;
1159         freq = xcalloc(1, sizeof(*freq));
1160         hashcpy(freq->sha1, sha1);
1161         freq->localfile = -1;
1163         filename = sha1_file_name(sha1);
1164         snprintf(freq->tmpfile, sizeof(freq->tmpfile),
1165                  "%s.temp", filename);
1167         snprintf(prevfile, sizeof(prevfile), "%s.prev", filename);
1168         unlink_or_warn(prevfile);
1169         rename(freq->tmpfile, prevfile);
1170         unlink_or_warn(freq->tmpfile);
1172         if (freq->localfile != -1)
1173                 error("fd leakage in start: %d", freq->localfile);
1174         freq->localfile = open(freq->tmpfile,
1175                                O_WRONLY | O_CREAT | O_EXCL, 0666);
1176         /*
1177          * This could have failed due to the "lazy directory creation";
1178          * try to mkdir the last path component.
1179          */
1180         if (freq->localfile < 0 && errno == ENOENT) {
1181                 char *dir = strrchr(freq->tmpfile, '/');
1182                 if (dir) {
1183                         *dir = 0;
1184                         mkdir(freq->tmpfile, 0777);
1185                         *dir = '/';
1186                 }
1187                 freq->localfile = open(freq->tmpfile,
1188                                        O_WRONLY | O_CREAT | O_EXCL, 0666);
1189         }
1191         if (freq->localfile < 0) {
1192                 error("Couldn't create temporary file %s: %s",
1193                       freq->tmpfile, strerror(errno));
1194                 goto abort;
1195         }
1197         git_inflate_init(&freq->stream);
1199         git_SHA1_Init(&freq->c);
1201         freq->url = get_remote_object_url(base_url, hex, 0);
1203         /*
1204          * If a previous temp file is present, process what was already
1205          * fetched.
1206          */
1207         prevlocal = open(prevfile, O_RDONLY);
1208         if (prevlocal != -1) {
1209                 do {
1210                         prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
1211                         if (prev_read>0) {
1212                                 if (fwrite_sha1_file(prev_buf,
1213                                                      1,
1214                                                      prev_read,
1215                                                      freq) == prev_read) {
1216                                         prev_posn += prev_read;
1217                                 } else {
1218                                         prev_read = -1;
1219                                 }
1220                         }
1221                 } while (prev_read > 0);
1222                 close(prevlocal);
1223         }
1224         unlink_or_warn(prevfile);
1226         /*
1227          * Reset inflate/SHA1 if there was an error reading the previous temp
1228          * file; also rewind to the beginning of the local file.
1229          */
1230         if (prev_read == -1) {
1231                 memset(&freq->stream, 0, sizeof(freq->stream));
1232                 git_inflate_init(&freq->stream);
1233                 git_SHA1_Init(&freq->c);
1234                 if (prev_posn>0) {
1235                         prev_posn = 0;
1236                         lseek(freq->localfile, 0, SEEK_SET);
1237                         if (ftruncate(freq->localfile, 0) < 0) {
1238                                 error("Couldn't truncate temporary file %s: %s",
1239                                           freq->tmpfile, strerror(errno));
1240                                 goto abort;
1241                         }
1242                 }
1243         }
1245         freq->slot = get_active_slot();
1247         curl_easy_setopt(freq->slot->curl, CURLOPT_FILE, freq);
1248         curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
1249         curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
1250         curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
1251         curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
1253         /*
1254          * If we have successfully processed data from a previous fetch
1255          * attempt, only fetch the data we don't already have.
1256          */
1257         if (prev_posn>0) {
1258                 if (http_is_verbose)
1259                         fprintf(stderr,
1260                                 "Resuming fetch of object %s at byte %ld\n",
1261                                 hex, prev_posn);
1262                 sprintf(range, "Range: bytes=%ld-", prev_posn);
1263                 range_header = curl_slist_append(range_header, range);
1264                 curl_easy_setopt(freq->slot->curl,
1265                                  CURLOPT_HTTPHEADER, range_header);
1266         }
1268         return freq;
1270 abort:
1271         free(freq->url);
1272         free(freq);
1273         return NULL;
1276 void process_http_object_request(struct http_object_request *freq)
1278         if (freq->slot == NULL)
1279                 return;
1280         freq->curl_result = freq->slot->curl_result;
1281         freq->http_code = freq->slot->http_code;
1282         freq->slot = NULL;
1285 int finish_http_object_request(struct http_object_request *freq)
1287         struct stat st;
1289         close(freq->localfile);
1290         freq->localfile = -1;
1292         process_http_object_request(freq);
1294         if (freq->http_code == 416) {
1295                 warning("requested range invalid; we may already have all the data.");
1296         } else if (freq->curl_result != CURLE_OK) {
1297                 if (stat(freq->tmpfile, &st) == 0)
1298                         if (st.st_size == 0)
1299                                 unlink_or_warn(freq->tmpfile);
1300                 return -1;
1301         }
1303         git_inflate_end(&freq->stream);
1304         git_SHA1_Final(freq->real_sha1, &freq->c);
1305         if (freq->zret != Z_STREAM_END) {
1306                 unlink_or_warn(freq->tmpfile);
1307                 return -1;
1308         }
1309         if (hashcmp(freq->sha1, freq->real_sha1)) {
1310                 unlink_or_warn(freq->tmpfile);
1311                 return -1;
1312         }
1313         freq->rename =
1314                 move_temp_to_file(freq->tmpfile, sha1_file_name(freq->sha1));
1316         return freq->rename;
1319 void abort_http_object_request(struct http_object_request *freq)
1321         unlink_or_warn(freq->tmpfile);
1323         release_http_object_request(freq);
1326 void release_http_object_request(struct http_object_request *freq)
1328         if (freq->localfile != -1) {
1329                 close(freq->localfile);
1330                 freq->localfile = -1;
1331         }
1332         if (freq->url != NULL) {
1333                 free(freq->url);
1334                 freq->url = NULL;
1335         }
1336         if (freq->slot != NULL) {
1337                 freq->slot->callback_func = NULL;
1338                 freq->slot->callback_data = NULL;
1339                 release_active_slot(freq->slot);
1340                 freq->slot = NULL;
1341         }