1 | #include "http.h"
|
---|
2 | #include "pack.h"
|
---|
3 | #include "sideband.h"
|
---|
4 | #include "run-command.h"
|
---|
5 | #include "url.h"
|
---|
6 | #include "urlmatch.h"
|
---|
7 | #include "credential.h"
|
---|
8 | #include "version.h"
|
---|
9 | #include "pkt-line.h"
|
---|
10 |
|
---|
11 | int active_requests;
|
---|
12 | int http_is_verbose;
|
---|
13 | size_t http_post_buffer = 16 * LARGE_PACKET_MAX;
|
---|
14 |
|
---|
15 | #if LIBCURL_VERSION_NUM >= 0x070a06
|
---|
16 | #define LIBCURL_CAN_HANDLE_AUTH_ANY
|
---|
17 | #endif
|
---|
18 |
|
---|
19 | static int min_curl_sessions = 1;
|
---|
20 | static int curl_session_count;
|
---|
21 | #ifdef USE_CURL_MULTI
|
---|
22 | static int max_requests = -1;
|
---|
23 | static CURLM *curlm;
|
---|
24 | #endif
|
---|
25 | #ifndef NO_CURL_EASY_DUPHANDLE
|
---|
26 | static CURL *curl_default;
|
---|
27 | #endif
|
---|
28 |
|
---|
29 | #define PREV_BUF_SIZE 4096
|
---|
30 | #define RANGE_HEADER_SIZE 30
|
---|
31 |
|
---|
32 | char curl_errorstr[CURL_ERROR_SIZE];
|
---|
33 |
|
---|
34 | static int curl_ssl_verify = -1;
|
---|
35 | static int curl_ssl_try;
|
---|
36 | static const char *ssl_cert;
|
---|
37 | #if LIBCURL_VERSION_NUM >= 0x070903
|
---|
38 | static const char *ssl_key;
|
---|
39 | #endif
|
---|
40 | #if LIBCURL_VERSION_NUM >= 0x070908
|
---|
41 | static const char *ssl_capath;
|
---|
42 | #endif
|
---|
43 | static const char *ssl_cainfo;
|
---|
44 | static long curl_low_speed_limit = -1;
|
---|
45 | static long curl_low_speed_time = -1;
|
---|
46 | static int curl_ftp_no_epsv;
|
---|
47 | static const char *curl_http_proxy;
|
---|
48 | static const char *curl_cookie_file;
|
---|
49 | static int curl_save_cookies;
|
---|
50 | struct credential http_auth = CREDENTIAL_INIT;
|
---|
51 | static int http_proactive_auth;
|
---|
52 | static const char *user_agent;
|
---|
53 |
|
---|
54 | #if LIBCURL_VERSION_NUM >= 0x071700
|
---|
55 | /* Use CURLOPT_KEYPASSWD as is */
|
---|
56 | #elif LIBCURL_VERSION_NUM >= 0x070903
|
---|
57 | #define CURLOPT_KEYPASSWD CURLOPT_SSLKEYPASSWD
|
---|
58 | #else
|
---|
59 | #define CURLOPT_KEYPASSWD CURLOPT_SSLCERTPASSWD
|
---|
60 | #endif
|
---|
61 |
|
---|
62 | static struct credential cert_auth = CREDENTIAL_INIT;
|
---|
63 | static int ssl_cert_password_required;
|
---|
64 |
|
---|
65 | static struct curl_slist *pragma_header;
|
---|
66 | static struct curl_slist *no_pragma_header;
|
---|
67 |
|
---|
68 | static struct active_request_slot *active_queue_head;
|
---|
69 |
|
---|
70 | size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
|
---|
71 | {
|
---|
72 | size_t size = eltsize * nmemb;
|
---|
73 | struct buffer *buffer = buffer_;
|
---|
74 |
|
---|
75 | if (size > buffer->buf.len - buffer->posn)
|
---|
76 | size = buffer->buf.len - buffer->posn;
|
---|
77 | memcpy(ptr, buffer->buf.buf + buffer->posn, size);
|
---|
78 | buffer->posn += size;
|
---|
79 |
|
---|
80 | return size;
|
---|
81 | }
|
---|
82 |
|
---|
83 | #ifndef NO_CURL_IOCTL
|
---|
84 | curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp)
|
---|
85 | {
|
---|
86 | struct buffer *buffer = clientp;
|
---|
87 |
|
---|
88 | switch (cmd) {
|
---|
89 | case CURLIOCMD_NOP:
|
---|
90 | return CURLIOE_OK;
|
---|
91 |
|
---|
92 | case CURLIOCMD_RESTARTREAD:
|
---|
93 | buffer->posn = 0;
|
---|
94 | return CURLIOE_OK;
|
---|
95 |
|
---|
96 | default:
|
---|
97 | return CURLIOE_UNKNOWNCMD;
|
---|
98 | }
|
---|
99 | }
|
---|
100 | #endif
|
---|
101 |
|
---|
102 | size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
|
---|
103 | {
|
---|
104 | size_t size = eltsize * nmemb;
|
---|
105 | struct strbuf *buffer = buffer_;
|
---|
106 |
|
---|
107 | strbuf_add(buffer, ptr, size);
|
---|
108 | return size;
|
---|
109 | }
|
---|
110 |
|
---|
111 | size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf)
|
---|
112 | {
|
---|
113 | return eltsize * nmemb;
|
---|
114 | }
|
---|
115 |
|
---|
116 | #ifdef USE_CURL_MULTI
|
---|
117 | static void process_curl_messages(void)
|
---|
118 | {
|
---|
119 | int num_messages;
|
---|
120 | struct active_request_slot *slot;
|
---|
121 | CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);
|
---|
122 |
|
---|
123 | while (curl_message != NULL) {
|
---|
124 | if (curl_message->msg == CURLMSG_DONE) {
|
---|
125 | int curl_result = curl_message->data.result;
|
---|
126 | slot = active_queue_head;
|
---|
127 | while (slot != NULL &&
|
---|
128 | slot->curl != curl_message->easy_handle)
|
---|
129 | slot = slot->next;
|
---|
130 | if (slot != NULL) {
|
---|
131 | curl_multi_remove_handle(curlm, slot->curl);
|
---|
132 | slot->curl_result = curl_result;
|
---|
133 | finish_active_slot(slot);
|
---|
134 | } else {
|
---|
135 | fprintf(stderr, "Received DONE message for unknown request!\n");
|
---|
136 | }
|
---|
137 | } else {
|
---|
138 | fprintf(stderr, "Unknown CURL message received: %d\n",
|
---|
139 | (int)curl_message->msg);
|
---|
140 | }
|
---|
141 | curl_message = curl_multi_info_read(curlm, &num_messages);
|
---|
142 | }
|
---|
143 | }
|
---|
144 | #endif
|
---|
145 |
|
---|
146 | static int http_options(const char *var, const char *value, void *cb)
|
---|
147 | {
|
---|
148 | if (!strcmp("http.sslverify", var)) {
|
---|
149 | curl_ssl_verify = git_config_bool(var, value);
|
---|
150 | return 0;
|
---|
151 | }
|
---|
152 | if (!strcmp("http.sslcert", var))
|
---|
153 | return git_config_string(&ssl_cert, var, value);
|
---|
154 | #if LIBCURL_VERSION_NUM >= 0x070903
|
---|
155 | if (!strcmp("http.sslkey", var))
|
---|
156 | return git_config_string(&ssl_key, var, value);
|
---|
157 | #endif
|
---|
158 | #if LIBCURL_VERSION_NUM >= 0x070908
|
---|
159 | if (!strcmp("http.sslcapath", var))
|
---|
160 | return git_config_string(&ssl_capath, var, value);
|
---|
161 | #endif
|
---|
162 | if (!strcmp("http.sslcainfo", var))
|
---|
163 | return git_config_string(&ssl_cainfo, var, value);
|
---|
164 | if (!strcmp("http.sslcertpasswordprotected", var)) {
|
---|
165 | ssl_cert_password_required = git_config_bool(var, value);
|
---|
166 | return 0;
|
---|
167 | }
|
---|
168 | if (!strcmp("http.ssltry", var)) {
|
---|
169 | curl_ssl_try = git_config_bool(var, value);
|
---|
170 | return 0;
|
---|
171 | }
|
---|
172 | if (!strcmp("http.minsessions", var)) {
|
---|
173 | min_curl_sessions = git_config_int(var, value);
|
---|
174 | #ifndef USE_CURL_MULTI
|
---|
175 | if (min_curl_sessions > 1)
|
---|
176 | min_curl_sessions = 1;
|
---|
177 | #endif
|
---|
178 | return 0;
|
---|
179 | }
|
---|
180 | #ifdef USE_CURL_MULTI
|
---|
181 | if (!strcmp("http.maxrequests", var)) {
|
---|
182 | max_requests = git_config_int(var, value);
|
---|
183 | return 0;
|
---|
184 | }
|
---|
185 | #endif
|
---|
186 | if (!strcmp("http.lowspeedlimit", var)) {
|
---|
187 | curl_low_speed_limit = (long)git_config_int(var, value);
|
---|
188 | return 0;
|
---|
189 | }
|
---|
190 | if (!strcmp("http.lowspeedtime", var)) {
|
---|
191 | curl_low_speed_time = (long)git_config_int(var, value);
|
---|
192 | return 0;
|
---|
193 | }
|
---|
194 |
|
---|
195 | if (!strcmp("http.noepsv", var)) {
|
---|
196 | curl_ftp_no_epsv = git_config_bool(var, value);
|
---|
197 | return 0;
|
---|
198 | }
|
---|
199 | if (!strcmp("http.proxy", var))
|
---|
200 | return git_config_string(&curl_http_proxy, var, value);
|
---|
201 |
|
---|
202 | if (!strcmp("http.cookiefile", var))
|
---|
203 | return git_config_string(&curl_cookie_file, var, value);
|
---|
204 | if (!strcmp("http.savecookies", var)) {
|
---|
205 | curl_save_cookies = git_config_bool(var, value);
|
---|
206 | return 0;
|
---|
207 | }
|
---|
208 |
|
---|
209 | if (!strcmp("http.postbuffer", var)) {
|
---|
210 | http_post_buffer = git_config_int(var, value);
|
---|
211 | if (http_post_buffer < LARGE_PACKET_MAX)
|
---|
212 | http_post_buffer = LARGE_PACKET_MAX;
|
---|
213 | return 0;
|
---|
214 | }
|
---|
215 |
|
---|
216 | if (!strcmp("http.useragent", var))
|
---|
217 | return git_config_string(&user_agent, var, value);
|
---|
218 |
|
---|
219 | /* Fall back on the default ones */
|
---|
220 | return git_default_config(var, value, cb);
|
---|
221 | }
|
---|
222 |
|
---|
223 | static void init_curl_http_auth(CURL *result)
|
---|
224 | {
|
---|
225 | if (!http_auth.username)
|
---|
226 | return;
|
---|
227 |
|
---|
228 | credential_fill(&http_auth);
|
---|
229 |
|
---|
230 | #if LIBCURL_VERSION_NUM >= 0x071301
|
---|
231 | curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
|
---|
232 | curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
|
---|
233 | #else
|
---|
234 | {
|
---|
235 | static struct strbuf up = STRBUF_INIT;
|
---|
236 | /*
|
---|
237 | * Note that we assume we only ever have a single set of
|
---|
238 | * credentials in a given program run, so we do not have
|
---|
239 | * to worry about updating this buffer, only setting its
|
---|
240 | * initial value.
|
---|
241 | */
|
---|
242 | if (!up.len)
|
---|
243 | strbuf_addf(&up, "%s:%s",
|
---|
244 | http_auth.username, http_auth.password);
|
---|
245 | curl_easy_setopt(result, CURLOPT_USERPWD, up.buf);
|
---|
246 | }
|
---|
247 | #endif
|
---|
248 | }
|
---|
249 |
|
---|
250 | static int has_cert_password(void)
|
---|
251 | {
|
---|
252 | if (ssl_cert == NULL || ssl_cert_password_required != 1)
|
---|
253 | return 0;
|
---|
254 | if (!cert_auth.password) {
|
---|
255 | cert_auth.protocol = xstrdup("cert");
|
---|
256 | cert_auth.username = xstrdup("");
|
---|
257 | cert_auth.path = xstrdup(ssl_cert);
|
---|
258 | credential_fill(&cert_auth);
|
---|
259 | }
|
---|
260 | return 1;
|
---|
261 | }
|
---|
262 |
|
---|
263 | #if LIBCURL_VERSION_NUM >= 0x071900
|
---|
264 | static void set_curl_keepalive(CURL *c)
|
---|
265 | {
|
---|
266 | curl_easy_setopt(c, CURLOPT_TCP_KEEPALIVE, 1);
|
---|
267 | }
|
---|
268 |
|
---|
269 | #elif LIBCURL_VERSION_NUM >= 0x071000
|
---|
270 | static int sockopt_callback(void *client, curl_socket_t fd, curlsocktype type)
|
---|
271 | {
|
---|
272 | int ka = 1;
|
---|
273 | int rc;
|
---|
274 | socklen_t len = (socklen_t)sizeof(ka);
|
---|
275 |
|
---|
276 | if (type != CURLSOCKTYPE_IPCXN)
|
---|
277 | return 0;
|
---|
278 |
|
---|
279 | rc = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&ka, len);
|
---|
280 | if (rc < 0)
|
---|
281 | warning("unable to set SO_KEEPALIVE on socket %s",
|
---|
282 | strerror(errno));
|
---|
283 |
|
---|
284 | return 0; /* CURL_SOCKOPT_OK only exists since curl 7.21.5 */
|
---|
285 | }
|
---|
286 |
|
---|
287 | static void set_curl_keepalive(CURL *c)
|
---|
288 | {
|
---|
289 | curl_easy_setopt(c, CURLOPT_SOCKOPTFUNCTION, sockopt_callback);
|
---|
290 | }
|
---|
291 |
|
---|
292 | #else
|
---|
293 | static void set_curl_keepalive(CURL *c)
|
---|
294 | {
|
---|
295 | /* not supported on older curl versions */
|
---|
296 | }
|
---|
297 | #endif
|
---|
298 |
|
---|
299 | static CURL *get_curl_handle(void)
|
---|
300 | {
|
---|
301 | CURL *result = curl_easy_init();
|
---|
302 |
|
---|
303 | if (!curl_ssl_verify) {
|
---|
304 | curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
|
---|
305 | curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0);
|
---|
306 | } else {
|
---|
307 | /* Verify authenticity of the peer's certificate */
|
---|
308 | curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 1);
|
---|
309 | /* The name in the cert must match whom we tried to connect */
|
---|
310 | curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
|
---|
311 | }
|
---|
312 |
|
---|
313 | #if LIBCURL_VERSION_NUM >= 0x070907
|
---|
314 | curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
|
---|
315 | #endif
|
---|
316 | #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
|
---|
317 | curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
|
---|
318 | #endif
|
---|
319 |
|
---|
320 | if (http_proactive_auth)
|
---|
321 | init_curl_http_auth(result);
|
---|
322 |
|
---|
323 | if (ssl_cert != NULL)
|
---|
324 | curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
|
---|
325 | if (has_cert_password())
|
---|
326 | curl_easy_setopt(result, CURLOPT_KEYPASSWD, cert_auth.password);
|
---|
327 | #if LIBCURL_VERSION_NUM >= 0x070903
|
---|
328 | if (ssl_key != NULL)
|
---|
329 | curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
|
---|
330 | #endif
|
---|
331 | #if LIBCURL_VERSION_NUM >= 0x070908
|
---|
332 | if (ssl_capath != NULL)
|
---|
333 | curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
|
---|
334 | #endif
|
---|
335 | if (ssl_cainfo != NULL)
|
---|
336 | curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
|
---|
337 |
|
---|
338 | if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
|
---|
339 | curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
|
---|
340 | curl_low_speed_limit);
|
---|
341 | curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,
|
---|
342 | curl_low_speed_time);
|
---|
343 | }
|
---|
344 |
|
---|
345 | curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
|
---|
346 | #if LIBCURL_VERSION_NUM >= 0x071301
|
---|
347 | curl_easy_setopt(result, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
|
---|
348 | #elif LIBCURL_VERSION_NUM >= 0x071101
|
---|
349 | curl_easy_setopt(result, CURLOPT_POST301, 1);
|
---|
350 | #endif
|
---|
351 |
|
---|
352 | if (getenv("GIT_CURL_VERBOSE"))
|
---|
353 | curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
|
---|
354 |
|
---|
355 | curl_easy_setopt(result, CURLOPT_USERAGENT,
|
---|
356 | user_agent ? user_agent : git_user_agent());
|
---|
357 |
|
---|
358 | if (curl_ftp_no_epsv)
|
---|
359 | curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
|
---|
360 |
|
---|
361 | #ifdef CURLOPT_USE_SSL
|
---|
362 | if (curl_ssl_try)
|
---|
363 | curl_easy_setopt(result, CURLOPT_USE_SSL, CURLUSESSL_TRY);
|
---|
364 | #endif
|
---|
365 |
|
---|
366 | if (curl_http_proxy) {
|
---|
367 | curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
|
---|
368 | curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
|
---|
369 | }
|
---|
370 |
|
---|
371 | set_curl_keepalive(result);
|
---|
372 |
|
---|
373 | return result;
|
---|
374 | }
|
---|
375 |
|
---|
376 | static void set_from_env(const char **var, const char *envname)
|
---|
377 | {
|
---|
378 | const char *val = getenv(envname);
|
---|
379 | if (val)
|
---|
380 | *var = val;
|
---|
381 | }
|
---|
382 |
|
---|
383 | void http_init(struct remote *remote, const char *url, int proactive_auth)
|
---|
384 | {
|
---|
385 | char *low_speed_limit;
|
---|
386 | char *low_speed_time;
|
---|
387 | char *normalized_url;
|
---|
388 | struct urlmatch_config config = { STRING_LIST_INIT_DUP };
|
---|
389 |
|
---|
390 | config.section = "http";
|
---|
391 | config.key = NULL;
|
---|
392 | config.collect_fn = http_options;
|
---|
393 | config.cascade_fn = git_default_config;
|
---|
394 | config.cb = NULL;
|
---|
395 |
|
---|
396 | http_is_verbose = 0;
|
---|
397 | normalized_url = url_normalize(url, &config.url);
|
---|
398 |
|
---|
399 | git_config(urlmatch_config_entry, &config);
|
---|
400 | free(normalized_url);
|
---|
401 |
|
---|
402 | curl_global_init(CURL_GLOBAL_ALL);
|
---|
403 |
|
---|
404 | http_proactive_auth = proactive_auth;
|
---|
405 |
|
---|
406 | if (remote && remote->http_proxy)
|
---|
407 | curl_http_proxy = xstrdup(remote->http_proxy);
|
---|
408 |
|
---|
409 | pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
|
---|
410 | no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
|
---|
411 |
|
---|
412 | #ifdef USE_CURL_MULTI
|
---|
413 | {
|
---|
414 | char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
|
---|
415 | if (http_max_requests != NULL)
|
---|
416 | max_requests = atoi(http_max_requests);
|
---|
417 | }
|
---|
418 |
|
---|
419 | curlm = curl_multi_init();
|
---|
420 | if (curlm == NULL) {
|
---|
421 | fprintf(stderr, "Error creating curl multi handle.\n");
|
---|
422 | exit(1);
|
---|
423 | }
|
---|
424 | #endif
|
---|
425 |
|
---|
426 | if (getenv("GIT_SSL_NO_VERIFY"))
|
---|
427 | curl_ssl_verify = 0;
|
---|
428 |
|
---|
429 | set_from_env(&ssl_cert, "GIT_SSL_CERT");
|
---|
430 | #if LIBCURL_VERSION_NUM >= 0x070903
|
---|
431 | set_from_env(&ssl_key, "GIT_SSL_KEY");
|
---|
432 | #endif
|
---|
433 | #if LIBCURL_VERSION_NUM >= 0x070908
|
---|
434 | set_from_env(&ssl_capath, "GIT_SSL_CAPATH");
|
---|
435 | #endif
|
---|
436 | set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
|
---|
437 |
|
---|
438 | set_from_env(&user_agent, "GIT_HTTP_USER_AGENT");
|
---|
439 |
|
---|
440 | low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
|
---|
441 | if (low_speed_limit != NULL)
|
---|
442 | curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
|
---|
443 | low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME");
|
---|
444 | if (low_speed_time != NULL)
|
---|
445 | curl_low_speed_time = strtol(low_speed_time, NULL, 10);
|
---|
446 |
|
---|
447 | if (curl_ssl_verify == -1)
|
---|
448 | curl_ssl_verify = 1;
|
---|
449 |
|
---|
450 | curl_session_count = 0;
|
---|
451 | #ifdef USE_CURL_MULTI
|
---|
452 | if (max_requests < 1)
|
---|
453 | max_requests = DEFAULT_MAX_REQUESTS;
|
---|
454 | #endif
|
---|
455 |
|
---|
456 | if (getenv("GIT_CURL_FTP_NO_EPSV"))
|
---|
457 | curl_ftp_no_epsv = 1;
|
---|
458 |
|
---|
459 | if (url) {
|
---|
460 | credential_from_url(&http_auth, url);
|
---|
461 | if (!ssl_cert_password_required &&
|
---|
462 | getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") &&
|
---|
463 | starts_with(url, "https://"))
|
---|
464 | ssl_cert_password_required = 1;
|
---|
465 | }
|
---|
466 |
|
---|
467 | #ifndef NO_CURL_EASY_DUPHANDLE
|
---|
468 | curl_default = get_curl_handle();
|
---|
469 | #endif
|
---|
470 | }
|
---|
471 |
|
---|
472 | void http_cleanup(void)
|
---|
473 | {
|
---|
474 | struct active_request_slot *slot = active_queue_head;
|
---|
475 |
|
---|
476 | while (slot != NULL) {
|
---|
477 | struct active_request_slot *next = slot->next;
|
---|
478 | if (slot->curl != NULL) {
|
---|
479 | #ifdef USE_CURL_MULTI
|
---|
480 | curl_multi_remove_handle(curlm, slot->curl);
|
---|
481 | #endif
|
---|
482 | curl_easy_cleanup(slot->curl);
|
---|
483 | }
|
---|
484 | free(slot);
|
---|
485 | slot = next;
|
---|
486 | }
|
---|
487 | active_queue_head = NULL;
|
---|
488 |
|
---|
489 | #ifndef NO_CURL_EASY_DUPHANDLE
|
---|
490 | curl_easy_cleanup(curl_default);
|
---|
491 | #endif
|
---|
492 |
|
---|
493 | #ifdef USE_CURL_MULTI
|
---|
494 | curl_multi_cleanup(curlm);
|
---|
495 | #endif
|
---|
496 | curl_global_cleanup();
|
---|
497 |
|
---|
498 | curl_slist_free_all(pragma_header);
|
---|
499 | pragma_header = NULL;
|
---|
500 |
|
---|
501 | curl_slist_free_all(no_pragma_header);
|
---|
502 | no_pragma_header = NULL;
|
---|
503 |
|
---|
504 | if (curl_http_proxy) {
|
---|
505 | free((void *)curl_http_proxy);
|
---|
506 | curl_http_proxy = NULL;
|
---|
507 | }
|
---|
508 |
|
---|
509 | if (cert_auth.password != NULL) {
|
---|
510 | memset(cert_auth.password, 0, strlen(cert_auth.password));
|
---|
511 | free(cert_auth.password);
|
---|
512 | cert_auth.password = NULL;
|
---|
513 | }
|
---|
514 | ssl_cert_password_required = 0;
|
---|
515 | }
|
---|
516 |
|
---|
517 | struct active_request_slot *get_active_slot(void)
|
---|
518 | {
|
---|
519 | struct active_request_slot *slot = active_queue_head;
|
---|
520 | struct active_request_slot *newslot;
|
---|
521 |
|
---|
522 | #ifdef USE_CURL_MULTI
|
---|
523 | int num_transfers;
|
---|
524 |
|
---|
525 | /* Wait for a slot to open up if the queue is full */
|
---|
526 | while (active_requests >= max_requests) {
|
---|
527 | curl_multi_perform(curlm, &num_transfers);
|
---|
528 | if (num_transfers < active_requests)
|
---|
529 | process_curl_messages();
|
---|
530 | }
|
---|
531 | #endif
|
---|
532 |
|
---|
533 | while (slot != NULL && slot->in_use)
|
---|
534 | slot = slot->next;
|
---|
535 |
|
---|
536 | if (slot == NULL) {
|
---|
537 | newslot = xmalloc(sizeof(*newslot));
|
---|
538 | newslot->curl = NULL;
|
---|
539 | newslot->in_use = 0;
|
---|
540 | newslot->next = NULL;
|
---|
541 |
|
---|
542 | slot = active_queue_head;
|
---|
543 | if (slot == NULL) {
|
---|
544 | active_queue_head = newslot;
|
---|
545 | } else {
|
---|
546 | while (slot->next != NULL)
|
---|
547 | slot = slot->next;
|
---|
548 | slot->next = newslot;
|
---|
549 | }
|
---|
550 | slot = newslot;
|
---|
551 | }
|
---|
552 |
|
---|
553 | if (slot->curl == NULL) {
|
---|
554 | #ifdef NO_CURL_EASY_DUPHANDLE
|
---|
555 | slot->curl = get_curl_handle();
|
---|
556 | #else
|
---|
557 | slot->curl = curl_easy_duphandle(curl_default);
|
---|
558 | #endif
|
---|
559 | curl_session_count++;
|
---|
560 | }
|
---|
561 |
|
---|
562 | active_requests++;
|
---|
563 | slot->in_use = 1;
|
---|
564 | slot->results = NULL;
|
---|
565 | slot->finished = NULL;
|
---|
566 | slot->callback_data = NULL;
|
---|
567 | slot->callback_func = NULL;
|
---|
568 | curl_easy_setopt(slot->curl, CURLOPT_COOKIEFILE, curl_cookie_file);
|
---|
569 | if (curl_save_cookies)
|
---|
570 | curl_easy_setopt(slot->curl, CURLOPT_COOKIEJAR, curl_cookie_file);
|
---|
571 | curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
|
---|
572 | curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
|
---|
573 | curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);
|
---|
574 | curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);
|
---|
575 | curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);
|
---|
576 | curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, NULL);
|
---|
577 | curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
|
---|
578 | curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
|
---|
579 | curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
|
---|
580 | if (http_auth.password)
|
---|
581 | init_curl_http_auth(slot->curl);
|
---|
582 |
|
---|
583 | return slot;
|
---|
584 | }
|
---|
585 |
|
---|
586 | int start_active_slot(struct active_request_slot *slot)
|
---|
587 | {
|
---|
588 | #ifdef USE_CURL_MULTI
|
---|
589 | CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);
|
---|
590 | int num_transfers;
|
---|
591 |
|
---|
592 | if (curlm_result != CURLM_OK &&
|
---|
593 | curlm_result != CURLM_CALL_MULTI_PERFORM) {
|
---|
594 | active_requests--;
|
---|
595 | slot->in_use = 0;
|
---|
596 | return 0;
|
---|
597 | }
|
---|
598 |
|
---|
599 | /*
|
---|
600 | * We know there must be something to do, since we just added
|
---|
601 | * something.
|
---|
602 | */
|
---|
603 | curl_multi_perform(curlm, &num_transfers);
|
---|
604 | #endif
|
---|
605 | return 1;
|
---|
606 | }
|
---|
607 |
|
---|
608 | #ifdef USE_CURL_MULTI
|
---|
609 | struct fill_chain {
|
---|
610 | void *data;
|
---|
611 | int (*fill)(void *);
|
---|
612 | struct fill_chain *next;
|
---|
613 | };
|
---|
614 |
|
---|
615 | static struct fill_chain *fill_cfg;
|
---|
616 |
|
---|
617 | void add_fill_function(void *data, int (*fill)(void *))
|
---|
618 | {
|
---|
619 | struct fill_chain *new = xmalloc(sizeof(*new));
|
---|
620 | struct fill_chain **linkp = &fill_cfg;
|
---|
621 | new->data = data;
|
---|
622 | new->fill = fill;
|
---|
623 | new->next = NULL;
|
---|
624 | while (*linkp)
|
---|
625 | linkp = &(*linkp)->next;
|
---|
626 | *linkp = new;
|
---|
627 | }
|
---|
628 |
|
---|
629 | void fill_active_slots(void)
|
---|
630 | {
|
---|
631 | struct active_request_slot *slot = active_queue_head;
|
---|
632 |
|
---|
633 | while (active_requests < max_requests) {
|
---|
634 | struct fill_chain *fill;
|
---|
635 | for (fill = fill_cfg; fill; fill = fill->next)
|
---|
636 | if (fill->fill(fill->data))
|
---|
637 | break;
|
---|
638 |
|
---|
639 | if (!fill)
|
---|
640 | break;
|
---|
641 | }
|
---|
642 |
|
---|
643 | while (slot != NULL) {
|
---|
644 | if (!slot->in_use && slot->curl != NULL
|
---|
645 | && curl_session_count > min_curl_sessions) {
|
---|
646 | curl_easy_cleanup(slot->curl);
|
---|
647 | slot->curl = NULL;
|
---|
648 | curl_session_count--;
|
---|
649 | }
|
---|
650 | slot = slot->next;
|
---|
651 | }
|
---|
652 | }
|
---|
653 |
|
---|
654 | void step_active_slots(void)
|
---|
655 | {
|
---|
656 | int num_transfers;
|
---|
657 | CURLMcode curlm_result;
|
---|
658 |
|
---|
659 | do {
|
---|
660 | curlm_result = curl_multi_perform(curlm, &num_transfers);
|
---|
661 | } while (curlm_result == CURLM_CALL_MULTI_PERFORM);
|
---|
662 | if (num_transfers < active_requests) {
|
---|
663 | process_curl_messages();
|
---|
664 | fill_active_slots();
|
---|
665 | }
|
---|
666 | }
|
---|
667 | #endif
|
---|
668 |
|
---|
669 | void run_active_slot(struct active_request_slot *slot)
|
---|
670 | {
|
---|
671 | #ifdef USE_CURL_MULTI
|
---|
672 | fd_set readfds;
|
---|
673 | fd_set writefds;
|
---|
674 | fd_set excfds;
|
---|
675 | int max_fd;
|
---|
676 | struct timeval select_timeout;
|
---|
677 | int finished = 0;
|
---|
678 |
|
---|
679 | slot->finished = &finished;
|
---|
680 | while (!finished) {
|
---|
681 | step_active_slots();
|
---|
682 |
|
---|
683 | if (slot->in_use) {
|
---|
684 | #if LIBCURL_VERSION_NUM >= 0x070f04
|
---|
685 | long curl_timeout;
|
---|
686 | curl_multi_timeout(curlm, &curl_timeout);
|
---|
687 | if (curl_timeout == 0) {
|
---|
688 | continue;
|
---|
689 | } else if (curl_timeout == -1) {
|
---|
690 | select_timeout.tv_sec = 0;
|
---|
691 | select_timeout.tv_usec = 50000;
|
---|
692 | } else {
|
---|
693 | select_timeout.tv_sec = curl_timeout / 1000;
|
---|
694 | select_timeout.tv_usec = (curl_timeout % 1000) * 1000;
|
---|
695 | }
|
---|
696 | #else
|
---|
697 | select_timeout.tv_sec = 0;
|
---|
698 | select_timeout.tv_usec = 50000;
|
---|
699 | #endif
|
---|
700 |
|
---|
701 | max_fd = -1;
|
---|
702 | FD_ZERO(&readfds);
|
---|
703 | FD_ZERO(&writefds);
|
---|
704 | FD_ZERO(&excfds);
|
---|
705 | curl_multi_fdset(curlm, &readfds, &writefds, &excfds, &max_fd);
|
---|
706 |
|
---|
707 | /*
|
---|
708 | * It can happen that curl_multi_timeout returns a pathologically
|
---|
709 | * long timeout when curl_multi_fdset returns no file descriptors
|
---|
710 | * to read. See commit message for more details.
|
---|
711 | */
|
---|
712 | if (max_fd < 0 &&
|
---|
713 | (select_timeout.tv_sec > 0 ||
|
---|
714 | select_timeout.tv_usec > 50000)) {
|
---|
715 | select_timeout.tv_sec = 0;
|
---|
716 | select_timeout.tv_usec = 50000;
|
---|
717 | }
|
---|
718 |
|
---|
719 | select(max_fd+1, &readfds, &writefds, &excfds, &select_timeout);
|
---|
720 | }
|
---|
721 | }
|
---|
722 | #else
|
---|
723 | while (slot->in_use) {
|
---|
724 | slot->curl_result = curl_easy_perform(slot->curl);
|
---|
725 | finish_active_slot(slot);
|
---|
726 | }
|
---|
727 | #endif
|
---|
728 | }
|
---|
729 |
|
---|
730 | static void closedown_active_slot(struct active_request_slot *slot)
|
---|
731 | {
|
---|
732 | active_requests--;
|
---|
733 | slot->in_use = 0;
|
---|
734 | }
|
---|
735 |
|
---|
736 | static void release_active_slot(struct active_request_slot *slot)
|
---|
737 | {
|
---|
738 | closedown_active_slot(slot);
|
---|
739 | if (slot->curl && curl_session_count > min_curl_sessions) {
|
---|
740 | #ifdef USE_CURL_MULTI
|
---|
741 | curl_multi_remove_handle(curlm, slot->curl);
|
---|
742 | #endif
|
---|
743 | curl_easy_cleanup(slot->curl);
|
---|
744 | slot->curl = NULL;
|
---|
745 | curl_session_count--;
|
---|
746 | }
|
---|
747 | #ifdef USE_CURL_MULTI
|
---|
748 | fill_active_slots();
|
---|
749 | #endif
|
---|
750 | }
|
---|
751 |
|
---|
752 | void finish_active_slot(struct active_request_slot *slot)
|
---|
753 | {
|
---|
754 | closedown_active_slot(slot);
|
---|
755 | curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
|
---|
756 |
|
---|
757 | if (slot->finished != NULL)
|
---|
758 | (*slot->finished) = 1;
|
---|
759 |
|
---|
760 | /* Store slot results so they can be read after the slot is reused */
|
---|
761 | if (slot->results != NULL) {
|
---|
762 | slot->results->curl_result = slot->curl_result;
|
---|
763 | slot->results->http_code = slot->http_code;
|
---|
764 | #if LIBCURL_VERSION_NUM >= 0x070a08
|
---|
765 | curl_easy_getinfo(slot->curl, CURLINFO_HTTPAUTH_AVAIL,
|
---|
766 | &slot->results->auth_avail);
|
---|
767 | #else
|
---|
768 | slot->results->auth_avail = 0;
|
---|
769 | #endif
|
---|
770 | }
|
---|
771 |
|
---|
772 | /* Run callback if appropriate */
|
---|
773 | if (slot->callback_func != NULL)
|
---|
774 | slot->callback_func(slot->callback_data);
|
---|
775 | }
|
---|
776 |
|
---|
777 | void finish_all_active_slots(void)
|
---|
778 | {
|
---|
779 | struct active_request_slot *slot = active_queue_head;
|
---|
780 |
|
---|
781 | while (slot != NULL)
|
---|
782 | if (slot->in_use) {
|
---|
783 | run_active_slot(slot);
|
---|
784 | slot = active_queue_head;
|
---|
785 | } else {
|
---|
786 | slot = slot->next;
|
---|
787 | }
|
---|
788 | }
|
---|
789 |
|
---|
790 | /* Helpers for modifying and creating URLs */
|
---|
791 | static inline int needs_quote(int ch)
|
---|
792 | {
|
---|
793 | if (((ch >= 'A') && (ch <= 'Z'))
|
---|
794 | || ((ch >= 'a') && (ch <= 'z'))
|
---|
795 | || ((ch >= '0') && (ch <= '9'))
|
---|
796 | || (ch == '/')
|
---|
797 | || (ch == '-')
|
---|
798 | || (ch == '.'))
|
---|
799 | return 0;
|
---|
800 | return 1;
|
---|
801 | }
|
---|
802 |
|
---|
803 | static char *quote_ref_url(const char *base, const char *ref)
|
---|
804 | {
|
---|
805 | struct strbuf buf = STRBUF_INIT;
|
---|
806 | const char *cp;
|
---|
807 | int ch;
|
---|
808 |
|
---|
809 | end_url_with_slash(&buf, base);
|
---|
810 |
|
---|
811 | for (cp = ref; (ch = *cp) != 0; cp++)
|
---|
812 | if (needs_quote(ch))
|
---|
813 | strbuf_addf(&buf, "%%%02x", ch);
|
---|
814 | else
|
---|
815 | strbuf_addch(&buf, *cp);
|
---|
816 |
|
---|
817 | return strbuf_detach(&buf, NULL);
|
---|
818 | }
|
---|
819 |
|
---|
820 | void append_remote_object_url(struct strbuf *buf, const char *url,
|
---|
821 | const char *hex,
|
---|
822 | int only_two_digit_prefix)
|
---|
823 | {
|
---|
824 | end_url_with_slash(buf, url);
|
---|
825 |
|
---|
826 | strbuf_addf(buf, "objects/%.*s/", 2, hex);
|
---|
827 | if (!only_two_digit_prefix)
|
---|
828 | strbuf_addf(buf, "%s", hex+2);
|
---|
829 | }
|
---|
830 |
|
---|
831 | char *get_remote_object_url(const char *url, const char *hex,
|
---|
832 | int only_two_digit_prefix)
|
---|
833 | {
|
---|
834 | struct strbuf buf = STRBUF_INIT;
|
---|
835 | append_remote_object_url(&buf, url, hex, only_two_digit_prefix);
|
---|
836 | return strbuf_detach(&buf, NULL);
|
---|
837 | }
|
---|
838 |
|
---|
839 | int handle_curl_result(struct slot_results *results)
|
---|
840 | {
|
---|
841 | /*
|
---|
842 | * If we see a failing http code with CURLE_OK, we have turned off
|
---|
843 | * FAILONERROR (to keep the server's custom error response), and should
|
---|
844 | * translate the code into failure here.
|
---|
845 | */
|
---|
846 | if (results->curl_result == CURLE_OK &&
|
---|
847 | results->http_code >= 400) {
|
---|
848 | results->curl_result = CURLE_HTTP_RETURNED_ERROR;
|
---|
849 | /*
|
---|
850 | * Normally curl will already have put the "reason phrase"
|
---|
851 | * from the server into curl_errorstr; unfortunately without
|
---|
852 | * FAILONERROR it is lost, so we can give only the numeric
|
---|
853 | * status code.
|
---|
854 | */
|
---|
855 | snprintf(curl_errorstr, sizeof(curl_errorstr),
|
---|
856 | "The requested URL returned error: %ld",
|
---|
857 | results->http_code);
|
---|
858 | }
|
---|
859 |
|
---|
860 | if (results->curl_result == CURLE_OK) {
|
---|
861 | credential_approve(&http_auth);
|
---|
862 | return HTTP_OK;
|
---|
863 | } else if (missing_target(results))
|
---|
864 | return HTTP_MISSING_TARGET;
|
---|
865 | else if (results->http_code == 401) {
|
---|
866 | if (http_auth.username && http_auth.password) {
|
---|
867 | credential_reject(&http_auth);
|
---|
868 | return HTTP_NOAUTH;
|
---|
869 | } else {
|
---|
870 | return HTTP_REAUTH;
|
---|
871 | }
|
---|
872 | } else {
|
---|
873 | #if LIBCURL_VERSION_NUM >= 0x070c00
|
---|
874 | if (!curl_errorstr[0])
|
---|
875 | strlcpy(curl_errorstr,
|
---|
876 | curl_easy_strerror(results->curl_result),
|
---|
877 | sizeof(curl_errorstr));
|
---|
878 | #endif
|
---|
879 | return HTTP_ERROR;
|
---|
880 | }
|
---|
881 | }
|
---|
882 |
|
---|
883 | int run_one_slot(struct active_request_slot *slot,
|
---|
884 | struct slot_results *results)
|
---|
885 | {
|
---|
886 | slot->results = results;
|
---|
887 | if (!start_active_slot(slot)) {
|
---|
888 | snprintf(curl_errorstr, sizeof(curl_errorstr),
|
---|
889 | "failed to start HTTP request");
|
---|
890 | return HTTP_START_FAILED;
|
---|
891 | }
|
---|
892 |
|
---|
893 | run_active_slot(slot);
|
---|
894 | return handle_curl_result(results);
|
---|
895 | }
|
---|
896 |
|
---|
897 | static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf)
|
---|
898 | {
|
---|
899 | char *ptr;
|
---|
900 | CURLcode ret;
|
---|
901 |
|
---|
902 | strbuf_reset(buf);
|
---|
903 | ret = curl_easy_getinfo(curl, info, &ptr);
|
---|
904 | if (!ret && ptr)
|
---|
905 | strbuf_addstr(buf, ptr);
|
---|
906 | return ret;
|
---|
907 | }
|
---|
908 |
|
---|
909 | /* http_request() targets */
|
---|
910 | #define HTTP_REQUEST_STRBUF 0
|
---|
911 | #define HTTP_REQUEST_FILE 1
|
---|
912 |
|
---|
913 | static int http_request(const char *url,
|
---|
914 | void *result, int target,
|
---|
915 | const struct http_get_options *options)
|
---|
916 | {
|
---|
917 | struct active_request_slot *slot;
|
---|
918 | struct slot_results results;
|
---|
919 | struct curl_slist *headers = NULL;
|
---|
920 | struct strbuf buf = STRBUF_INIT;
|
---|
921 | int ret;
|
---|
922 |
|
---|
923 | slot = get_active_slot();
|
---|
924 | curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
|
---|
925 |
|
---|
926 | if (result == NULL) {
|
---|
927 | curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
|
---|
928 | } else {
|
---|
929 | curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
|
---|
930 | curl_easy_setopt(slot->curl, CURLOPT_FILE, result);
|
---|
931 |
|
---|
932 | if (target == HTTP_REQUEST_FILE) {
|
---|
933 | long posn = ftell(result);
|
---|
934 | curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
|
---|
935 | fwrite);
|
---|
936 | if (posn > 0) {
|
---|
937 | strbuf_addf(&buf, "Range: bytes=%ld-", posn);
|
---|
938 | headers = curl_slist_append(headers, buf.buf);
|
---|
939 | strbuf_reset(&buf);
|
---|
940 | }
|
---|
941 | } else
|
---|
942 | curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
|
---|
943 | fwrite_buffer);
|
---|
944 | }
|
---|
945 |
|
---|
946 | strbuf_addstr(&buf, "Pragma:");
|
---|
947 | if (options && options->no_cache)
|
---|
948 | strbuf_addstr(&buf, " no-cache");
|
---|
949 | if (options && options->keep_error)
|
---|
950 | curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
|
---|
951 |
|
---|
952 | headers = curl_slist_append(headers, buf.buf);
|
---|
953 |
|
---|
954 | curl_easy_setopt(slot->curl, CURLOPT_URL, url);
|
---|
955 | curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
|
---|
956 | curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "gzip");
|
---|
957 |
|
---|
958 | ret = run_one_slot(slot, &results);
|
---|
959 |
|
---|
960 | if (options && options->content_type)
|
---|
961 | curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE,
|
---|
962 | options->content_type);
|
---|
963 |
|
---|
964 | if (options && options->effective_url)
|
---|
965 | curlinfo_strbuf(slot->curl, CURLINFO_EFFECTIVE_URL,
|
---|
966 | options->effective_url);
|
---|
967 |
|
---|
968 | curl_slist_free_all(headers);
|
---|
969 | strbuf_release(&buf);
|
---|
970 |
|
---|
971 | return ret;
|
---|
972 | }
|
---|
973 |
|
---|
974 | /*
|
---|
975 | * Update the "base" url to a more appropriate value, as deduced by
|
---|
976 | * redirects seen when requesting a URL starting with "url".
|
---|
977 | *
|
---|
978 | * The "asked" parameter is a URL that we asked curl to access, and must begin
|
---|
979 | * with "base".
|
---|
980 | *
|
---|
981 | * The "got" parameter is the URL that curl reported to us as where we ended
|
---|
982 | * up.
|
---|
983 | *
|
---|
984 | * Returns 1 if we updated the base url, 0 otherwise.
|
---|
985 | *
|
---|
986 | * Our basic strategy is to compare "base" and "asked" to find the bits
|
---|
987 | * specific to our request. We then strip those bits off of "got" to yield the
|
---|
988 | * new base. So for example, if our base is "http://example.com/foo.git",
|
---|
989 | * and we ask for "http://example.com/foo.git/info/refs", we might end up
|
---|
990 | * with "https://other.example.com/foo.git/info/refs". We would want the
|
---|
991 | * new URL to become "https://other.example.com/foo.git".
|
---|
992 | *
|
---|
993 | * Note that this assumes a sane redirect scheme. It's entirely possible
|
---|
994 | * in the example above to end up at a URL that does not even end in
|
---|
995 | * "info/refs". In such a case we simply punt, as there is not much we can
|
---|
996 | * do (and such a scheme is unlikely to represent a real git repository,
|
---|
997 | * which means we are likely about to abort anyway).
|
---|
998 | */
|
---|
999 | static int update_url_from_redirect(struct strbuf *base,
|
---|
1000 | const char *asked,
|
---|
1001 | const struct strbuf *got)
|
---|
1002 | {
|
---|
1003 | const char *tail;
|
---|
1004 | size_t tail_len;
|
---|
1005 |
|
---|
1006 | if (!strcmp(asked, got->buf))
|
---|
1007 | return 0;
|
---|
1008 |
|
---|
1009 | if (!starts_with(asked, base->buf))
|
---|
1010 | die("BUG: update_url_from_redirect: %s is not a superset of %s",
|
---|
1011 | asked, base->buf);
|
---|
1012 |
|
---|
1013 | tail = asked + base->len;
|
---|
1014 | tail_len = strlen(tail);
|
---|
1015 |
|
---|
1016 | if (got->len < tail_len ||
|
---|
1017 | strcmp(tail, got->buf + got->len - tail_len))
|
---|
1018 | return 0; /* insane redirect scheme */
|
---|
1019 |
|
---|
1020 | strbuf_reset(base);
|
---|
1021 | strbuf_add(base, got->buf, got->len - tail_len);
|
---|
1022 | return 1;
|
---|
1023 | }
|
---|
1024 |
|
---|
1025 | static int http_request_reauth(const char *url,
|
---|
1026 | void *result, int target,
|
---|
1027 | struct http_get_options *options)
|
---|
1028 | {
|
---|
1029 | int ret = http_request(url, result, target, options);
|
---|
1030 |
|
---|
1031 | if (options && options->effective_url && options->base_url) {
|
---|
1032 | if (update_url_from_redirect(options->base_url,
|
---|
1033 | url, options->effective_url)) {
|
---|
1034 | credential_from_url(&http_auth, options->base_url->buf);
|
---|
1035 | url = options->effective_url->buf;
|
---|
1036 | }
|
---|
1037 | }
|
---|
1038 |
|
---|
1039 | if (ret != HTTP_REAUTH)
|
---|
1040 | return ret;
|
---|
1041 |
|
---|
1042 | /*
|
---|
1043 | * If we are using KEEP_ERROR, the previous request may have
|
---|
1044 | * put cruft into our output stream; we should clear it out before
|
---|
1045 | * making our next request. We only know how to do this for
|
---|
1046 | * the strbuf case, but that is enough to satisfy current callers.
|
---|
1047 | */
|
---|
1048 | if (options && options->keep_error) {
|
---|
1049 | switch (target) {
|
---|
1050 | case HTTP_REQUEST_STRBUF:
|
---|
1051 | strbuf_reset(result);
|
---|
1052 | break;
|
---|
1053 | default:
|
---|
1054 | die("BUG: HTTP_KEEP_ERROR is only supported with strbufs");
|
---|
1055 | }
|
---|
1056 | }
|
---|
1057 |
|
---|
1058 | credential_fill(&http_auth);
|
---|
1059 |
|
---|
1060 | return http_request(url, result, target, options);
|
---|
1061 | }
|
---|
1062 |
|
---|
1063 | int http_get_strbuf(const char *url,
|
---|
1064 | struct strbuf *result,
|
---|
1065 | struct http_get_options *options)
|
---|
1066 | {
|
---|
1067 | return http_request_reauth(url, result, HTTP_REQUEST_STRBUF, options);
|
---|
1068 | }
|
---|
1069 |
|
---|
1070 | /*
|
---|
1071 | * Downloads a URL and stores the result in the given file.
|
---|
1072 | *
|
---|
1073 | * If a previous interrupted download is detected (i.e. a previous temporary
|
---|
1074 | * file is still around) the download is resumed.
|
---|
1075 | */
|
---|
1076 | static int http_get_file(const char *url, const char *filename,
|
---|
1077 | struct http_get_options *options)
|
---|
1078 | {
|
---|
1079 | int ret;
|
---|
1080 | struct strbuf tmpfile = STRBUF_INIT;
|
---|
1081 | FILE *result;
|
---|
1082 |
|
---|
1083 | strbuf_addf(&tmpfile, "%s.temp", filename);
|
---|
1084 | result = fopen(tmpfile.buf, "a");
|
---|
1085 | if (!result) {
|
---|
1086 | error("Unable to open local file %s", tmpfile.buf);
|
---|
1087 | ret = HTTP_ERROR;
|
---|
1088 | goto cleanup;
|
---|
1089 | }
|
---|
1090 |
|
---|
1091 | ret = http_request_reauth(url, result, HTTP_REQUEST_FILE, options);
|
---|
1092 | fclose(result);
|
---|
1093 |
|
---|
1094 | if (ret == HTTP_OK && move_temp_to_file(tmpfile.buf, filename))
|
---|
1095 | ret = HTTP_ERROR;
|
---|
1096 | cleanup:
|
---|
1097 | strbuf_release(&tmpfile);
|
---|
1098 | return ret;
|
---|
1099 | }
|
---|
1100 |
|
---|
1101 | int http_fetch_ref(const char *base, struct ref *ref)
|
---|
1102 | {
|
---|
1103 | struct http_get_options options = {0};
|
---|
1104 | char *url;
|
---|
1105 | struct strbuf buffer = STRBUF_INIT;
|
---|
1106 | int ret = -1;
|
---|
1107 |
|
---|
1108 | options.no_cache = 1;
|
---|
1109 |
|
---|
1110 | url = quote_ref_url(base, ref->name);
|
---|
1111 | if (http_get_strbuf(url, &buffer, &options) == HTTP_OK) {
|
---|
1112 | strbuf_rtrim(&buffer);
|
---|
1113 | if (buffer.len == 40)
|
---|
1114 | ret = get_sha1_hex(buffer.buf, ref->old_sha1);
|
---|
1115 | else if (starts_with(buffer.buf, "ref: ")) {
|
---|
1116 | ref->symref = xstrdup(buffer.buf + 5);
|
---|
1117 | ret = 0;
|
---|
1118 | }
|
---|
1119 | }
|
---|
1120 |
|
---|
1121 | strbuf_release(&buffer);
|
---|
1122 | free(url);
|
---|
1123 | return ret;
|
---|
1124 | }
|
---|
1125 |
|
---|
1126 | /* Helpers for fetching packs */
|
---|
1127 | static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
|
---|
1128 | {
|
---|
1129 | char *url, *tmp;
|
---|
1130 | struct strbuf buf = STRBUF_INIT;
|
---|
1131 |
|
---|
1132 | if (http_is_verbose)
|
---|
1133 | fprintf(stderr, "Getting index for pack %s\n", sha1_to_hex(sha1));
|
---|
1134 |
|
---|
1135 | end_url_with_slash(&buf, base_url);
|
---|
1136 | strbuf_addf(&buf, "objects/pack/pack-%s.idx", sha1_to_hex(sha1));
|
---|
1137 | url = strbuf_detach(&buf, NULL);
|
---|
1138 |
|
---|
1139 | strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1));
|
---|
1140 | tmp = strbuf_detach(&buf, NULL);
|
---|
1141 |
|
---|
1142 | if (http_get_file(url, tmp, NULL) != HTTP_OK) {
|
---|
1143 | error("Unable to get pack index %s", url);
|
---|
1144 | free(tmp);
|
---|
1145 | tmp = NULL;
|
---|
1146 | }
|
---|
1147 |
|
---|
1148 | free(url);
|
---|
1149 | return tmp;
|
---|
1150 | }
|
---|
1151 |
|
---|
1152 | static int fetch_and_setup_pack_index(struct packed_git **packs_head,
|
---|
1153 | unsigned char *sha1, const char *base_url)
|
---|
1154 | {
|
---|
1155 | struct packed_git *new_pack;
|
---|
1156 | char *tmp_idx = NULL;
|
---|
1157 | int ret;
|
---|
1158 |
|
---|
1159 | if (has_pack_index(sha1)) {
|
---|
1160 | new_pack = parse_pack_index(sha1, NULL);
|
---|
1161 | if (!new_pack)
|
---|
1162 | return -1; /* parse_pack_index() already issued error message */
|
---|
1163 | goto add_pack;
|
---|
1164 | }
|
---|
1165 |
|
---|
1166 | tmp_idx = fetch_pack_index(sha1, base_url);
|
---|
1167 | if (!tmp_idx)
|
---|
1168 | return -1;
|
---|
1169 |
|
---|
1170 | new_pack = parse_pack_index(sha1, tmp_idx);
|
---|
1171 | if (!new_pack) {
|
---|
1172 | unlink(tmp_idx);
|
---|
1173 | free(tmp_idx);
|
---|
1174 |
|
---|
1175 | return -1; /* parse_pack_index() already issued error message */
|
---|
1176 | }
|
---|
1177 |
|
---|
1178 | ret = verify_pack_index(new_pack);
|
---|
1179 | if (!ret) {
|
---|
1180 | close_pack_index(new_pack);
|
---|
1181 | ret = move_temp_to_file(tmp_idx, sha1_pack_index_name(sha1));
|
---|
1182 | }
|
---|
1183 | free(tmp_idx);
|
---|
1184 | if (ret)
|
---|
1185 | return -1;
|
---|
1186 |
|
---|
1187 | add_pack:
|
---|
1188 | new_pack->next = *packs_head;
|
---|
1189 | *packs_head = new_pack;
|
---|
1190 | return 0;
|
---|
1191 | }
|
---|
1192 |
|
---|
1193 | int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
|
---|
1194 | {
|
---|
1195 | struct http_get_options options = {0};
|
---|
1196 | int ret = 0, i = 0;
|
---|
1197 | char *url, *data;
|
---|
1198 | struct strbuf buf = STRBUF_INIT;
|
---|
1199 | unsigned char sha1[20];
|
---|
1200 |
|
---|
1201 | end_url_with_slash(&buf, base_url);
|
---|
1202 | strbuf_addstr(&buf, "objects/info/packs");
|
---|
1203 | url = strbuf_detach(&buf, NULL);
|
---|
1204 |
|
---|
1205 | options.no_cache = 1;
|
---|
1206 | ret = http_get_strbuf(url, &buf, &options);
|
---|
1207 | if (ret != HTTP_OK)
|
---|
1208 | goto cleanup;
|
---|
1209 |
|
---|
1210 | data = buf.buf;
|
---|
1211 | while (i < buf.len) {
|
---|
1212 | switch (data[i]) {
|
---|
1213 | case 'P':
|
---|
1214 | i++;
|
---|
1215 | if (i + 52 <= buf.len &&
|
---|
1216 | starts_with(data + i, " pack-") &&
|
---|
1217 | starts_with(data + i + 46, ".pack\n")) {
|
---|
1218 | get_sha1_hex(data + i + 6, sha1);
|
---|
1219 | fetch_and_setup_pack_index(packs_head, sha1,
|
---|
1220 | base_url);
|
---|
1221 | i += 51;
|
---|
1222 | break;
|
---|
1223 | }
|
---|
1224 | default:
|
---|
1225 | while (i < buf.len && data[i] != '\n')
|
---|
1226 | i++;
|
---|
1227 | }
|
---|
1228 | i++;
|
---|
1229 | }
|
---|
1230 |
|
---|
1231 | cleanup:
|
---|
1232 | free(url);
|
---|
1233 | return ret;
|
---|
1234 | }
|
---|
1235 |
|
---|
1236 | void release_http_pack_request(struct http_pack_request *preq)
|
---|
1237 | {
|
---|
1238 | if (preq->packfile != NULL) {
|
---|
1239 | fclose(preq->packfile);
|
---|
1240 | preq->packfile = NULL;
|
---|
1241 | }
|
---|
1242 | if (preq->range_header != NULL) {
|
---|
1243 | curl_slist_free_all(preq->range_header);
|
---|
1244 | preq->range_header = NULL;
|
---|
1245 | }
|
---|
1246 | preq->slot = NULL;
|
---|
1247 | free(preq->url);
|
---|
1248 | }
|
---|
1249 |
|
---|
1250 | int finish_http_pack_request(struct http_pack_request *preq)
|
---|
1251 | {
|
---|
1252 | struct packed_git **lst;
|
---|
1253 | struct packed_git *p = preq->target;
|
---|
1254 | char *tmp_idx;
|
---|
1255 | struct child_process ip;
|
---|
1256 | const char *ip_argv[8];
|
---|
1257 |
|
---|
1258 | close_pack_index(p);
|
---|
1259 |
|
---|
1260 | fclose(preq->packfile);
|
---|
1261 | preq->packfile = NULL;
|
---|
1262 |
|
---|
1263 | lst = preq->lst;
|
---|
1264 | while (*lst != p)
|
---|
1265 | lst = &((*lst)->next);
|
---|
1266 | *lst = (*lst)->next;
|
---|
1267 |
|
---|
1268 | tmp_idx = xstrdup(preq->tmpfile);
|
---|
1269 | strcpy(tmp_idx + strlen(tmp_idx) - strlen(".pack.temp"),
|
---|
1270 | ".idx.temp");
|
---|
1271 |
|
---|
1272 | ip_argv[0] = "index-pack";
|
---|
1273 | ip_argv[1] = "-o";
|
---|
1274 | ip_argv[2] = tmp_idx;
|
---|
1275 | ip_argv[3] = preq->tmpfile;
|
---|
1276 | ip_argv[4] = NULL;
|
---|
1277 |
|
---|
1278 | memset(&ip, 0, sizeof(ip));
|
---|
1279 | ip.argv = ip_argv;
|
---|
1280 | ip.git_cmd = 1;
|
---|
1281 | ip.no_stdin = 1;
|
---|
1282 | ip.no_stdout = 1;
|
---|
1283 |
|
---|
1284 | if (run_command(&ip)) {
|
---|
1285 | unlink(preq->tmpfile);
|
---|
1286 | unlink(tmp_idx);
|
---|
1287 | free(tmp_idx);
|
---|
1288 | return -1;
|
---|
1289 | }
|
---|
1290 |
|
---|
1291 | unlink(sha1_pack_index_name(p->sha1));
|
---|
1292 |
|
---|
1293 | if (move_temp_to_file(preq->tmpfile, sha1_pack_name(p->sha1))
|
---|
1294 | || move_temp_to_file(tmp_idx, sha1_pack_index_name(p->sha1))) {
|
---|
1295 | free(tmp_idx);
|
---|
1296 | return -1;
|
---|
1297 | }
|
---|
1298 |
|
---|
1299 | install_packed_git(p);
|
---|
1300 | free(tmp_idx);
|
---|
1301 | return 0;
|
---|
1302 | }
|
---|
1303 |
|
---|
1304 | struct http_pack_request *new_http_pack_request(
|
---|
1305 | struct packed_git *target, const char *base_url)
|
---|
1306 | {
|
---|
1307 | long prev_posn = 0;
|
---|
1308 | char range[RANGE_HEADER_SIZE];
|
---|
1309 | struct strbuf buf = STRBUF_INIT;
|
---|
1310 | struct http_pack_request *preq;
|
---|
1311 |
|
---|
1312 | preq = xcalloc(1, sizeof(*preq));
|
---|
1313 | preq->target = target;
|
---|
1314 |
|
---|
1315 | end_url_with_slash(&buf, base_url);
|
---|
1316 | strbuf_addf(&buf, "objects/pack/pack-%s.pack",
|
---|
1317 | sha1_to_hex(target->sha1));
|
---|
1318 | preq->url = strbuf_detach(&buf, NULL);
|
---|
1319 |
|
---|
1320 | snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp",
|
---|
1321 | sha1_pack_name(target->sha1));
|
---|
1322 | preq->packfile = fopen(preq->tmpfile, "a");
|
---|
1323 | if (!preq->packfile) {
|
---|
1324 | error("Unable to open local file %s for pack",
|
---|
1325 | preq->tmpfile);
|
---|
1326 | goto abort;
|
---|
1327 | }
|
---|
1328 |
|
---|
1329 | preq->slot = get_active_slot();
|
---|
1330 | curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile);
|
---|
1331 | curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
|
---|
1332 | curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
|
---|
1333 | curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
|
---|
1334 | no_pragma_header);
|
---|
1335 |
|
---|
1336 | /*
|
---|
1337 | * If there is data present from a previous transfer attempt,
|
---|
1338 | * resume where it left off
|
---|
1339 | */
|
---|
1340 | prev_posn = ftell(preq->packfile);
|
---|
1341 | if (prev_posn>0) {
|
---|
1342 | if (http_is_verbose)
|
---|
1343 | fprintf(stderr,
|
---|
1344 | "Resuming fetch of pack %s at byte %ld\n",
|
---|
1345 | sha1_to_hex(target->sha1), prev_posn);
|
---|
1346 | sprintf(range, "Range: bytes=%ld-", prev_posn);
|
---|
1347 | preq->range_header = curl_slist_append(NULL, range);
|
---|
1348 | curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
|
---|
1349 | preq->range_header);
|
---|
1350 | }
|
---|
1351 |
|
---|
1352 | return preq;
|
---|
1353 |
|
---|
1354 | abort:
|
---|
1355 | free(preq->url);
|
---|
1356 | free(preq);
|
---|
1357 | return NULL;
|
---|
1358 | }
|
---|
1359 |
|
---|
1360 | /* Helpers for fetching objects (loose) */
|
---|
1361 | static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,
|
---|
1362 | void *data)
|
---|
1363 | {
|
---|
1364 | unsigned char expn[4096];
|
---|
1365 | size_t size = eltsize * nmemb;
|
---|
1366 | int posn = 0;
|
---|
1367 | struct http_object_request *freq =
|
---|
1368 | (struct http_object_request *)data;
|
---|
1369 | do {
|
---|
1370 | ssize_t retval = xwrite(freq->localfile,
|
---|
1371 | (char *) ptr + posn, size - posn);
|
---|
1372 | if (retval < 0)
|
---|
1373 | return posn;
|
---|
1374 | posn += retval;
|
---|
1375 | } while (posn < size);
|
---|
1376 |
|
---|
1377 | freq->stream.avail_in = size;
|
---|
1378 | freq->stream.next_in = (void *)ptr;
|
---|
1379 | do {
|
---|
1380 | freq->stream.next_out = expn;
|
---|
1381 | freq->stream.avail_out = sizeof(expn);
|
---|
1382 | freq->zret = git_inflate(&freq->stream, Z_SYNC_FLUSH);
|
---|
1383 | git_SHA1_Update(&freq->c, expn,
|
---|
1384 | sizeof(expn) - freq->stream.avail_out);
|
---|
1385 | } while (freq->stream.avail_in && freq->zret == Z_OK);
|
---|
1386 | return size;
|
---|
1387 | }
|
---|
1388 |
|
---|
1389 | struct http_object_request *new_http_object_request(const char *base_url,
|
---|
1390 | unsigned char *sha1)
|
---|
1391 | {
|
---|
1392 | char *hex = sha1_to_hex(sha1);
|
---|
1393 | const char *filename;
|
---|
1394 | char prevfile[PATH_MAX];
|
---|
1395 | int prevlocal;
|
---|
1396 | char prev_buf[PREV_BUF_SIZE];
|
---|
1397 | ssize_t prev_read = 0;
|
---|
1398 | long prev_posn = 0;
|
---|
1399 | char range[RANGE_HEADER_SIZE];
|
---|
1400 | struct curl_slist *range_header = NULL;
|
---|
1401 | struct http_object_request *freq;
|
---|
1402 |
|
---|
1403 | freq = xcalloc(1, sizeof(*freq));
|
---|
1404 | hashcpy(freq->sha1, sha1);
|
---|
1405 | freq->localfile = -1;
|
---|
1406 |
|
---|
1407 | filename = sha1_file_name(sha1);
|
---|
1408 | snprintf(freq->tmpfile, sizeof(freq->tmpfile),
|
---|
1409 | "%s.temp", filename);
|
---|
1410 |
|
---|
1411 | snprintf(prevfile, sizeof(prevfile), "%s.prev", filename);
|
---|
1412 | unlink_or_warn(prevfile);
|
---|
1413 | rename(freq->tmpfile, prevfile);
|
---|
1414 | unlink_or_warn(freq->tmpfile);
|
---|
1415 |
|
---|
1416 | if (freq->localfile != -1)
|
---|
1417 | error("fd leakage in start: %d", freq->localfile);
|
---|
1418 | freq->localfile = open(freq->tmpfile,
|
---|
1419 | O_WRONLY | O_CREAT | O_EXCL, 0666);
|
---|
1420 | /*
|
---|
1421 | * This could have failed due to the "lazy directory creation";
|
---|
1422 | * try to mkdir the last path component.
|
---|
1423 | */
|
---|
1424 | if (freq->localfile < 0 && errno == ENOENT) {
|
---|
1425 | char *dir = strrchr(freq->tmpfile, '/');
|
---|
1426 | if (dir) {
|
---|
1427 | *dir = 0;
|
---|
1428 | mkdir(freq->tmpfile, 0777);
|
---|
1429 | *dir = '/';
|
---|
1430 | }
|
---|
1431 | freq->localfile = open(freq->tmpfile,
|
---|
1432 | O_WRONLY | O_CREAT | O_EXCL, 0666);
|
---|
1433 | }
|
---|
1434 |
|
---|
1435 | if (freq->localfile < 0) {
|
---|
1436 | error("Couldn't create temporary file %s: %s",
|
---|
1437 | freq->tmpfile, strerror(errno));
|
---|
1438 | goto abort;
|
---|
1439 | }
|
---|
1440 |
|
---|
1441 | git_inflate_init(&freq->stream);
|
---|
1442 |
|
---|
1443 | git_SHA1_Init(&freq->c);
|
---|
1444 |
|
---|
1445 | freq->url = get_remote_object_url(base_url, hex, 0);
|
---|
1446 |
|
---|
1447 | /*
|
---|
1448 | * If a previous temp file is present, process what was already
|
---|
1449 | * fetched.
|
---|
1450 | */
|
---|
1451 | prevlocal = open(prevfile, O_RDONLY);
|
---|
1452 | if (prevlocal != -1) {
|
---|
1453 | do {
|
---|
1454 | prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
|
---|
1455 | if (prev_read>0) {
|
---|
1456 | if (fwrite_sha1_file(prev_buf,
|
---|
1457 | 1,
|
---|
1458 | prev_read,
|
---|
1459 | freq) == prev_read) {
|
---|
1460 | prev_posn += prev_read;
|
---|
1461 | } else {
|
---|
1462 | prev_read = -1;
|
---|
1463 | }
|
---|
1464 | }
|
---|
1465 | } while (prev_read > 0);
|
---|
1466 | close(prevlocal);
|
---|
1467 | }
|
---|
1468 | unlink_or_warn(prevfile);
|
---|
1469 |
|
---|
1470 | /*
|
---|
1471 | * Reset inflate/SHA1 if there was an error reading the previous temp
|
---|
1472 | * file; also rewind to the beginning of the local file.
|
---|
1473 | */
|
---|
1474 | if (prev_read == -1) {
|
---|
1475 | memset(&freq->stream, 0, sizeof(freq->stream));
|
---|
1476 | git_inflate_init(&freq->stream);
|
---|
1477 | git_SHA1_Init(&freq->c);
|
---|
1478 | if (prev_posn>0) {
|
---|
1479 | prev_posn = 0;
|
---|
1480 | lseek(freq->localfile, 0, SEEK_SET);
|
---|
1481 | if (ftruncate(freq->localfile, 0) < 0) {
|
---|
1482 | error("Couldn't truncate temporary file %s: %s",
|
---|
1483 | freq->tmpfile, strerror(errno));
|
---|
1484 | goto abort;
|
---|
1485 | }
|
---|
1486 | }
|
---|
1487 | }
|
---|
1488 |
|
---|
1489 | freq->slot = get_active_slot();
|
---|
1490 |
|
---|
1491 | curl_easy_setopt(freq->slot->curl, CURLOPT_FILE, freq);
|
---|
1492 | curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
|
---|
1493 | curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
|
---|
1494 | curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
|
---|
1495 | curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
|
---|
1496 |
|
---|
1497 | /*
|
---|
1498 | * If we have successfully processed data from a previous fetch
|
---|
1499 | * attempt, only fetch the data we don't already have.
|
---|
1500 | */
|
---|
1501 | if (prev_posn>0) {
|
---|
1502 | if (http_is_verbose)
|
---|
1503 | fprintf(stderr,
|
---|
1504 | "Resuming fetch of object %s at byte %ld\n",
|
---|
1505 | hex, prev_posn);
|
---|
1506 | sprintf(range, "Range: bytes=%ld-", prev_posn);
|
---|
1507 | range_header = curl_slist_append(range_header, range);
|
---|
1508 | curl_easy_setopt(freq->slot->curl,
|
---|
1509 | CURLOPT_HTTPHEADER, range_header);
|
---|
1510 | }
|
---|
1511 |
|
---|
1512 | return freq;
|
---|
1513 |
|
---|
1514 | abort:
|
---|
1515 | free(freq->url);
|
---|
1516 | free(freq);
|
---|
1517 | return NULL;
|
---|
1518 | }
|
---|
1519 |
|
---|
1520 | void process_http_object_request(struct http_object_request *freq)
|
---|
1521 | {
|
---|
1522 | if (freq->slot == NULL)
|
---|
1523 | return;
|
---|
1524 | freq->curl_result = freq->slot->curl_result;
|
---|
1525 | freq->http_code = freq->slot->http_code;
|
---|
1526 | freq->slot = NULL;
|
---|
1527 | }
|
---|
1528 |
|
---|
1529 | int finish_http_object_request(struct http_object_request *freq)
|
---|
1530 | {
|
---|
1531 | struct stat st;
|
---|
1532 |
|
---|
1533 | close(freq->localfile);
|
---|
1534 | freq->localfile = -1;
|
---|
1535 |
|
---|
1536 | process_http_object_request(freq);
|
---|
1537 |
|
---|
1538 | if (freq->http_code == 416) {
|
---|
1539 | warning("requested range invalid; we may already have all the data.");
|
---|
1540 | } else if (freq->curl_result != CURLE_OK) {
|
---|
1541 | if (stat(freq->tmpfile, &st) == 0)
|
---|
1542 | if (st.st_size == 0)
|
---|
1543 | unlink_or_warn(freq->tmpfile);
|
---|
1544 | return -1;
|
---|
1545 | }
|
---|
1546 |
|
---|
1547 | git_inflate_end(&freq->stream);
|
---|
1548 | git_SHA1_Final(freq->real_sha1, &freq->c);
|
---|
1549 | if (freq->zret != Z_STREAM_END) {
|
---|
1550 | unlink_or_warn(freq->tmpfile);
|
---|
1551 | return -1;
|
---|
1552 | }
|
---|
1553 | if (hashcmp(freq->sha1, freq->real_sha1)) {
|
---|
1554 | unlink_or_warn(freq->tmpfile);
|
---|
1555 | return -1;
|
---|
1556 | }
|
---|
1557 | freq->rename =
|
---|
1558 | move_temp_to_file(freq->tmpfile, sha1_file_name(freq->sha1));
|
---|
1559 |
|
---|
1560 | return freq->rename;
|
---|
1561 | }
|
---|
1562 |
|
---|
1563 | void abort_http_object_request(struct http_object_request *freq)
|
---|
1564 | {
|
---|
1565 | unlink_or_warn(freq->tmpfile);
|
---|
1566 |
|
---|
1567 | release_http_object_request(freq);
|
---|
1568 | }
|
---|
1569 |
|
---|
1570 | void release_http_object_request(struct http_object_request *freq)
|
---|
1571 | {
|
---|
1572 | if (freq->localfile != -1) {
|
---|
1573 | close(freq->localfile);
|
---|
1574 | freq->localfile = -1;
|
---|
1575 | }
|
---|
1576 | if (freq->url != NULL) {
|
---|
1577 | free(freq->url);
|
---|
1578 | freq->url = NULL;
|
---|
1579 | }
|
---|
1580 | if (freq->slot != NULL) {
|
---|
1581 | freq->slot->callback_func = NULL;
|
---|
1582 | freq->slot->callback_data = NULL;
|
---|
1583 | release_active_slot(freq->slot);
|
---|
1584 | freq->slot = NULL;
|
---|
1585 | }
|
---|
1586 | }
|
---|