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