7 static int max_requests
= -1;
10 #ifndef NO_CURL_EASY_DUPHANDLE
11 static CURL
*curl_default
;
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
;
20 #if LIBCURL_VERSION_NUM >= 0x070908
21 static const char *ssl_capath
;
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 static struct curl_slist
*pragma_header
;
32 static struct active_request_slot
*active_queue_head
;
34 size_t fread_buffer(void *ptr
, size_t eltsize
, size_t nmemb
, void *buffer_
)
36 size_t size
= eltsize
* nmemb
;
37 struct buffer
*buffer
= buffer_
;
39 if (size
> buffer
->buf
.len
- buffer
->posn
)
40 size
= buffer
->buf
.len
- buffer
->posn
;
41 memcpy(ptr
, buffer
->buf
.buf
+ buffer
->posn
, size
);
47 size_t fwrite_buffer(const void *ptr
, size_t eltsize
, size_t nmemb
, void *buffer_
)
49 size_t size
= eltsize
* nmemb
;
50 struct strbuf
*buffer
= buffer_
;
52 strbuf_add(buffer
, ptr
, size
);
57 size_t fwrite_null(const void *ptr
, size_t eltsize
, size_t nmemb
, void *strbuf
)
60 return eltsize
* nmemb
;
63 static void finish_active_slot(struct active_request_slot
*slot
);
66 static void process_curl_messages(void)
69 struct active_request_slot
*slot
;
70 CURLMsg
*curl_message
= curl_multi_info_read(curlm
, &num_messages
);
72 while (curl_message
!= NULL
) {
73 if (curl_message
->msg
== CURLMSG_DONE
) {
74 int curl_result
= curl_message
->data
.result
;
75 slot
= active_queue_head
;
76 while (slot
!= NULL
&&
77 slot
->curl
!= curl_message
->easy_handle
)
80 curl_multi_remove_handle(curlm
, slot
->curl
);
81 slot
->curl_result
= curl_result
;
82 finish_active_slot(slot
);
84 fprintf(stderr
, "Received DONE message for unknown request!\n");
87 fprintf(stderr
, "Unknown CURL message received: %d\n",
88 (int)curl_message
->msg
);
90 curl_message
= curl_multi_info_read(curlm
, &num_messages
);
95 static int http_options(const char *var
, const char *value
, void *cb
)
97 if (!strcmp("http.sslverify", var
)) {
98 curl_ssl_verify
= git_config_bool(var
, value
);
101 if (!strcmp("http.sslcert", var
))
102 return git_config_string(&ssl_cert
, var
, value
);
103 #if LIBCURL_VERSION_NUM >= 0x070902
104 if (!strcmp("http.sslkey", var
))
105 return git_config_string(&ssl_key
, var
, value
);
107 #if LIBCURL_VERSION_NUM >= 0x070908
108 if (!strcmp("http.sslcapath", var
))
109 return git_config_string(&ssl_capath
, var
, value
);
111 if (!strcmp("http.sslcainfo", var
))
112 return git_config_string(&ssl_cainfo
, var
, value
);
113 #ifdef USE_CURL_MULTI
114 if (!strcmp("http.maxrequests", var
)) {
115 max_requests
= git_config_int(var
, value
);
119 if (!strcmp("http.lowspeedlimit", var
)) {
120 curl_low_speed_limit
= (long)git_config_int(var
, value
);
123 if (!strcmp("http.lowspeedtime", var
)) {
124 curl_low_speed_time
= (long)git_config_int(var
, value
);
128 if (!strcmp("http.noepsv", var
)) {
129 curl_ftp_no_epsv
= git_config_bool(var
, value
);
132 if (!strcmp("http.proxy", var
))
133 return git_config_string(&curl_http_proxy
, var
, value
);
135 /* Fall back on the default ones */
136 return git_default_config(var
, value
, cb
);
139 static void init_curl_http_auth(CURL
*result
)
142 struct strbuf up
= STRBUF_INIT
;
144 user_pass
= xstrdup(getpass("Password: "));
145 strbuf_addf(&up
, "%s:%s", user_name
, user_pass
);
146 curl_easy_setopt(result
, CURLOPT_USERPWD
,
147 strbuf_detach(&up
, NULL
));
151 static CURL
*get_curl_handle(void)
153 CURL
*result
= curl_easy_init();
155 if (!curl_ssl_verify
) {
156 curl_easy_setopt(result
, CURLOPT_SSL_VERIFYPEER
, 0);
157 curl_easy_setopt(result
, CURLOPT_SSL_VERIFYHOST
, 0);
159 /* Verify authenticity of the peer's certificate */
160 curl_easy_setopt(result
, CURLOPT_SSL_VERIFYPEER
, 1);
161 /* The name in the cert must match whom we tried to connect */
162 curl_easy_setopt(result
, CURLOPT_SSL_VERIFYHOST
, 2);
165 #if LIBCURL_VERSION_NUM >= 0x070907
166 curl_easy_setopt(result
, CURLOPT_NETRC
, CURL_NETRC_OPTIONAL
);
169 init_curl_http_auth(result
);
171 if (ssl_cert
!= NULL
)
172 curl_easy_setopt(result
, CURLOPT_SSLCERT
, ssl_cert
);
173 #if LIBCURL_VERSION_NUM >= 0x070902
175 curl_easy_setopt(result
, CURLOPT_SSLKEY
, ssl_key
);
177 #if LIBCURL_VERSION_NUM >= 0x070908
178 if (ssl_capath
!= NULL
)
179 curl_easy_setopt(result
, CURLOPT_CAPATH
, ssl_capath
);
181 if (ssl_cainfo
!= NULL
)
182 curl_easy_setopt(result
, CURLOPT_CAINFO
, ssl_cainfo
);
183 curl_easy_setopt(result
, CURLOPT_FAILONERROR
, 1);
185 if (curl_low_speed_limit
> 0 && curl_low_speed_time
> 0) {
186 curl_easy_setopt(result
, CURLOPT_LOW_SPEED_LIMIT
,
187 curl_low_speed_limit
);
188 curl_easy_setopt(result
, CURLOPT_LOW_SPEED_TIME
,
189 curl_low_speed_time
);
192 curl_easy_setopt(result
, CURLOPT_FOLLOWLOCATION
, 1);
194 if (getenv("GIT_CURL_VERBOSE"))
195 curl_easy_setopt(result
, CURLOPT_VERBOSE
, 1);
197 curl_easy_setopt(result
, CURLOPT_USERAGENT
, GIT_USER_AGENT
);
199 if (curl_ftp_no_epsv
)
200 curl_easy_setopt(result
, CURLOPT_FTP_USE_EPSV
, 0);
203 curl_easy_setopt(result
, CURLOPT_PROXY
, curl_http_proxy
);
208 static void http_auth_init(const char *url
)
210 char *at
, *colon
, *cp
, *slash
;
213 cp
= strstr(url
, "://");
218 * Ok, the URL looks like "proto://something". Which one?
219 * "proto://<user>:<pass>@<host>/...",
220 * "proto://<user>@<host>/...", or just
221 * "proto://<host>/..."?
224 at
= strchr(cp
, '@');
225 colon
= strchr(cp
, ':');
226 slash
= strchrnul(cp
, '/');
227 if (!at
|| slash
<= at
)
228 return; /* No credentials */
229 if (!colon
|| at
<= colon
) {
232 user_name
= xmalloc(len
+ 1);
233 memcpy(user_name
, cp
, len
);
234 user_name
[len
] = '\0';
238 user_name
= xmalloc(len
+ 1);
239 memcpy(user_name
, cp
, len
);
240 user_name
[len
] = '\0';
241 len
= at
- (colon
+ 1);
242 user_pass
= xmalloc(len
+ 1);
243 memcpy(user_pass
, colon
+ 1, len
);
244 user_pass
[len
] = '\0';
248 static void set_from_env(const char **var
, const char *envname
)
250 const char *val
= getenv(envname
);
255 void http_init(struct remote
*remote
)
257 char *low_speed_limit
;
258 char *low_speed_time
;
260 git_config(http_options
, NULL
);
262 curl_global_init(CURL_GLOBAL_ALL
);
264 if (remote
&& remote
->http_proxy
)
265 curl_http_proxy
= xstrdup(remote
->http_proxy
);
267 pragma_header
= curl_slist_append(pragma_header
, "Pragma: no-cache");
269 #ifdef USE_CURL_MULTI
271 char *http_max_requests
= getenv("GIT_HTTP_MAX_REQUESTS");
272 if (http_max_requests
!= NULL
)
273 max_requests
= atoi(http_max_requests
);
276 curlm
= curl_multi_init();
278 fprintf(stderr
, "Error creating curl multi handle.\n");
283 if (getenv("GIT_SSL_NO_VERIFY"))
286 set_from_env(&ssl_cert
, "GIT_SSL_CERT");
287 #if LIBCURL_VERSION_NUM >= 0x070902
288 set_from_env(&ssl_key
, "GIT_SSL_KEY");
290 #if LIBCURL_VERSION_NUM >= 0x070908
291 set_from_env(&ssl_capath
, "GIT_SSL_CAPATH");
293 set_from_env(&ssl_cainfo
, "GIT_SSL_CAINFO");
295 low_speed_limit
= getenv("GIT_HTTP_LOW_SPEED_LIMIT");
296 if (low_speed_limit
!= NULL
)
297 curl_low_speed_limit
= strtol(low_speed_limit
, NULL
, 10);
298 low_speed_time
= getenv("GIT_HTTP_LOW_SPEED_TIME");
299 if (low_speed_time
!= NULL
)
300 curl_low_speed_time
= strtol(low_speed_time
, NULL
, 10);
302 if (curl_ssl_verify
== -1)
305 #ifdef USE_CURL_MULTI
306 if (max_requests
< 1)
307 max_requests
= DEFAULT_MAX_REQUESTS
;
310 if (getenv("GIT_CURL_FTP_NO_EPSV"))
311 curl_ftp_no_epsv
= 1;
313 if (remote
&& remote
->url
&& remote
->url
[0])
314 http_auth_init(remote
->url
[0]);
316 #ifndef NO_CURL_EASY_DUPHANDLE
317 curl_default
= get_curl_handle();
321 void http_cleanup(void)
323 struct active_request_slot
*slot
= active_queue_head
;
325 while (slot
!= NULL
) {
326 struct active_request_slot
*next
= slot
->next
;
327 if (slot
->curl
!= NULL
) {
328 #ifdef USE_CURL_MULTI
329 curl_multi_remove_handle(curlm
, slot
->curl
);
331 curl_easy_cleanup(slot
->curl
);
336 active_queue_head
= NULL
;
338 #ifndef NO_CURL_EASY_DUPHANDLE
339 curl_easy_cleanup(curl_default
);
342 #ifdef USE_CURL_MULTI
343 curl_multi_cleanup(curlm
);
345 curl_global_cleanup();
347 curl_slist_free_all(pragma_header
);
348 pragma_header
= NULL
;
350 if (curl_http_proxy
) {
351 free((void *)curl_http_proxy
);
352 curl_http_proxy
= NULL
;
356 struct active_request_slot
*get_active_slot(void)
358 struct active_request_slot
*slot
= active_queue_head
;
359 struct active_request_slot
*newslot
;
361 #ifdef USE_CURL_MULTI
364 /* Wait for a slot to open up if the queue is full */
365 while (active_requests
>= max_requests
) {
366 curl_multi_perform(curlm
, &num_transfers
);
367 if (num_transfers
< active_requests
)
368 process_curl_messages();
372 while (slot
!= NULL
&& slot
->in_use
)
376 newslot
= xmalloc(sizeof(*newslot
));
377 newslot
->curl
= NULL
;
379 newslot
->next
= NULL
;
381 slot
= active_queue_head
;
383 active_queue_head
= newslot
;
385 while (slot
->next
!= NULL
)
387 slot
->next
= newslot
;
392 if (slot
->curl
== NULL
) {
393 #ifdef NO_CURL_EASY_DUPHANDLE
394 slot
->curl
= get_curl_handle();
396 slot
->curl
= curl_easy_duphandle(curl_default
);
403 slot
->results
= NULL
;
404 slot
->finished
= NULL
;
405 slot
->callback_data
= NULL
;
406 slot
->callback_func
= NULL
;
407 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, pragma_header
);
408 curl_easy_setopt(slot
->curl
, CURLOPT_ERRORBUFFER
, curl_errorstr
);
409 curl_easy_setopt(slot
->curl
, CURLOPT_CUSTOMREQUEST
, NULL
);
410 curl_easy_setopt(slot
->curl
, CURLOPT_READFUNCTION
, NULL
);
411 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, NULL
);
412 curl_easy_setopt(slot
->curl
, CURLOPT_UPLOAD
, 0);
413 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPGET
, 1);
418 int start_active_slot(struct active_request_slot
*slot
)
420 #ifdef USE_CURL_MULTI
421 CURLMcode curlm_result
= curl_multi_add_handle(curlm
, slot
->curl
);
424 if (curlm_result
!= CURLM_OK
&&
425 curlm_result
!= CURLM_CALL_MULTI_PERFORM
) {
432 * We know there must be something to do, since we just added
435 curl_multi_perform(curlm
, &num_transfers
);
440 #ifdef USE_CURL_MULTI
444 struct fill_chain
*next
;
447 static struct fill_chain
*fill_cfg
;
449 void add_fill_function(void *data
, int (*fill
)(void *))
451 struct fill_chain
*new = xmalloc(sizeof(*new));
452 struct fill_chain
**linkp
= &fill_cfg
;
457 linkp
= &(*linkp
)->next
;
461 void fill_active_slots(void)
463 struct active_request_slot
*slot
= active_queue_head
;
465 while (active_requests
< max_requests
) {
466 struct fill_chain
*fill
;
467 for (fill
= fill_cfg
; fill
; fill
= fill
->next
)
468 if (fill
->fill(fill
->data
))
475 while (slot
!= NULL
) {
476 if (!slot
->in_use
&& slot
->curl
!= NULL
) {
477 curl_easy_cleanup(slot
->curl
);
484 void step_active_slots(void)
487 CURLMcode curlm_result
;
490 curlm_result
= curl_multi_perform(curlm
, &num_transfers
);
491 } while (curlm_result
== CURLM_CALL_MULTI_PERFORM
);
492 if (num_transfers
< active_requests
) {
493 process_curl_messages();
499 void run_active_slot(struct active_request_slot
*slot
)
501 #ifdef USE_CURL_MULTI
508 struct timeval select_timeout
;
511 slot
->finished
= &finished
;
516 if (!data_received
&& slot
->local
!= NULL
) {
517 current_pos
= ftell(slot
->local
);
518 if (current_pos
> last_pos
)
520 last_pos
= current_pos
;
523 if (slot
->in_use
&& !data_received
) {
528 select_timeout
.tv_sec
= 0;
529 select_timeout
.tv_usec
= 50000;
530 select(max_fd
, &readfds
, &writefds
,
531 &excfds
, &select_timeout
);
535 while (slot
->in_use
) {
536 slot
->curl_result
= curl_easy_perform(slot
->curl
);
537 finish_active_slot(slot
);
542 static void closedown_active_slot(struct active_request_slot
*slot
)
548 void release_active_slot(struct active_request_slot
*slot
)
550 closedown_active_slot(slot
);
552 #ifdef USE_CURL_MULTI
553 curl_multi_remove_handle(curlm
, slot
->curl
);
555 curl_easy_cleanup(slot
->curl
);
558 #ifdef USE_CURL_MULTI
563 static void finish_active_slot(struct active_request_slot
*slot
)
565 closedown_active_slot(slot
);
566 curl_easy_getinfo(slot
->curl
, CURLINFO_HTTP_CODE
, &slot
->http_code
);
568 if (slot
->finished
!= NULL
)
569 (*slot
->finished
) = 1;
571 /* Store slot results so they can be read after the slot is reused */
572 if (slot
->results
!= NULL
) {
573 slot
->results
->curl_result
= slot
->curl_result
;
574 slot
->results
->http_code
= slot
->http_code
;
577 /* Run callback if appropriate */
578 if (slot
->callback_func
!= NULL
)
579 slot
->callback_func(slot
->callback_data
);
582 void finish_all_active_slots(void)
584 struct active_request_slot
*slot
= active_queue_head
;
588 run_active_slot(slot
);
589 slot
= active_queue_head
;
595 static inline int needs_quote(int ch
)
597 if (((ch
>= 'A') && (ch
<= 'Z'))
598 || ((ch
>= 'a') && (ch
<= 'z'))
599 || ((ch
>= '0') && (ch
<= '9'))
607 static inline int hex(int v
)
615 static char *quote_ref_url(const char *base
, const char *ref
)
617 struct strbuf buf
= STRBUF_INIT
;
621 strbuf_addstr(&buf
, base
);
622 if (buf
.len
&& buf
.buf
[buf
.len
- 1] != '/' && *ref
!= '/')
623 strbuf_addstr(&buf
, "/");
625 for (cp
= ref
; (ch
= *cp
) != 0; cp
++)
627 strbuf_addf(&buf
, "%%%02x", ch
);
629 strbuf_addch(&buf
, *cp
);
631 return strbuf_detach(&buf
, NULL
);
634 int http_fetch_ref(const char *base
, struct ref
*ref
)
637 struct strbuf buffer
= STRBUF_INIT
;
638 struct active_request_slot
*slot
;
639 struct slot_results results
;
642 url
= quote_ref_url(base
, ref
->name
);
643 slot
= get_active_slot();
644 slot
->results
= &results
;
645 curl_easy_setopt(slot
->curl
, CURLOPT_FILE
, &buffer
);
646 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, fwrite_buffer
);
647 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, NULL
);
648 curl_easy_setopt(slot
->curl
, CURLOPT_URL
, url
);
649 if (start_active_slot(slot
)) {
650 run_active_slot(slot
);
651 if (results
.curl_result
== CURLE_OK
) {
652 strbuf_rtrim(&buffer
);
653 if (buffer
.len
== 40)
654 ret
= get_sha1_hex(buffer
.buf
, ref
->old_sha1
);
655 else if (!prefixcmp(buffer
.buf
, "ref: ")) {
656 ref
->symref
= xstrdup(buffer
.buf
+ 5);
661 ret
= error("Couldn't get %s for %s\n%s",
662 url
, ref
->name
, curl_errorstr
);
665 ret
= error("Unable to start request");
668 strbuf_release(&buffer
);