Code

1c138135d74ecef0ae6887a1951ce275c89502ca
[git.git] / http.c
1 #include "http.h"
3 int data_received;
4 int active_requests;
6 #ifdef USE_CURL_MULTI
7 static int max_requests = -1;
8 static CURLM *curlm;
9 #endif
10 #ifndef NO_CURL_EASY_DUPHANDLE
11 static CURL *curl_default;
12 #endif
13 char curl_errorstr[CURL_ERROR_SIZE];
15 static int curl_ssl_verify = -1;
16 static const char *ssl_cert;
17 #if LIBCURL_VERSION_NUM >= 0x070902
18 static const char *ssl_key;
19 #endif
20 #if LIBCURL_VERSION_NUM >= 0x070908
21 static const char *ssl_capath;
22 #endif
23 static const char *ssl_cainfo;
24 static long curl_low_speed_limit = -1;
25 static long curl_low_speed_time = -1;
26 static int curl_ftp_no_epsv;
27 static const char *curl_http_proxy;
28 static char *user_name, *user_pass;
30 #if LIBCURL_VERSION_NUM >= 0x071700
31 /* Use CURLOPT_KEYPASSWD as is */
32 #elif LIBCURL_VERSION_NUM >= 0x070903
33 #define CURLOPT_KEYPASSWD CURLOPT_SSLKEYPASSWD
34 #else
35 #define CURLOPT_KEYPASSWD CURLOPT_SSLCERTPASSWD
36 #endif
38 static char *ssl_cert_password;
39 static int ssl_cert_password_required;
41 static struct curl_slist *pragma_header;
43 static struct active_request_slot *active_queue_head;
45 size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
46 {
47         size_t size = eltsize * nmemb;
48         struct buffer *buffer = buffer_;
50         if (size > buffer->buf.len - buffer->posn)
51                 size = buffer->buf.len - buffer->posn;
52         memcpy(ptr, buffer->buf.buf + buffer->posn, size);
53         buffer->posn += size;
55         return size;
56 }
58 #ifndef NO_CURL_IOCTL
59 curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp)
60 {
61         struct buffer *buffer = clientp;
63         switch (cmd) {
64         case CURLIOCMD_NOP:
65                 return CURLIOE_OK;
67         case CURLIOCMD_RESTARTREAD:
68                 buffer->posn = 0;
69                 return CURLIOE_OK;
71         default:
72                 return CURLIOE_UNKNOWNCMD;
73         }
74 }
75 #endif
77 size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
78 {
79         size_t size = eltsize * nmemb;
80         struct strbuf *buffer = buffer_;
82         strbuf_add(buffer, ptr, size);
83         data_received++;
84         return size;
85 }
87 size_t fwrite_null(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf)
88 {
89         data_received++;
90         return eltsize * nmemb;
91 }
93 static void finish_active_slot(struct active_request_slot *slot);
95 #ifdef USE_CURL_MULTI
96 static void process_curl_messages(void)
97 {
98         int num_messages;
99         struct active_request_slot *slot;
100         CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);
102         while (curl_message != NULL) {
103                 if (curl_message->msg == CURLMSG_DONE) {
104                         int curl_result = curl_message->data.result;
105                         slot = active_queue_head;
106                         while (slot != NULL &&
107                                slot->curl != curl_message->easy_handle)
108                                 slot = slot->next;
109                         if (slot != NULL) {
110                                 curl_multi_remove_handle(curlm, slot->curl);
111                                 slot->curl_result = curl_result;
112                                 finish_active_slot(slot);
113                         } else {
114                                 fprintf(stderr, "Received DONE message for unknown request!\n");
115                         }
116                 } else {
117                         fprintf(stderr, "Unknown CURL message received: %d\n",
118                                 (int)curl_message->msg);
119                 }
120                 curl_message = curl_multi_info_read(curlm, &num_messages);
121         }
123 #endif
125 static int http_options(const char *var, const char *value, void *cb)
127         if (!strcmp("http.sslverify", var)) {
128                 curl_ssl_verify = git_config_bool(var, value);
129                 return 0;
130         }
131         if (!strcmp("http.sslcert", var))
132                 return git_config_string(&ssl_cert, var, value);
133 #if LIBCURL_VERSION_NUM >= 0x070902
134         if (!strcmp("http.sslkey", var))
135                 return git_config_string(&ssl_key, var, value);
136 #endif
137 #if LIBCURL_VERSION_NUM >= 0x070908
138         if (!strcmp("http.sslcapath", var))
139                 return git_config_string(&ssl_capath, var, value);
140 #endif
141         if (!strcmp("http.sslcainfo", var))
142                 return git_config_string(&ssl_cainfo, var, value);
143 #ifdef USE_CURL_MULTI
144         if (!strcmp("http.maxrequests", var)) {
145                 max_requests = git_config_int(var, value);
146                 return 0;
147         }
148 #endif
149         if (!strcmp("http.lowspeedlimit", var)) {
150                 curl_low_speed_limit = (long)git_config_int(var, value);
151                 return 0;
152         }
153         if (!strcmp("http.lowspeedtime", var)) {
154                 curl_low_speed_time = (long)git_config_int(var, value);
155                 return 0;
156         }
158         if (!strcmp("http.noepsv", var)) {
159                 curl_ftp_no_epsv = git_config_bool(var, value);
160                 return 0;
161         }
162         if (!strcmp("http.proxy", var))
163                 return git_config_string(&curl_http_proxy, var, value);
165         /* Fall back on the default ones */
166         return git_default_config(var, value, cb);
169 static void init_curl_http_auth(CURL *result)
171         if (user_name) {
172                 struct strbuf up = STRBUF_INIT;
173                 if (!user_pass)
174                         user_pass = xstrdup(getpass("Password: "));
175                 strbuf_addf(&up, "%s:%s", user_name, user_pass);
176                 curl_easy_setopt(result, CURLOPT_USERPWD,
177                                  strbuf_detach(&up, NULL));
178         }
181 static int has_cert_password(void)
183         if (ssl_cert_password != NULL)
184                 return 1;
185         if (ssl_cert == NULL || ssl_cert_password_required != 1)
186                 return 0;
187         /* Only prompt the user once. */
188         ssl_cert_password_required = -1;
189         ssl_cert_password = getpass("Certificate Password: ");
190         if (ssl_cert_password != NULL) {
191                 ssl_cert_password = xstrdup(ssl_cert_password);
192                 return 1;
193         } else
194                 return 0;
197 static CURL *get_curl_handle(void)
199         CURL *result = curl_easy_init();
201         if (!curl_ssl_verify) {
202                 curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
203                 curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0);
204         } else {
205                 /* Verify authenticity of the peer's certificate */
206                 curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 1);
207                 /* The name in the cert must match whom we tried to connect */
208                 curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
209         }
211 #if LIBCURL_VERSION_NUM >= 0x070907
212         curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
213 #endif
215         init_curl_http_auth(result);
217         if (ssl_cert != NULL)
218                 curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
219         if (has_cert_password())
220                 curl_easy_setopt(result, CURLOPT_KEYPASSWD, ssl_cert_password);
221 #if LIBCURL_VERSION_NUM >= 0x070902
222         if (ssl_key != NULL)
223                 curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
224 #endif
225 #if LIBCURL_VERSION_NUM >= 0x070908
226         if (ssl_capath != NULL)
227                 curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
228 #endif
229         if (ssl_cainfo != NULL)
230                 curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
231         curl_easy_setopt(result, CURLOPT_FAILONERROR, 1);
233         if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
234                 curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
235                                  curl_low_speed_limit);
236                 curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,
237                                  curl_low_speed_time);
238         }
240         curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
242         if (getenv("GIT_CURL_VERBOSE"))
243                 curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
245         curl_easy_setopt(result, CURLOPT_USERAGENT, GIT_USER_AGENT);
247         if (curl_ftp_no_epsv)
248                 curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
250         if (curl_http_proxy)
251                 curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
253         return result;
256 static void http_auth_init(const char *url)
258         char *at, *colon, *cp, *slash;
259         int len;
261         cp = strstr(url, "://");
262         if (!cp)
263                 return;
265         /*
266          * Ok, the URL looks like "proto://something".  Which one?
267          * "proto://<user>:<pass>@<host>/...",
268          * "proto://<user>@<host>/...", or just
269          * "proto://<host>/..."?
270          */
271         cp += 3;
272         at = strchr(cp, '@');
273         colon = strchr(cp, ':');
274         slash = strchrnul(cp, '/');
275         if (!at || slash <= at)
276                 return; /* No credentials */
277         if (!colon || at <= colon) {
278                 /* Only username */
279                 len = at - cp;
280                 user_name = xmalloc(len + 1);
281                 memcpy(user_name, cp, len);
282                 user_name[len] = '\0';
283                 user_pass = NULL;
284         } else {
285                 len = colon - cp;
286                 user_name = xmalloc(len + 1);
287                 memcpy(user_name, cp, len);
288                 user_name[len] = '\0';
289                 len = at - (colon + 1);
290                 user_pass = xmalloc(len + 1);
291                 memcpy(user_pass, colon + 1, len);
292                 user_pass[len] = '\0';
293         }
296 static void set_from_env(const char **var, const char *envname)
298         const char *val = getenv(envname);
299         if (val)
300                 *var = val;
303 void http_init(struct remote *remote)
305         char *low_speed_limit;
306         char *low_speed_time;
308         git_config(http_options, NULL);
310         curl_global_init(CURL_GLOBAL_ALL);
312         if (remote && remote->http_proxy)
313                 curl_http_proxy = xstrdup(remote->http_proxy);
315         pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
317 #ifdef USE_CURL_MULTI
318         {
319                 char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
320                 if (http_max_requests != NULL)
321                         max_requests = atoi(http_max_requests);
322         }
324         curlm = curl_multi_init();
325         if (curlm == NULL) {
326                 fprintf(stderr, "Error creating curl multi handle.\n");
327                 exit(1);
328         }
329 #endif
331         if (getenv("GIT_SSL_NO_VERIFY"))
332                 curl_ssl_verify = 0;
334         set_from_env(&ssl_cert, "GIT_SSL_CERT");
335 #if LIBCURL_VERSION_NUM >= 0x070902
336         set_from_env(&ssl_key, "GIT_SSL_KEY");
337 #endif
338 #if LIBCURL_VERSION_NUM >= 0x070908
339         set_from_env(&ssl_capath, "GIT_SSL_CAPATH");
340 #endif
341         set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
343         low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
344         if (low_speed_limit != NULL)
345                 curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
346         low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME");
347         if (low_speed_time != NULL)
348                 curl_low_speed_time = strtol(low_speed_time, NULL, 10);
350         if (curl_ssl_verify == -1)
351                 curl_ssl_verify = 1;
353 #ifdef USE_CURL_MULTI
354         if (max_requests < 1)
355                 max_requests = DEFAULT_MAX_REQUESTS;
356 #endif
358         if (getenv("GIT_CURL_FTP_NO_EPSV"))
359                 curl_ftp_no_epsv = 1;
361         if (remote && remote->url && remote->url[0]) {
362                 http_auth_init(remote->url[0]);
363                 if (!prefixcmp(remote->url[0], "https://"))
364                         ssl_cert_password_required = 1;
365         }
367 #ifndef NO_CURL_EASY_DUPHANDLE
368         curl_default = get_curl_handle();
369 #endif
372 void http_cleanup(void)
374         struct active_request_slot *slot = active_queue_head;
376         while (slot != NULL) {
377                 struct active_request_slot *next = slot->next;
378                 if (slot->curl != NULL) {
379 #ifdef USE_CURL_MULTI
380                         curl_multi_remove_handle(curlm, slot->curl);
381 #endif
382                         curl_easy_cleanup(slot->curl);
383                 }
384                 free(slot);
385                 slot = next;
386         }
387         active_queue_head = NULL;
389 #ifndef NO_CURL_EASY_DUPHANDLE
390         curl_easy_cleanup(curl_default);
391 #endif
393 #ifdef USE_CURL_MULTI
394         curl_multi_cleanup(curlm);
395 #endif
396         curl_global_cleanup();
398         curl_slist_free_all(pragma_header);
399         pragma_header = NULL;
401         if (curl_http_proxy) {
402                 free((void *)curl_http_proxy);
403                 curl_http_proxy = NULL;
404         }
406         if (ssl_cert_password != NULL) {
407                 memset(ssl_cert_password, 0, strlen(ssl_cert_password));
408                 free(ssl_cert_password);
409                 ssl_cert_password = NULL;
410         }
411         ssl_cert_password_required = 0;
414 struct active_request_slot *get_active_slot(void)
416         struct active_request_slot *slot = active_queue_head;
417         struct active_request_slot *newslot;
419 #ifdef USE_CURL_MULTI
420         int num_transfers;
422         /* Wait for a slot to open up if the queue is full */
423         while (active_requests >= max_requests) {
424                 curl_multi_perform(curlm, &num_transfers);
425                 if (num_transfers < active_requests)
426                         process_curl_messages();
427         }
428 #endif
430         while (slot != NULL && slot->in_use)
431                 slot = slot->next;
433         if (slot == NULL) {
434                 newslot = xmalloc(sizeof(*newslot));
435                 newslot->curl = NULL;
436                 newslot->in_use = 0;
437                 newslot->next = NULL;
439                 slot = active_queue_head;
440                 if (slot == NULL) {
441                         active_queue_head = newslot;
442                 } else {
443                         while (slot->next != NULL)
444                                 slot = slot->next;
445                         slot->next = newslot;
446                 }
447                 slot = newslot;
448         }
450         if (slot->curl == NULL) {
451 #ifdef NO_CURL_EASY_DUPHANDLE
452                 slot->curl = get_curl_handle();
453 #else
454                 slot->curl = curl_easy_duphandle(curl_default);
455 #endif
456         }
458         active_requests++;
459         slot->in_use = 1;
460         slot->local = NULL;
461         slot->results = NULL;
462         slot->finished = NULL;
463         slot->callback_data = NULL;
464         slot->callback_func = NULL;
465         curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
466         curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
467         curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);
468         curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);
469         curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);
470         curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
471         curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
473         return slot;
476 int start_active_slot(struct active_request_slot *slot)
478 #ifdef USE_CURL_MULTI
479         CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);
480         int num_transfers;
482         if (curlm_result != CURLM_OK &&
483             curlm_result != CURLM_CALL_MULTI_PERFORM) {
484                 active_requests--;
485                 slot->in_use = 0;
486                 return 0;
487         }
489         /*
490          * We know there must be something to do, since we just added
491          * something.
492          */
493         curl_multi_perform(curlm, &num_transfers);
494 #endif
495         return 1;
498 #ifdef USE_CURL_MULTI
499 struct fill_chain {
500         void *data;
501         int (*fill)(void *);
502         struct fill_chain *next;
503 };
505 static struct fill_chain *fill_cfg;
507 void add_fill_function(void *data, int (*fill)(void *))
509         struct fill_chain *new = xmalloc(sizeof(*new));
510         struct fill_chain **linkp = &fill_cfg;
511         new->data = data;
512         new->fill = fill;
513         new->next = NULL;
514         while (*linkp)
515                 linkp = &(*linkp)->next;
516         *linkp = new;
519 void fill_active_slots(void)
521         struct active_request_slot *slot = active_queue_head;
523         while (active_requests < max_requests) {
524                 struct fill_chain *fill;
525                 for (fill = fill_cfg; fill; fill = fill->next)
526                         if (fill->fill(fill->data))
527                                 break;
529                 if (!fill)
530                         break;
531         }
533         while (slot != NULL) {
534                 if (!slot->in_use && slot->curl != NULL) {
535                         curl_easy_cleanup(slot->curl);
536                         slot->curl = NULL;
537                 }
538                 slot = slot->next;
539         }
542 void step_active_slots(void)
544         int num_transfers;
545         CURLMcode curlm_result;
547         do {
548                 curlm_result = curl_multi_perform(curlm, &num_transfers);
549         } while (curlm_result == CURLM_CALL_MULTI_PERFORM);
550         if (num_transfers < active_requests) {
551                 process_curl_messages();
552                 fill_active_slots();
553         }
555 #endif
557 void run_active_slot(struct active_request_slot *slot)
559 #ifdef USE_CURL_MULTI
560         long last_pos = 0;
561         long current_pos;
562         fd_set readfds;
563         fd_set writefds;
564         fd_set excfds;
565         int max_fd;
566         struct timeval select_timeout;
567         int finished = 0;
569         slot->finished = &finished;
570         while (!finished) {
571                 data_received = 0;
572                 step_active_slots();
574                 if (!data_received && slot->local != NULL) {
575                         current_pos = ftell(slot->local);
576                         if (current_pos > last_pos)
577                                 data_received++;
578                         last_pos = current_pos;
579                 }
581                 if (slot->in_use && !data_received) {
582                         max_fd = 0;
583                         FD_ZERO(&readfds);
584                         FD_ZERO(&writefds);
585                         FD_ZERO(&excfds);
586                         select_timeout.tv_sec = 0;
587                         select_timeout.tv_usec = 50000;
588                         select(max_fd, &readfds, &writefds,
589                                &excfds, &select_timeout);
590                 }
591         }
592 #else
593         while (slot->in_use) {
594                 slot->curl_result = curl_easy_perform(slot->curl);
595                 finish_active_slot(slot);
596         }
597 #endif
600 static void closedown_active_slot(struct active_request_slot *slot)
602         active_requests--;
603         slot->in_use = 0;
606 void release_active_slot(struct active_request_slot *slot)
608         closedown_active_slot(slot);
609         if (slot->curl) {
610 #ifdef USE_CURL_MULTI
611                 curl_multi_remove_handle(curlm, slot->curl);
612 #endif
613                 curl_easy_cleanup(slot->curl);
614                 slot->curl = NULL;
615         }
616 #ifdef USE_CURL_MULTI
617         fill_active_slots();
618 #endif
621 static void finish_active_slot(struct active_request_slot *slot)
623         closedown_active_slot(slot);
624         curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
626         if (slot->finished != NULL)
627                 (*slot->finished) = 1;
629         /* Store slot results so they can be read after the slot is reused */
630         if (slot->results != NULL) {
631                 slot->results->curl_result = slot->curl_result;
632                 slot->results->http_code = slot->http_code;
633         }
635         /* Run callback if appropriate */
636         if (slot->callback_func != NULL)
637                 slot->callback_func(slot->callback_data);
640 void finish_all_active_slots(void)
642         struct active_request_slot *slot = active_queue_head;
644         while (slot != NULL)
645                 if (slot->in_use) {
646                         run_active_slot(slot);
647                         slot = active_queue_head;
648                 } else {
649                         slot = slot->next;
650                 }
653 static inline int needs_quote(int ch)
655         if (((ch >= 'A') && (ch <= 'Z'))
656                         || ((ch >= 'a') && (ch <= 'z'))
657                         || ((ch >= '0') && (ch <= '9'))
658                         || (ch == '/')
659                         || (ch == '-')
660                         || (ch == '.'))
661                 return 0;
662         return 1;
665 static inline int hex(int v)
667         if (v < 10)
668                 return '0' + v;
669         else
670                 return 'A' + v - 10;
673 static char *quote_ref_url(const char *base, const char *ref)
675         struct strbuf buf = STRBUF_INIT;
676         const char *cp;
677         int ch;
679         strbuf_addstr(&buf, base);
680         if (buf.len && buf.buf[buf.len - 1] != '/' && *ref != '/')
681                 strbuf_addstr(&buf, "/");
683         for (cp = ref; (ch = *cp) != 0; cp++)
684                 if (needs_quote(ch))
685                         strbuf_addf(&buf, "%%%02x", ch);
686                 else
687                         strbuf_addch(&buf, *cp);
689         return strbuf_detach(&buf, NULL);
692 int http_fetch_ref(const char *base, struct ref *ref)
694         char *url;
695         struct strbuf buffer = STRBUF_INIT;
696         struct active_request_slot *slot;
697         struct slot_results results;
698         int ret;
700         url = quote_ref_url(base, ref->name);
701         slot = get_active_slot();
702         slot->results = &results;
703         curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
704         curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
705         curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
706         curl_easy_setopt(slot->curl, CURLOPT_URL, url);
707         if (start_active_slot(slot)) {
708                 run_active_slot(slot);
709                 if (results.curl_result == CURLE_OK) {
710                         strbuf_rtrim(&buffer);
711                         if (buffer.len == 40)
712                                 ret = get_sha1_hex(buffer.buf, ref->old_sha1);
713                         else if (!prefixcmp(buffer.buf, "ref: ")) {
714                                 ref->symref = xstrdup(buffer.buf + 5);
715                                 ret = 0;
716                         } else
717                                 ret = 1;
718                 } else {
719                         ret = error("Couldn't get %s for %s\n%s",
720                                     url, ref->name, curl_errorstr);
721                 }
722         } else {
723                 ret = error("Unable to start request");
724         }
726         strbuf_release(&buffer);
727         free(url);
728         return ret;