http: maintain curl sessions
[git] / http.c
1 #include "http.h"
2 #include "pack.h"
3 #include "sideband.h"
4
5 int data_received;
6 int active_requests;
7 int http_is_verbose;
8 size_t http_post_buffer = 16 * LARGE_PACKET_MAX;
9
10 static int min_curl_sessions = 1;
11 static int curl_session_count;
12 #ifdef USE_CURL_MULTI
13 static int max_requests = -1;
14 static CURLM *curlm;
15 #endif
16 #ifndef NO_CURL_EASY_DUPHANDLE
17 static CURL *curl_default;
18 #endif
19
20 #define PREV_BUF_SIZE 4096
21 #define RANGE_HEADER_SIZE 30
22
23 char curl_errorstr[CURL_ERROR_SIZE];
24
25 static int curl_ssl_verify = -1;
26 static const char *ssl_cert;
27 #if LIBCURL_VERSION_NUM >= 0x070903
28 static const char *ssl_key;
29 #endif
30 #if LIBCURL_VERSION_NUM >= 0x070908
31 static const char *ssl_capath;
32 #endif
33 static const char *ssl_cainfo;
34 static long curl_low_speed_limit = -1;
35 static long curl_low_speed_time = -1;
36 static int curl_ftp_no_epsv;
37 static const char *curl_http_proxy;
38 static char *user_name, *user_pass;
39
40 #if LIBCURL_VERSION_NUM >= 0x071700
41 /* Use CURLOPT_KEYPASSWD as is */
42 #elif LIBCURL_VERSION_NUM >= 0x070903
43 #define CURLOPT_KEYPASSWD CURLOPT_SSLKEYPASSWD
44 #else
45 #define CURLOPT_KEYPASSWD CURLOPT_SSLCERTPASSWD
46 #endif
47
48 static char *ssl_cert_password;
49 static int ssl_cert_password_required;
50
51 static struct curl_slist *pragma_header;
52 static struct curl_slist *no_pragma_header;
53
54 static struct active_request_slot *active_queue_head;
55
56 size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
57 {
58         size_t size = eltsize * nmemb;
59         struct buffer *buffer = buffer_;
60
61         if (size > buffer->buf.len - buffer->posn)
62                 size = buffer->buf.len - buffer->posn;
63         memcpy(ptr, buffer->buf.buf + buffer->posn, size);
64         buffer->posn += size;
65
66         return size;
67 }
68
69 #ifndef NO_CURL_IOCTL
70 curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp)
71 {
72         struct buffer *buffer = clientp;
73
74         switch (cmd) {
75         case CURLIOCMD_NOP:
76                 return CURLIOE_OK;
77
78         case CURLIOCMD_RESTARTREAD:
79                 buffer->posn = 0;
80                 return CURLIOE_OK;
81
82         default:
83                 return CURLIOE_UNKNOWNCMD;
84         }
85 }
86 #endif
87
88 size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
89 {
90         size_t size = eltsize * nmemb;
91         struct strbuf *buffer = buffer_;
92
93         strbuf_add(buffer, ptr, size);
94         data_received++;
95         return size;
96 }
97
98 size_t fwrite_null(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf)
99 {
100         data_received++;
101         return eltsize * nmemb;
102 }
103
104 #ifdef USE_CURL_MULTI
105 static void process_curl_messages(void)
106 {
107         int num_messages;
108         struct active_request_slot *slot;
109         CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);
110
111         while (curl_message != NULL) {
112                 if (curl_message->msg == CURLMSG_DONE) {
113                         int curl_result = curl_message->data.result;
114                         slot = active_queue_head;
115                         while (slot != NULL &&
116                                slot->curl != curl_message->easy_handle)
117                                 slot = slot->next;
118                         if (slot != NULL) {
119                                 curl_multi_remove_handle(curlm, slot->curl);
120                                 slot->curl_result = curl_result;
121                                 finish_active_slot(slot);
122                         } else {
123                                 fprintf(stderr, "Received DONE message for unknown request!\n");
124                         }
125                 } else {
126                         fprintf(stderr, "Unknown CURL message received: %d\n",
127                                 (int)curl_message->msg);
128                 }
129                 curl_message = curl_multi_info_read(curlm, &num_messages);
130         }
131 }
132 #endif
133
134 static int http_options(const char *var, const char *value, void *cb)
135 {
136         if (!strcmp("http.sslverify", var)) {
137                 curl_ssl_verify = git_config_bool(var, value);
138                 return 0;
139         }
140         if (!strcmp("http.sslcert", var))
141                 return git_config_string(&ssl_cert, var, value);
142 #if LIBCURL_VERSION_NUM >= 0x070903
143         if (!strcmp("http.sslkey", var))
144                 return git_config_string(&ssl_key, var, value);
145 #endif
146 #if LIBCURL_VERSION_NUM >= 0x070908
147         if (!strcmp("http.sslcapath", var))
148                 return git_config_string(&ssl_capath, var, value);
149 #endif
150         if (!strcmp("http.sslcainfo", var))
151                 return git_config_string(&ssl_cainfo, var, value);
152         if (!strcmp("http.sslcertpasswordprotected", var)) {
153                 if (git_config_bool(var, value))
154                         ssl_cert_password_required = 1;
155                 return 0;
156         }
157         if (!strcmp("http.minsessions", var)) {
158                 min_curl_sessions = git_config_int(var, value);
159 #ifndef USE_CURL_MULTI
160                 if (min_curl_sessions > 1)
161                         min_curl_sessions = 1;
162 #endif
163                 return 0;
164         }
165 #ifdef USE_CURL_MULTI
166         if (!strcmp("http.maxrequests", var)) {
167                 max_requests = git_config_int(var, value);
168                 return 0;
169         }
170 #endif
171         if (!strcmp("http.lowspeedlimit", var)) {
172                 curl_low_speed_limit = (long)git_config_int(var, value);
173                 return 0;
174         }
175         if (!strcmp("http.lowspeedtime", var)) {
176                 curl_low_speed_time = (long)git_config_int(var, value);
177                 return 0;
178         }
179
180         if (!strcmp("http.noepsv", var)) {
181                 curl_ftp_no_epsv = git_config_bool(var, value);
182                 return 0;
183         }
184         if (!strcmp("http.proxy", var))
185                 return git_config_string(&curl_http_proxy, var, value);
186
187         if (!strcmp("http.postbuffer", var)) {
188                 http_post_buffer = git_config_int(var, value);
189                 if (http_post_buffer < LARGE_PACKET_MAX)
190                         http_post_buffer = LARGE_PACKET_MAX;
191                 return 0;
192         }
193
194         /* Fall back on the default ones */
195         return git_default_config(var, value, cb);
196 }
197
198 static void init_curl_http_auth(CURL *result)
199 {
200         if (user_name) {
201                 struct strbuf up = STRBUF_INIT;
202                 if (!user_pass)
203                         user_pass = xstrdup(getpass("Password: "));
204                 strbuf_addf(&up, "%s:%s", user_name, user_pass);
205                 curl_easy_setopt(result, CURLOPT_USERPWD,
206                                  strbuf_detach(&up, NULL));
207         }
208 }
209
210 static int has_cert_password(void)
211 {
212         if (ssl_cert_password != NULL)
213                 return 1;
214         if (ssl_cert == NULL || ssl_cert_password_required != 1)
215                 return 0;
216         /* Only prompt the user once. */
217         ssl_cert_password_required = -1;
218         ssl_cert_password = getpass("Certificate Password: ");
219         if (ssl_cert_password != NULL) {
220                 ssl_cert_password = xstrdup(ssl_cert_password);
221                 return 1;
222         } else
223                 return 0;
224 }
225
226 static CURL *get_curl_handle(void)
227 {
228         CURL *result = curl_easy_init();
229
230         if (!curl_ssl_verify) {
231                 curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
232                 curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0);
233         } else {
234                 /* Verify authenticity of the peer's certificate */
235                 curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 1);
236                 /* The name in the cert must match whom we tried to connect */
237                 curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
238         }
239
240 #if LIBCURL_VERSION_NUM >= 0x070907
241         curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
242 #endif
243
244         init_curl_http_auth(result);
245
246         if (ssl_cert != NULL)
247                 curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
248         if (has_cert_password())
249                 curl_easy_setopt(result, CURLOPT_KEYPASSWD, ssl_cert_password);
250 #if LIBCURL_VERSION_NUM >= 0x070903
251         if (ssl_key != NULL)
252                 curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
253 #endif
254 #if LIBCURL_VERSION_NUM >= 0x070908
255         if (ssl_capath != NULL)
256                 curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
257 #endif
258         if (ssl_cainfo != NULL)
259                 curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
260         curl_easy_setopt(result, CURLOPT_FAILONERROR, 1);
261
262         if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
263                 curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
264                                  curl_low_speed_limit);
265                 curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,
266                                  curl_low_speed_time);
267         }
268
269         curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
270
271         if (getenv("GIT_CURL_VERBOSE"))
272                 curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
273
274         curl_easy_setopt(result, CURLOPT_USERAGENT, GIT_USER_AGENT);
275
276         if (curl_ftp_no_epsv)
277                 curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
278
279         if (curl_http_proxy)
280                 curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
281
282         return result;
283 }
284
285 static void http_auth_init(const char *url)
286 {
287         char *at, *colon, *cp, *slash;
288         int len;
289
290         cp = strstr(url, "://");
291         if (!cp)
292                 return;
293
294         /*
295          * Ok, the URL looks like "proto://something".  Which one?
296          * "proto://<user>:<pass>@<host>/...",
297          * "proto://<user>@<host>/...", or just
298          * "proto://<host>/..."?
299          */
300         cp += 3;
301         at = strchr(cp, '@');
302         colon = strchr(cp, ':');
303         slash = strchrnul(cp, '/');
304         if (!at || slash <= at)
305                 return; /* No credentials */
306         if (!colon || at <= colon) {
307                 /* Only username */
308                 len = at - cp;
309                 user_name = xmalloc(len + 1);
310                 memcpy(user_name, cp, len);
311                 user_name[len] = '\0';
312                 user_pass = NULL;
313         } else {
314                 len = colon - cp;
315                 user_name = xmalloc(len + 1);
316                 memcpy(user_name, cp, len);
317                 user_name[len] = '\0';
318                 len = at - (colon + 1);
319                 user_pass = xmalloc(len + 1);
320                 memcpy(user_pass, colon + 1, len);
321                 user_pass[len] = '\0';
322         }
323 }
324
325 static void set_from_env(const char **var, const char *envname)
326 {
327         const char *val = getenv(envname);
328         if (val)
329                 *var = val;
330 }
331
332 void http_init(struct remote *remote)
333 {
334         char *low_speed_limit;
335         char *low_speed_time;
336
337         http_is_verbose = 0;
338
339         git_config(http_options, NULL);
340
341         curl_global_init(CURL_GLOBAL_ALL);
342
343         if (remote && remote->http_proxy)
344                 curl_http_proxy = xstrdup(remote->http_proxy);
345
346         pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
347         no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
348
349 #ifdef USE_CURL_MULTI
350         {
351                 char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
352                 if (http_max_requests != NULL)
353                         max_requests = atoi(http_max_requests);
354         }
355
356         curlm = curl_multi_init();
357         if (curlm == NULL) {
358                 fprintf(stderr, "Error creating curl multi handle.\n");
359                 exit(1);
360         }
361 #endif
362
363         if (getenv("GIT_SSL_NO_VERIFY"))
364                 curl_ssl_verify = 0;
365
366         set_from_env(&ssl_cert, "GIT_SSL_CERT");
367 #if LIBCURL_VERSION_NUM >= 0x070903
368         set_from_env(&ssl_key, "GIT_SSL_KEY");
369 #endif
370 #if LIBCURL_VERSION_NUM >= 0x070908
371         set_from_env(&ssl_capath, "GIT_SSL_CAPATH");
372 #endif
373         set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
374
375         low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
376         if (low_speed_limit != NULL)
377                 curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
378         low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME");
379         if (low_speed_time != NULL)
380                 curl_low_speed_time = strtol(low_speed_time, NULL, 10);
381
382         if (curl_ssl_verify == -1)
383                 curl_ssl_verify = 1;
384
385         curl_session_count = 0;
386 #ifdef USE_CURL_MULTI
387         if (max_requests < 1)
388                 max_requests = DEFAULT_MAX_REQUESTS;
389 #endif
390
391         if (getenv("GIT_CURL_FTP_NO_EPSV"))
392                 curl_ftp_no_epsv = 1;
393
394         if (remote && remote->url && remote->url[0]) {
395                 http_auth_init(remote->url[0]);
396                 if (!ssl_cert_password_required &&
397                     getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") &&
398                     !prefixcmp(remote->url[0], "https://"))
399                         ssl_cert_password_required = 1;
400         }
401
402 #ifndef NO_CURL_EASY_DUPHANDLE
403         curl_default = get_curl_handle();
404 #endif
405 }
406
407 void http_cleanup(void)
408 {
409         struct active_request_slot *slot = active_queue_head;
410
411         while (slot != NULL) {
412                 struct active_request_slot *next = slot->next;
413                 if (slot->curl != NULL) {
414 #ifdef USE_CURL_MULTI
415                         curl_multi_remove_handle(curlm, slot->curl);
416 #endif
417                         curl_easy_cleanup(slot->curl);
418                 }
419                 free(slot);
420                 slot = next;
421         }
422         active_queue_head = NULL;
423
424 #ifndef NO_CURL_EASY_DUPHANDLE
425         curl_easy_cleanup(curl_default);
426 #endif
427
428 #ifdef USE_CURL_MULTI
429         curl_multi_cleanup(curlm);
430 #endif
431         curl_global_cleanup();
432
433         curl_slist_free_all(pragma_header);
434         pragma_header = NULL;
435
436         curl_slist_free_all(no_pragma_header);
437         no_pragma_header = NULL;
438
439         if (curl_http_proxy) {
440                 free((void *)curl_http_proxy);
441                 curl_http_proxy = NULL;
442         }
443
444         if (ssl_cert_password != NULL) {
445                 memset(ssl_cert_password, 0, strlen(ssl_cert_password));
446                 free(ssl_cert_password);
447                 ssl_cert_password = NULL;
448         }
449         ssl_cert_password_required = 0;
450 }
451
452 struct active_request_slot *get_active_slot(void)
453 {
454         struct active_request_slot *slot = active_queue_head;
455         struct active_request_slot *newslot;
456
457 #ifdef USE_CURL_MULTI
458         int num_transfers;
459
460         /* Wait for a slot to open up if the queue is full */
461         while (active_requests >= max_requests) {
462                 curl_multi_perform(curlm, &num_transfers);
463                 if (num_transfers < active_requests)
464                         process_curl_messages();
465         }
466 #endif
467
468         while (slot != NULL && slot->in_use)
469                 slot = slot->next;
470
471         if (slot == NULL) {
472                 newslot = xmalloc(sizeof(*newslot));
473                 newslot->curl = NULL;
474                 newslot->in_use = 0;
475                 newslot->next = NULL;
476
477                 slot = active_queue_head;
478                 if (slot == NULL) {
479                         active_queue_head = newslot;
480                 } else {
481                         while (slot->next != NULL)
482                                 slot = slot->next;
483                         slot->next = newslot;
484                 }
485                 slot = newslot;
486         }
487
488         if (slot->curl == NULL) {
489 #ifdef NO_CURL_EASY_DUPHANDLE
490                 slot->curl = get_curl_handle();
491 #else
492                 slot->curl = curl_easy_duphandle(curl_default);
493 #endif
494                 curl_session_count++;
495         }
496
497         active_requests++;
498         slot->in_use = 1;
499         slot->local = NULL;
500         slot->results = NULL;
501         slot->finished = NULL;
502         slot->callback_data = NULL;
503         slot->callback_func = NULL;
504         curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
505         curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
506         curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);
507         curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);
508         curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);
509         curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
510         curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
511
512         return slot;
513 }
514
515 int start_active_slot(struct active_request_slot *slot)
516 {
517 #ifdef USE_CURL_MULTI
518         CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);
519         int num_transfers;
520
521         if (curlm_result != CURLM_OK &&
522             curlm_result != CURLM_CALL_MULTI_PERFORM) {
523                 active_requests--;
524                 slot->in_use = 0;
525                 return 0;
526         }
527
528         /*
529          * We know there must be something to do, since we just added
530          * something.
531          */
532         curl_multi_perform(curlm, &num_transfers);
533 #endif
534         return 1;
535 }
536
537 #ifdef USE_CURL_MULTI
538 struct fill_chain {
539         void *data;
540         int (*fill)(void *);
541         struct fill_chain *next;
542 };
543
544 static struct fill_chain *fill_cfg;
545
546 void add_fill_function(void *data, int (*fill)(void *))
547 {
548         struct fill_chain *new = xmalloc(sizeof(*new));
549         struct fill_chain **linkp = &fill_cfg;
550         new->data = data;
551         new->fill = fill;
552         new->next = NULL;
553         while (*linkp)
554                 linkp = &(*linkp)->next;
555         *linkp = new;
556 }
557
558 void fill_active_slots(void)
559 {
560         struct active_request_slot *slot = active_queue_head;
561
562         while (active_requests < max_requests) {
563                 struct fill_chain *fill;
564                 for (fill = fill_cfg; fill; fill = fill->next)
565                         if (fill->fill(fill->data))
566                                 break;
567
568                 if (!fill)
569                         break;
570         }
571
572         while (slot != NULL) {
573                 if (!slot->in_use && slot->curl != NULL
574                         && curl_session_count > min_curl_sessions) {
575                         curl_easy_cleanup(slot->curl);
576                         slot->curl = NULL;
577                         curl_session_count--;
578                 }
579                 slot = slot->next;
580         }
581 }
582
583 void step_active_slots(void)
584 {
585         int num_transfers;
586         CURLMcode curlm_result;
587
588         do {
589                 curlm_result = curl_multi_perform(curlm, &num_transfers);
590         } while (curlm_result == CURLM_CALL_MULTI_PERFORM);
591         if (num_transfers < active_requests) {
592                 process_curl_messages();
593                 fill_active_slots();
594         }
595 }
596 #endif
597
598 void run_active_slot(struct active_request_slot *slot)
599 {
600 #ifdef USE_CURL_MULTI
601         long last_pos = 0;
602         long current_pos;
603         fd_set readfds;
604         fd_set writefds;
605         fd_set excfds;
606         int max_fd;
607         struct timeval select_timeout;
608         int finished = 0;
609
610         slot->finished = &finished;
611         while (!finished) {
612                 data_received = 0;
613                 step_active_slots();
614
615                 if (!data_received && slot->local != NULL) {
616                         current_pos = ftell(slot->local);
617                         if (current_pos > last_pos)
618                                 data_received++;
619                         last_pos = current_pos;
620                 }
621
622                 if (slot->in_use && !data_received) {
623                         max_fd = 0;
624                         FD_ZERO(&readfds);
625                         FD_ZERO(&writefds);
626                         FD_ZERO(&excfds);
627                         select_timeout.tv_sec = 0;
628                         select_timeout.tv_usec = 50000;
629                         select(max_fd, &readfds, &writefds,
630                                &excfds, &select_timeout);
631                 }
632         }
633 #else
634         while (slot->in_use) {
635                 slot->curl_result = curl_easy_perform(slot->curl);
636                 finish_active_slot(slot);
637         }
638 #endif
639 }
640
641 static void closedown_active_slot(struct active_request_slot *slot)
642 {
643         active_requests--;
644         slot->in_use = 0;
645 }
646
647 void release_active_slot(struct active_request_slot *slot)
648 {
649         closedown_active_slot(slot);
650         if (slot->curl && curl_session_count > min_curl_sessions) {
651 #ifdef USE_CURL_MULTI
652                 curl_multi_remove_handle(curlm, slot->curl);
653 #endif
654                 curl_easy_cleanup(slot->curl);
655                 slot->curl = NULL;
656                 curl_session_count--;
657         }
658 #ifdef USE_CURL_MULTI
659         fill_active_slots();
660 #endif
661 }
662
663 void finish_active_slot(struct active_request_slot *slot)
664 {
665         closedown_active_slot(slot);
666         curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
667
668         if (slot->finished != NULL)
669                 (*slot->finished) = 1;
670
671         /* Store slot results so they can be read after the slot is reused */
672         if (slot->results != NULL) {
673                 slot->results->curl_result = slot->curl_result;
674                 slot->results->http_code = slot->http_code;
675         }
676
677         /* Run callback if appropriate */
678         if (slot->callback_func != NULL)
679                 slot->callback_func(slot->callback_data);
680 }
681
682 void finish_all_active_slots(void)
683 {
684         struct active_request_slot *slot = active_queue_head;
685
686         while (slot != NULL)
687                 if (slot->in_use) {
688                         run_active_slot(slot);
689                         slot = active_queue_head;
690                 } else {
691                         slot = slot->next;
692                 }
693 }
694
695 /* Helpers for modifying and creating URLs */
696 static inline int needs_quote(int ch)
697 {
698         if (((ch >= 'A') && (ch <= 'Z'))
699                         || ((ch >= 'a') && (ch <= 'z'))
700                         || ((ch >= '0') && (ch <= '9'))
701                         || (ch == '/')
702                         || (ch == '-')
703                         || (ch == '.'))
704                 return 0;
705         return 1;
706 }
707
708 static inline int hex(int v)
709 {
710         if (v < 10)
711                 return '0' + v;
712         else
713                 return 'A' + v - 10;
714 }
715
716 static void end_url_with_slash(struct strbuf *buf, const char *url)
717 {
718         strbuf_addstr(buf, url);
719         if (buf->len && buf->buf[buf->len - 1] != '/')
720                 strbuf_addstr(buf, "/");
721 }
722
723 static char *quote_ref_url(const char *base, const char *ref)
724 {
725         struct strbuf buf = STRBUF_INIT;
726         const char *cp;
727         int ch;
728
729         end_url_with_slash(&buf, base);
730
731         for (cp = ref; (ch = *cp) != 0; cp++)
732                 if (needs_quote(ch))
733                         strbuf_addf(&buf, "%%%02x", ch);
734                 else
735                         strbuf_addch(&buf, *cp);
736
737         return strbuf_detach(&buf, NULL);
738 }
739
740 void append_remote_object_url(struct strbuf *buf, const char *url,
741                               const char *hex,
742                               int only_two_digit_prefix)
743 {
744         end_url_with_slash(buf, url);
745
746         strbuf_addf(buf, "objects/%.*s/", 2, hex);
747         if (!only_two_digit_prefix)
748                 strbuf_addf(buf, "%s", hex+2);
749 }
750
751 char *get_remote_object_url(const char *url, const char *hex,
752                             int only_two_digit_prefix)
753 {
754         struct strbuf buf = STRBUF_INIT;
755         append_remote_object_url(&buf, url, hex, only_two_digit_prefix);
756         return strbuf_detach(&buf, NULL);
757 }
758
759 /* http_request() targets */
760 #define HTTP_REQUEST_STRBUF     0
761 #define HTTP_REQUEST_FILE       1
762
763 static int http_request(const char *url, void *result, int target, int options)
764 {
765         struct active_request_slot *slot;
766         struct slot_results results;
767         struct curl_slist *headers = NULL;
768         struct strbuf buf = STRBUF_INIT;
769         int ret;
770
771         slot = get_active_slot();
772         slot->results = &results;
773         curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
774
775         if (result == NULL) {
776                 curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
777         } else {
778                 curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
779                 curl_easy_setopt(slot->curl, CURLOPT_FILE, result);
780
781                 if (target == HTTP_REQUEST_FILE) {
782                         long posn = ftell(result);
783                         curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
784                                          fwrite);
785                         if (posn > 0) {
786                                 strbuf_addf(&buf, "Range: bytes=%ld-", posn);
787                                 headers = curl_slist_append(headers, buf.buf);
788                                 strbuf_reset(&buf);
789                         }
790                         slot->local = result;
791                 } else
792                         curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
793                                          fwrite_buffer);
794         }
795
796         strbuf_addstr(&buf, "Pragma:");
797         if (options & HTTP_NO_CACHE)
798                 strbuf_addstr(&buf, " no-cache");
799
800         headers = curl_slist_append(headers, buf.buf);
801
802         curl_easy_setopt(slot->curl, CURLOPT_URL, url);
803         curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
804
805         if (start_active_slot(slot)) {
806                 run_active_slot(slot);
807                 if (results.curl_result == CURLE_OK)
808                         ret = HTTP_OK;
809                 else if (missing_target(&results))
810                         ret = HTTP_MISSING_TARGET;
811                 else
812                         ret = HTTP_ERROR;
813         } else {
814                 error("Unable to start HTTP request for %s", url);
815                 ret = HTTP_START_FAILED;
816         }
817
818         slot->local = NULL;
819         curl_slist_free_all(headers);
820         strbuf_release(&buf);
821
822         return ret;
823 }
824
825 int http_get_strbuf(const char *url, struct strbuf *result, int options)
826 {
827         return http_request(url, result, HTTP_REQUEST_STRBUF, options);
828 }
829
830 int http_get_file(const char *url, const char *filename, int options)
831 {
832         int ret;
833         struct strbuf tmpfile = STRBUF_INIT;
834         FILE *result;
835
836         strbuf_addf(&tmpfile, "%s.temp", filename);
837         result = fopen(tmpfile.buf, "a");
838         if (! result) {
839                 error("Unable to open local file %s", tmpfile.buf);
840                 ret = HTTP_ERROR;
841                 goto cleanup;
842         }
843
844         ret = http_request(url, result, HTTP_REQUEST_FILE, options);
845         fclose(result);
846
847         if ((ret == HTTP_OK) && move_temp_to_file(tmpfile.buf, filename))
848                 ret = HTTP_ERROR;
849 cleanup:
850         strbuf_release(&tmpfile);
851         return ret;
852 }
853
854 int http_error(const char *url, int ret)
855 {
856         /* http_request has already handled HTTP_START_FAILED. */
857         if (ret != HTTP_START_FAILED)
858                 error("%s while accessing %s\n", curl_errorstr, url);
859
860         return ret;
861 }
862
863 int http_fetch_ref(const char *base, struct ref *ref)
864 {
865         char *url;
866         struct strbuf buffer = STRBUF_INIT;
867         int ret = -1;
868
869         url = quote_ref_url(base, ref->name);
870         if (http_get_strbuf(url, &buffer, HTTP_NO_CACHE) == HTTP_OK) {
871                 strbuf_rtrim(&buffer);
872                 if (buffer.len == 40)
873                         ret = get_sha1_hex(buffer.buf, ref->old_sha1);
874                 else if (!prefixcmp(buffer.buf, "ref: ")) {
875                         ref->symref = xstrdup(buffer.buf + 5);
876                         ret = 0;
877                 }
878         }
879
880         strbuf_release(&buffer);
881         free(url);
882         return ret;
883 }
884
885 /* Helpers for fetching packs */
886 static int fetch_pack_index(unsigned char *sha1, const char *base_url)
887 {
888         int ret = 0;
889         char *hex = xstrdup(sha1_to_hex(sha1));
890         char *filename;
891         char *url = NULL;
892         struct strbuf buf = STRBUF_INIT;
893
894         if (has_pack_index(sha1)) {
895                 ret = 0;
896                 goto cleanup;
897         }
898
899         if (http_is_verbose)
900                 fprintf(stderr, "Getting index for pack %s\n", hex);
901
902         end_url_with_slash(&buf, base_url);
903         strbuf_addf(&buf, "objects/pack/pack-%s.idx", hex);
904         url = strbuf_detach(&buf, NULL);
905
906         filename = sha1_pack_index_name(sha1);
907         if (http_get_file(url, filename, 0) != HTTP_OK)
908                 ret = error("Unable to get pack index %s\n", url);
909
910 cleanup:
911         free(hex);
912         free(url);
913         return ret;
914 }
915
916 static int fetch_and_setup_pack_index(struct packed_git **packs_head,
917         unsigned char *sha1, const char *base_url)
918 {
919         struct packed_git *new_pack;
920
921         if (fetch_pack_index(sha1, base_url))
922                 return -1;
923
924         new_pack = parse_pack_index(sha1);
925         if (!new_pack)
926                 return -1; /* parse_pack_index() already issued error message */
927         new_pack->next = *packs_head;
928         *packs_head = new_pack;
929         return 0;
930 }
931
932 int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
933 {
934         int ret = 0, i = 0;
935         char *url, *data;
936         struct strbuf buf = STRBUF_INIT;
937         unsigned char sha1[20];
938
939         end_url_with_slash(&buf, base_url);
940         strbuf_addstr(&buf, "objects/info/packs");
941         url = strbuf_detach(&buf, NULL);
942
943         ret = http_get_strbuf(url, &buf, HTTP_NO_CACHE);
944         if (ret != HTTP_OK)
945                 goto cleanup;
946
947         data = buf.buf;
948         while (i < buf.len) {
949                 switch (data[i]) {
950                 case 'P':
951                         i++;
952                         if (i + 52 <= buf.len &&
953                             !prefixcmp(data + i, " pack-") &&
954                             !prefixcmp(data + i + 46, ".pack\n")) {
955                                 get_sha1_hex(data + i + 6, sha1);
956                                 fetch_and_setup_pack_index(packs_head, sha1,
957                                                       base_url);
958                                 i += 51;
959                                 break;
960                         }
961                 default:
962                         while (i < buf.len && data[i] != '\n')
963                                 i++;
964                 }
965                 i++;
966         }
967
968 cleanup:
969         free(url);
970         return ret;
971 }
972
973 void release_http_pack_request(struct http_pack_request *preq)
974 {
975         if (preq->packfile != NULL) {
976                 fclose(preq->packfile);
977                 preq->packfile = NULL;
978                 preq->slot->local = NULL;
979         }
980         if (preq->range_header != NULL) {
981                 curl_slist_free_all(preq->range_header);
982                 preq->range_header = NULL;
983         }
984         preq->slot = NULL;
985         free(preq->url);
986 }
987
988 int finish_http_pack_request(struct http_pack_request *preq)
989 {
990         int ret;
991         struct packed_git **lst;
992
993         preq->target->pack_size = ftell(preq->packfile);
994
995         if (preq->packfile != NULL) {
996                 fclose(preq->packfile);
997                 preq->packfile = NULL;
998                 preq->slot->local = NULL;
999         }
1000
1001         ret = move_temp_to_file(preq->tmpfile, preq->filename);
1002         if (ret)
1003                 return ret;
1004
1005         lst = preq->lst;
1006         while (*lst != preq->target)
1007                 lst = &((*lst)->next);
1008         *lst = (*lst)->next;
1009
1010         if (verify_pack(preq->target))
1011                 return -1;
1012         install_packed_git(preq->target);
1013
1014         return 0;
1015 }
1016
1017 struct http_pack_request *new_http_pack_request(
1018         struct packed_git *target, const char *base_url)
1019 {
1020         char *filename;
1021         long prev_posn = 0;
1022         char range[RANGE_HEADER_SIZE];
1023         struct strbuf buf = STRBUF_INIT;
1024         struct http_pack_request *preq;
1025
1026         preq = xmalloc(sizeof(*preq));
1027         preq->target = target;
1028         preq->range_header = NULL;
1029
1030         end_url_with_slash(&buf, base_url);
1031         strbuf_addf(&buf, "objects/pack/pack-%s.pack",
1032                 sha1_to_hex(target->sha1));
1033         preq->url = strbuf_detach(&buf, NULL);
1034
1035         filename = sha1_pack_name(target->sha1);
1036         snprintf(preq->filename, sizeof(preq->filename), "%s", filename);
1037         snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp", filename);
1038         preq->packfile = fopen(preq->tmpfile, "a");
1039         if (!preq->packfile) {
1040                 error("Unable to open local file %s for pack",
1041                       preq->tmpfile);
1042                 goto abort;
1043         }
1044
1045         preq->slot = get_active_slot();
1046         preq->slot->local = preq->packfile;
1047         curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile);
1048         curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
1049         curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
1050         curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
1051                 no_pragma_header);
1052
1053         /*
1054          * If there is data present from a previous transfer attempt,
1055          * resume where it left off
1056          */
1057         prev_posn = ftell(preq->packfile);
1058         if (prev_posn>0) {
1059                 if (http_is_verbose)
1060                         fprintf(stderr,
1061                                 "Resuming fetch of pack %s at byte %ld\n",
1062                                 sha1_to_hex(target->sha1), prev_posn);
1063                 sprintf(range, "Range: bytes=%ld-", prev_posn);
1064                 preq->range_header = curl_slist_append(NULL, range);
1065                 curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
1066                         preq->range_header);
1067         }
1068
1069         return preq;
1070
1071 abort:
1072         free(filename);
1073         free(preq->url);
1074         free(preq);
1075         return NULL;
1076 }
1077
1078 /* Helpers for fetching objects (loose) */
1079 static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
1080                                void *data)
1081 {
1082         unsigned char expn[4096];
1083         size_t size = eltsize * nmemb;
1084         int posn = 0;
1085         struct http_object_request *freq =
1086                 (struct http_object_request *)data;
1087         do {
1088                 ssize_t retval = xwrite(freq->localfile,
1089                                         (char *) ptr + posn, size - posn);
1090                 if (retval < 0)
1091                         return posn;
1092                 posn += retval;
1093         } while (posn < size);
1094
1095         freq->stream.avail_in = size;
1096         freq->stream.next_in = ptr;
1097         do {
1098                 freq->stream.next_out = expn;
1099                 freq->stream.avail_out = sizeof(expn);
1100                 freq->zret = git_inflate(&freq->stream, Z_SYNC_FLUSH);
1101                 git_SHA1_Update(&freq->c, expn,
1102                                 sizeof(expn) - freq->stream.avail_out);
1103         } while (freq->stream.avail_in && freq->zret == Z_OK);
1104         data_received++;
1105         return size;
1106 }
1107
1108 struct http_object_request *new_http_object_request(const char *base_url,
1109         unsigned char *sha1)
1110 {
1111         char *hex = sha1_to_hex(sha1);
1112         char *filename;
1113         char prevfile[PATH_MAX];
1114         int prevlocal;
1115         unsigned char prev_buf[PREV_BUF_SIZE];
1116         ssize_t prev_read = 0;
1117         long prev_posn = 0;
1118         char range[RANGE_HEADER_SIZE];
1119         struct curl_slist *range_header = NULL;
1120         struct http_object_request *freq;
1121
1122         freq = xmalloc(sizeof(*freq));
1123         hashcpy(freq->sha1, sha1);
1124         freq->localfile = -1;
1125
1126         filename = sha1_file_name(sha1);
1127         snprintf(freq->filename, sizeof(freq->filename), "%s", filename);
1128         snprintf(freq->tmpfile, sizeof(freq->tmpfile),
1129                  "%s.temp", filename);
1130
1131         snprintf(prevfile, sizeof(prevfile), "%s.prev", filename);
1132         unlink_or_warn(prevfile);
1133         rename(freq->tmpfile, prevfile);
1134         unlink_or_warn(freq->tmpfile);
1135
1136         if (freq->localfile != -1)
1137                 error("fd leakage in start: %d", freq->localfile);
1138         freq->localfile = open(freq->tmpfile,
1139                                O_WRONLY | O_CREAT | O_EXCL, 0666);
1140         /*
1141          * This could have failed due to the "lazy directory creation";
1142          * try to mkdir the last path component.
1143          */
1144         if (freq->localfile < 0 && errno == ENOENT) {
1145                 char *dir = strrchr(freq->tmpfile, '/');
1146                 if (dir) {
1147                         *dir = 0;
1148                         mkdir(freq->tmpfile, 0777);
1149                         *dir = '/';
1150                 }
1151                 freq->localfile = open(freq->tmpfile,
1152                                        O_WRONLY | O_CREAT | O_EXCL, 0666);
1153         }
1154
1155         if (freq->localfile < 0) {
1156                 error("Couldn't create temporary file %s for %s: %s",
1157                       freq->tmpfile, freq->filename, strerror(errno));
1158                 goto abort;
1159         }
1160
1161         memset(&freq->stream, 0, sizeof(freq->stream));
1162
1163         git_inflate_init(&freq->stream);
1164
1165         git_SHA1_Init(&freq->c);
1166
1167         freq->url = get_remote_object_url(base_url, hex, 0);
1168
1169         /*
1170          * If a previous temp file is present, process what was already
1171          * fetched.
1172          */
1173         prevlocal = open(prevfile, O_RDONLY);
1174         if (prevlocal != -1) {
1175                 do {
1176                         prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
1177                         if (prev_read>0) {
1178                                 if (fwrite_sha1_file(prev_buf,
1179                                                      1,
1180                                                      prev_read,
1181                                                      freq) == prev_read) {
1182                                         prev_posn += prev_read;
1183                                 } else {
1184                                         prev_read = -1;
1185                                 }
1186                         }
1187                 } while (prev_read > 0);
1188                 close(prevlocal);
1189         }
1190         unlink_or_warn(prevfile);
1191
1192         /*
1193          * Reset inflate/SHA1 if there was an error reading the previous temp
1194          * file; also rewind to the beginning of the local file.
1195          */
1196         if (prev_read == -1) {
1197                 memset(&freq->stream, 0, sizeof(freq->stream));
1198                 git_inflate_init(&freq->stream);
1199                 git_SHA1_Init(&freq->c);
1200                 if (prev_posn>0) {
1201                         prev_posn = 0;
1202                         lseek(freq->localfile, 0, SEEK_SET);
1203                         if (ftruncate(freq->localfile, 0) < 0) {
1204                                 error("Couldn't truncate temporary file %s for %s: %s",
1205                                           freq->tmpfile, freq->filename, strerror(errno));
1206                                 goto abort;
1207                         }
1208                 }
1209         }
1210
1211         freq->slot = get_active_slot();
1212
1213         curl_easy_setopt(freq->slot->curl, CURLOPT_FILE, freq);
1214         curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
1215         curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
1216         curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
1217         curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
1218
1219         /*
1220          * If we have successfully processed data from a previous fetch
1221          * attempt, only fetch the data we don't already have.
1222          */
1223         if (prev_posn>0) {
1224                 if (http_is_verbose)
1225                         fprintf(stderr,
1226                                 "Resuming fetch of object %s at byte %ld\n",
1227                                 hex, prev_posn);
1228                 sprintf(range, "Range: bytes=%ld-", prev_posn);
1229                 range_header = curl_slist_append(range_header, range);
1230                 curl_easy_setopt(freq->slot->curl,
1231                                  CURLOPT_HTTPHEADER, range_header);
1232         }
1233
1234         return freq;
1235
1236 abort:
1237         free(filename);
1238         free(freq->url);
1239         free(freq);
1240         return NULL;
1241 }
1242
1243 void process_http_object_request(struct http_object_request *freq)
1244 {
1245         if (freq->slot == NULL)
1246                 return;
1247         freq->curl_result = freq->slot->curl_result;
1248         freq->http_code = freq->slot->http_code;
1249         freq->slot = NULL;
1250 }
1251
1252 int finish_http_object_request(struct http_object_request *freq)
1253 {
1254         struct stat st;
1255
1256         close(freq->localfile);
1257         freq->localfile = -1;
1258
1259         process_http_object_request(freq);
1260
1261         if (freq->http_code == 416) {
1262                 fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
1263         } else if (freq->curl_result != CURLE_OK) {
1264                 if (stat(freq->tmpfile, &st) == 0)
1265                         if (st.st_size == 0)
1266                                 unlink_or_warn(freq->tmpfile);
1267                 return -1;
1268         }
1269
1270         git_inflate_end(&freq->stream);
1271         git_SHA1_Final(freq->real_sha1, &freq->c);
1272         if (freq->zret != Z_STREAM_END) {
1273                 unlink_or_warn(freq->tmpfile);
1274                 return -1;
1275         }
1276         if (hashcmp(freq->sha1, freq->real_sha1)) {
1277                 unlink_or_warn(freq->tmpfile);
1278                 return -1;
1279         }
1280         freq->rename =
1281                 move_temp_to_file(freq->tmpfile, freq->filename);
1282
1283         return freq->rename;
1284 }
1285
1286 void abort_http_object_request(struct http_object_request *freq)
1287 {
1288         unlink_or_warn(freq->tmpfile);
1289
1290         release_http_object_request(freq);
1291 }
1292
1293 void release_http_object_request(struct http_object_request *freq)
1294 {
1295         if (freq->localfile != -1) {
1296                 close(freq->localfile);
1297                 freq->localfile = -1;
1298         }
1299         if (freq->url != NULL) {
1300                 free(freq->url);
1301                 freq->url = NULL;
1302         }
1303         if (freq->slot != NULL) {
1304                 freq->slot->callback_func = NULL;
1305                 freq->slot->callback_data = NULL;
1306                 release_active_slot(freq->slot);
1307                 freq->slot = NULL;
1308         }
1309 }