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
);
48 curlioerr
ioctl_buffer(CURL
*handle
, int cmd
, void *clientp
)
50 struct buffer
*buffer
= clientp
;
56 case CURLIOCMD_RESTARTREAD
:
61 return CURLIOE_UNKNOWNCMD
;
66 size_t fwrite_buffer(const void *ptr
, size_t eltsize
, size_t nmemb
, void *buffer_
)
68 size_t size
= eltsize
* nmemb
;
69 struct strbuf
*buffer
= buffer_
;
71 strbuf_add(buffer
, ptr
, size
);
76 size_t fwrite_null(const void *ptr
, size_t eltsize
, size_t nmemb
, void *strbuf
)
79 return eltsize
* nmemb
;
82 static void finish_active_slot(struct active_request_slot
*slot
);
85 static void process_curl_messages(void)
88 struct active_request_slot
*slot
;
89 CURLMsg
*curl_message
= curl_multi_info_read(curlm
, &num_messages
);
91 while (curl_message
!= NULL
) {
92 if (curl_message
->msg
== CURLMSG_DONE
) {
93 int curl_result
= curl_message
->data
.result
;
94 slot
= active_queue_head
;
95 while (slot
!= NULL
&&
96 slot
->curl
!= curl_message
->easy_handle
)
99 curl_multi_remove_handle(curlm
, slot
->curl
);
100 slot
->curl_result
= curl_result
;
101 finish_active_slot(slot
);
103 fprintf(stderr
, "Received DONE message for unknown request!\n");
106 fprintf(stderr
, "Unknown CURL message received: %d\n",
107 (int)curl_message
->msg
);
109 curl_message
= curl_multi_info_read(curlm
, &num_messages
);
114 static int http_options(const char *var
, const char *value
, void *cb
)
116 if (!strcmp("http.sslverify", var
)) {
117 curl_ssl_verify
= git_config_bool(var
, value
);
120 if (!strcmp("http.sslcert", var
))
121 return git_config_string(&ssl_cert
, var
, value
);
122 #if LIBCURL_VERSION_NUM >= 0x070902
123 if (!strcmp("http.sslkey", var
))
124 return git_config_string(&ssl_key
, var
, value
);
126 #if LIBCURL_VERSION_NUM >= 0x070908
127 if (!strcmp("http.sslcapath", var
))
128 return git_config_string(&ssl_capath
, var
, value
);
130 if (!strcmp("http.sslcainfo", var
))
131 return git_config_string(&ssl_cainfo
, var
, value
);
132 #ifdef USE_CURL_MULTI
133 if (!strcmp("http.maxrequests", var
)) {
134 max_requests
= git_config_int(var
, value
);
138 if (!strcmp("http.lowspeedlimit", var
)) {
139 curl_low_speed_limit
= (long)git_config_int(var
, value
);
142 if (!strcmp("http.lowspeedtime", var
)) {
143 curl_low_speed_time
= (long)git_config_int(var
, value
);
147 if (!strcmp("http.noepsv", var
)) {
148 curl_ftp_no_epsv
= git_config_bool(var
, value
);
151 if (!strcmp("http.proxy", var
))
152 return git_config_string(&curl_http_proxy
, var
, value
);
154 /* Fall back on the default ones */
155 return git_default_config(var
, value
, cb
);
158 static void init_curl_http_auth(CURL
*result
)
161 struct strbuf up
= STRBUF_INIT
;
163 user_pass
= xstrdup(getpass("Password: "));
164 strbuf_addf(&up
, "%s:%s", user_name
, user_pass
);
165 curl_easy_setopt(result
, CURLOPT_USERPWD
,
166 strbuf_detach(&up
, NULL
));
170 static CURL
*get_curl_handle(void)
172 CURL
*result
= curl_easy_init();
174 if (!curl_ssl_verify
) {
175 curl_easy_setopt(result
, CURLOPT_SSL_VERIFYPEER
, 0);
176 curl_easy_setopt(result
, CURLOPT_SSL_VERIFYHOST
, 0);
178 /* Verify authenticity of the peer's certificate */
179 curl_easy_setopt(result
, CURLOPT_SSL_VERIFYPEER
, 1);
180 /* The name in the cert must match whom we tried to connect */
181 curl_easy_setopt(result
, CURLOPT_SSL_VERIFYHOST
, 2);
184 #if LIBCURL_VERSION_NUM >= 0x070907
185 curl_easy_setopt(result
, CURLOPT_NETRC
, CURL_NETRC_OPTIONAL
);
188 init_curl_http_auth(result
);
190 if (ssl_cert
!= NULL
)
191 curl_easy_setopt(result
, CURLOPT_SSLCERT
, ssl_cert
);
192 #if LIBCURL_VERSION_NUM >= 0x070902
194 curl_easy_setopt(result
, CURLOPT_SSLKEY
, ssl_key
);
196 #if LIBCURL_VERSION_NUM >= 0x070908
197 if (ssl_capath
!= NULL
)
198 curl_easy_setopt(result
, CURLOPT_CAPATH
, ssl_capath
);
200 if (ssl_cainfo
!= NULL
)
201 curl_easy_setopt(result
, CURLOPT_CAINFO
, ssl_cainfo
);
202 curl_easy_setopt(result
, CURLOPT_FAILONERROR
, 1);
204 if (curl_low_speed_limit
> 0 && curl_low_speed_time
> 0) {
205 curl_easy_setopt(result
, CURLOPT_LOW_SPEED_LIMIT
,
206 curl_low_speed_limit
);
207 curl_easy_setopt(result
, CURLOPT_LOW_SPEED_TIME
,
208 curl_low_speed_time
);
211 curl_easy_setopt(result
, CURLOPT_FOLLOWLOCATION
, 1);
213 if (getenv("GIT_CURL_VERBOSE"))
214 curl_easy_setopt(result
, CURLOPT_VERBOSE
, 1);
216 curl_easy_setopt(result
, CURLOPT_USERAGENT
, GIT_USER_AGENT
);
218 if (curl_ftp_no_epsv
)
219 curl_easy_setopt(result
, CURLOPT_FTP_USE_EPSV
, 0);
222 curl_easy_setopt(result
, CURLOPT_PROXY
, curl_http_proxy
);
227 static void http_auth_init(const char *url
)
229 char *at
, *colon
, *cp
, *slash
;
232 cp
= strstr(url
, "://");
237 * Ok, the URL looks like "proto://something". Which one?
238 * "proto://<user>:<pass>@<host>/...",
239 * "proto://<user>@<host>/...", or just
240 * "proto://<host>/..."?
243 at
= strchr(cp
, '@');
244 colon
= strchr(cp
, ':');
245 slash
= strchrnul(cp
, '/');
246 if (!at
|| slash
<= at
)
247 return; /* No credentials */
248 if (!colon
|| at
<= colon
) {
251 user_name
= xmalloc(len
+ 1);
252 memcpy(user_name
, cp
, len
);
253 user_name
[len
] = '\0';
257 user_name
= xmalloc(len
+ 1);
258 memcpy(user_name
, cp
, len
);
259 user_name
[len
] = '\0';
260 len
= at
- (colon
+ 1);
261 user_pass
= xmalloc(len
+ 1);
262 memcpy(user_pass
, colon
+ 1, len
);
263 user_pass
[len
] = '\0';
267 static void set_from_env(const char **var
, const char *envname
)
269 const char *val
= getenv(envname
);
274 void http_init(struct remote
*remote
)
276 char *low_speed_limit
;
277 char *low_speed_time
;
279 git_config(http_options
, NULL
);
281 curl_global_init(CURL_GLOBAL_ALL
);
283 if (remote
&& remote
->http_proxy
)
284 curl_http_proxy
= xstrdup(remote
->http_proxy
);
286 pragma_header
= curl_slist_append(pragma_header
, "Pragma: no-cache");
288 #ifdef USE_CURL_MULTI
290 char *http_max_requests
= getenv("GIT_HTTP_MAX_REQUESTS");
291 if (http_max_requests
!= NULL
)
292 max_requests
= atoi(http_max_requests
);
295 curlm
= curl_multi_init();
297 fprintf(stderr
, "Error creating curl multi handle.\n");
302 if (getenv("GIT_SSL_NO_VERIFY"))
305 set_from_env(&ssl_cert
, "GIT_SSL_CERT");
306 #if LIBCURL_VERSION_NUM >= 0x070902
307 set_from_env(&ssl_key
, "GIT_SSL_KEY");
309 #if LIBCURL_VERSION_NUM >= 0x070908
310 set_from_env(&ssl_capath
, "GIT_SSL_CAPATH");
312 set_from_env(&ssl_cainfo
, "GIT_SSL_CAINFO");
314 low_speed_limit
= getenv("GIT_HTTP_LOW_SPEED_LIMIT");
315 if (low_speed_limit
!= NULL
)
316 curl_low_speed_limit
= strtol(low_speed_limit
, NULL
, 10);
317 low_speed_time
= getenv("GIT_HTTP_LOW_SPEED_TIME");
318 if (low_speed_time
!= NULL
)
319 curl_low_speed_time
= strtol(low_speed_time
, NULL
, 10);
321 if (curl_ssl_verify
== -1)
324 #ifdef USE_CURL_MULTI
325 if (max_requests
< 1)
326 max_requests
= DEFAULT_MAX_REQUESTS
;
329 if (getenv("GIT_CURL_FTP_NO_EPSV"))
330 curl_ftp_no_epsv
= 1;
332 if (remote
&& remote
->url
&& remote
->url
[0])
333 http_auth_init(remote
->url
[0]);
335 #ifndef NO_CURL_EASY_DUPHANDLE
336 curl_default
= get_curl_handle();
340 void http_cleanup(void)
342 struct active_request_slot
*slot
= active_queue_head
;
344 while (slot
!= NULL
) {
345 struct active_request_slot
*next
= slot
->next
;
346 if (slot
->curl
!= NULL
) {
347 #ifdef USE_CURL_MULTI
348 curl_multi_remove_handle(curlm
, slot
->curl
);
350 curl_easy_cleanup(slot
->curl
);
355 active_queue_head
= NULL
;
357 #ifndef NO_CURL_EASY_DUPHANDLE
358 curl_easy_cleanup(curl_default
);
361 #ifdef USE_CURL_MULTI
362 curl_multi_cleanup(curlm
);
364 curl_global_cleanup();
366 curl_slist_free_all(pragma_header
);
367 pragma_header
= NULL
;
369 if (curl_http_proxy
) {
370 free((void *)curl_http_proxy
);
371 curl_http_proxy
= NULL
;
375 struct active_request_slot
*get_active_slot(void)
377 struct active_request_slot
*slot
= active_queue_head
;
378 struct active_request_slot
*newslot
;
380 #ifdef USE_CURL_MULTI
383 /* Wait for a slot to open up if the queue is full */
384 while (active_requests
>= max_requests
) {
385 curl_multi_perform(curlm
, &num_transfers
);
386 if (num_transfers
< active_requests
)
387 process_curl_messages();
391 while (slot
!= NULL
&& slot
->in_use
)
395 newslot
= xmalloc(sizeof(*newslot
));
396 newslot
->curl
= NULL
;
398 newslot
->next
= NULL
;
400 slot
= active_queue_head
;
402 active_queue_head
= newslot
;
404 while (slot
->next
!= NULL
)
406 slot
->next
= newslot
;
411 if (slot
->curl
== NULL
) {
412 #ifdef NO_CURL_EASY_DUPHANDLE
413 slot
->curl
= get_curl_handle();
415 slot
->curl
= curl_easy_duphandle(curl_default
);
422 slot
->results
= NULL
;
423 slot
->finished
= NULL
;
424 slot
->callback_data
= NULL
;
425 slot
->callback_func
= NULL
;
426 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, pragma_header
);
427 curl_easy_setopt(slot
->curl
, CURLOPT_ERRORBUFFER
, curl_errorstr
);
428 curl_easy_setopt(slot
->curl
, CURLOPT_CUSTOMREQUEST
, NULL
);
429 curl_easy_setopt(slot
->curl
, CURLOPT_READFUNCTION
, NULL
);
430 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, NULL
);
431 curl_easy_setopt(slot
->curl
, CURLOPT_UPLOAD
, 0);
432 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPGET
, 1);
437 int start_active_slot(struct active_request_slot
*slot
)
439 #ifdef USE_CURL_MULTI
440 CURLMcode curlm_result
= curl_multi_add_handle(curlm
, slot
->curl
);
443 if (curlm_result
!= CURLM_OK
&&
444 curlm_result
!= CURLM_CALL_MULTI_PERFORM
) {
451 * We know there must be something to do, since we just added
454 curl_multi_perform(curlm
, &num_transfers
);
459 #ifdef USE_CURL_MULTI
463 struct fill_chain
*next
;
466 static struct fill_chain
*fill_cfg
;
468 void add_fill_function(void *data
, int (*fill
)(void *))
470 struct fill_chain
*new = xmalloc(sizeof(*new));
471 struct fill_chain
**linkp
= &fill_cfg
;
476 linkp
= &(*linkp
)->next
;
480 void fill_active_slots(void)
482 struct active_request_slot
*slot
= active_queue_head
;
484 while (active_requests
< max_requests
) {
485 struct fill_chain
*fill
;
486 for (fill
= fill_cfg
; fill
; fill
= fill
->next
)
487 if (fill
->fill(fill
->data
))
494 while (slot
!= NULL
) {
495 if (!slot
->in_use
&& slot
->curl
!= NULL
) {
496 curl_easy_cleanup(slot
->curl
);
503 void step_active_slots(void)
506 CURLMcode curlm_result
;
509 curlm_result
= curl_multi_perform(curlm
, &num_transfers
);
510 } while (curlm_result
== CURLM_CALL_MULTI_PERFORM
);
511 if (num_transfers
< active_requests
) {
512 process_curl_messages();
518 void run_active_slot(struct active_request_slot
*slot
)
520 #ifdef USE_CURL_MULTI
527 struct timeval select_timeout
;
530 slot
->finished
= &finished
;
535 if (!data_received
&& slot
->local
!= NULL
) {
536 current_pos
= ftell(slot
->local
);
537 if (current_pos
> last_pos
)
539 last_pos
= current_pos
;
542 if (slot
->in_use
&& !data_received
) {
547 select_timeout
.tv_sec
= 0;
548 select_timeout
.tv_usec
= 50000;
549 select(max_fd
, &readfds
, &writefds
,
550 &excfds
, &select_timeout
);
554 while (slot
->in_use
) {
555 slot
->curl_result
= curl_easy_perform(slot
->curl
);
556 finish_active_slot(slot
);
561 static void closedown_active_slot(struct active_request_slot
*slot
)
567 void release_active_slot(struct active_request_slot
*slot
)
569 closedown_active_slot(slot
);
571 #ifdef USE_CURL_MULTI
572 curl_multi_remove_handle(curlm
, slot
->curl
);
574 curl_easy_cleanup(slot
->curl
);
577 #ifdef USE_CURL_MULTI
582 static void finish_active_slot(struct active_request_slot
*slot
)
584 closedown_active_slot(slot
);
585 curl_easy_getinfo(slot
->curl
, CURLINFO_HTTP_CODE
, &slot
->http_code
);
587 if (slot
->finished
!= NULL
)
588 (*slot
->finished
) = 1;
590 /* Store slot results so they can be read after the slot is reused */
591 if (slot
->results
!= NULL
) {
592 slot
->results
->curl_result
= slot
->curl_result
;
593 slot
->results
->http_code
= slot
->http_code
;
596 /* Run callback if appropriate */
597 if (slot
->callback_func
!= NULL
)
598 slot
->callback_func(slot
->callback_data
);
601 void finish_all_active_slots(void)
603 struct active_request_slot
*slot
= active_queue_head
;
607 run_active_slot(slot
);
608 slot
= active_queue_head
;
614 static inline int needs_quote(int ch
)
616 if (((ch
>= 'A') && (ch
<= 'Z'))
617 || ((ch
>= 'a') && (ch
<= 'z'))
618 || ((ch
>= '0') && (ch
<= '9'))
626 static inline int hex(int v
)
634 static char *quote_ref_url(const char *base
, const char *ref
)
636 struct strbuf buf
= STRBUF_INIT
;
640 strbuf_addstr(&buf
, base
);
641 if (buf
.len
&& buf
.buf
[buf
.len
- 1] != '/' && *ref
!= '/')
642 strbuf_addstr(&buf
, "/");
644 for (cp
= ref
; (ch
= *cp
) != 0; cp
++)
646 strbuf_addf(&buf
, "%%%02x", ch
);
648 strbuf_addch(&buf
, *cp
);
650 return strbuf_detach(&buf
, NULL
);
653 int http_fetch_ref(const char *base
, struct ref
*ref
)
656 struct strbuf buffer
= STRBUF_INIT
;
657 struct active_request_slot
*slot
;
658 struct slot_results results
;
661 url
= quote_ref_url(base
, ref
->name
);
662 slot
= get_active_slot();
663 slot
->results
= &results
;
664 curl_easy_setopt(slot
->curl
, CURLOPT_FILE
, &buffer
);
665 curl_easy_setopt(slot
->curl
, CURLOPT_WRITEFUNCTION
, fwrite_buffer
);
666 curl_easy_setopt(slot
->curl
, CURLOPT_HTTPHEADER
, NULL
);
667 curl_easy_setopt(slot
->curl
, CURLOPT_URL
, url
);
668 if (start_active_slot(slot
)) {
669 run_active_slot(slot
);
670 if (results
.curl_result
== CURLE_OK
) {
671 strbuf_rtrim(&buffer
);
672 if (buffer
.len
== 40)
673 ret
= get_sha1_hex(buffer
.buf
, ref
->old_sha1
);
674 else if (!prefixcmp(buffer
.buf
, "ref: ")) {
675 ref
->symref
= xstrdup(buffer
.buf
+ 5);
680 ret
= error("Couldn't get %s for %s\n%s",
681 url
, ref
->name
, curl_errorstr
);
684 ret
= error("Unable to start request");
687 strbuf_release(&buffer
);