2005-11-19 03:02:58 +08:00
|
|
|
#include "http.h"
|
2009-06-06 16:44:01 +08:00
|
|
|
#include "pack.h"
|
2005-11-19 03:02:58 +08:00
|
|
|
|
|
|
|
int data_received;
|
2009-03-10 09:47:29 +08:00
|
|
|
int active_requests;
|
2009-06-06 16:43:41 +08:00
|
|
|
int http_is_verbose;
|
2005-11-19 03:02:58 +08:00
|
|
|
|
|
|
|
#ifdef USE_CURL_MULTI
|
2007-12-10 01:04:57 +08:00
|
|
|
static int max_requests = -1;
|
|
|
|
static CURLM *curlm;
|
2005-11-19 03:02:58 +08:00
|
|
|
#endif
|
|
|
|
#ifndef NO_CURL_EASY_DUPHANDLE
|
2007-12-10 01:04:57 +08:00
|
|
|
static CURL *curl_default;
|
2005-11-19 03:02:58 +08:00
|
|
|
#endif
|
http*: add helper methods for fetching objects (loose)
The code handling the fetching of loose objects in http-push.c and
http-walker.c have been refactored into new methods and a new struct
(object_http_request) in http.c. They are not meant to be invoked
elsewhere.
The new methods in http.c are
- new_http_object_request
- process_http_object_request
- finish_http_object_request
- abort_http_object_request
- release_http_object_request
and the new struct is http_object_request.
RANGER_HEADER_SIZE and no_pragma_header is no longer made available
outside of http.c, since after the above changes, there are no other
instances of usage outside of http.c.
Remove members of the transfer_request struct in http-push.c and
http-walker.c, including filename, real_sha1 and zret, as they are used
no longer used.
Move the methods append_remote_object_url() and get_remote_object_url()
from http-push.c to http.c. Additionally, get_remote_object_url() is no
longer defined only when USE_CURL_MULTI is defined, since
non-USE_CURL_MULTI code in http.c uses it (namely, in
new_http_object_request()).
Refactor code from http-push.c::start_fetch_loose() and
http-walker.c::start_object_fetch_request() that deals with the details
of coming up with the filename to store the retrieved object, resuming
a previously aborted request, and making a new curl request, into a new
function, new_http_object_request().
Refactor code from http-walker.c::process_object_request() into the
function, process_http_object_request().
Refactor code from http-push.c::finish_request() and
http-walker.c::finish_object_request() into a new function,
finish_http_object_request(). It returns the result of the
move_temp_to_file() invocation.
Add a function, release_http_object_request(), which cleans up object
request data. http-push.c and http-walker.c invoke this function
separately; http-push.c::release_request() and
http-walker.c::release_object_request() do not invoke this function.
Add a function, abort_http_object_request(), which unlink()s the object
file and invokes release_http_object_request(). Update
http-walker.c::abort_object_request() to use this.
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-06-06 16:44:02 +08:00
|
|
|
|
|
|
|
#define PREV_BUF_SIZE 4096
|
|
|
|
#define RANGE_HEADER_SIZE 30
|
|
|
|
|
2005-11-19 03:02:58 +08:00
|
|
|
char curl_errorstr[CURL_ERROR_SIZE];
|
|
|
|
|
2007-12-10 01:04:57 +08:00
|
|
|
static int curl_ssl_verify = -1;
|
2009-03-10 09:47:29 +08:00
|
|
|
static const char *ssl_cert;
|
2009-06-15 10:39:00 +08:00
|
|
|
#if LIBCURL_VERSION_NUM >= 0x070903
|
2009-03-10 09:47:29 +08:00
|
|
|
static const char *ssl_key;
|
2005-11-19 03:02:58 +08:00
|
|
|
#endif
|
|
|
|
#if LIBCURL_VERSION_NUM >= 0x070908
|
2009-03-10 09:47:29 +08:00
|
|
|
static const char *ssl_capath;
|
2005-11-19 03:02:58 +08:00
|
|
|
#endif
|
2009-03-10 09:47:29 +08:00
|
|
|
static const char *ssl_cainfo;
|
2007-12-10 01:04:57 +08:00
|
|
|
static long curl_low_speed_limit = -1;
|
|
|
|
static long curl_low_speed_time = -1;
|
2009-03-10 09:47:29 +08:00
|
|
|
static int curl_ftp_no_epsv;
|
|
|
|
static const char *curl_http_proxy;
|
2009-03-10 14:34:25 +08:00
|
|
|
static char *user_name, *user_pass;
|
2005-11-19 03:02:58 +08:00
|
|
|
|
2009-05-28 11:16:02 +08:00
|
|
|
#if LIBCURL_VERSION_NUM >= 0x071700
|
|
|
|
/* Use CURLOPT_KEYPASSWD as is */
|
|
|
|
#elif LIBCURL_VERSION_NUM >= 0x070903
|
|
|
|
#define CURLOPT_KEYPASSWD CURLOPT_SSLKEYPASSWD
|
|
|
|
#else
|
|
|
|
#define CURLOPT_KEYPASSWD CURLOPT_SSLCERTPASSWD
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static char *ssl_cert_password;
|
|
|
|
static int ssl_cert_password_required;
|
|
|
|
|
2007-12-10 01:04:57 +08:00
|
|
|
static struct curl_slist *pragma_header;
|
http*: add helper methods for fetching objects (loose)
The code handling the fetching of loose objects in http-push.c and
http-walker.c have been refactored into new methods and a new struct
(object_http_request) in http.c. They are not meant to be invoked
elsewhere.
The new methods in http.c are
- new_http_object_request
- process_http_object_request
- finish_http_object_request
- abort_http_object_request
- release_http_object_request
and the new struct is http_object_request.
RANGER_HEADER_SIZE and no_pragma_header is no longer made available
outside of http.c, since after the above changes, there are no other
instances of usage outside of http.c.
Remove members of the transfer_request struct in http-push.c and
http-walker.c, including filename, real_sha1 and zret, as they are used
no longer used.
Move the methods append_remote_object_url() and get_remote_object_url()
from http-push.c to http.c. Additionally, get_remote_object_url() is no
longer defined only when USE_CURL_MULTI is defined, since
non-USE_CURL_MULTI code in http.c uses it (namely, in
new_http_object_request()).
Refactor code from http-push.c::start_fetch_loose() and
http-walker.c::start_object_fetch_request() that deals with the details
of coming up with the filename to store the retrieved object, resuming
a previously aborted request, and making a new curl request, into a new
function, new_http_object_request().
Refactor code from http-walker.c::process_object_request() into the
function, process_http_object_request().
Refactor code from http-push.c::finish_request() and
http-walker.c::finish_object_request() into a new function,
finish_http_object_request(). It returns the result of the
move_temp_to_file() invocation.
Add a function, release_http_object_request(), which cleans up object
request data. http-push.c and http-walker.c invoke this function
separately; http-push.c::release_request() and
http-walker.c::release_object_request() do not invoke this function.
Add a function, abort_http_object_request(), which unlink()s the object
file and invokes release_http_object_request(). Update
http-walker.c::abort_object_request() to use this.
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-06-06 16:44:02 +08:00
|
|
|
static struct curl_slist *no_pragma_header;
|
2009-06-06 16:43:41 +08:00
|
|
|
|
2009-03-10 09:47:29 +08:00
|
|
|
static struct active_request_slot *active_queue_head;
|
2005-11-19 03:02:58 +08:00
|
|
|
|
2008-07-04 15:37:40 +08:00
|
|
|
size_t fread_buffer(void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
|
2005-11-19 03:02:58 +08:00
|
|
|
{
|
|
|
|
size_t size = eltsize * nmemb;
|
2008-07-04 15:37:40 +08:00
|
|
|
struct buffer *buffer = buffer_;
|
|
|
|
|
2007-12-10 03:30:59 +08:00
|
|
|
if (size > buffer->buf.len - buffer->posn)
|
|
|
|
size = buffer->buf.len - buffer->posn;
|
|
|
|
memcpy(ptr, buffer->buf.buf + buffer->posn, size);
|
2005-11-19 03:02:58 +08:00
|
|
|
buffer->posn += size;
|
2007-12-10 03:30:59 +08:00
|
|
|
|
2005-11-19 03:02:58 +08:00
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2009-04-02 00:48:24 +08:00
|
|
|
#ifndef NO_CURL_IOCTL
|
|
|
|
curlioerr ioctl_buffer(CURL *handle, int cmd, void *clientp)
|
|
|
|
{
|
|
|
|
struct buffer *buffer = clientp;
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case CURLIOCMD_NOP:
|
|
|
|
return CURLIOE_OK;
|
|
|
|
|
|
|
|
case CURLIOCMD_RESTARTREAD:
|
|
|
|
buffer->posn = 0;
|
|
|
|
return CURLIOE_OK;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return CURLIOE_UNKNOWNCMD;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-07-04 15:37:40 +08:00
|
|
|
size_t fwrite_buffer(const void *ptr, size_t eltsize, size_t nmemb, void *buffer_)
|
2005-11-19 03:02:58 +08:00
|
|
|
{
|
|
|
|
size_t size = eltsize * nmemb;
|
2008-07-04 15:37:40 +08:00
|
|
|
struct strbuf *buffer = buffer_;
|
|
|
|
|
2007-12-10 03:30:59 +08:00
|
|
|
strbuf_add(buffer, ptr, size);
|
2005-11-19 03:02:58 +08:00
|
|
|
data_received++;
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2008-07-04 15:37:40 +08:00
|
|
|
size_t fwrite_null(const void *ptr, size_t eltsize, size_t nmemb, void *strbuf)
|
2005-11-19 03:02:58 +08:00
|
|
|
{
|
|
|
|
data_received++;
|
|
|
|
return eltsize * nmemb;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void finish_active_slot(struct active_request_slot *slot);
|
|
|
|
|
|
|
|
#ifdef USE_CURL_MULTI
|
|
|
|
static void process_curl_messages(void)
|
|
|
|
{
|
|
|
|
int num_messages;
|
|
|
|
struct active_request_slot *slot;
|
|
|
|
CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);
|
|
|
|
|
|
|
|
while (curl_message != NULL) {
|
|
|
|
if (curl_message->msg == CURLMSG_DONE) {
|
|
|
|
int curl_result = curl_message->data.result;
|
|
|
|
slot = active_queue_head;
|
|
|
|
while (slot != NULL &&
|
|
|
|
slot->curl != curl_message->easy_handle)
|
|
|
|
slot = slot->next;
|
|
|
|
if (slot != NULL) {
|
|
|
|
curl_multi_remove_handle(curlm, slot->curl);
|
|
|
|
slot->curl_result = curl_result;
|
|
|
|
finish_active_slot(slot);
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Received DONE message for unknown request!\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fprintf(stderr, "Unknown CURL message received: %d\n",
|
|
|
|
(int)curl_message->msg);
|
|
|
|
}
|
|
|
|
curl_message = curl_multi_info_read(curlm, &num_messages);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-05-15 01:46:53 +08:00
|
|
|
static int http_options(const char *var, const char *value, void *cb)
|
2005-11-19 03:02:58 +08:00
|
|
|
{
|
|
|
|
if (!strcmp("http.sslverify", var)) {
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
curl_ssl_verify = git_config_bool(var, value);
|
2005-11-19 03:02:58 +08:00
|
|
|
return 0;
|
|
|
|
}
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
if (!strcmp("http.sslcert", var))
|
|
|
|
return git_config_string(&ssl_cert, var, value);
|
2009-06-15 10:39:00 +08:00
|
|
|
#if LIBCURL_VERSION_NUM >= 0x070903
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
if (!strcmp("http.sslkey", var))
|
|
|
|
return git_config_string(&ssl_key, var, value);
|
2005-11-19 03:02:58 +08:00
|
|
|
#endif
|
|
|
|
#if LIBCURL_VERSION_NUM >= 0x070908
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
if (!strcmp("http.sslcapath", var))
|
|
|
|
return git_config_string(&ssl_capath, var, value);
|
2005-11-19 03:02:58 +08:00
|
|
|
#endif
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
if (!strcmp("http.sslcainfo", var))
|
|
|
|
return git_config_string(&ssl_cainfo, var, value);
|
2009-05-28 11:16:03 +08:00
|
|
|
if (!strcmp("http.sslcertpasswordprotected", var)) {
|
|
|
|
if (git_config_bool(var, value))
|
|
|
|
ssl_cert_password_required = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
2007-06-07 15:04:01 +08:00
|
|
|
#ifdef USE_CURL_MULTI
|
2005-11-19 03:02:58 +08:00
|
|
|
if (!strcmp("http.maxrequests", var)) {
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
max_requests = git_config_int(var, value);
|
2005-11-19 03:02:58 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (!strcmp("http.lowspeedlimit", var)) {
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
curl_low_speed_limit = (long)git_config_int(var, value);
|
2005-11-19 03:02:58 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!strcmp("http.lowspeedtime", var)) {
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
curl_low_speed_time = (long)git_config_int(var, value);
|
2005-11-19 03:02:58 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-09-29 08:10:44 +08:00
|
|
|
if (!strcmp("http.noepsv", var)) {
|
|
|
|
curl_ftp_no_epsv = git_config_bool(var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
if (!strcmp("http.proxy", var))
|
|
|
|
return git_config_string(&curl_http_proxy, var, value);
|
2006-09-29 08:10:44 +08:00
|
|
|
|
2005-11-19 03:02:58 +08:00
|
|
|
/* Fall back on the default ones */
|
2008-05-15 01:46:53 +08:00
|
|
|
return git_default_config(var, value, cb);
|
2005-11-19 03:02:58 +08:00
|
|
|
}
|
|
|
|
|
2009-03-10 14:34:25 +08:00
|
|
|
static void init_curl_http_auth(CURL *result)
|
|
|
|
{
|
2009-03-13 13:34:43 +08:00
|
|
|
if (user_name) {
|
2009-03-10 14:34:25 +08:00
|
|
|
struct strbuf up = STRBUF_INIT;
|
|
|
|
if (!user_pass)
|
|
|
|
user_pass = xstrdup(getpass("Password: "));
|
|
|
|
strbuf_addf(&up, "%s:%s", user_name, user_pass);
|
|
|
|
curl_easy_setopt(result, CURLOPT_USERPWD,
|
|
|
|
strbuf_detach(&up, NULL));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-28 11:16:02 +08:00
|
|
|
static int has_cert_password(void)
|
|
|
|
{
|
|
|
|
if (ssl_cert_password != NULL)
|
|
|
|
return 1;
|
|
|
|
if (ssl_cert == NULL || ssl_cert_password_required != 1)
|
|
|
|
return 0;
|
|
|
|
/* Only prompt the user once. */
|
|
|
|
ssl_cert_password_required = -1;
|
|
|
|
ssl_cert_password = getpass("Certificate Password: ");
|
|
|
|
if (ssl_cert_password != NULL) {
|
|
|
|
ssl_cert_password = xstrdup(ssl_cert_password);
|
|
|
|
return 1;
|
|
|
|
} else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-03-10 09:47:29 +08:00
|
|
|
static CURL *get_curl_handle(void)
|
2005-11-19 09:06:46 +08:00
|
|
|
{
|
2009-03-10 09:47:29 +08:00
|
|
|
CURL *result = curl_easy_init();
|
2005-11-19 09:06:46 +08:00
|
|
|
|
2008-02-22 07:10:37 +08:00
|
|
|
if (!curl_ssl_verify) {
|
|
|
|
curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
|
|
|
|
curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0);
|
|
|
|
} else {
|
|
|
|
/* Verify authenticity of the peer's certificate */
|
|
|
|
curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 1);
|
|
|
|
/* The name in the cert must match whom we tried to connect */
|
|
|
|
curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
|
|
|
|
}
|
|
|
|
|
2005-11-19 09:06:46 +08:00
|
|
|
#if LIBCURL_VERSION_NUM >= 0x070907
|
|
|
|
curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
|
|
|
|
#endif
|
|
|
|
|
2009-03-10 14:34:25 +08:00
|
|
|
init_curl_http_auth(result);
|
|
|
|
|
2005-11-19 09:06:46 +08:00
|
|
|
if (ssl_cert != NULL)
|
|
|
|
curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
|
2009-05-28 11:16:02 +08:00
|
|
|
if (has_cert_password())
|
|
|
|
curl_easy_setopt(result, CURLOPT_KEYPASSWD, ssl_cert_password);
|
2009-06-15 10:39:00 +08:00
|
|
|
#if LIBCURL_VERSION_NUM >= 0x070903
|
2005-11-19 09:06:46 +08:00
|
|
|
if (ssl_key != NULL)
|
|
|
|
curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
|
|
|
|
#endif
|
|
|
|
#if LIBCURL_VERSION_NUM >= 0x070908
|
|
|
|
if (ssl_capath != NULL)
|
|
|
|
curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);
|
|
|
|
#endif
|
|
|
|
if (ssl_cainfo != NULL)
|
|
|
|
curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);
|
|
|
|
curl_easy_setopt(result, CURLOPT_FAILONERROR, 1);
|
|
|
|
|
|
|
|
if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {
|
|
|
|
curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,
|
|
|
|
curl_low_speed_limit);
|
|
|
|
curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,
|
|
|
|
curl_low_speed_time);
|
|
|
|
}
|
|
|
|
|
|
|
|
curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
|
|
|
|
|
2006-02-01 19:44:37 +08:00
|
|
|
if (getenv("GIT_CURL_VERBOSE"))
|
|
|
|
curl_easy_setopt(result, CURLOPT_VERBOSE, 1);
|
|
|
|
|
2006-04-05 01:11:29 +08:00
|
|
|
curl_easy_setopt(result, CURLOPT_USERAGENT, GIT_USER_AGENT);
|
|
|
|
|
2006-09-29 08:10:44 +08:00
|
|
|
if (curl_ftp_no_epsv)
|
|
|
|
curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);
|
|
|
|
|
2007-11-23 08:07:00 +08:00
|
|
|
if (curl_http_proxy)
|
|
|
|
curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
|
|
|
|
|
2005-11-19 09:06:46 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2009-03-10 14:34:25 +08:00
|
|
|
static void http_auth_init(const char *url)
|
|
|
|
{
|
|
|
|
char *at, *colon, *cp, *slash;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
cp = strstr(url, "://");
|
|
|
|
if (!cp)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ok, the URL looks like "proto://something". Which one?
|
|
|
|
* "proto://<user>:<pass>@<host>/...",
|
|
|
|
* "proto://<user>@<host>/...", or just
|
|
|
|
* "proto://<host>/..."?
|
|
|
|
*/
|
|
|
|
cp += 3;
|
|
|
|
at = strchr(cp, '@');
|
|
|
|
colon = strchr(cp, ':');
|
|
|
|
slash = strchrnul(cp, '/');
|
|
|
|
if (!at || slash <= at)
|
|
|
|
return; /* No credentials */
|
|
|
|
if (!colon || at <= colon) {
|
|
|
|
/* Only username */
|
|
|
|
len = at - cp;
|
|
|
|
user_name = xmalloc(len + 1);
|
|
|
|
memcpy(user_name, cp, len);
|
|
|
|
user_name[len] = '\0';
|
|
|
|
user_pass = NULL;
|
|
|
|
} else {
|
|
|
|
len = colon - cp;
|
|
|
|
user_name = xmalloc(len + 1);
|
|
|
|
memcpy(user_name, cp, len);
|
|
|
|
user_name[len] = '\0';
|
|
|
|
len = at - (colon + 1);
|
|
|
|
user_pass = xmalloc(len + 1);
|
|
|
|
memcpy(user_pass, colon + 1, len);
|
|
|
|
user_pass[len] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
static void set_from_env(const char **var, const char *envname)
|
|
|
|
{
|
|
|
|
const char *val = getenv(envname);
|
|
|
|
if (val)
|
|
|
|
*var = val;
|
|
|
|
}
|
|
|
|
|
2008-02-28 04:35:50 +08:00
|
|
|
void http_init(struct remote *remote)
|
2005-11-19 03:02:58 +08:00
|
|
|
{
|
|
|
|
char *low_speed_limit;
|
|
|
|
char *low_speed_time;
|
|
|
|
|
2009-06-06 16:43:41 +08:00
|
|
|
http_is_verbose = 0;
|
|
|
|
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
git_config(http_options, NULL);
|
|
|
|
|
2005-11-19 03:02:58 +08:00
|
|
|
curl_global_init(CURL_GLOBAL_ALL);
|
|
|
|
|
2008-02-28 04:35:50 +08:00
|
|
|
if (remote && remote->http_proxy)
|
|
|
|
curl_http_proxy = xstrdup(remote->http_proxy);
|
|
|
|
|
2005-11-19 03:02:58 +08:00
|
|
|
pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
|
2009-06-06 16:43:41 +08:00
|
|
|
no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
|
2005-11-19 03:02:58 +08:00
|
|
|
|
|
|
|
#ifdef USE_CURL_MULTI
|
|
|
|
{
|
|
|
|
char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
|
|
|
|
if (http_max_requests != NULL)
|
|
|
|
max_requests = atoi(http_max_requests);
|
|
|
|
}
|
|
|
|
|
|
|
|
curlm = curl_multi_init();
|
|
|
|
if (curlm == NULL) {
|
|
|
|
fprintf(stderr, "Error creating curl multi handle.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (getenv("GIT_SSL_NO_VERIFY"))
|
|
|
|
curl_ssl_verify = 0;
|
|
|
|
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
set_from_env(&ssl_cert, "GIT_SSL_CERT");
|
2009-06-15 10:39:00 +08:00
|
|
|
#if LIBCURL_VERSION_NUM >= 0x070903
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
set_from_env(&ssl_key, "GIT_SSL_KEY");
|
2005-11-19 03:02:58 +08:00
|
|
|
#endif
|
|
|
|
#if LIBCURL_VERSION_NUM >= 0x070908
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
set_from_env(&ssl_capath, "GIT_SSL_CAPATH");
|
2005-11-19 03:02:58 +08:00
|
|
|
#endif
|
http_init(): Fix config file parsing
We honor the command line options, environment variables, variables in
repository configuration file, variables in user's global configuration
file, variables in the system configuration file, and then finally use
built-in default. To implement this semantics, the code should:
- start from built-in default values;
- call git_config() with the configuration parser callback, which
implements "later definition overrides earlier ones" logic
(git_config() reads the system's, user's and then repository's
configuration file in this order);
- override the result from the above with environment variables if set;
- override the result from the above with command line options.
The initialization code http_init() for http transfer got this wrong, and
implemented a "first one wins, ignoring the later ones" in http_options(),
to compensate this mistake, read environment variables before calling
git_config(). This is all wrong.
As a second class citizen, the http codepath hasn't been audited as
closely as other parts of the system, but we should try to bring sanity to
it, before inviting contributors to improve on it.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-03-10 10:00:30 +08:00
|
|
|
set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");
|
2005-11-19 03:02:58 +08:00
|
|
|
|
|
|
|
low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");
|
|
|
|
if (low_speed_limit != NULL)
|
|
|
|
curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);
|
|
|
|
low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME");
|
|
|
|
if (low_speed_time != NULL)
|
|
|
|
curl_low_speed_time = strtol(low_speed_time, NULL, 10);
|
|
|
|
|
|
|
|
if (curl_ssl_verify == -1)
|
|
|
|
curl_ssl_verify = 1;
|
|
|
|
|
|
|
|
#ifdef USE_CURL_MULTI
|
|
|
|
if (max_requests < 1)
|
|
|
|
max_requests = DEFAULT_MAX_REQUESTS;
|
|
|
|
#endif
|
|
|
|
|
2006-09-29 08:10:44 +08:00
|
|
|
if (getenv("GIT_CURL_FTP_NO_EPSV"))
|
|
|
|
curl_ftp_no_epsv = 1;
|
|
|
|
|
2009-05-28 11:16:02 +08:00
|
|
|
if (remote && remote->url && remote->url[0]) {
|
2009-03-10 14:34:25 +08:00
|
|
|
http_auth_init(remote->url[0]);
|
2009-05-28 11:16:03 +08:00
|
|
|
if (!ssl_cert_password_required &&
|
|
|
|
getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") &&
|
|
|
|
!prefixcmp(remote->url[0], "https://"))
|
2009-05-28 11:16:02 +08:00
|
|
|
ssl_cert_password_required = 1;
|
|
|
|
}
|
2009-03-10 14:34:25 +08:00
|
|
|
|
2005-11-19 03:02:58 +08:00
|
|
|
#ifndef NO_CURL_EASY_DUPHANDLE
|
|
|
|
curl_default = get_curl_handle();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void http_cleanup(void)
|
|
|
|
{
|
|
|
|
struct active_request_slot *slot = active_queue_head;
|
|
|
|
|
|
|
|
while (slot != NULL) {
|
2007-09-15 15:23:00 +08:00
|
|
|
struct active_request_slot *next = slot->next;
|
2008-03-04 03:30:16 +08:00
|
|
|
if (slot->curl != NULL) {
|
2005-11-19 03:02:58 +08:00
|
|
|
#ifdef USE_CURL_MULTI
|
2008-03-04 03:30:16 +08:00
|
|
|
curl_multi_remove_handle(curlm, slot->curl);
|
2005-11-19 03:02:58 +08:00
|
|
|
#endif
|
|
|
|
curl_easy_cleanup(slot->curl);
|
2008-03-04 03:30:16 +08:00
|
|
|
}
|
2007-09-15 15:23:00 +08:00
|
|
|
free(slot);
|
|
|
|
slot = next;
|
2005-11-19 03:02:58 +08:00
|
|
|
}
|
2007-09-15 15:23:00 +08:00
|
|
|
active_queue_head = NULL;
|
2005-11-19 03:02:58 +08:00
|
|
|
|
|
|
|
#ifndef NO_CURL_EASY_DUPHANDLE
|
|
|
|
curl_easy_cleanup(curl_default);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef USE_CURL_MULTI
|
|
|
|
curl_multi_cleanup(curlm);
|
|
|
|
#endif
|
|
|
|
curl_global_cleanup();
|
2006-06-07 00:41:32 +08:00
|
|
|
|
|
|
|
curl_slist_free_all(pragma_header);
|
2007-09-15 15:23:00 +08:00
|
|
|
pragma_header = NULL;
|
2008-02-28 04:35:50 +08:00
|
|
|
|
2009-06-06 16:43:41 +08:00
|
|
|
curl_slist_free_all(no_pragma_header);
|
|
|
|
no_pragma_header = NULL;
|
|
|
|
|
2008-02-28 04:35:50 +08:00
|
|
|
if (curl_http_proxy) {
|
2008-12-07 08:45:37 +08:00
|
|
|
free((void *)curl_http_proxy);
|
2008-02-28 04:35:50 +08:00
|
|
|
curl_http_proxy = NULL;
|
|
|
|
}
|
2009-05-28 11:16:02 +08:00
|
|
|
|
|
|
|
if (ssl_cert_password != NULL) {
|
|
|
|
memset(ssl_cert_password, 0, strlen(ssl_cert_password));
|
|
|
|
free(ssl_cert_password);
|
|
|
|
ssl_cert_password = NULL;
|
|
|
|
}
|
|
|
|
ssl_cert_password_required = 0;
|
2005-11-19 03:02:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
struct active_request_slot *get_active_slot(void)
|
|
|
|
{
|
|
|
|
struct active_request_slot *slot = active_queue_head;
|
|
|
|
struct active_request_slot *newslot;
|
|
|
|
|
|
|
|
#ifdef USE_CURL_MULTI
|
|
|
|
int num_transfers;
|
|
|
|
|
|
|
|
/* Wait for a slot to open up if the queue is full */
|
|
|
|
while (active_requests >= max_requests) {
|
|
|
|
curl_multi_perform(curlm, &num_transfers);
|
2009-03-10 09:47:29 +08:00
|
|
|
if (num_transfers < active_requests)
|
2005-11-19 03:02:58 +08:00
|
|
|
process_curl_messages();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-03-10 09:47:29 +08:00
|
|
|
while (slot != NULL && slot->in_use)
|
2005-11-19 03:02:58 +08:00
|
|
|
slot = slot->next;
|
2009-03-10 09:47:29 +08:00
|
|
|
|
2005-11-19 03:02:58 +08:00
|
|
|
if (slot == NULL) {
|
|
|
|
newslot = xmalloc(sizeof(*newslot));
|
|
|
|
newslot->curl = NULL;
|
|
|
|
newslot->in_use = 0;
|
|
|
|
newslot->next = NULL;
|
|
|
|
|
|
|
|
slot = active_queue_head;
|
|
|
|
if (slot == NULL) {
|
|
|
|
active_queue_head = newslot;
|
|
|
|
} else {
|
2009-03-10 09:47:29 +08:00
|
|
|
while (slot->next != NULL)
|
2005-11-19 03:02:58 +08:00
|
|
|
slot = slot->next;
|
|
|
|
slot->next = newslot;
|
|
|
|
}
|
|
|
|
slot = newslot;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (slot->curl == NULL) {
|
|
|
|
#ifdef NO_CURL_EASY_DUPHANDLE
|
|
|
|
slot->curl = get_curl_handle();
|
|
|
|
#else
|
|
|
|
slot->curl = curl_easy_duphandle(curl_default);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
active_requests++;
|
|
|
|
slot->in_use = 1;
|
|
|
|
slot->local = NULL;
|
2006-02-01 03:06:55 +08:00
|
|
|
slot->results = NULL;
|
2006-03-11 12:18:01 +08:00
|
|
|
slot->finished = NULL;
|
2005-11-19 03:02:58 +08:00
|
|
|
slot->callback_data = NULL;
|
|
|
|
slot->callback_func = NULL;
|
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
|
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
|
2006-06-01 07:25:03 +08:00
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);
|
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);
|
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);
|
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
|
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
|
2005-11-19 03:02:58 +08:00
|
|
|
|
|
|
|
return slot;
|
|
|
|
}
|
|
|
|
|
|
|
|
int start_active_slot(struct active_request_slot *slot)
|
|
|
|
{
|
|
|
|
#ifdef USE_CURL_MULTI
|
|
|
|
CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);
|
2007-09-11 11:02:28 +08:00
|
|
|
int num_transfers;
|
2005-11-19 03:02:58 +08:00
|
|
|
|
|
|
|
if (curlm_result != CURLM_OK &&
|
|
|
|
curlm_result != CURLM_CALL_MULTI_PERFORM) {
|
|
|
|
active_requests--;
|
|
|
|
slot->in_use = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
2007-09-11 11:02:28 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We know there must be something to do, since we just added
|
|
|
|
* something.
|
|
|
|
*/
|
|
|
|
curl_multi_perform(curlm, &num_transfers);
|
2005-11-19 03:02:58 +08:00
|
|
|
#endif
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef USE_CURL_MULTI
|
2007-09-11 11:02:34 +08:00
|
|
|
struct fill_chain {
|
|
|
|
void *data;
|
|
|
|
int (*fill)(void *);
|
|
|
|
struct fill_chain *next;
|
|
|
|
};
|
|
|
|
|
2009-03-10 09:47:29 +08:00
|
|
|
static struct fill_chain *fill_cfg;
|
2007-09-11 11:02:34 +08:00
|
|
|
|
|
|
|
void add_fill_function(void *data, int (*fill)(void *))
|
|
|
|
{
|
2008-09-10 02:57:10 +08:00
|
|
|
struct fill_chain *new = xmalloc(sizeof(*new));
|
2007-09-11 11:02:34 +08:00
|
|
|
struct fill_chain **linkp = &fill_cfg;
|
|
|
|
new->data = data;
|
|
|
|
new->fill = fill;
|
|
|
|
new->next = NULL;
|
|
|
|
while (*linkp)
|
|
|
|
linkp = &(*linkp)->next;
|
|
|
|
*linkp = new;
|
|
|
|
}
|
|
|
|
|
2007-09-11 11:02:28 +08:00
|
|
|
void fill_active_slots(void)
|
|
|
|
{
|
|
|
|
struct active_request_slot *slot = active_queue_head;
|
|
|
|
|
2007-09-11 11:02:34 +08:00
|
|
|
while (active_requests < max_requests) {
|
|
|
|
struct fill_chain *fill;
|
|
|
|
for (fill = fill_cfg; fill; fill = fill->next)
|
|
|
|
if (fill->fill(fill->data))
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (!fill)
|
2007-09-11 11:02:28 +08:00
|
|
|
break;
|
2007-09-11 11:02:34 +08:00
|
|
|
}
|
2007-09-11 11:02:28 +08:00
|
|
|
|
|
|
|
while (slot != NULL) {
|
|
|
|
if (!slot->in_use && slot->curl != NULL) {
|
|
|
|
curl_easy_cleanup(slot->curl);
|
|
|
|
slot->curl = NULL;
|
|
|
|
}
|
|
|
|
slot = slot->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-11-19 03:02:58 +08:00
|
|
|
void step_active_slots(void)
|
|
|
|
{
|
|
|
|
int num_transfers;
|
|
|
|
CURLMcode curlm_result;
|
|
|
|
|
|
|
|
do {
|
|
|
|
curlm_result = curl_multi_perform(curlm, &num_transfers);
|
|
|
|
} while (curlm_result == CURLM_CALL_MULTI_PERFORM);
|
|
|
|
if (num_transfers < active_requests) {
|
|
|
|
process_curl_messages();
|
|
|
|
fill_active_slots();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
void run_active_slot(struct active_request_slot *slot)
|
|
|
|
{
|
|
|
|
#ifdef USE_CURL_MULTI
|
|
|
|
long last_pos = 0;
|
|
|
|
long current_pos;
|
|
|
|
fd_set readfds;
|
|
|
|
fd_set writefds;
|
|
|
|
fd_set excfds;
|
|
|
|
int max_fd;
|
|
|
|
struct timeval select_timeout;
|
2006-03-11 12:18:01 +08:00
|
|
|
int finished = 0;
|
2005-11-19 03:02:58 +08:00
|
|
|
|
2006-03-11 12:18:01 +08:00
|
|
|
slot->finished = &finished;
|
|
|
|
while (!finished) {
|
2005-11-19 03:02:58 +08:00
|
|
|
data_received = 0;
|
|
|
|
step_active_slots();
|
|
|
|
|
|
|
|
if (!data_received && slot->local != NULL) {
|
|
|
|
current_pos = ftell(slot->local);
|
|
|
|
if (current_pos > last_pos)
|
|
|
|
data_received++;
|
|
|
|
last_pos = current_pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (slot->in_use && !data_received) {
|
|
|
|
max_fd = 0;
|
|
|
|
FD_ZERO(&readfds);
|
|
|
|
FD_ZERO(&writefds);
|
|
|
|
FD_ZERO(&excfds);
|
|
|
|
select_timeout.tv_sec = 0;
|
|
|
|
select_timeout.tv_usec = 50000;
|
|
|
|
select(max_fd, &readfds, &writefds,
|
|
|
|
&excfds, &select_timeout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
while (slot->in_use) {
|
|
|
|
slot->curl_result = curl_easy_perform(slot->curl);
|
|
|
|
finish_active_slot(slot);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2006-02-07 18:07:39 +08:00
|
|
|
static void closedown_active_slot(struct active_request_slot *slot)
|
2005-11-19 03:02:58 +08:00
|
|
|
{
|
2007-12-10 03:30:59 +08:00
|
|
|
active_requests--;
|
|
|
|
slot->in_use = 0;
|
2006-02-07 18:07:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void release_active_slot(struct active_request_slot *slot)
|
|
|
|
{
|
|
|
|
closedown_active_slot(slot);
|
|
|
|
if (slot->curl) {
|
2006-06-07 00:41:32 +08:00
|
|
|
#ifdef USE_CURL_MULTI
|
2006-02-07 18:07:39 +08:00
|
|
|
curl_multi_remove_handle(curlm, slot->curl);
|
2006-06-07 00:41:32 +08:00
|
|
|
#endif
|
2006-02-07 18:07:39 +08:00
|
|
|
curl_easy_cleanup(slot->curl);
|
|
|
|
slot->curl = NULL;
|
|
|
|
}
|
2006-06-07 00:41:32 +08:00
|
|
|
#ifdef USE_CURL_MULTI
|
2006-02-07 18:07:39 +08:00
|
|
|
fill_active_slots();
|
2006-06-07 00:41:32 +08:00
|
|
|
#endif
|
2006-02-07 18:07:39 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void finish_active_slot(struct active_request_slot *slot)
|
|
|
|
{
|
|
|
|
closedown_active_slot(slot);
|
2007-12-10 03:30:59 +08:00
|
|
|
curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
|
2006-02-01 03:06:55 +08:00
|
|
|
|
2006-03-11 12:18:01 +08:00
|
|
|
if (slot->finished != NULL)
|
|
|
|
(*slot->finished) = 1;
|
|
|
|
|
2006-02-01 03:06:55 +08:00
|
|
|
/* Store slot results so they can be read after the slot is reused */
|
|
|
|
if (slot->results != NULL) {
|
|
|
|
slot->results->curl_result = slot->curl_result;
|
|
|
|
slot->results->http_code = slot->http_code;
|
|
|
|
}
|
|
|
|
|
2007-12-10 03:30:59 +08:00
|
|
|
/* Run callback if appropriate */
|
2009-03-10 09:47:29 +08:00
|
|
|
if (slot->callback_func != NULL)
|
2007-12-10 03:30:59 +08:00
|
|
|
slot->callback_func(slot->callback_data);
|
2005-11-19 03:02:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void finish_all_active_slots(void)
|
|
|
|
{
|
|
|
|
struct active_request_slot *slot = active_queue_head;
|
|
|
|
|
|
|
|
while (slot != NULL)
|
|
|
|
if (slot->in_use) {
|
|
|
|
run_active_slot(slot);
|
|
|
|
slot = active_queue_head;
|
|
|
|
} else {
|
|
|
|
slot = slot->next;
|
|
|
|
}
|
|
|
|
}
|
2007-12-11 07:08:25 +08:00
|
|
|
|
2009-06-06 16:43:43 +08:00
|
|
|
/* Helpers for modifying and creating URLs */
|
2007-12-11 07:08:25 +08:00
|
|
|
static inline int needs_quote(int ch)
|
|
|
|
{
|
|
|
|
if (((ch >= 'A') && (ch <= 'Z'))
|
|
|
|
|| ((ch >= 'a') && (ch <= 'z'))
|
|
|
|
|| ((ch >= '0') && (ch <= '9'))
|
|
|
|
|| (ch == '/')
|
|
|
|
|| (ch == '-')
|
|
|
|
|| (ch == '.'))
|
|
|
|
return 0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int hex(int v)
|
|
|
|
{
|
2009-03-10 09:47:29 +08:00
|
|
|
if (v < 10)
|
|
|
|
return '0' + v;
|
|
|
|
else
|
|
|
|
return 'A' + v - 10;
|
2007-12-11 07:08:25 +08:00
|
|
|
}
|
|
|
|
|
2009-06-06 16:43:43 +08:00
|
|
|
static void end_url_with_slash(struct strbuf *buf, const char *url)
|
|
|
|
{
|
|
|
|
strbuf_addstr(buf, url);
|
|
|
|
if (buf->len && buf->buf[buf->len - 1] != '/')
|
|
|
|
strbuf_addstr(buf, "/");
|
|
|
|
}
|
|
|
|
|
2007-12-11 07:08:25 +08:00
|
|
|
static char *quote_ref_url(const char *base, const char *ref)
|
|
|
|
{
|
2009-03-08 00:47:21 +08:00
|
|
|
struct strbuf buf = STRBUF_INIT;
|
2007-12-11 07:08:25 +08:00
|
|
|
const char *cp;
|
2009-03-08 00:47:21 +08:00
|
|
|
int ch;
|
2007-12-11 07:08:25 +08:00
|
|
|
|
2009-06-06 16:43:43 +08:00
|
|
|
end_url_with_slash(&buf, base);
|
2009-03-08 00:47:21 +08:00
|
|
|
|
|
|
|
for (cp = ref; (ch = *cp) != 0; cp++)
|
2007-12-11 07:08:25 +08:00
|
|
|
if (needs_quote(ch))
|
2009-03-08 00:47:21 +08:00
|
|
|
strbuf_addf(&buf, "%%%02x", ch);
|
2007-12-11 07:08:25 +08:00
|
|
|
else
|
2009-03-08 00:47:21 +08:00
|
|
|
strbuf_addch(&buf, *cp);
|
2007-12-11 07:08:25 +08:00
|
|
|
|
2009-03-08 00:47:21 +08:00
|
|
|
return strbuf_detach(&buf, NULL);
|
2007-12-11 07:08:25 +08:00
|
|
|
}
|
|
|
|
|
http*: add helper methods for fetching objects (loose)
The code handling the fetching of loose objects in http-push.c and
http-walker.c have been refactored into new methods and a new struct
(object_http_request) in http.c. They are not meant to be invoked
elsewhere.
The new methods in http.c are
- new_http_object_request
- process_http_object_request
- finish_http_object_request
- abort_http_object_request
- release_http_object_request
and the new struct is http_object_request.
RANGER_HEADER_SIZE and no_pragma_header is no longer made available
outside of http.c, since after the above changes, there are no other
instances of usage outside of http.c.
Remove members of the transfer_request struct in http-push.c and
http-walker.c, including filename, real_sha1 and zret, as they are used
no longer used.
Move the methods append_remote_object_url() and get_remote_object_url()
from http-push.c to http.c. Additionally, get_remote_object_url() is no
longer defined only when USE_CURL_MULTI is defined, since
non-USE_CURL_MULTI code in http.c uses it (namely, in
new_http_object_request()).
Refactor code from http-push.c::start_fetch_loose() and
http-walker.c::start_object_fetch_request() that deals with the details
of coming up with the filename to store the retrieved object, resuming
a previously aborted request, and making a new curl request, into a new
function, new_http_object_request().
Refactor code from http-walker.c::process_object_request() into the
function, process_http_object_request().
Refactor code from http-push.c::finish_request() and
http-walker.c::finish_object_request() into a new function,
finish_http_object_request(). It returns the result of the
move_temp_to_file() invocation.
Add a function, release_http_object_request(), which cleans up object
request data. http-push.c and http-walker.c invoke this function
separately; http-push.c::release_request() and
http-walker.c::release_object_request() do not invoke this function.
Add a function, abort_http_object_request(), which unlink()s the object
file and invokes release_http_object_request(). Update
http-walker.c::abort_object_request() to use this.
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-06-06 16:44:02 +08:00
|
|
|
void append_remote_object_url(struct strbuf *buf, const char *url,
|
|
|
|
const char *hex,
|
|
|
|
int only_two_digit_prefix)
|
|
|
|
{
|
2009-08-17 17:09:43 +08:00
|
|
|
end_url_with_slash(buf, url);
|
|
|
|
|
|
|
|
strbuf_addf(buf, "objects/%.*s/", 2, hex);
|
http*: add helper methods for fetching objects (loose)
The code handling the fetching of loose objects in http-push.c and
http-walker.c have been refactored into new methods and a new struct
(object_http_request) in http.c. They are not meant to be invoked
elsewhere.
The new methods in http.c are
- new_http_object_request
- process_http_object_request
- finish_http_object_request
- abort_http_object_request
- release_http_object_request
and the new struct is http_object_request.
RANGER_HEADER_SIZE and no_pragma_header is no longer made available
outside of http.c, since after the above changes, there are no other
instances of usage outside of http.c.
Remove members of the transfer_request struct in http-push.c and
http-walker.c, including filename, real_sha1 and zret, as they are used
no longer used.
Move the methods append_remote_object_url() and get_remote_object_url()
from http-push.c to http.c. Additionally, get_remote_object_url() is no
longer defined only when USE_CURL_MULTI is defined, since
non-USE_CURL_MULTI code in http.c uses it (namely, in
new_http_object_request()).
Refactor code from http-push.c::start_fetch_loose() and
http-walker.c::start_object_fetch_request() that deals with the details
of coming up with the filename to store the retrieved object, resuming
a previously aborted request, and making a new curl request, into a new
function, new_http_object_request().
Refactor code from http-walker.c::process_object_request() into the
function, process_http_object_request().
Refactor code from http-push.c::finish_request() and
http-walker.c::finish_object_request() into a new function,
finish_http_object_request(). It returns the result of the
move_temp_to_file() invocation.
Add a function, release_http_object_request(), which cleans up object
request data. http-push.c and http-walker.c invoke this function
separately; http-push.c::release_request() and
http-walker.c::release_object_request() do not invoke this function.
Add a function, abort_http_object_request(), which unlink()s the object
file and invokes release_http_object_request(). Update
http-walker.c::abort_object_request() to use this.
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-06-06 16:44:02 +08:00
|
|
|
if (!only_two_digit_prefix)
|
|
|
|
strbuf_addf(buf, "%s", hex+2);
|
|
|
|
}
|
|
|
|
|
|
|
|
char *get_remote_object_url(const char *url, const char *hex,
|
|
|
|
int only_two_digit_prefix)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
append_remote_object_url(&buf, url, hex, only_two_digit_prefix);
|
|
|
|
return strbuf_detach(&buf, NULL);
|
|
|
|
}
|
|
|
|
|
2009-06-06 16:43:53 +08:00
|
|
|
/* http_request() targets */
|
|
|
|
#define HTTP_REQUEST_STRBUF 0
|
|
|
|
#define HTTP_REQUEST_FILE 1
|
|
|
|
|
|
|
|
static int http_request(const char *url, void *result, int target, int options)
|
|
|
|
{
|
|
|
|
struct active_request_slot *slot;
|
|
|
|
struct slot_results results;
|
|
|
|
struct curl_slist *headers = NULL;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
slot = get_active_slot();
|
|
|
|
slot->results = &results;
|
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
|
|
|
|
|
|
|
|
if (result == NULL) {
|
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);
|
|
|
|
} else {
|
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
|
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_FILE, result);
|
|
|
|
|
|
|
|
if (target == HTTP_REQUEST_FILE) {
|
|
|
|
long posn = ftell(result);
|
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
|
|
|
|
fwrite);
|
|
|
|
if (posn > 0) {
|
|
|
|
strbuf_addf(&buf, "Range: bytes=%ld-", posn);
|
|
|
|
headers = curl_slist_append(headers, buf.buf);
|
|
|
|
strbuf_reset(&buf);
|
|
|
|
}
|
|
|
|
slot->local = result;
|
|
|
|
} else
|
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,
|
|
|
|
fwrite_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_addstr(&buf, "Pragma:");
|
|
|
|
if (options & HTTP_NO_CACHE)
|
|
|
|
strbuf_addstr(&buf, " no-cache");
|
|
|
|
|
|
|
|
headers = curl_slist_append(headers, buf.buf);
|
|
|
|
|
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_URL, url);
|
|
|
|
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
|
|
|
|
|
|
|
|
if (start_active_slot(slot)) {
|
|
|
|
run_active_slot(slot);
|
|
|
|
if (results.curl_result == CURLE_OK)
|
|
|
|
ret = HTTP_OK;
|
|
|
|
else if (missing_target(&results))
|
|
|
|
ret = HTTP_MISSING_TARGET;
|
|
|
|
else
|
|
|
|
ret = HTTP_ERROR;
|
|
|
|
} else {
|
|
|
|
error("Unable to start HTTP request for %s", url);
|
|
|
|
ret = HTTP_START_FAILED;
|
|
|
|
}
|
|
|
|
|
|
|
|
slot->local = NULL;
|
|
|
|
curl_slist_free_all(headers);
|
|
|
|
strbuf_release(&buf);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int http_get_strbuf(const char *url, struct strbuf *result, int options)
|
|
|
|
{
|
|
|
|
return http_request(url, result, HTTP_REQUEST_STRBUF, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
int http_get_file(const char *url, const char *filename, int options)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct strbuf tmpfile = STRBUF_INIT;
|
|
|
|
FILE *result;
|
|
|
|
|
|
|
|
strbuf_addf(&tmpfile, "%s.temp", filename);
|
|
|
|
result = fopen(tmpfile.buf, "a");
|
|
|
|
if (! result) {
|
|
|
|
error("Unable to open local file %s", tmpfile.buf);
|
|
|
|
ret = HTTP_ERROR;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = http_request(url, result, HTTP_REQUEST_FILE, options);
|
|
|
|
fclose(result);
|
|
|
|
|
|
|
|
if ((ret == HTTP_OK) && move_temp_to_file(tmpfile.buf, filename))
|
|
|
|
ret = HTTP_ERROR;
|
|
|
|
cleanup:
|
|
|
|
strbuf_release(&tmpfile);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int http_error(const char *url, int ret)
|
|
|
|
{
|
|
|
|
/* http_request has already handled HTTP_START_FAILED. */
|
|
|
|
if (ret != HTTP_START_FAILED)
|
|
|
|
error("%s while accessing %s\n", curl_errorstr, url);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
Make walker.fetch_ref() take a struct ref.
This simplifies a few things, makes a few things slightly more
complicated, but, more importantly, allows that, when struct ref can
represent a symref, http_fetch_ref() can return one.
Incidentally makes the string that http_fetch_ref() gets include "refs/"
(if appropriate), because that's how the name field of struct ref works.
As far as I can tell, the usage in walker:interpret_target() wouldn't have
worked previously, if it ever would have been used, which it wouldn't
(since the fetch process uses the hash instead of the name of the ref
there).
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-04-27 03:53:09 +08:00
|
|
|
int http_fetch_ref(const char *base, struct ref *ref)
|
2007-12-11 07:08:25 +08:00
|
|
|
{
|
|
|
|
char *url;
|
|
|
|
struct strbuf buffer = STRBUF_INIT;
|
2009-06-06 16:43:55 +08:00
|
|
|
int ret = -1;
|
2007-12-11 07:08:25 +08:00
|
|
|
|
Make walker.fetch_ref() take a struct ref.
This simplifies a few things, makes a few things slightly more
complicated, but, more importantly, allows that, when struct ref can
represent a symref, http_fetch_ref() can return one.
Incidentally makes the string that http_fetch_ref() gets include "refs/"
(if appropriate), because that's how the name field of struct ref works.
As far as I can tell, the usage in walker:interpret_target() wouldn't have
worked previously, if it ever would have been used, which it wouldn't
(since the fetch process uses the hash instead of the name of the ref
there).
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2008-04-27 03:53:09 +08:00
|
|
|
url = quote_ref_url(base, ref->name);
|
2009-06-06 16:43:55 +08:00
|
|
|
if (http_get_strbuf(url, &buffer, HTTP_NO_CACHE) == HTTP_OK) {
|
|
|
|
strbuf_rtrim(&buffer);
|
|
|
|
if (buffer.len == 40)
|
|
|
|
ret = get_sha1_hex(buffer.buf, ref->old_sha1);
|
|
|
|
else if (!prefixcmp(buffer.buf, "ref: ")) {
|
|
|
|
ref->symref = xstrdup(buffer.buf + 5);
|
|
|
|
ret = 0;
|
2007-12-11 07:08:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
strbuf_release(&buffer);
|
|
|
|
free(url);
|
|
|
|
return ret;
|
|
|
|
}
|
2009-06-06 16:43:59 +08:00
|
|
|
|
|
|
|
/* Helpers for fetching packs */
|
|
|
|
static int fetch_pack_index(unsigned char *sha1, const char *base_url)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
char *hex = xstrdup(sha1_to_hex(sha1));
|
|
|
|
char *filename;
|
|
|
|
char *url;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
|
|
|
|
/* Don't use the index if the pack isn't there */
|
|
|
|
end_url_with_slash(&buf, base_url);
|
|
|
|
strbuf_addf(&buf, "objects/pack/pack-%s.pack", hex);
|
|
|
|
url = strbuf_detach(&buf, 0);
|
|
|
|
|
2009-06-06 16:44:00 +08:00
|
|
|
if (http_get_strbuf(url, NULL, 0)) {
|
|
|
|
ret = error("Unable to verify pack %s is available",
|
|
|
|
hex);
|
|
|
|
goto cleanup;
|
2009-06-06 16:43:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (has_pack_index(sha1)) {
|
|
|
|
ret = 0;
|
2009-06-06 16:44:00 +08:00
|
|
|
goto cleanup;
|
2009-06-06 16:43:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (http_is_verbose)
|
|
|
|
fprintf(stderr, "Getting index for pack %s\n", hex);
|
|
|
|
|
|
|
|
end_url_with_slash(&buf, base_url);
|
|
|
|
strbuf_addf(&buf, "objects/pack/pack-%s.idx", hex);
|
|
|
|
url = strbuf_detach(&buf, NULL);
|
|
|
|
|
|
|
|
filename = sha1_pack_index_name(sha1);
|
2009-06-06 16:44:00 +08:00
|
|
|
if (http_get_file(url, filename, 0) != HTTP_OK)
|
|
|
|
ret = error("Unable to get pack index %s\n", url);
|
2009-06-06 16:43:59 +08:00
|
|
|
|
2009-06-06 16:44:00 +08:00
|
|
|
cleanup:
|
2009-06-06 16:43:59 +08:00
|
|
|
free(hex);
|
|
|
|
free(url);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fetch_and_setup_pack_index(struct packed_git **packs_head,
|
|
|
|
unsigned char *sha1, const char *base_url)
|
|
|
|
{
|
|
|
|
struct packed_git *new_pack;
|
|
|
|
|
|
|
|
if (fetch_pack_index(sha1, base_url))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
new_pack = parse_pack_index(sha1);
|
|
|
|
if (!new_pack)
|
|
|
|
return -1; /* parse_pack_index() already issued error message */
|
|
|
|
new_pack->next = *packs_head;
|
|
|
|
*packs_head = new_pack;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
|
|
|
|
{
|
|
|
|
int ret = 0, i = 0;
|
|
|
|
char *url, *data;
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
unsigned char sha1[20];
|
|
|
|
|
|
|
|
end_url_with_slash(&buf, base_url);
|
|
|
|
strbuf_addstr(&buf, "objects/info/packs");
|
|
|
|
url = strbuf_detach(&buf, NULL);
|
|
|
|
|
|
|
|
ret = http_get_strbuf(url, &buf, HTTP_NO_CACHE);
|
|
|
|
if (ret != HTTP_OK)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
data = buf.buf;
|
|
|
|
while (i < buf.len) {
|
|
|
|
switch (data[i]) {
|
|
|
|
case 'P':
|
|
|
|
i++;
|
|
|
|
if (i + 52 <= buf.len &&
|
|
|
|
!prefixcmp(data + i, " pack-") &&
|
|
|
|
!prefixcmp(data + i + 46, ".pack\n")) {
|
|
|
|
get_sha1_hex(data + i + 6, sha1);
|
|
|
|
fetch_and_setup_pack_index(packs_head, sha1,
|
|
|
|
base_url);
|
|
|
|
i += 51;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
while (i < buf.len && data[i] != '\n')
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
free(url);
|
|
|
|
return ret;
|
|
|
|
}
|
2009-06-06 16:44:01 +08:00
|
|
|
|
|
|
|
void release_http_pack_request(struct http_pack_request *preq)
|
|
|
|
{
|
|
|
|
if (preq->packfile != NULL) {
|
|
|
|
fclose(preq->packfile);
|
|
|
|
preq->packfile = NULL;
|
|
|
|
preq->slot->local = NULL;
|
|
|
|
}
|
|
|
|
if (preq->range_header != NULL) {
|
|
|
|
curl_slist_free_all(preq->range_header);
|
|
|
|
preq->range_header = NULL;
|
|
|
|
}
|
|
|
|
preq->slot = NULL;
|
|
|
|
free(preq->url);
|
|
|
|
}
|
|
|
|
|
|
|
|
int finish_http_pack_request(struct http_pack_request *preq)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct packed_git **lst;
|
|
|
|
|
|
|
|
preq->target->pack_size = ftell(preq->packfile);
|
|
|
|
|
|
|
|
if (preq->packfile != NULL) {
|
|
|
|
fclose(preq->packfile);
|
|
|
|
preq->packfile = NULL;
|
|
|
|
preq->slot->local = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = move_temp_to_file(preq->tmpfile, preq->filename);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
lst = preq->lst;
|
|
|
|
while (*lst != preq->target)
|
|
|
|
lst = &((*lst)->next);
|
|
|
|
*lst = (*lst)->next;
|
|
|
|
|
|
|
|
if (verify_pack(preq->target))
|
|
|
|
return -1;
|
|
|
|
install_packed_git(preq->target);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct http_pack_request *new_http_pack_request(
|
|
|
|
struct packed_git *target, const char *base_url)
|
|
|
|
{
|
|
|
|
char *url;
|
|
|
|
char *filename;
|
|
|
|
long prev_posn = 0;
|
|
|
|
char range[RANGE_HEADER_SIZE];
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
struct http_pack_request *preq;
|
|
|
|
|
|
|
|
preq = xmalloc(sizeof(*preq));
|
|
|
|
preq->target = target;
|
|
|
|
preq->range_header = NULL;
|
|
|
|
|
|
|
|
end_url_with_slash(&buf, base_url);
|
|
|
|
strbuf_addf(&buf, "objects/pack/pack-%s.pack",
|
|
|
|
sha1_to_hex(target->sha1));
|
|
|
|
url = strbuf_detach(&buf, NULL);
|
|
|
|
preq->url = xstrdup(url);
|
|
|
|
|
|
|
|
filename = sha1_pack_name(target->sha1);
|
|
|
|
snprintf(preq->filename, sizeof(preq->filename), "%s", filename);
|
|
|
|
snprintf(preq->tmpfile, sizeof(preq->tmpfile), "%s.temp", filename);
|
|
|
|
preq->packfile = fopen(preq->tmpfile, "a");
|
|
|
|
if (!preq->packfile) {
|
|
|
|
error("Unable to open local file %s for pack",
|
|
|
|
preq->tmpfile);
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
|
|
|
|
preq->slot = get_active_slot();
|
|
|
|
preq->slot->local = preq->packfile;
|
|
|
|
curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile);
|
|
|
|
curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
|
|
|
|
curl_easy_setopt(preq->slot->curl, CURLOPT_URL, url);
|
|
|
|
curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
|
|
|
|
no_pragma_header);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there is data present from a previous transfer attempt,
|
|
|
|
* resume where it left off
|
|
|
|
*/
|
|
|
|
prev_posn = ftell(preq->packfile);
|
|
|
|
if (prev_posn>0) {
|
|
|
|
if (http_is_verbose)
|
|
|
|
fprintf(stderr,
|
|
|
|
"Resuming fetch of pack %s at byte %ld\n",
|
|
|
|
sha1_to_hex(target->sha1), prev_posn);
|
|
|
|
sprintf(range, "Range: bytes=%ld-", prev_posn);
|
|
|
|
preq->range_header = curl_slist_append(NULL, range);
|
|
|
|
curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
|
|
|
|
preq->range_header);
|
|
|
|
}
|
|
|
|
|
|
|
|
return preq;
|
|
|
|
|
|
|
|
abort:
|
|
|
|
free(filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
http*: add helper methods for fetching objects (loose)
The code handling the fetching of loose objects in http-push.c and
http-walker.c have been refactored into new methods and a new struct
(object_http_request) in http.c. They are not meant to be invoked
elsewhere.
The new methods in http.c are
- new_http_object_request
- process_http_object_request
- finish_http_object_request
- abort_http_object_request
- release_http_object_request
and the new struct is http_object_request.
RANGER_HEADER_SIZE and no_pragma_header is no longer made available
outside of http.c, since after the above changes, there are no other
instances of usage outside of http.c.
Remove members of the transfer_request struct in http-push.c and
http-walker.c, including filename, real_sha1 and zret, as they are used
no longer used.
Move the methods append_remote_object_url() and get_remote_object_url()
from http-push.c to http.c. Additionally, get_remote_object_url() is no
longer defined only when USE_CURL_MULTI is defined, since
non-USE_CURL_MULTI code in http.c uses it (namely, in
new_http_object_request()).
Refactor code from http-push.c::start_fetch_loose() and
http-walker.c::start_object_fetch_request() that deals with the details
of coming up with the filename to store the retrieved object, resuming
a previously aborted request, and making a new curl request, into a new
function, new_http_object_request().
Refactor code from http-walker.c::process_object_request() into the
function, process_http_object_request().
Refactor code from http-push.c::finish_request() and
http-walker.c::finish_object_request() into a new function,
finish_http_object_request(). It returns the result of the
move_temp_to_file() invocation.
Add a function, release_http_object_request(), which cleans up object
request data. http-push.c and http-walker.c invoke this function
separately; http-push.c::release_request() and
http-walker.c::release_object_request() do not invoke this function.
Add a function, abort_http_object_request(), which unlink()s the object
file and invokes release_http_object_request(). Update
http-walker.c::abort_object_request() to use this.
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-06-06 16:44:02 +08:00
|
|
|
|
|
|
|
/* Helpers for fetching objects (loose) */
|
|
|
|
static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
unsigned char expn[4096];
|
|
|
|
size_t size = eltsize * nmemb;
|
|
|
|
int posn = 0;
|
|
|
|
struct http_object_request *freq =
|
|
|
|
(struct http_object_request *)data;
|
|
|
|
do {
|
|
|
|
ssize_t retval = xwrite(freq->localfile,
|
|
|
|
(char *) ptr + posn, size - posn);
|
|
|
|
if (retval < 0)
|
|
|
|
return posn;
|
|
|
|
posn += retval;
|
|
|
|
} while (posn < size);
|
|
|
|
|
|
|
|
freq->stream.avail_in = size;
|
|
|
|
freq->stream.next_in = ptr;
|
|
|
|
do {
|
|
|
|
freq->stream.next_out = expn;
|
|
|
|
freq->stream.avail_out = sizeof(expn);
|
|
|
|
freq->zret = git_inflate(&freq->stream, Z_SYNC_FLUSH);
|
|
|
|
git_SHA1_Update(&freq->c, expn,
|
|
|
|
sizeof(expn) - freq->stream.avail_out);
|
|
|
|
} while (freq->stream.avail_in && freq->zret == Z_OK);
|
|
|
|
data_received++;
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct http_object_request *new_http_object_request(const char *base_url,
|
|
|
|
unsigned char *sha1)
|
|
|
|
{
|
|
|
|
char *hex = sha1_to_hex(sha1);
|
|
|
|
char *filename;
|
|
|
|
char prevfile[PATH_MAX];
|
|
|
|
char *url;
|
|
|
|
int prevlocal;
|
|
|
|
unsigned char prev_buf[PREV_BUF_SIZE];
|
|
|
|
ssize_t prev_read = 0;
|
|
|
|
long prev_posn = 0;
|
|
|
|
char range[RANGE_HEADER_SIZE];
|
|
|
|
struct curl_slist *range_header = NULL;
|
|
|
|
struct http_object_request *freq;
|
|
|
|
|
|
|
|
freq = xmalloc(sizeof(*freq));
|
|
|
|
hashcpy(freq->sha1, sha1);
|
|
|
|
freq->localfile = -1;
|
|
|
|
|
|
|
|
filename = sha1_file_name(sha1);
|
|
|
|
snprintf(freq->filename, sizeof(freq->filename), "%s", filename);
|
|
|
|
snprintf(freq->tmpfile, sizeof(freq->tmpfile),
|
|
|
|
"%s.temp", filename);
|
|
|
|
|
|
|
|
snprintf(prevfile, sizeof(prevfile), "%s.prev", filename);
|
|
|
|
unlink_or_warn(prevfile);
|
|
|
|
rename(freq->tmpfile, prevfile);
|
|
|
|
unlink_or_warn(freq->tmpfile);
|
|
|
|
|
|
|
|
if (freq->localfile != -1)
|
|
|
|
error("fd leakage in start: %d", freq->localfile);
|
|
|
|
freq->localfile = open(freq->tmpfile,
|
|
|
|
O_WRONLY | O_CREAT | O_EXCL, 0666);
|
|
|
|
/*
|
|
|
|
* This could have failed due to the "lazy directory creation";
|
|
|
|
* try to mkdir the last path component.
|
|
|
|
*/
|
|
|
|
if (freq->localfile < 0 && errno == ENOENT) {
|
|
|
|
char *dir = strrchr(freq->tmpfile, '/');
|
|
|
|
if (dir) {
|
|
|
|
*dir = 0;
|
|
|
|
mkdir(freq->tmpfile, 0777);
|
|
|
|
*dir = '/';
|
|
|
|
}
|
|
|
|
freq->localfile = open(freq->tmpfile,
|
|
|
|
O_WRONLY | O_CREAT | O_EXCL, 0666);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (freq->localfile < 0) {
|
|
|
|
error("Couldn't create temporary file %s for %s: %s",
|
|
|
|
freq->tmpfile, freq->filename, strerror(errno));
|
|
|
|
goto abort;
|
|
|
|
}
|
|
|
|
|
|
|
|
memset(&freq->stream, 0, sizeof(freq->stream));
|
|
|
|
|
|
|
|
git_inflate_init(&freq->stream);
|
|
|
|
|
|
|
|
git_SHA1_Init(&freq->c);
|
|
|
|
|
|
|
|
url = get_remote_object_url(base_url, hex, 0);
|
|
|
|
freq->url = xstrdup(url);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If a previous temp file is present, process what was already
|
|
|
|
* fetched.
|
|
|
|
*/
|
|
|
|
prevlocal = open(prevfile, O_RDONLY);
|
|
|
|
if (prevlocal != -1) {
|
|
|
|
do {
|
|
|
|
prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
|
|
|
|
if (prev_read>0) {
|
|
|
|
if (fwrite_sha1_file(prev_buf,
|
|
|
|
1,
|
|
|
|
prev_read,
|
|
|
|
freq) == prev_read) {
|
|
|
|
prev_posn += prev_read;
|
|
|
|
} else {
|
|
|
|
prev_read = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (prev_read > 0);
|
|
|
|
close(prevlocal);
|
|
|
|
}
|
|
|
|
unlink_or_warn(prevfile);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Reset inflate/SHA1 if there was an error reading the previous temp
|
|
|
|
* file; also rewind to the beginning of the local file.
|
|
|
|
*/
|
|
|
|
if (prev_read == -1) {
|
|
|
|
memset(&freq->stream, 0, sizeof(freq->stream));
|
|
|
|
git_inflate_init(&freq->stream);
|
|
|
|
git_SHA1_Init(&freq->c);
|
|
|
|
if (prev_posn>0) {
|
|
|
|
prev_posn = 0;
|
|
|
|
lseek(freq->localfile, 0, SEEK_SET);
|
|
|
|
ftruncate(freq->localfile, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
freq->slot = get_active_slot();
|
|
|
|
|
|
|
|
curl_easy_setopt(freq->slot->curl, CURLOPT_FILE, freq);
|
|
|
|
curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
|
|
|
|
curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
|
|
|
|
curl_easy_setopt(freq->slot->curl, CURLOPT_URL, url);
|
|
|
|
curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we have successfully processed data from a previous fetch
|
|
|
|
* attempt, only fetch the data we don't already have.
|
|
|
|
*/
|
|
|
|
if (prev_posn>0) {
|
|
|
|
if (http_is_verbose)
|
|
|
|
fprintf(stderr,
|
|
|
|
"Resuming fetch of object %s at byte %ld\n",
|
|
|
|
hex, prev_posn);
|
|
|
|
sprintf(range, "Range: bytes=%ld-", prev_posn);
|
|
|
|
range_header = curl_slist_append(range_header, range);
|
|
|
|
curl_easy_setopt(freq->slot->curl,
|
|
|
|
CURLOPT_HTTPHEADER, range_header);
|
|
|
|
}
|
|
|
|
|
|
|
|
return freq;
|
|
|
|
|
|
|
|
free(url);
|
|
|
|
abort:
|
|
|
|
free(filename);
|
|
|
|
free(freq);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void process_http_object_request(struct http_object_request *freq)
|
|
|
|
{
|
|
|
|
if (freq->slot == NULL)
|
|
|
|
return;
|
|
|
|
freq->curl_result = freq->slot->curl_result;
|
|
|
|
freq->http_code = freq->slot->http_code;
|
|
|
|
freq->slot = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int finish_http_object_request(struct http_object_request *freq)
|
|
|
|
{
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
close(freq->localfile);
|
|
|
|
freq->localfile = -1;
|
|
|
|
|
|
|
|
process_http_object_request(freq);
|
|
|
|
|
|
|
|
if (freq->http_code == 416) {
|
|
|
|
fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
|
|
|
|
} else if (freq->curl_result != CURLE_OK) {
|
|
|
|
if (stat(freq->tmpfile, &st) == 0)
|
|
|
|
if (st.st_size == 0)
|
|
|
|
unlink_or_warn(freq->tmpfile);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
git_inflate_end(&freq->stream);
|
|
|
|
git_SHA1_Final(freq->real_sha1, &freq->c);
|
|
|
|
if (freq->zret != Z_STREAM_END) {
|
|
|
|
unlink_or_warn(freq->tmpfile);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (hashcmp(freq->sha1, freq->real_sha1)) {
|
|
|
|
unlink_or_warn(freq->tmpfile);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
freq->rename =
|
|
|
|
move_temp_to_file(freq->tmpfile, freq->filename);
|
|
|
|
|
|
|
|
return freq->rename;
|
|
|
|
}
|
|
|
|
|
|
|
|
void abort_http_object_request(struct http_object_request *freq)
|
|
|
|
{
|
|
|
|
unlink_or_warn(freq->tmpfile);
|
|
|
|
|
|
|
|
release_http_object_request(freq);
|
|
|
|
}
|
|
|
|
|
|
|
|
void release_http_object_request(struct http_object_request *freq)
|
|
|
|
{
|
|
|
|
if (freq->localfile != -1) {
|
|
|
|
close(freq->localfile);
|
|
|
|
freq->localfile = -1;
|
|
|
|
}
|
|
|
|
if (freq->url != NULL) {
|
|
|
|
free(freq->url);
|
|
|
|
freq->url = NULL;
|
|
|
|
}
|
2009-08-26 20:20:53 +08:00
|
|
|
if (freq->slot != NULL) {
|
|
|
|
freq->slot->callback_func = NULL;
|
|
|
|
freq->slot->callback_data = NULL;
|
|
|
|
release_active_slot(freq->slot);
|
|
|
|
freq->slot = NULL;
|
|
|
|
}
|
http*: add helper methods for fetching objects (loose)
The code handling the fetching of loose objects in http-push.c and
http-walker.c have been refactored into new methods and a new struct
(object_http_request) in http.c. They are not meant to be invoked
elsewhere.
The new methods in http.c are
- new_http_object_request
- process_http_object_request
- finish_http_object_request
- abort_http_object_request
- release_http_object_request
and the new struct is http_object_request.
RANGER_HEADER_SIZE and no_pragma_header is no longer made available
outside of http.c, since after the above changes, there are no other
instances of usage outside of http.c.
Remove members of the transfer_request struct in http-push.c and
http-walker.c, including filename, real_sha1 and zret, as they are used
no longer used.
Move the methods append_remote_object_url() and get_remote_object_url()
from http-push.c to http.c. Additionally, get_remote_object_url() is no
longer defined only when USE_CURL_MULTI is defined, since
non-USE_CURL_MULTI code in http.c uses it (namely, in
new_http_object_request()).
Refactor code from http-push.c::start_fetch_loose() and
http-walker.c::start_object_fetch_request() that deals with the details
of coming up with the filename to store the retrieved object, resuming
a previously aborted request, and making a new curl request, into a new
function, new_http_object_request().
Refactor code from http-walker.c::process_object_request() into the
function, process_http_object_request().
Refactor code from http-push.c::finish_request() and
http-walker.c::finish_object_request() into a new function,
finish_http_object_request(). It returns the result of the
move_temp_to_file() invocation.
Add a function, release_http_object_request(), which cleans up object
request data. http-push.c and http-walker.c invoke this function
separately; http-push.c::release_request() and
http-walker.c::release_object_request() do not invoke this function.
Add a function, abort_http_object_request(), which unlink()s the object
file and invokes release_http_object_request(). Update
http-walker.c::abort_object_request() to use this.
Signed-off-by: Tay Ray Chuan <rctay89@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2009-06-06 16:44:02 +08:00
|
|
|
}
|