]> git.ipfire.org Git - thirdparty/git.git/blame - http.c
http*: add helper methods for fetching packs
[thirdparty/git.git] / http.c
CommitLineData
29508e1e 1#include "http.h"
2264dfa5 2#include "pack.h"
29508e1e
NH
3
4int data_received;
4251ccbd 5int active_requests;
e9176745 6int http_is_verbose;
29508e1e
NH
7
8#ifdef USE_CURL_MULTI
cc3530e8
MH
9static int max_requests = -1;
10static CURLM *curlm;
29508e1e
NH
11#endif
12#ifndef NO_CURL_EASY_DUPHANDLE
cc3530e8 13static CURL *curl_default;
29508e1e
NH
14#endif
15char curl_errorstr[CURL_ERROR_SIZE];
16
cc3530e8 17static int curl_ssl_verify = -1;
4251ccbd 18static const char *ssl_cert;
29508e1e 19#if LIBCURL_VERSION_NUM >= 0x070902
4251ccbd 20static const char *ssl_key;
29508e1e
NH
21#endif
22#if LIBCURL_VERSION_NUM >= 0x070908
4251ccbd 23static const char *ssl_capath;
29508e1e 24#endif
4251ccbd 25static const char *ssl_cainfo;
cc3530e8
MH
26static long curl_low_speed_limit = -1;
27static long curl_low_speed_time = -1;
4251ccbd
JH
28static int curl_ftp_no_epsv;
29static const char *curl_http_proxy;
c33976cb 30static char *user_name, *user_pass;
29508e1e 31
cc3530e8 32static struct curl_slist *pragma_header;
29508e1e 33
e9176745
TRC
34struct curl_slist *no_pragma_header;
35
4251ccbd 36static struct active_request_slot *active_queue_head;
29508e1e 37
f444e528 38size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
29508e1e
NH
39{
40 size_t size = eltsize * nmemb;
f444e528
JH
41 struct buffer *buffer = buffer_;
42
028c2976
MH
43 if (size > buffer->buf.len - buffer->posn)
44 size = buffer->buf.len - buffer->posn;
45 memcpy(ptr, buffer->buf.buf + buffer->posn, size);
29508e1e 46 buffer->posn += size;
028c2976 47
29508e1e
NH
48 return size;
49}
50
3944ba0c
MS
51#ifndef NO_CURL_IOCTL
52curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp)
53{
54 struct buffer *buffer = clientp;
55
56 switch (cmd) {
57 case CURLIOCMD_NOP:
58 return CURLIOE_OK;
59
60 case CURLIOCMD_RESTARTREAD:
61 buffer->posn = 0;
62 return CURLIOE_OK;
63
64 default:
65 return CURLIOE_UNKNOWNCMD;
66 }
67}
68#endif
69
f444e528 70size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
29508e1e
NH
71{
72 size_t size = eltsize * nmemb;
f444e528
JH
73 struct strbuf *buffer = buffer_;
74
028c2976 75 strbuf_add(buffer, ptr, size);
29508e1e
NH
76 data_received++;
77 return size;
78}
79
f444e528 80size_t fwrite_null(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf)
29508e1e
NH
81{
82 data_received++;
83 return eltsize * nmemb;
84}
85
86static void finish_active_slot(struct active_request_slot *slot);
87
88#ifdef USE_CURL_MULTI
89static void process_curl_messages(void)
90{
91 int num_messages;
92 struct active_request_slot *slot;
93 CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);
94
95 while (curl_message != NULL) {
96 if (curl_message->msg == CURLMSG_DONE) {
97 int curl_result = curl_message->data.result;
98 slot = active_queue_head;
99 while (slot != NULL &&
100 slot->curl != curl_message->easy_handle)
101 slot = slot->next;
102 if (slot != NULL) {
103 curl_multi_remove_handle(curlm, slot->curl);
104 slot->curl_result = curl_result;
105 finish_active_slot(slot);
106 } else {
107 fprintf(stderr, "Received DONE message for unknown request!\n");
108 }
109 } else {
110 fprintf(stderr, "Unknown CURL message received: %d\n",
111 (int)curl_message->msg);
112 }
113 curl_message = curl_multi_info_read(curlm, &num_messages);
114 }
115}
116#endif
117
ef90d6d4 118static int http_options(const char *var, const char *value, void *cb)
29508e1e
NH
119{
120 if (!strcmp("http.sslverify", var)) {
7059cd99 121 curl_ssl_verify = git_config_bool(var, value);
29508e1e
NH
122 return 0;
123 }
7059cd99
JH
124 if (!strcmp("http.sslcert", var))
125 return git_config_string(&ssl_cert, var, value);
29508e1e 126#if LIBCURL_VERSION_NUM >= 0x070902
7059cd99
JH
127 if (!strcmp("http.sslkey", var))
128 return git_config_string(&ssl_key, var, value);
29508e1e
NH
129#endif
130#if LIBCURL_VERSION_NUM >= 0x070908
7059cd99
JH
131 if (!strcmp("http.sslcapath", var))
132 return git_config_string(&ssl_capath, var, value);
29508e1e 133#endif
7059cd99
JH
134 if (!strcmp("http.sslcainfo", var))
135 return git_config_string(&ssl_cainfo, var, value);
a6080a0a 136#ifdef USE_CURL_MULTI
29508e1e 137 if (!strcmp("http.maxrequests", var)) {
7059cd99 138 max_requests = git_config_int(var, value);
29508e1e
NH
139 return 0;
140 }
141#endif
29508e1e 142 if (!strcmp("http.lowspeedlimit", var)) {
7059cd99 143 curl_low_speed_limit = (long)git_config_int(var, value);
29508e1e
NH
144 return 0;
145 }
146 if (!strcmp("http.lowspeedtime", var)) {
7059cd99 147 curl_low_speed_time = (long)git_config_int(var, value);
29508e1e
NH
148 return 0;
149 }
150
3ea099d4
SK
151 if (!strcmp("http.noepsv", var)) {
152 curl_ftp_no_epsv = git_config_bool(var, value);
153 return 0;
154 }
7059cd99
JH
155 if (!strcmp("http.proxy", var))
156 return git_config_string(&curl_http_proxy, var, value);
3ea099d4 157
29508e1e 158 /* Fall back on the default ones */
ef90d6d4 159 return git_default_config(var, value, cb);
29508e1e
NH
160}
161
c33976cb
JH
162static void init_curl_http_auth(CURL *result)
163{
750d9305 164 if (user_name) {
c33976cb
JH
165 struct strbuf up = STRBUF_INIT;
166 if (!user_pass)
167 user_pass = xstrdup(getpass("Password: "));
168 strbuf_addf(&up, "%s:%s", user_name, user_pass);
169 curl_easy_setopt(result, CURLOPT_USERPWD,
170 strbuf_detach(&up, NULL));
171 }
172}
173
4251ccbd 174static CURL *get_curl_handle(void)
11979b98 175{
4251ccbd 176 CURL *result = curl_easy_init();
11979b98 177
a5ccc597
JH
178 if (!curl_ssl_verify) {
179 curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
180 curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0);
181 } else {
182 /* Verify authenticity of the peer's certificate */
183 curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 1);
184 /* The name in the cert must match whom we tried to connect */
185 curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
186 }
187
11979b98
JH
188#if LIBCURL_VERSION_NUM >= 0x070907
189 curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
190#endif
191
c33976cb
JH
192 init_curl_http_auth(result);
193
11979b98
JH
194 if (ssl_cert != NULL)
195 curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
196#if LIBCURL_VERSION_NUM >= 0x070902
197 if (ssl_key != NULL)
198 curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
199#endif
200#if LIBCURL_VERSION_NUM >= 0x070908
201 if (ssl_capath != NULL)
202 curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
203#endif
204 if (ssl_cainfo != NULL)
205 curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
206 curl_easy_setopt(result, CURLOPT_FAILONERROR, 1);
207
208 if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
209 curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
210 curl_low_speed_limit);
211 curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,
212 curl_low_speed_time);
213 }
214
215 curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
216
7982d74e
MW
217 if (getenv("GIT_CURL_VERBOSE"))
218 curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
219
20fc9bc5
NH
220 curl_easy_setopt(result, CURLOPT_USERAGENT, GIT_USER_AGENT);
221
3ea099d4
SK
222 if (curl_ftp_no_epsv)
223 curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
224
9c5665aa
SV
225 if (curl_http_proxy)
226 curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
227
11979b98
JH
228 return result;
229}
230
c33976cb
JH
231static void http_auth_init(const char *url)
232{
233 char *at, *colon, *cp, *slash;
234 int len;
235
236 cp = strstr(url, "://");
237 if (!cp)
238 return;
239
240 /*
241 * Ok, the URL looks like "proto://something". Which one?
242 * "proto://<user>:<pass>@<host>/...",
243 * "proto://<user>@<host>/...", or just
244 * "proto://<host>/..."?
245 */
246 cp += 3;
247 at = strchr(cp, '@');
248 colon = strchr(cp, ':');
249 slash = strchrnul(cp, '/');
250 if (!at || slash <= at)
251 return; /* No credentials */
252 if (!colon || at <= colon) {
253 /* Only username */
254 len = at - cp;
255 user_name = xmalloc(len + 1);
256 memcpy(user_name, cp, len);
257 user_name[len] = '\0';
258 user_pass = NULL;
259 } else {
260 len = colon - cp;
261 user_name = xmalloc(len + 1);
262 memcpy(user_name, cp, len);
263 user_name[len] = '\0';
264 len = at - (colon + 1);
265 user_pass = xmalloc(len + 1);
266 memcpy(user_pass, colon + 1, len);
267 user_pass[len] = '\0';
268 }
269}
270
7059cd99
JH
271static void set_from_env(const char **var, const char *envname)
272{
273 const char *val = getenv(envname);
274 if (val)
275 *var = val;
276}
277
9fc6440d 278void http_init(struct remote *remote)
29508e1e
NH
279{
280 char *low_speed_limit;
281 char *low_speed_time;
282
e9176745
TRC
283 http_is_verbose = 0;
284
7059cd99
JH
285 git_config(http_options, NULL);
286
29508e1e
NH
287 curl_global_init(CURL_GLOBAL_ALL);
288
9fc6440d
MH
289 if (remote && remote->http_proxy)
290 curl_http_proxy = xstrdup(remote->http_proxy);
291
29508e1e 292 pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
e9176745 293 no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
29508e1e
NH
294
295#ifdef USE_CURL_MULTI
296 {
297 char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
298 if (http_max_requests != NULL)
299 max_requests = atoi(http_max_requests);
300 }
301
302 curlm = curl_multi_init();
303 if (curlm == NULL) {
304 fprintf(stderr, "Error creating curl multi handle.\n");
305 exit(1);
306 }
307#endif
308
309 if (getenv("GIT_SSL_NO_VERIFY"))
310 curl_ssl_verify = 0;
311
7059cd99 312 set_from_env(&ssl_cert, "GIT_SSL_CERT");
29508e1e 313#if LIBCURL_VERSION_NUM >= 0x070902
7059cd99 314 set_from_env(&ssl_key, "GIT_SSL_KEY");
29508e1e
NH
315#endif
316#if LIBCURL_VERSION_NUM >= 0x070908
7059cd99 317 set_from_env(&ssl_capath, "GIT_SSL_CAPATH");
29508e1e 318#endif
7059cd99 319 set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
29508e1e
NH
320
321 low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
322 if (low_speed_limit != NULL)
323 curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
324 low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME");
325 if (low_speed_time != NULL)
326 curl_low_speed_time = strtol(low_speed_time, NULL, 10);
327
29508e1e
NH
328 if (curl_ssl_verify == -1)
329 curl_ssl_verify = 1;
330
331#ifdef USE_CURL_MULTI
332 if (max_requests < 1)
333 max_requests = DEFAULT_MAX_REQUESTS;
334#endif
335
3ea099d4
SK
336 if (getenv("GIT_CURL_FTP_NO_EPSV"))
337 curl_ftp_no_epsv = 1;
338
c33976cb
JH
339 if (remote && remote->url && remote->url[0])
340 http_auth_init(remote->url[0]);
341
29508e1e
NH
342#ifndef NO_CURL_EASY_DUPHANDLE
343 curl_default = get_curl_handle();
344#endif
345}
346
347void http_cleanup(void)
348{
349 struct active_request_slot *slot = active_queue_head;
29508e1e
NH
350
351 while (slot != NULL) {
3278cd0a 352 struct active_request_slot *next = slot->next;
f23d1f76 353 if (slot->curl != NULL) {
29508e1e 354#ifdef USE_CURL_MULTI
f23d1f76 355 curl_multi_remove_handle(curlm, slot->curl);
29508e1e 356#endif
29508e1e 357 curl_easy_cleanup(slot->curl);
f23d1f76 358 }
3278cd0a
SP
359 free(slot);
360 slot = next;
29508e1e 361 }
3278cd0a 362 active_queue_head = NULL;
29508e1e
NH
363
364#ifndef NO_CURL_EASY_DUPHANDLE
365 curl_easy_cleanup(curl_default);
366#endif
367
368#ifdef USE_CURL_MULTI
369 curl_multi_cleanup(curlm);
370#endif
371 curl_global_cleanup();
b3ca4e4e
NH
372
373 curl_slist_free_all(pragma_header);
3278cd0a 374 pragma_header = NULL;
9fc6440d 375
e9176745
TRC
376 curl_slist_free_all(no_pragma_header);
377 no_pragma_header = NULL;
378
9fc6440d 379 if (curl_http_proxy) {
e4a80ecf 380 free((void *)curl_http_proxy);
9fc6440d
MH
381 curl_http_proxy = NULL;
382 }
29508e1e
NH
383}
384
29508e1e
NH
385struct active_request_slot *get_active_slot(void)
386{
387 struct active_request_slot *slot = active_queue_head;
388 struct active_request_slot *newslot;
389
390#ifdef USE_CURL_MULTI
391 int num_transfers;
392
393 /* Wait for a slot to open up if the queue is full */
394 while (active_requests >= max_requests) {
395 curl_multi_perform(curlm, &num_transfers);
4251ccbd 396 if (num_transfers < active_requests)
29508e1e 397 process_curl_messages();
29508e1e
NH
398 }
399#endif
400
4251ccbd 401 while (slot != NULL && slot->in_use)
29508e1e 402 slot = slot->next;
4251ccbd 403
29508e1e
NH
404 if (slot == NULL) {
405 newslot = xmalloc(sizeof(*newslot));
406 newslot->curl = NULL;
407 newslot->in_use = 0;
408 newslot->next = NULL;
409
410 slot = active_queue_head;
411 if (slot == NULL) {
412 active_queue_head = newslot;
413 } else {
4251ccbd 414 while (slot->next != NULL)
29508e1e 415 slot = slot->next;
29508e1e
NH
416 slot->next = newslot;
417 }
418 slot = newslot;
419 }
420
421 if (slot->curl == NULL) {
422#ifdef NO_CURL_EASY_DUPHANDLE
423 slot->curl = get_curl_handle();
424#else
425 slot->curl = curl_easy_duphandle(curl_default);
426#endif
427 }
428
429 active_requests++;
430 slot->in_use = 1;
431 slot->local = NULL;
c8568e13 432 slot->results = NULL;
baa7b67d 433 slot->finished = NULL;
29508e1e
NH
434 slot->callback_data = NULL;
435 slot->callback_func = NULL;
436 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
29508e1e 437 curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
9094950d
NH
438 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);
439 curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);
440 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);
441 curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
442 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
29508e1e
NH
443
444 return slot;
445}
446
447int start_active_slot(struct active_request_slot *slot)
448{
449#ifdef USE_CURL_MULTI
450 CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);
45c17412 451 int num_transfers;
29508e1e
NH
452
453 if (curlm_result != CURLM_OK &&
454 curlm_result != CURLM_CALL_MULTI_PERFORM) {
455 active_requests--;
456 slot->in_use = 0;
457 return 0;
458 }
45c17412
DB
459
460 /*
461 * We know there must be something to do, since we just added
462 * something.
463 */
464 curl_multi_perform(curlm, &num_transfers);
29508e1e
NH
465#endif
466 return 1;
467}
468
469#ifdef USE_CURL_MULTI
fc57b6aa
DB
470struct fill_chain {
471 void *data;
472 int (*fill)(void *);
473 struct fill_chain *next;
474};
475
4251ccbd 476static struct fill_chain *fill_cfg;
fc57b6aa
DB
477
478void add_fill_function(void *data, int (*fill)(void *))
479{
e8eec71d 480 struct fill_chain *new = xmalloc(sizeof(*new));
fc57b6aa
DB
481 struct fill_chain **linkp = &fill_cfg;
482 new->data = data;
483 new->fill = fill;
484 new->next = NULL;
485 while (*linkp)
486 linkp = &(*linkp)->next;
487 *linkp = new;
488}
489
45c17412
DB
490void fill_active_slots(void)
491{
492 struct active_request_slot *slot = active_queue_head;
493
fc57b6aa
DB
494 while (active_requests < max_requests) {
495 struct fill_chain *fill;
496 for (fill = fill_cfg; fill; fill = fill->next)
497 if (fill->fill(fill->data))
498 break;
499
500 if (!fill)
45c17412 501 break;
fc57b6aa 502 }
45c17412
DB
503
504 while (slot != NULL) {
505 if (!slot->in_use && slot->curl != NULL) {
506 curl_easy_cleanup(slot->curl);
507 slot->curl = NULL;
508 }
509 slot = slot->next;
510 }
511}
512
29508e1e
NH
513void step_active_slots(void)
514{
515 int num_transfers;
516 CURLMcode curlm_result;
517
518 do {
519 curlm_result = curl_multi_perform(curlm, &num_transfers);
520 } while (curlm_result == CURLM_CALL_MULTI_PERFORM);
521 if (num_transfers < active_requests) {
522 process_curl_messages();
523 fill_active_slots();
524 }
525}
526#endif
527
528void run_active_slot(struct active_request_slot *slot)
529{
530#ifdef USE_CURL_MULTI
531 long last_pos = 0;
532 long current_pos;
533 fd_set readfds;
534 fd_set writefds;
535 fd_set excfds;
536 int max_fd;
537 struct timeval select_timeout;
baa7b67d 538 int finished = 0;
29508e1e 539
baa7b67d
NH
540 slot->finished = &finished;
541 while (!finished) {
29508e1e
NH
542 data_received = 0;
543 step_active_slots();
544
545 if (!data_received && slot->local != NULL) {
546 current_pos = ftell(slot->local);
547 if (current_pos > last_pos)
548 data_received++;
549 last_pos = current_pos;
550 }
551
552 if (slot->in_use && !data_received) {
553 max_fd = 0;
554 FD_ZERO(&readfds);
555 FD_ZERO(&writefds);
556 FD_ZERO(&excfds);
557 select_timeout.tv_sec = 0;
558 select_timeout.tv_usec = 50000;
559 select(max_fd, &readfds, &writefds,
560 &excfds, &select_timeout);
561 }
562 }
563#else
564 while (slot->in_use) {
565 slot->curl_result = curl_easy_perform(slot->curl);
566 finish_active_slot(slot);
567 }
568#endif
569}
570
53f31389 571static void closedown_active_slot(struct active_request_slot *slot)
29508e1e 572{
028c2976
MH
573 active_requests--;
574 slot->in_use = 0;
53f31389
MW
575}
576
577void release_active_slot(struct active_request_slot *slot)
578{
579 closedown_active_slot(slot);
580 if (slot->curl) {
b3ca4e4e 581#ifdef USE_CURL_MULTI
53f31389 582 curl_multi_remove_handle(curlm, slot->curl);
b3ca4e4e 583#endif
53f31389
MW
584 curl_easy_cleanup(slot->curl);
585 slot->curl = NULL;
586 }
b3ca4e4e 587#ifdef USE_CURL_MULTI
53f31389 588 fill_active_slots();
b3ca4e4e 589#endif
53f31389
MW
590}
591
592static void finish_active_slot(struct active_request_slot *slot)
593{
594 closedown_active_slot(slot);
028c2976 595 curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
c8568e13 596
baa7b67d
NH
597 if (slot->finished != NULL)
598 (*slot->finished) = 1;
599
c8568e13
NH
600 /* Store slot results so they can be read after the slot is reused */
601 if (slot->results != NULL) {
602 slot->results->curl_result = slot->curl_result;
603 slot->results->http_code = slot->http_code;
604 }
605
028c2976 606 /* Run callback if appropriate */
4251ccbd 607 if (slot->callback_func != NULL)
028c2976 608 slot->callback_func(slot->callback_data);
29508e1e
NH
609}
610
611void finish_all_active_slots(void)
612{
613 struct active_request_slot *slot = active_queue_head;
614
615 while (slot != NULL)
616 if (slot->in_use) {
617 run_active_slot(slot);
618 slot = active_queue_head;
619 } else {
620 slot = slot->next;
621 }
622}
d7e92806 623
5ace994f 624/* Helpers for modifying and creating URLs */
d7e92806
MH
625static inline int needs_quote(int ch)
626{
627 if (((ch >= 'A') && (ch <= 'Z'))
628 || ((ch >= 'a') && (ch <= 'z'))
629 || ((ch >= '0') && (ch <= '9'))
630 || (ch == '/')
631 || (ch == '-')
632 || (ch == '.'))
633 return 0;
634 return 1;
635}
636
637static inline int hex(int v)
638{
4251ccbd
JH
639 if (v < 10)
640 return '0' + v;
641 else
642 return 'A' + v - 10;
d7e92806
MH
643}
644
5ace994f
TRC
645static void end_url_with_slash(struct strbuf *buf, const char *url)
646{
647 strbuf_addstr(buf, url);
648 if (buf->len && buf->buf[buf->len - 1] != '/')
649 strbuf_addstr(buf, "/");
650}
651
d7e92806
MH
652static char *quote_ref_url(const char *base, const char *ref)
653{
113106e0 654 struct strbuf buf = STRBUF_INIT;
d7e92806 655 const char *cp;
113106e0 656 int ch;
d7e92806 657
5ace994f 658 end_url_with_slash(&buf, base);
113106e0
TRC
659
660 for (cp = ref; (ch = *cp) != 0; cp++)
d7e92806 661 if (needs_quote(ch))
113106e0 662 strbuf_addf(&buf, "%%%02x", ch);
d7e92806 663 else
113106e0 664 strbuf_addch(&buf, *cp);
d7e92806 665
113106e0 666 return strbuf_detach(&buf, NULL);
d7e92806
MH
667}
668
e929cd20
MH
669/* http_request() targets */
670#define HTTP_REQUEST_STRBUF 0
671#define HTTP_REQUEST_FILE 1
672
673static int http_request(const char *url, void *result, int target, int options)
674{
675 struct active_request_slot *slot;
676 struct slot_results results;
677 struct curl_slist *headers = NULL;
678 struct strbuf buf = STRBUF_INIT;
679 int ret;
680
681 slot = get_active_slot();
682 slot->results = &results;
683 curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
684
685 if (result == NULL) {
686 curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
687 } else {
688 curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
689 curl_easy_setopt(slot->curl, CURLOPT_FILE, result);
690
691 if (target == HTTP_REQUEST_FILE) {
692 long posn = ftell(result);
693 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
694 fwrite);
695 if (posn > 0) {
696 strbuf_addf(&buf, "Range: bytes=%ld-", posn);
697 headers = curl_slist_append(headers, buf.buf);
698 strbuf_reset(&buf);
699 }
700 slot->local = result;
701 } else
702 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
703 fwrite_buffer);
704 }
705
706 strbuf_addstr(&buf, "Pragma:");
707 if (options & HTTP_NO_CACHE)
708 strbuf_addstr(&buf, " no-cache");
709
710 headers = curl_slist_append(headers, buf.buf);
711
712 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
713 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
714
715 if (start_active_slot(slot)) {
716 run_active_slot(slot);
717 if (results.curl_result == CURLE_OK)
718 ret = HTTP_OK;
719 else if (missing_target(&results))
720 ret = HTTP_MISSING_TARGET;
721 else
722 ret = HTTP_ERROR;
723 } else {
724 error("Unable to start HTTP request for %s", url);
725 ret = HTTP_START_FAILED;
726 }
727
728 slot->local = NULL;
729 curl_slist_free_all(headers);
730 strbuf_release(&buf);
731
732 return ret;
733}
734
735int http_get_strbuf(const char *url, struct strbuf *result, int options)
736{
737 return http_request(url, result, HTTP_REQUEST_STRBUF, options);
738}
739
740int http_get_file(const char *url, const char *filename, int options)
741{
742 int ret;
743 struct strbuf tmpfile = STRBUF_INIT;
744 FILE *result;
745
746 strbuf_addf(&tmpfile, "%s.temp", filename);
747 result = fopen(tmpfile.buf, "a");
748 if (! result) {
749 error("Unable to open local file %s", tmpfile.buf);
750 ret = HTTP_ERROR;
751 goto cleanup;
752 }
753
754 ret = http_request(url, result, HTTP_REQUEST_FILE, options);
755 fclose(result);
756
757 if ((ret == HTTP_OK) && move_temp_to_file(tmpfile.buf, filename))
758 ret = HTTP_ERROR;
759cleanup:
760 strbuf_release(&tmpfile);
761 return ret;
762}
763
764int http_error(const char *url, int ret)
765{
766 /* http_request has already handled HTTP_START_FAILED. */
767 if (ret != HTTP_START_FAILED)
768 error("%s while accessing %s\n", curl_errorstr, url);
769
770 return ret;
771}
772
c13b2633 773int http_fetch_ref(const char *base, struct ref *ref)
d7e92806
MH
774{
775 char *url;
776 struct strbuf buffer = STRBUF_INIT;
0d5896e1 777 int ret = -1;
d7e92806 778
c13b2633 779 url = quote_ref_url(base, ref->name);
0d5896e1
MH
780 if (http_get_strbuf(url, &buffer, HTTP_NO_CACHE) == HTTP_OK) {
781 strbuf_rtrim(&buffer);
782 if (buffer.len == 40)
783 ret = get_sha1_hex(buffer.buf, ref->old_sha1);
784 else if (!prefixcmp(buffer.buf, "ref: ")) {
785 ref->symref = xstrdup(buffer.buf + 5);
786 ret = 0;
d7e92806 787 }
d7e92806
MH
788 }
789
790 strbuf_release(&buffer);
791 free(url);
792 return ret;
793}
b8caac2b
TRC
794
795/* Helpers for fetching packs */
796static int fetch_pack_index(unsigned char *sha1, const char *base_url)
797{
798 int ret = 0;
799 char *hex = xstrdup(sha1_to_hex(sha1));
800 char *filename;
801 char *url;
b8caac2b 802 struct strbuf buf = STRBUF_INIT;
b8caac2b
TRC
803
804 /* Don't use the index if the pack isn't there */
805 end_url_with_slash(&buf, base_url);
806 strbuf_addf(&buf, "objects/pack/pack-%s.pack", hex);
807 url = strbuf_detach(&buf, 0);
808
39dc52cf
TRC
809 if (http_get_strbuf(url, NULL, 0)) {
810 ret = error("Unable to verify pack %s is available",
811 hex);
812 goto cleanup;
b8caac2b
TRC
813 }
814
815 if (has_pack_index(sha1)) {
816 ret = 0;
39dc52cf 817 goto cleanup;
b8caac2b
TRC
818 }
819
820 if (http_is_verbose)
821 fprintf(stderr, "Getting index for pack %s\n", hex);
822
823 end_url_with_slash(&buf, base_url);
824 strbuf_addf(&buf, "objects/pack/pack-%s.idx", hex);
825 url = strbuf_detach(&buf, NULL);
826
827 filename = sha1_pack_index_name(sha1);
39dc52cf
TRC
828 if (http_get_file(url, filename, 0) != HTTP_OK)
829 ret = error("Unable to get pack index %s\n", url);
b8caac2b 830
39dc52cf 831cleanup:
b8caac2b
TRC
832 free(hex);
833 free(url);
834 return ret;
835}
836
837static int fetch_and_setup_pack_index(struct packed_git **packs_head,
838 unsigned char *sha1, const char *base_url)
839{
840 struct packed_git *new_pack;
841
842 if (fetch_pack_index(sha1, base_url))
843 return -1;
844
845 new_pack = parse_pack_index(sha1);
846 if (!new_pack)
847 return -1; /* parse_pack_index() already issued error message */
848 new_pack->next = *packs_head;
849 *packs_head = new_pack;
850 return 0;
851}
852
853int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
854{
855 int ret = 0, i = 0;
856 char *url, *data;
857 struct strbuf buf = STRBUF_INIT;
858 unsigned char sha1[20];
859
860 end_url_with_slash(&buf, base_url);
861 strbuf_addstr(&buf, "objects/info/packs");
862 url = strbuf_detach(&buf, NULL);
863
864 ret = http_get_strbuf(url, &buf, HTTP_NO_CACHE);
865 if (ret != HTTP_OK)
866 goto cleanup;
867
868 data = buf.buf;
869 while (i < buf.len) {
870 switch (data[i]) {
871 case 'P':
872 i++;
873 if (i + 52 <= buf.len &&
874 !prefixcmp(data + i, " pack-") &&
875 !prefixcmp(data + i + 46, ".pack\n")) {
876 get_sha1_hex(data + i + 6, sha1);
877 fetch_and_setup_pack_index(packs_head, sha1,
878 base_url);
879 i += 51;
880 break;
881 }
882 default:
883 while (i < buf.len && data[i] != '\n')
884 i++;
885 }
886 i++;
887 }
888
889cleanup:
890 free(url);
891 return ret;
892}
2264dfa5
TRC
893
894void release_http_pack_request(struct http_pack_request *preq)
895{
896 if (preq->packfile != NULL) {
897 fclose(preq->packfile);
898 preq->packfile = NULL;
899 preq->slot->local = NULL;
900 }
901 if (preq->range_header != NULL) {
902 curl_slist_free_all(preq->range_header);
903 preq->range_header = NULL;
904 }
905 preq->slot = NULL;
906 free(preq->url);
907}
908
909int finish_http_pack_request(struct http_pack_request *preq)
910{
911 int ret;
912 struct packed_git **lst;
913
914 preq->target->pack_size = ftell(preq->packfile);
915
916 if (preq->packfile != NULL) {
917 fclose(preq->packfile);
918 preq->packfile = NULL;
919 preq->slot->local = NULL;
920 }
921
922 ret = move_temp_to_file(preq->tmpfile, preq->filename);
923 if (ret)
924 return ret;
925
926 lst = preq->lst;
927 while (*lst != preq->target)
928 lst = &((*lst)->next);
929 *lst = (*lst)->next;
930
931 if (verify_pack(preq->target))
932 return -1;
933 install_packed_git(preq->target);
934
935 return 0;
936}
937
938struct http_pack_request *new_http_pack_request(
939 struct packed_git *target, const char *base_url)
940{
941 char *url;
942 char *filename;
943 long prev_posn = 0;
944 char range[RANGE_HEADER_SIZE];
945 struct strbuf buf = STRBUF_INIT;
946 struct http_pack_request *preq;
947
948 preq = xmalloc(sizeof(*preq));
949 preq->target = target;
950 preq->range_header = NULL;
951
952 end_url_with_slash(&buf, base_url);
953 strbuf_addf(&buf, "objects/pack/pack-%s.pack",
954 sha1_to_hex(target->sha1));
955 url = strbuf_detach(&buf, NULL);
956 preq->url = xstrdup(url);
957
958 filename = sha1_pack_name(target->sha1);
959 snprintf(preq->filename, sizeof(preq->filename), "%s", filename);
960 snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp", filename);
961 preq->packfile = fopen(preq->tmpfile, "a");
962 if (!preq->packfile) {
963 error("Unable to open local file %s for pack",
964 preq->tmpfile);
965 goto abort;
966 }
967
968 preq->slot = get_active_slot();
969 preq->slot->local = preq->packfile;
970 curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile);
971 curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
972 curl_easy_setopt(preq->slot->curl, CURLOPT_URL, url);
973 curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
974 no_pragma_header);
975
976 /*
977 * If there is data present from a previous transfer attempt,
978 * resume where it left off
979 */
980 prev_posn = ftell(preq->packfile);
981 if (prev_posn>0) {
982 if (http_is_verbose)
983 fprintf(stderr,
984 "Resuming fetch of pack %s at byte %ld\n",
985 sha1_to_hex(target->sha1), prev_posn);
986 sprintf(range, "Range: bytes=%ld-", prev_posn);
987 preq->range_header = curl_slist_append(NULL, range);
988 curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
989 preq->range_header);
990 }
991
992 return preq;
993
994abort:
995 free(filename);
996 return NULL;
997}