git
/
http.c
2840 строк · 75.1 Кб
1#define USE_THE_REPOSITORY_VARIABLE2
3#include "git-compat-util.h"4#include "git-curl-compat.h"5#include "hex.h"6#include "http.h"7#include "config.h"8#include "pack.h"9#include "run-command.h"10#include "url.h"11#include "urlmatch.h"12#include "credential.h"13#include "version.h"14#include "pkt-line.h"15#include "gettext.h"16#include "trace.h"17#include "transport.h"18#include "packfile.h"19#include "string-list.h"20#include "object-file.h"21#include "object-store-ll.h"22
23static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);24static int trace_curl_data = 1;25static int trace_curl_redact = 1;26long int git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;27int active_requests;28int http_is_verbose;29ssize_t http_post_buffer = 16 * LARGE_PACKET_MAX;30
31static int min_curl_sessions = 1;32static int curl_session_count;33static int max_requests = -1;34static CURLM *curlm;35static CURL *curl_default;36
37#define PREV_BUF_SIZE 409638
39char curl_errorstr[CURL_ERROR_SIZE];40
41static int curl_ssl_verify = -1;42static int curl_ssl_try;43static char *curl_http_version;44static char *ssl_cert;45static char *ssl_cert_type;46static char *ssl_cipherlist;47static char *ssl_version;48static struct {49const char *name;50long ssl_version;51} sslversions[] = {52{ "sslv2", CURL_SSLVERSION_SSLv2 },53{ "sslv3", CURL_SSLVERSION_SSLv3 },54{ "tlsv1", CURL_SSLVERSION_TLSv1 },55#ifdef GIT_CURL_HAVE_CURL_SSLVERSION_TLSv1_056{ "tlsv1.0", CURL_SSLVERSION_TLSv1_0 },57{ "tlsv1.1", CURL_SSLVERSION_TLSv1_1 },58{ "tlsv1.2", CURL_SSLVERSION_TLSv1_2 },59#endif60#ifdef GIT_CURL_HAVE_CURL_SSLVERSION_TLSv1_361{ "tlsv1.3", CURL_SSLVERSION_TLSv1_3 },62#endif63};64static char *ssl_key;65static char *ssl_key_type;66static char *ssl_capath;67static char *curl_no_proxy;68#ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY69static char *ssl_pinnedkey;70#endif71static char *ssl_cainfo;72static long curl_low_speed_limit = -1;73static long curl_low_speed_time = -1;74static int curl_ftp_no_epsv;75static char *curl_http_proxy;76static char *http_proxy_authmethod;77
78static char *http_proxy_ssl_cert;79static char *http_proxy_ssl_key;80static char *http_proxy_ssl_ca_info;81static struct credential proxy_cert_auth = CREDENTIAL_INIT;82static int proxy_ssl_cert_password_required;83
84static struct {85const char *name;86long curlauth_param;87} proxy_authmethods[] = {88{ "basic", CURLAUTH_BASIC },89{ "digest", CURLAUTH_DIGEST },90{ "negotiate", CURLAUTH_GSSNEGOTIATE },91{ "ntlm", CURLAUTH_NTLM },92{ "anyauth", CURLAUTH_ANY },93/*94* CURLAUTH_DIGEST_IE has no corresponding command-line option in
95* curl(1) and is not included in CURLAUTH_ANY, so we leave it out
96* here, too
97*/
98};99#ifdef CURLGSSAPI_DELEGATION_FLAG100static char *curl_deleg;101static struct {102const char *name;103long curl_deleg_param;104} curl_deleg_levels[] = {105{ "none", CURLGSSAPI_DELEGATION_NONE },106{ "policy", CURLGSSAPI_DELEGATION_POLICY_FLAG },107{ "always", CURLGSSAPI_DELEGATION_FLAG },108};109#endif110
111enum proactive_auth {112PROACTIVE_AUTH_NONE = 0,113PROACTIVE_AUTH_IF_CREDENTIALS,114PROACTIVE_AUTH_AUTO,115PROACTIVE_AUTH_BASIC,116};117
118static struct credential proxy_auth = CREDENTIAL_INIT;119static const char *curl_proxyuserpwd;120static char *curl_cookie_file;121static int curl_save_cookies;122struct credential http_auth = CREDENTIAL_INIT;123static enum proactive_auth http_proactive_auth;124static char *user_agent;125static int curl_empty_auth = -1;126
127enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL;128
129static struct credential cert_auth = CREDENTIAL_INIT;130static int ssl_cert_password_required;131static unsigned long http_auth_methods = CURLAUTH_ANY;132static int http_auth_methods_restricted;133/* Modes for which empty_auth cannot actually help us. */
134static unsigned long empty_auth_useless =135CURLAUTH_BASIC
136| CURLAUTH_DIGEST_IE137| CURLAUTH_DIGEST;138
139static struct curl_slist *pragma_header;140static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;141
142static struct curl_slist *host_resolutions;143
144static struct active_request_slot *active_queue_head;145
146static char *cached_accept_language;147
148static char *http_ssl_backend;149
150static int http_schannel_check_revoke = 1;151/*
152* With the backend being set to `schannel`, setting sslCAinfo would override
153* the Certificate Store in cURL v7.60.0 and later, which is not what we want
154* by default.
155*/
156static int http_schannel_use_ssl_cainfo;157
158static int always_auth_proactively(void)159{
160return http_proactive_auth != PROACTIVE_AUTH_NONE &&161http_proactive_auth != PROACTIVE_AUTH_IF_CREDENTIALS;162}
163
164size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)165{
166size_t size = eltsize * nmemb;167struct buffer *buffer = buffer_;168
169if (size > buffer->buf.len - buffer->posn)170size = buffer->buf.len - buffer->posn;171memcpy(ptr, buffer->buf.buf + buffer->posn, size);172buffer->posn += size;173
174return size / eltsize;175}
176
177int seek_buffer(void *clientp, curl_off_t offset, int origin)178{
179struct buffer *buffer = clientp;180
181if (origin != SEEK_SET)182BUG("seek_buffer only handles SEEK_SET");183if (offset < 0 || offset >= buffer->buf.len) {184error("curl seek would be outside of buffer");185return CURL_SEEKFUNC_FAIL;186}187
188buffer->posn = offset;189return CURL_SEEKFUNC_OK;190}
191
192size_t fwrite_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)193{
194size_t size = eltsize * nmemb;195struct strbuf *buffer = buffer_;196
197strbuf_add(buffer, ptr, size);198return nmemb;199}
200
201/*
202* A folded header continuation line starts with any number of spaces or
203* horizontal tab characters (SP or HTAB) as per RFC 7230 section 3.2.
204* It is not a continuation line if the line starts with any other character.
205*/
206static inline int is_hdr_continuation(const char *ptr, const size_t size)207{
208return size && (*ptr == ' ' || *ptr == '\t');209}
210
211static size_t fwrite_wwwauth(char *ptr, size_t eltsize, size_t nmemb, void *p UNUSED)212{
213size_t size = eltsize * nmemb;214struct strvec *values = &http_auth.wwwauth_headers;215struct strbuf buf = STRBUF_INIT;216const char *val;217size_t val_len;218
219/*220* Header lines may not come NULL-terminated from libcurl so we must
221* limit all scans to the maximum length of the header line, or leverage
222* strbufs for all operations.
223*
224* In addition, it is possible that header values can be split over
225* multiple lines as per RFC 7230. 'Line folding' has been deprecated
226* but older servers may still emit them. A continuation header field
227* value is identified as starting with a space or horizontal tab.
228*
229* The formal definition of a header field as given in RFC 7230 is:
230*
231* header-field = field-name ":" OWS field-value OWS
232*
233* field-name = token
234* field-value = *( field-content / obs-fold )
235* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
236* field-vchar = VCHAR / obs-text
237*
238* obs-fold = CRLF 1*( SP / HTAB )
239* ; obsolete line folding
240* ; see Section 3.2.4
241*/
242
243/* Start of a new WWW-Authenticate header */244if (skip_iprefix_mem(ptr, size, "www-authenticate:", &val, &val_len)) {245strbuf_add(&buf, val, val_len);246
247/*248* Strip the CRLF that should be present at the end of each
249* field as well as any trailing or leading whitespace from the
250* value.
251*/
252strbuf_trim(&buf);253
254strvec_push(values, buf.buf);255http_auth.header_is_last_match = 1;256goto exit;257}258
259/*260* This line could be a continuation of the previously matched header
261* field. If this is the case then we should append this value to the
262* end of the previously consumed value.
263*/
264if (http_auth.header_is_last_match && is_hdr_continuation(ptr, size)) {265/*266* Trim the CRLF and any leading or trailing from this line.
267*/
268strbuf_add(&buf, ptr, size);269strbuf_trim(&buf);270
271/*272* At this point we should always have at least one existing
273* value, even if it is empty. Do not bother appending the new
274* value if this continuation header is itself empty.
275*/
276if (!values->nr) {277BUG("should have at least one existing header value");278} else if (buf.len) {279char *prev = xstrdup(values->v[values->nr - 1]);280
281/* Join two non-empty values with a single space. */282const char *const sp = *prev ? " " : "";283
284strvec_pop(values);285strvec_pushf(values, "%s%s%s", prev, sp, buf.buf);286free(prev);287}288
289goto exit;290}291
292/* Not a continuation of a previously matched auth header line. */293http_auth.header_is_last_match = 0;294
295/*296* If this is a HTTP status line and not a header field, this signals
297* a different HTTP response. libcurl writes all the output of all
298* response headers of all responses, including redirects.
299* We only care about the last HTTP request response's headers so clear
300* the existing array.
301*/
302if (skip_iprefix_mem(ptr, size, "http/", &val, &val_len))303strvec_clear(values);304
305exit:306strbuf_release(&buf);307return size;308}
309
310size_t fwrite_null(char *ptr UNUSED, size_t eltsize UNUSED, size_t nmemb,311void *data UNUSED)312{
313return nmemb;314}
315
316static struct curl_slist *object_request_headers(void)317{
318return curl_slist_append(http_copy_default_headers(), "Pragma:");319}
320
321static void closedown_active_slot(struct active_request_slot *slot)322{
323active_requests--;324slot->in_use = 0;325}
326
327static void finish_active_slot(struct active_request_slot *slot)328{
329closedown_active_slot(slot);330curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);331
332if (slot->finished)333(*slot->finished) = 1;334
335/* Store slot results so they can be read after the slot is reused */336if (slot->results) {337slot->results->curl_result = slot->curl_result;338slot->results->http_code = slot->http_code;339curl_easy_getinfo(slot->curl, CURLINFO_HTTPAUTH_AVAIL,340&slot->results->auth_avail);341
342curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CONNECTCODE,343&slot->results->http_connectcode);344}345
346/* Run callback if appropriate */347if (slot->callback_func)348slot->callback_func(slot->callback_data);349}
350
351static void xmulti_remove_handle(struct active_request_slot *slot)352{
353curl_multi_remove_handle(curlm, slot->curl);354}
355
356static void process_curl_messages(void)357{
358int num_messages;359struct active_request_slot *slot;360CURLMsg *curl_message = curl_multi_info_read(curlm, &num_messages);361
362while (curl_message != NULL) {363if (curl_message->msg == CURLMSG_DONE) {364int curl_result = curl_message->data.result;365slot = active_queue_head;366while (slot != NULL &&367slot->curl != curl_message->easy_handle)368slot = slot->next;369if (slot) {370xmulti_remove_handle(slot);371slot->curl_result = curl_result;372finish_active_slot(slot);373} else {374fprintf(stderr, "Received DONE message for unknown request!\n");375}376} else {377fprintf(stderr, "Unknown CURL message received: %d\n",378(int)curl_message->msg);379}380curl_message = curl_multi_info_read(curlm, &num_messages);381}382}
383
384static int http_options(const char *var, const char *value,385const struct config_context *ctx, void *data)386{
387if (!strcmp("http.version", var)) {388return git_config_string(&curl_http_version, var, value);389}390if (!strcmp("http.sslverify", var)) {391curl_ssl_verify = git_config_bool(var, value);392return 0;393}394if (!strcmp("http.sslcipherlist", var))395return git_config_string(&ssl_cipherlist, var, value);396if (!strcmp("http.sslversion", var))397return git_config_string(&ssl_version, var, value);398if (!strcmp("http.sslcert", var))399return git_config_pathname(&ssl_cert, var, value);400if (!strcmp("http.sslcerttype", var))401return git_config_string(&ssl_cert_type, var, value);402if (!strcmp("http.sslkey", var))403return git_config_pathname(&ssl_key, var, value);404if (!strcmp("http.sslkeytype", var))405return git_config_string(&ssl_key_type, var, value);406if (!strcmp("http.sslcapath", var))407return git_config_pathname(&ssl_capath, var, value);408if (!strcmp("http.sslcainfo", var))409return git_config_pathname(&ssl_cainfo, var, value);410if (!strcmp("http.sslcertpasswordprotected", var)) {411ssl_cert_password_required = git_config_bool(var, value);412return 0;413}414if (!strcmp("http.ssltry", var)) {415curl_ssl_try = git_config_bool(var, value);416return 0;417}418if (!strcmp("http.sslbackend", var)) {419free(http_ssl_backend);420http_ssl_backend = xstrdup_or_null(value);421return 0;422}423
424if (!strcmp("http.schannelcheckrevoke", var)) {425http_schannel_check_revoke = git_config_bool(var, value);426return 0;427}428
429if (!strcmp("http.schannelusesslcainfo", var)) {430http_schannel_use_ssl_cainfo = git_config_bool(var, value);431return 0;432}433
434if (!strcmp("http.minsessions", var)) {435min_curl_sessions = git_config_int(var, value, ctx->kvi);436if (min_curl_sessions > 1)437min_curl_sessions = 1;438return 0;439}440if (!strcmp("http.maxrequests", var)) {441max_requests = git_config_int(var, value, ctx->kvi);442return 0;443}444if (!strcmp("http.lowspeedlimit", var)) {445curl_low_speed_limit = (long)git_config_int(var, value, ctx->kvi);446return 0;447}448if (!strcmp("http.lowspeedtime", var)) {449curl_low_speed_time = (long)git_config_int(var, value, ctx->kvi);450return 0;451}452
453if (!strcmp("http.noepsv", var)) {454curl_ftp_no_epsv = git_config_bool(var, value);455return 0;456}457if (!strcmp("http.proxy", var))458return git_config_string(&curl_http_proxy, var, value);459
460if (!strcmp("http.proxyauthmethod", var))461return git_config_string(&http_proxy_authmethod, var, value);462
463if (!strcmp("http.proxysslcert", var))464return git_config_string(&http_proxy_ssl_cert, var, value);465
466if (!strcmp("http.proxysslkey", var))467return git_config_string(&http_proxy_ssl_key, var, value);468
469if (!strcmp("http.proxysslcainfo", var))470return git_config_string(&http_proxy_ssl_ca_info, var, value);471
472if (!strcmp("http.proxysslcertpasswordprotected", var)) {473proxy_ssl_cert_password_required = git_config_bool(var, value);474return 0;475}476
477if (!strcmp("http.cookiefile", var))478return git_config_pathname(&curl_cookie_file, var, value);479if (!strcmp("http.savecookies", var)) {480curl_save_cookies = git_config_bool(var, value);481return 0;482}483
484if (!strcmp("http.postbuffer", var)) {485http_post_buffer = git_config_ssize_t(var, value, ctx->kvi);486if (http_post_buffer < 0)487warning(_("negative value for http.postBuffer; defaulting to %d"), LARGE_PACKET_MAX);488if (http_post_buffer < LARGE_PACKET_MAX)489http_post_buffer = LARGE_PACKET_MAX;490return 0;491}492
493if (!strcmp("http.useragent", var))494return git_config_string(&user_agent, var, value);495
496if (!strcmp("http.emptyauth", var)) {497if (value && !strcmp("auto", value))498curl_empty_auth = -1;499else500curl_empty_auth = git_config_bool(var, value);501return 0;502}503
504if (!strcmp("http.delegation", var)) {505#ifdef CURLGSSAPI_DELEGATION_FLAG506return git_config_string(&curl_deleg, var, value);507#else508warning(_("Delegation control is not supported with cURL < 7.22.0"));509return 0;510#endif511}512
513if (!strcmp("http.pinnedpubkey", var)) {514#ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY515return git_config_pathname(&ssl_pinnedkey, var, value);516#else517warning(_("Public key pinning not supported with cURL < 7.39.0"));518return 0;519#endif520}521
522if (!strcmp("http.extraheader", var)) {523if (!value) {524return config_error_nonbool(var);525} else if (!*value) {526string_list_clear(&extra_http_headers, 0);527} else {528string_list_append(&extra_http_headers, value);529}530return 0;531}532
533if (!strcmp("http.curloptresolve", var)) {534if (!value) {535return config_error_nonbool(var);536} else if (!*value) {537curl_slist_free_all(host_resolutions);538host_resolutions = NULL;539} else {540host_resolutions = curl_slist_append(host_resolutions, value);541}542return 0;543}544
545if (!strcmp("http.followredirects", var)) {546if (value && !strcmp(value, "initial"))547http_follow_config = HTTP_FOLLOW_INITIAL;548else if (git_config_bool(var, value))549http_follow_config = HTTP_FOLLOW_ALWAYS;550else551http_follow_config = HTTP_FOLLOW_NONE;552return 0;553}554
555if (!strcmp("http.proactiveauth", var)) {556if (!value)557return config_error_nonbool(var);558if (!strcmp(value, "auto"))559http_proactive_auth = PROACTIVE_AUTH_AUTO;560else if (!strcmp(value, "basic"))561http_proactive_auth = PROACTIVE_AUTH_BASIC;562else if (!strcmp(value, "none"))563http_proactive_auth = PROACTIVE_AUTH_NONE;564else565warning(_("Unknown value for http.proactiveauth"));566return 0;567}568
569/* Fall back on the default ones */570return git_default_config(var, value, ctx, data);571}
572
573static int curl_empty_auth_enabled(void)574{
575if (curl_empty_auth >= 0)576return curl_empty_auth;577
578/*579* In the automatic case, kick in the empty-auth
580* hack as long as we would potentially try some
581* method more exotic than "Basic" or "Digest".
582*
583* But only do this when this is our second or
584* subsequent request, as by then we know what
585* methods are available.
586*/
587if (http_auth_methods_restricted &&588(http_auth_methods & ~empty_auth_useless))589return 1;590return 0;591}
592
593struct curl_slist *http_append_auth_header(const struct credential *c,594struct curl_slist *headers)595{
596if (c->authtype && c->credential) {597struct strbuf auth = STRBUF_INIT;598strbuf_addf(&auth, "Authorization: %s %s",599c->authtype, c->credential);600headers = curl_slist_append(headers, auth.buf);601strbuf_release(&auth);602}603return headers;604}
605
606static void init_curl_http_auth(CURL *result)607{
608if ((!http_auth.username || !*http_auth.username) &&609(!http_auth.credential || !*http_auth.credential)) {610int empty_auth = curl_empty_auth_enabled();611if ((empty_auth != -1 && !always_auth_proactively()) || empty_auth == 1) {612curl_easy_setopt(result, CURLOPT_USERPWD, ":");613return;614} else if (!always_auth_proactively()) {615return;616} else if (http_proactive_auth == PROACTIVE_AUTH_BASIC) {617strvec_push(&http_auth.wwwauth_headers, "Basic");618}619}620
621credential_fill(&http_auth, 1);622
623if (http_auth.password) {624if (always_auth_proactively()) {625/*626* We got a credential without an authtype and we don't
627* know what's available. Since our only two options at
628* the moment are auto (which defaults to basic) and
629* basic, use basic for now.
630*/
631curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);632}633curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);634curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);635}636}
637
638/* *var must be free-able */
639static void var_override(char **var, char *value)640{
641if (value) {642free(*var);643*var = xstrdup(value);644}645}
646
647static void set_proxyauth_name_password(CURL *result)648{
649if (proxy_auth.password) {650curl_easy_setopt(result, CURLOPT_PROXYUSERNAME,651proxy_auth.username);652curl_easy_setopt(result, CURLOPT_PROXYPASSWORD,653proxy_auth.password);654} else if (proxy_auth.authtype && proxy_auth.credential) {655curl_easy_setopt(result, CURLOPT_PROXYHEADER,656http_append_auth_header(&proxy_auth, NULL));657}658}
659
660static void init_curl_proxy_auth(CURL *result)661{
662if (proxy_auth.username) {663if (!proxy_auth.password && !proxy_auth.credential)664credential_fill(&proxy_auth, 1);665set_proxyauth_name_password(result);666}667
668var_override(&http_proxy_authmethod, getenv("GIT_HTTP_PROXY_AUTHMETHOD"));669
670if (http_proxy_authmethod) {671int i;672for (i = 0; i < ARRAY_SIZE(proxy_authmethods); i++) {673if (!strcmp(http_proxy_authmethod, proxy_authmethods[i].name)) {674curl_easy_setopt(result, CURLOPT_PROXYAUTH,675proxy_authmethods[i].curlauth_param);676break;677}678}679if (i == ARRAY_SIZE(proxy_authmethods)) {680warning("unsupported proxy authentication method %s: using anyauth",681http_proxy_authmethod);682curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);683}684}685else686curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);687}
688
689static int has_cert_password(void)690{
691if (ssl_cert == NULL || ssl_cert_password_required != 1)692return 0;693if (!cert_auth.password) {694cert_auth.protocol = xstrdup("cert");695cert_auth.host = xstrdup("");696cert_auth.username = xstrdup("");697cert_auth.path = xstrdup(ssl_cert);698credential_fill(&cert_auth, 0);699}700return 1;701}
702
703#ifdef GIT_CURL_HAVE_CURLOPT_PROXY_KEYPASSWD704static int has_proxy_cert_password(void)705{
706if (http_proxy_ssl_cert == NULL || proxy_ssl_cert_password_required != 1)707return 0;708if (!proxy_cert_auth.password) {709proxy_cert_auth.protocol = xstrdup("cert");710proxy_cert_auth.host = xstrdup("");711proxy_cert_auth.username = xstrdup("");712proxy_cert_auth.path = xstrdup(http_proxy_ssl_cert);713credential_fill(&proxy_cert_auth, 0);714}715return 1;716}
717#endif718
719#ifdef GITCURL_HAVE_CURLOPT_TCP_KEEPALIVE720static void set_curl_keepalive(CURL *c)721{
722curl_easy_setopt(c, CURLOPT_TCP_KEEPALIVE, 1);723}
724
725#else726static int sockopt_callback(void *client, curl_socket_t fd, curlsocktype type)727{
728int ka = 1;729int rc;730socklen_t len = (socklen_t)sizeof(ka);731
732if (type != CURLSOCKTYPE_IPCXN)733return 0;734
735rc = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&ka, len);736if (rc < 0)737warning_errno("unable to set SO_KEEPALIVE on socket");738
739return CURL_SOCKOPT_OK;740}
741
742static void set_curl_keepalive(CURL *c)743{
744curl_easy_setopt(c, CURLOPT_SOCKOPTFUNCTION, sockopt_callback);745}
746#endif747
748/* Return 1 if redactions have been made, 0 otherwise. */
749static int redact_sensitive_header(struct strbuf *header, size_t offset)750{
751int ret = 0;752const char *sensitive_header;753
754if (trace_curl_redact &&755(skip_iprefix(header->buf + offset, "Authorization:", &sensitive_header) ||756skip_iprefix(header->buf + offset, "Proxy-Authorization:", &sensitive_header))) {757/* The first token is the type, which is OK to log */758while (isspace(*sensitive_header))759sensitive_header++;760while (*sensitive_header && !isspace(*sensitive_header))761sensitive_header++;762/* Everything else is opaque and possibly sensitive */763strbuf_setlen(header, sensitive_header - header->buf);764strbuf_addstr(header, " <redacted>");765ret = 1;766} else if (trace_curl_redact &&767skip_iprefix(header->buf + offset, "Cookie:", &sensitive_header)) {768struct strbuf redacted_header = STRBUF_INIT;769const char *cookie;770
771while (isspace(*sensitive_header))772sensitive_header++;773
774cookie = sensitive_header;775
776while (cookie) {777char *equals;778char *semicolon = strstr(cookie, "; ");779if (semicolon)780*semicolon = 0;781equals = strchrnul(cookie, '=');782if (!equals) {783/* invalid cookie, just append and continue */784strbuf_addstr(&redacted_header, cookie);785continue;786}787strbuf_add(&redacted_header, cookie, equals - cookie);788strbuf_addstr(&redacted_header, "=<redacted>");789if (semicolon) {790/*791* There are more cookies. (Or, for some
792* reason, the input string ends in "; ".)
793*/
794strbuf_addstr(&redacted_header, "; ");795cookie = semicolon + strlen("; ");796} else {797cookie = NULL;798}799}800
801strbuf_setlen(header, sensitive_header - header->buf);802strbuf_addbuf(header, &redacted_header);803ret = 1;804}805return ret;806}
807
808static int match_curl_h2_trace(const char *line, const char **out)809{
810const char *p;811
812/*813* curl prior to 8.1.0 gives us:
814*
815* h2h3 [<header-name>: <header-val>]
816*
817* Starting in 8.1.0, the first token became just "h2".
818*/
819if (skip_iprefix(line, "h2h3 [", out) ||820skip_iprefix(line, "h2 [", out))821return 1;822
823/*824* curl 8.3.0 uses:
825* [HTTP/2] [<stream-id>] [<header-name>: <header-val>]
826* where <stream-id> is numeric.
827*/
828if (skip_iprefix(line, "[HTTP/2] [", &p)) {829while (isdigit(*p))830p++;831if (skip_prefix(p, "] [", out))832return 1;833}834
835return 0;836}
837
838/* Redact headers in info */
839static void redact_sensitive_info_header(struct strbuf *header)840{
841const char *sensitive_header;842
843if (trace_curl_redact &&844match_curl_h2_trace(header->buf, &sensitive_header)) {845if (redact_sensitive_header(header, sensitive_header - header->buf)) {846/* redaction ate our closing bracket */847strbuf_addch(header, ']');848}849}850}
851
852static void curl_dump_header(const char *text, unsigned char *ptr, size_t size, int hide_sensitive_header)853{
854struct strbuf out = STRBUF_INIT;855struct strbuf **headers, **header;856
857strbuf_addf(&out, "%s, %10.10ld bytes (0x%8.8lx)\n",858text, (long)size, (long)size);859trace_strbuf(&trace_curl, &out);860strbuf_reset(&out);861strbuf_add(&out, ptr, size);862headers = strbuf_split_max(&out, '\n', 0);863
864for (header = headers; *header; header++) {865if (hide_sensitive_header)866redact_sensitive_header(*header, 0);867strbuf_insertstr((*header), 0, text);868strbuf_insertstr((*header), strlen(text), ": ");869strbuf_rtrim((*header));870strbuf_addch((*header), '\n');871trace_strbuf(&trace_curl, (*header));872}873strbuf_list_free(headers);874strbuf_release(&out);875}
876
877static void curl_dump_data(const char *text, unsigned char *ptr, size_t size)878{
879size_t i;880struct strbuf out = STRBUF_INIT;881unsigned int width = 60;882
883strbuf_addf(&out, "%s, %10.10ld bytes (0x%8.8lx)\n",884text, (long)size, (long)size);885trace_strbuf(&trace_curl, &out);886
887for (i = 0; i < size; i += width) {888size_t w;889
890strbuf_reset(&out);891strbuf_addf(&out, "%s: ", text);892for (w = 0; (w < width) && (i + w < size); w++) {893unsigned char ch = ptr[i + w];894
895strbuf_addch(&out,896(ch >= 0x20) && (ch < 0x80)897? ch : '.');898}899strbuf_addch(&out, '\n');900trace_strbuf(&trace_curl, &out);901}902strbuf_release(&out);903}
904
905static void curl_dump_info(char *data, size_t size)906{
907struct strbuf buf = STRBUF_INIT;908
909strbuf_add(&buf, data, size);910
911redact_sensitive_info_header(&buf);912trace_printf_key(&trace_curl, "== Info: %s", buf.buf);913
914strbuf_release(&buf);915}
916
917static int curl_trace(CURL *handle UNUSED, curl_infotype type,918char *data, size_t size,919void *userp UNUSED)920{
921const char *text;922enum { NO_FILTER = 0, DO_FILTER = 1 };923
924switch (type) {925case CURLINFO_TEXT:926curl_dump_info(data, size);927break;928case CURLINFO_HEADER_OUT:929text = "=> Send header";930curl_dump_header(text, (unsigned char *)data, size, DO_FILTER);931break;932case CURLINFO_DATA_OUT:933if (trace_curl_data) {934text = "=> Send data";935curl_dump_data(text, (unsigned char *)data, size);936}937break;938case CURLINFO_SSL_DATA_OUT:939if (trace_curl_data) {940text = "=> Send SSL data";941curl_dump_data(text, (unsigned char *)data, size);942}943break;944case CURLINFO_HEADER_IN:945text = "<= Recv header";946curl_dump_header(text, (unsigned char *)data, size, NO_FILTER);947break;948case CURLINFO_DATA_IN:949if (trace_curl_data) {950text = "<= Recv data";951curl_dump_data(text, (unsigned char *)data, size);952}953break;954case CURLINFO_SSL_DATA_IN:955if (trace_curl_data) {956text = "<= Recv SSL data";957curl_dump_data(text, (unsigned char *)data, size);958}959break;960
961default: /* we ignore unknown types by default */962return 0;963}964return 0;965}
966
967void http_trace_curl_no_data(void)968{
969trace_override_envvar(&trace_curl, "1");970trace_curl_data = 0;971}
972
973void setup_curl_trace(CURL *handle)974{
975if (!trace_want(&trace_curl))976return;977curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);978curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, curl_trace);979curl_easy_setopt(handle, CURLOPT_DEBUGDATA, NULL);980}
981
982static void proto_list_append(struct strbuf *list, const char *proto)983{
984if (!list)985return;986if (list->len)987strbuf_addch(list, ',');988strbuf_addstr(list, proto);989}
990
991static long get_curl_allowed_protocols(int from_user, struct strbuf *list)992{
993long bits = 0;994
995if (is_transport_allowed("http", from_user)) {996bits |= CURLPROTO_HTTP;997proto_list_append(list, "http");998}999if (is_transport_allowed("https", from_user)) {1000bits |= CURLPROTO_HTTPS;1001proto_list_append(list, "https");1002}1003if (is_transport_allowed("ftp", from_user)) {1004bits |= CURLPROTO_FTP;1005proto_list_append(list, "ftp");1006}1007if (is_transport_allowed("ftps", from_user)) {1008bits |= CURLPROTO_FTPS;1009proto_list_append(list, "ftps");1010}1011
1012return bits;1013}
1014
1015#ifdef GIT_CURL_HAVE_CURL_HTTP_VERSION_21016static int get_curl_http_version_opt(const char *version_string, long *opt)1017{
1018int i;1019static struct {1020const char *name;1021long opt_token;1022} choice[] = {1023{ "HTTP/1.1", CURL_HTTP_VERSION_1_1 },1024{ "HTTP/2", CURL_HTTP_VERSION_2 }1025};1026
1027for (i = 0; i < ARRAY_SIZE(choice); i++) {1028if (!strcmp(version_string, choice[i].name)) {1029*opt = choice[i].opt_token;1030return 0;1031}1032}1033
1034warning("unknown value given to http.version: '%s'", version_string);1035return -1; /* not found */1036}
1037
1038#endif1039
1040static CURL *get_curl_handle(void)1041{
1042CURL *result = curl_easy_init();1043
1044if (!result)1045die("curl_easy_init failed");1046
1047if (!curl_ssl_verify) {1048curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);1049curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0);1050} else {1051/* Verify authenticity of the peer's certificate */1052curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 1);1053/* The name in the cert must match whom we tried to connect */1054curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);1055}1056
1057#ifdef GIT_CURL_HAVE_CURL_HTTP_VERSION_21058if (curl_http_version) {1059long opt;1060if (!get_curl_http_version_opt(curl_http_version, &opt)) {1061/* Set request use http version */1062curl_easy_setopt(result, CURLOPT_HTTP_VERSION, opt);1063}1064}1065#endif1066
1067curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);1068curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY);1069
1070#ifdef CURLGSSAPI_DELEGATION_FLAG1071if (curl_deleg) {1072int i;1073for (i = 0; i < ARRAY_SIZE(curl_deleg_levels); i++) {1074if (!strcmp(curl_deleg, curl_deleg_levels[i].name)) {1075curl_easy_setopt(result, CURLOPT_GSSAPI_DELEGATION,1076curl_deleg_levels[i].curl_deleg_param);1077break;1078}1079}1080if (i == ARRAY_SIZE(curl_deleg_levels))1081warning("Unknown delegation method '%s': using default",1082curl_deleg);1083}1084#endif1085
1086if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) &&1087!http_schannel_check_revoke) {1088#ifdef GIT_CURL_HAVE_CURLSSLOPT_NO_REVOKE1089curl_easy_setopt(result, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);1090#else1091warning(_("CURLSSLOPT_NO_REVOKE not supported with cURL < 7.44.0"));1092#endif1093}1094
1095if (http_proactive_auth != PROACTIVE_AUTH_NONE)1096init_curl_http_auth(result);1097
1098if (getenv("GIT_SSL_VERSION"))1099ssl_version = getenv("GIT_SSL_VERSION");1100if (ssl_version && *ssl_version) {1101int i;1102for (i = 0; i < ARRAY_SIZE(sslversions); i++) {1103if (!strcmp(ssl_version, sslversions[i].name)) {1104curl_easy_setopt(result, CURLOPT_SSLVERSION,1105sslversions[i].ssl_version);1106break;1107}1108}1109if (i == ARRAY_SIZE(sslversions))1110warning("unsupported ssl version %s: using default",1111ssl_version);1112}1113
1114if (getenv("GIT_SSL_CIPHER_LIST"))1115ssl_cipherlist = getenv("GIT_SSL_CIPHER_LIST");1116if (ssl_cipherlist != NULL && *ssl_cipherlist)1117curl_easy_setopt(result, CURLOPT_SSL_CIPHER_LIST,1118ssl_cipherlist);1119
1120if (ssl_cert)1121curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);1122if (ssl_cert_type)1123curl_easy_setopt(result, CURLOPT_SSLCERTTYPE, ssl_cert_type);1124if (has_cert_password())1125curl_easy_setopt(result, CURLOPT_KEYPASSWD, cert_auth.password);1126if (ssl_key)1127curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);1128if (ssl_key_type)1129curl_easy_setopt(result, CURLOPT_SSLKEYTYPE, ssl_key_type);1130if (ssl_capath)1131curl_easy_setopt(result, CURLOPT_CAPATH, ssl_capath);1132#ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY1133if (ssl_pinnedkey)1134curl_easy_setopt(result, CURLOPT_PINNEDPUBLICKEY, ssl_pinnedkey);1135#endif1136if (http_ssl_backend && !strcmp("schannel", http_ssl_backend) &&1137!http_schannel_use_ssl_cainfo) {1138curl_easy_setopt(result, CURLOPT_CAINFO, NULL);1139#ifdef GIT_CURL_HAVE_CURLOPT_PROXY_CAINFO1140curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, NULL);1141#endif1142} else if (ssl_cainfo != NULL || http_proxy_ssl_ca_info != NULL) {1143if (ssl_cainfo)1144curl_easy_setopt(result, CURLOPT_CAINFO, ssl_cainfo);1145#ifdef GIT_CURL_HAVE_CURLOPT_PROXY_CAINFO1146if (http_proxy_ssl_ca_info)1147curl_easy_setopt(result, CURLOPT_PROXY_CAINFO, http_proxy_ssl_ca_info);1148#endif1149}1150
1151if (curl_low_speed_limit > 0 && curl_low_speed_time > 0) {1152curl_easy_setopt(result, CURLOPT_LOW_SPEED_LIMIT,1153curl_low_speed_limit);1154curl_easy_setopt(result, CURLOPT_LOW_SPEED_TIME,1155curl_low_speed_time);1156}1157
1158curl_easy_setopt(result, CURLOPT_MAXREDIRS, 20);1159curl_easy_setopt(result, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);1160
1161#ifdef GIT_CURL_HAVE_CURLOPT_PROTOCOLS_STR1162{1163struct strbuf buf = STRBUF_INIT;1164
1165get_curl_allowed_protocols(0, &buf);1166curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS_STR, buf.buf);1167strbuf_reset(&buf);1168
1169get_curl_allowed_protocols(-1, &buf);1170curl_easy_setopt(result, CURLOPT_PROTOCOLS_STR, buf.buf);1171strbuf_release(&buf);1172}1173#else1174curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS,1175get_curl_allowed_protocols(0, NULL));1176curl_easy_setopt(result, CURLOPT_PROTOCOLS,1177get_curl_allowed_protocols(-1, NULL));1178#endif1179
1180if (getenv("GIT_CURL_VERBOSE"))1181http_trace_curl_no_data();1182setup_curl_trace(result);1183if (getenv("GIT_TRACE_CURL_NO_DATA"))1184trace_curl_data = 0;1185if (!git_env_bool("GIT_TRACE_REDACT", 1))1186trace_curl_redact = 0;1187
1188curl_easy_setopt(result, CURLOPT_USERAGENT,1189user_agent ? user_agent : git_user_agent());1190
1191if (curl_ftp_no_epsv)1192curl_easy_setopt(result, CURLOPT_FTP_USE_EPSV, 0);1193
1194if (curl_ssl_try)1195curl_easy_setopt(result, CURLOPT_USE_SSL, CURLUSESSL_TRY);1196
1197/*1198* CURL also examines these variables as a fallback; but we need to query
1199* them here in order to decide whether to prompt for missing password (cf.
1200* init_curl_proxy_auth()).
1201*
1202* Unlike many other common environment variables, these are historically
1203* lowercase only. It appears that CURL did not know this and implemented
1204* only uppercase variants, which was later corrected to take both - with
1205* the exception of http_proxy, which is lowercase only also in CURL. As
1206* the lowercase versions are the historical quasi-standard, they take
1207* precedence here, as in CURL.
1208*/
1209if (!curl_http_proxy) {1210if (http_auth.protocol && !strcmp(http_auth.protocol, "https")) {1211var_override(&curl_http_proxy, getenv("HTTPS_PROXY"));1212var_override(&curl_http_proxy, getenv("https_proxy"));1213} else {1214var_override(&curl_http_proxy, getenv("http_proxy"));1215}1216if (!curl_http_proxy) {1217var_override(&curl_http_proxy, getenv("ALL_PROXY"));1218var_override(&curl_http_proxy, getenv("all_proxy"));1219}1220}1221
1222if (curl_http_proxy && curl_http_proxy[0] == '\0') {1223/*1224* Handle case with the empty http.proxy value here to keep
1225* common code clean.
1226* NB: empty option disables proxying at all.
1227*/
1228curl_easy_setopt(result, CURLOPT_PROXY, "");1229} else if (curl_http_proxy) {1230struct strbuf proxy = STRBUF_INIT;1231
1232if (starts_with(curl_http_proxy, "socks5h"))1233curl_easy_setopt(result,1234CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);1235else if (starts_with(curl_http_proxy, "socks5"))1236curl_easy_setopt(result,1237CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);1238else if (starts_with(curl_http_proxy, "socks4a"))1239curl_easy_setopt(result,1240CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4A);1241else if (starts_with(curl_http_proxy, "socks"))1242curl_easy_setopt(result,1243CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);1244#ifdef GIT_CURL_HAVE_CURLOPT_PROXY_KEYPASSWD1245else if (starts_with(curl_http_proxy, "https")) {1246curl_easy_setopt(result, CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);1247
1248if (http_proxy_ssl_cert)1249curl_easy_setopt(result, CURLOPT_PROXY_SSLCERT, http_proxy_ssl_cert);1250
1251if (http_proxy_ssl_key)1252curl_easy_setopt(result, CURLOPT_PROXY_SSLKEY, http_proxy_ssl_key);1253
1254if (has_proxy_cert_password())1255curl_easy_setopt(result, CURLOPT_PROXY_KEYPASSWD, proxy_cert_auth.password);1256}1257#endif1258if (strstr(curl_http_proxy, "://"))1259credential_from_url(&proxy_auth, curl_http_proxy);1260else {1261struct strbuf url = STRBUF_INIT;1262strbuf_addf(&url, "http://%s", curl_http_proxy);1263credential_from_url(&proxy_auth, url.buf);1264strbuf_release(&url);1265}1266
1267if (!proxy_auth.host)1268die("Invalid proxy URL '%s'", curl_http_proxy);1269
1270strbuf_addstr(&proxy, proxy_auth.host);1271if (proxy_auth.path) {1272curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);1273
1274if (ver->version_num < 0x075400)1275die("libcurl 7.84 or later is required to support paths in proxy URLs");1276
1277if (!starts_with(proxy_auth.protocol, "socks"))1278die("Invalid proxy URL '%s': only SOCKS proxies support paths",1279curl_http_proxy);1280
1281if (strcasecmp(proxy_auth.host, "localhost"))1282die("Invalid proxy URL '%s': host must be localhost if a path is present",1283curl_http_proxy);1284
1285strbuf_addch(&proxy, '/');1286strbuf_add_percentencode(&proxy, proxy_auth.path, 0);1287}1288curl_easy_setopt(result, CURLOPT_PROXY, proxy.buf);1289strbuf_release(&proxy);1290
1291var_override(&curl_no_proxy, getenv("NO_PROXY"));1292var_override(&curl_no_proxy, getenv("no_proxy"));1293curl_easy_setopt(result, CURLOPT_NOPROXY, curl_no_proxy);1294}1295init_curl_proxy_auth(result);1296
1297set_curl_keepalive(result);1298
1299return result;1300}
1301
1302static void set_from_env(char **var, const char *envname)1303{
1304const char *val = getenv(envname);1305if (val) {1306FREE_AND_NULL(*var);1307*var = xstrdup(val);1308}1309}
1310
1311void http_init(struct remote *remote, const char *url, int proactive_auth)1312{
1313char *low_speed_limit;1314char *low_speed_time;1315char *normalized_url;1316struct urlmatch_config config = URLMATCH_CONFIG_INIT;1317
1318config.section = "http";1319config.key = NULL;1320config.collect_fn = http_options;1321config.cascade_fn = git_default_config;1322config.cb = NULL;1323
1324http_is_verbose = 0;1325normalized_url = url_normalize(url, &config.url);1326
1327git_config(urlmatch_config_entry, &config);1328free(normalized_url);1329string_list_clear(&config.vars, 1);1330
1331#ifdef GIT_CURL_HAVE_CURLSSLSET_NO_BACKENDS1332if (http_ssl_backend) {1333const curl_ssl_backend **backends;1334struct strbuf buf = STRBUF_INIT;1335int i;1336
1337switch (curl_global_sslset(-1, http_ssl_backend, &backends)) {1338case CURLSSLSET_UNKNOWN_BACKEND:1339strbuf_addf(&buf, _("Unsupported SSL backend '%s'. "1340"Supported SSL backends:"),1341http_ssl_backend);1342for (i = 0; backends[i]; i++)1343strbuf_addf(&buf, "\n\t%s", backends[i]->name);1344die("%s", buf.buf);1345case CURLSSLSET_NO_BACKENDS:1346die(_("Could not set SSL backend to '%s': "1347"cURL was built without SSL backends"),1348http_ssl_backend);1349case CURLSSLSET_TOO_LATE:1350die(_("Could not set SSL backend to '%s': already set"),1351http_ssl_backend);1352case CURLSSLSET_OK:1353break; /* Okay! */1354}1355}1356#endif1357
1358if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)1359die("curl_global_init failed");1360
1361if (proactive_auth && http_proactive_auth == PROACTIVE_AUTH_NONE)1362http_proactive_auth = PROACTIVE_AUTH_IF_CREDENTIALS;1363
1364if (remote && remote->http_proxy)1365curl_http_proxy = xstrdup(remote->http_proxy);1366
1367if (remote)1368var_override(&http_proxy_authmethod, remote->http_proxy_authmethod);1369
1370pragma_header = curl_slist_append(http_copy_default_headers(),1371"Pragma: no-cache");1372
1373{1374char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");1375if (http_max_requests)1376max_requests = atoi(http_max_requests);1377}1378
1379curlm = curl_multi_init();1380if (!curlm)1381die("curl_multi_init failed");1382
1383if (getenv("GIT_SSL_NO_VERIFY"))1384curl_ssl_verify = 0;1385
1386set_from_env(&ssl_cert, "GIT_SSL_CERT");1387set_from_env(&ssl_cert_type, "GIT_SSL_CERT_TYPE");1388set_from_env(&ssl_key, "GIT_SSL_KEY");1389set_from_env(&ssl_key_type, "GIT_SSL_KEY_TYPE");1390set_from_env(&ssl_capath, "GIT_SSL_CAPATH");1391set_from_env(&ssl_cainfo, "GIT_SSL_CAINFO");1392
1393set_from_env(&user_agent, "GIT_HTTP_USER_AGENT");1394
1395low_speed_limit = getenv("GIT_HTTP_LOW_SPEED_LIMIT");1396if (low_speed_limit)1397curl_low_speed_limit = strtol(low_speed_limit, NULL, 10);1398low_speed_time = getenv("GIT_HTTP_LOW_SPEED_TIME");1399if (low_speed_time)1400curl_low_speed_time = strtol(low_speed_time, NULL, 10);1401
1402if (curl_ssl_verify == -1)1403curl_ssl_verify = 1;1404
1405curl_session_count = 0;1406if (max_requests < 1)1407max_requests = DEFAULT_MAX_REQUESTS;1408
1409set_from_env(&http_proxy_ssl_cert, "GIT_PROXY_SSL_CERT");1410set_from_env(&http_proxy_ssl_key, "GIT_PROXY_SSL_KEY");1411set_from_env(&http_proxy_ssl_ca_info, "GIT_PROXY_SSL_CAINFO");1412
1413if (getenv("GIT_PROXY_SSL_CERT_PASSWORD_PROTECTED"))1414proxy_ssl_cert_password_required = 1;1415
1416if (getenv("GIT_CURL_FTP_NO_EPSV"))1417curl_ftp_no_epsv = 1;1418
1419if (url) {1420credential_from_url(&http_auth, url);1421if (!ssl_cert_password_required &&1422getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") &&1423starts_with(url, "https://"))1424ssl_cert_password_required = 1;1425}1426
1427curl_default = get_curl_handle();1428}
1429
1430void http_cleanup(void)1431{
1432struct active_request_slot *slot = active_queue_head;1433
1434while (slot != NULL) {1435struct active_request_slot *next = slot->next;1436if (slot->curl) {1437xmulti_remove_handle(slot);1438curl_easy_cleanup(slot->curl);1439}1440free(slot);1441slot = next;1442}1443active_queue_head = NULL;1444
1445curl_easy_cleanup(curl_default);1446
1447curl_multi_cleanup(curlm);1448curl_global_cleanup();1449
1450string_list_clear(&extra_http_headers, 0);1451
1452curl_slist_free_all(pragma_header);1453pragma_header = NULL;1454
1455curl_slist_free_all(host_resolutions);1456host_resolutions = NULL;1457
1458if (curl_http_proxy) {1459free((void *)curl_http_proxy);1460curl_http_proxy = NULL;1461}1462
1463if (proxy_auth.password) {1464memset(proxy_auth.password, 0, strlen(proxy_auth.password));1465FREE_AND_NULL(proxy_auth.password);1466}1467
1468free((void *)curl_proxyuserpwd);1469curl_proxyuserpwd = NULL;1470
1471free((void *)http_proxy_authmethod);1472http_proxy_authmethod = NULL;1473
1474if (cert_auth.password) {1475memset(cert_auth.password, 0, strlen(cert_auth.password));1476FREE_AND_NULL(cert_auth.password);1477}1478ssl_cert_password_required = 0;1479
1480if (proxy_cert_auth.password) {1481memset(proxy_cert_auth.password, 0, strlen(proxy_cert_auth.password));1482FREE_AND_NULL(proxy_cert_auth.password);1483}1484proxy_ssl_cert_password_required = 0;1485
1486FREE_AND_NULL(cached_accept_language);1487}
1488
1489struct active_request_slot *get_active_slot(void)1490{
1491struct active_request_slot *slot = active_queue_head;1492struct active_request_slot *newslot;1493
1494int num_transfers;1495
1496/* Wait for a slot to open up if the queue is full */1497while (active_requests >= max_requests) {1498curl_multi_perform(curlm, &num_transfers);1499if (num_transfers < active_requests)1500process_curl_messages();1501}1502
1503while (slot != NULL && slot->in_use)1504slot = slot->next;1505
1506if (!slot) {1507newslot = xmalloc(sizeof(*newslot));1508newslot->curl = NULL;1509newslot->in_use = 0;1510newslot->next = NULL;1511
1512slot = active_queue_head;1513if (!slot) {1514active_queue_head = newslot;1515} else {1516while (slot->next != NULL)1517slot = slot->next;1518slot->next = newslot;1519}1520slot = newslot;1521}1522
1523if (!slot->curl) {1524slot->curl = curl_easy_duphandle(curl_default);1525curl_session_count++;1526}1527
1528active_requests++;1529slot->in_use = 1;1530slot->results = NULL;1531slot->finished = NULL;1532slot->callback_data = NULL;1533slot->callback_func = NULL;1534
1535if (curl_cookie_file && !strcmp(curl_cookie_file, "-")) {1536warning(_("refusing to read cookies from http.cookiefile '-'"));1537FREE_AND_NULL(curl_cookie_file);1538}1539curl_easy_setopt(slot->curl, CURLOPT_COOKIEFILE, curl_cookie_file);1540if (curl_save_cookies && (!curl_cookie_file || !curl_cookie_file[0])) {1541curl_save_cookies = 0;1542warning(_("ignoring http.savecookies for empty http.cookiefile"));1543}1544if (curl_save_cookies)1545curl_easy_setopt(slot->curl, CURLOPT_COOKIEJAR, curl_cookie_file);1546curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);1547curl_easy_setopt(slot->curl, CURLOPT_RESOLVE, host_resolutions);1548curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);1549curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);1550curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);1551curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);1552curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, NULL);1553curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, -1L);1554curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);1555curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);1556curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);1557curl_easy_setopt(slot->curl, CURLOPT_RANGE, NULL);1558
1559/*1560* Default following to off unless "ALWAYS" is configured; this gives
1561* callers a sane starting point, and they can tweak for individual
1562* HTTP_FOLLOW_* cases themselves.
1563*/
1564if (http_follow_config == HTTP_FOLLOW_ALWAYS)1565curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1);1566else1567curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 0);1568
1569curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);1570curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);1571if (http_auth.password || http_auth.credential || curl_empty_auth_enabled())1572init_curl_http_auth(slot->curl);1573
1574return slot;1575}
1576
1577int start_active_slot(struct active_request_slot *slot)1578{
1579CURLMcode curlm_result = curl_multi_add_handle(curlm, slot->curl);1580int num_transfers;1581
1582if (curlm_result != CURLM_OK &&1583curlm_result != CURLM_CALL_MULTI_PERFORM) {1584warning("curl_multi_add_handle failed: %s",1585curl_multi_strerror(curlm_result));1586active_requests--;1587slot->in_use = 0;1588return 0;1589}1590
1591/*1592* We know there must be something to do, since we just added
1593* something.
1594*/
1595curl_multi_perform(curlm, &num_transfers);1596return 1;1597}
1598
1599struct fill_chain {1600void *data;1601int (*fill)(void *);1602struct fill_chain *next;1603};1604
1605static struct fill_chain *fill_cfg;1606
1607void add_fill_function(void *data, int (*fill)(void *))1608{
1609struct fill_chain *new_fill = xmalloc(sizeof(*new_fill));1610struct fill_chain **linkp = &fill_cfg;1611new_fill->data = data;1612new_fill->fill = fill;1613new_fill->next = NULL;1614while (*linkp)1615linkp = &(*linkp)->next;1616*linkp = new_fill;1617}
1618
1619void fill_active_slots(void)1620{
1621struct active_request_slot *slot = active_queue_head;1622
1623while (active_requests < max_requests) {1624struct fill_chain *fill;1625for (fill = fill_cfg; fill; fill = fill->next)1626if (fill->fill(fill->data))1627break;1628
1629if (!fill)1630break;1631}1632
1633while (slot != NULL) {1634if (!slot->in_use && slot->curl != NULL1635&& curl_session_count > min_curl_sessions) {1636curl_easy_cleanup(slot->curl);1637slot->curl = NULL;1638curl_session_count--;1639}1640slot = slot->next;1641}1642}
1643
1644void step_active_slots(void)1645{
1646int num_transfers;1647CURLMcode curlm_result;1648
1649do {1650curlm_result = curl_multi_perform(curlm, &num_transfers);1651} while (curlm_result == CURLM_CALL_MULTI_PERFORM);1652if (num_transfers < active_requests) {1653process_curl_messages();1654fill_active_slots();1655}1656}
1657
1658void run_active_slot(struct active_request_slot *slot)1659{
1660fd_set readfds;1661fd_set writefds;1662fd_set excfds;1663int max_fd;1664struct timeval select_timeout;1665int finished = 0;1666
1667slot->finished = &finished;1668while (!finished) {1669step_active_slots();1670
1671if (slot->in_use) {1672long curl_timeout;1673curl_multi_timeout(curlm, &curl_timeout);1674if (curl_timeout == 0) {1675continue;1676} else if (curl_timeout == -1) {1677select_timeout.tv_sec = 0;1678select_timeout.tv_usec = 50000;1679} else {1680select_timeout.tv_sec = curl_timeout / 1000;1681select_timeout.tv_usec = (curl_timeout % 1000) * 1000;1682}1683
1684max_fd = -1;1685FD_ZERO(&readfds);1686FD_ZERO(&writefds);1687FD_ZERO(&excfds);1688curl_multi_fdset(curlm, &readfds, &writefds, &excfds, &max_fd);1689
1690/*1691* It can happen that curl_multi_timeout returns a pathologically
1692* long timeout when curl_multi_fdset returns no file descriptors
1693* to read. See commit message for more details.
1694*/
1695if (max_fd < 0 &&1696(select_timeout.tv_sec > 0 ||1697select_timeout.tv_usec > 50000)) {1698select_timeout.tv_sec = 0;1699select_timeout.tv_usec = 50000;1700}1701
1702select(max_fd+1, &readfds, &writefds, &excfds, &select_timeout);1703}1704}1705
1706/*1707* The value of slot->finished we set before the loop was used
1708* to set our "finished" variable when our request completed.
1709*
1710* 1. The slot may not have been reused for another requst
1711* yet, in which case it still has &finished.
1712*
1713* 2. The slot may already be in-use to serve another request,
1714* which can further be divided into two cases:
1715*
1716* (a) If call run_active_slot() hasn't been called for that
1717* other request, slot->finished would have been cleared
1718* by get_active_slot() and has NULL.
1719*
1720* (b) If the request did call run_active_slot(), then the
1721* call would have updated slot->finished at the beginning
1722* of this function, and with the clearing of the member
1723* below, we would find that slot->finished is now NULL.
1724*
1725* In all cases, slot->finished has no useful information to
1726* anybody at this point. Some compilers warn us for
1727* attempting to smuggle a pointer that is about to become
1728* invalid, i.e. &finished. We clear it here to assure them.
1729*/
1730slot->finished = NULL;1731}
1732
1733static void release_active_slot(struct active_request_slot *slot)1734{
1735closedown_active_slot(slot);1736if (slot->curl) {1737xmulti_remove_handle(slot);1738if (curl_session_count > min_curl_sessions) {1739curl_easy_cleanup(slot->curl);1740slot->curl = NULL;1741curl_session_count--;1742}1743}1744fill_active_slots();1745}
1746
1747void finish_all_active_slots(void)1748{
1749struct active_request_slot *slot = active_queue_head;1750
1751while (slot != NULL)1752if (slot->in_use) {1753run_active_slot(slot);1754slot = active_queue_head;1755} else {1756slot = slot->next;1757}1758}
1759
1760/* Helpers for modifying and creating URLs */
1761static inline int needs_quote(int ch)1762{
1763if (((ch >= 'A') && (ch <= 'Z'))1764|| ((ch >= 'a') && (ch <= 'z'))1765|| ((ch >= '0') && (ch <= '9'))1766|| (ch == '/')1767|| (ch == '-')1768|| (ch == '.'))1769return 0;1770return 1;1771}
1772
1773static char *quote_ref_url(const char *base, const char *ref)1774{
1775struct strbuf buf = STRBUF_INIT;1776const char *cp;1777int ch;1778
1779end_url_with_slash(&buf, base);1780
1781for (cp = ref; (ch = *cp) != 0; cp++)1782if (needs_quote(ch))1783strbuf_addf(&buf, "%%%02x", ch);1784else1785strbuf_addch(&buf, *cp);1786
1787return strbuf_detach(&buf, NULL);1788}
1789
1790void append_remote_object_url(struct strbuf *buf, const char *url,1791const char *hex,1792int only_two_digit_prefix)1793{
1794end_url_with_slash(buf, url);1795
1796strbuf_addf(buf, "objects/%.*s/", 2, hex);1797if (!only_two_digit_prefix)1798strbuf_addstr(buf, hex + 2);1799}
1800
1801char *get_remote_object_url(const char *url, const char *hex,1802int only_two_digit_prefix)1803{
1804struct strbuf buf = STRBUF_INIT;1805append_remote_object_url(&buf, url, hex, only_two_digit_prefix);1806return strbuf_detach(&buf, NULL);1807}
1808
1809void normalize_curl_result(CURLcode *result, long http_code,1810char *errorstr, size_t errorlen)1811{
1812/*1813* If we see a failing http code with CURLE_OK, we have turned off
1814* FAILONERROR (to keep the server's custom error response), and should
1815* translate the code into failure here.
1816*
1817* Likewise, if we see a redirect (30x code), that means we turned off
1818* redirect-following, and we should treat the result as an error.
1819*/
1820if (*result == CURLE_OK && http_code >= 300) {1821*result = CURLE_HTTP_RETURNED_ERROR;1822/*1823* Normally curl will already have put the "reason phrase"
1824* from the server into curl_errorstr; unfortunately without
1825* FAILONERROR it is lost, so we can give only the numeric
1826* status code.
1827*/
1828xsnprintf(errorstr, errorlen,1829"The requested URL returned error: %ld",1830http_code);1831}1832}
1833
1834static int handle_curl_result(struct slot_results *results)1835{
1836normalize_curl_result(&results->curl_result, results->http_code,1837curl_errorstr, sizeof(curl_errorstr));1838
1839if (results->curl_result == CURLE_OK) {1840credential_approve(&http_auth);1841credential_approve(&proxy_auth);1842credential_approve(&cert_auth);1843return HTTP_OK;1844} else if (results->curl_result == CURLE_SSL_CERTPROBLEM) {1845/*1846* We can't tell from here whether it's a bad path, bad
1847* certificate, bad password, or something else wrong
1848* with the certificate. So we reject the credential to
1849* avoid caching or saving a bad password.
1850*/
1851credential_reject(&cert_auth);1852return HTTP_NOAUTH;1853#ifdef GIT_CURL_HAVE_CURLE_SSL_PINNEDPUBKEYNOTMATCH1854} else if (results->curl_result == CURLE_SSL_PINNEDPUBKEYNOTMATCH) {1855return HTTP_NOMATCHPUBLICKEY;1856#endif1857} else if (missing_target(results))1858return HTTP_MISSING_TARGET;1859else if (results->http_code == 401) {1860if ((http_auth.username && http_auth.password) ||\1861(http_auth.authtype && http_auth.credential)) {1862if (http_auth.multistage) {1863credential_clear_secrets(&http_auth);1864return HTTP_REAUTH;1865}1866credential_reject(&http_auth);1867if (always_auth_proactively())1868http_proactive_auth = PROACTIVE_AUTH_NONE;1869return HTTP_NOAUTH;1870} else {1871http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;1872if (results->auth_avail) {1873http_auth_methods &= results->auth_avail;1874http_auth_methods_restricted = 1;1875}1876return HTTP_REAUTH;1877}1878} else {1879if (results->http_connectcode == 407)1880credential_reject(&proxy_auth);1881if (!curl_errorstr[0])1882strlcpy(curl_errorstr,1883curl_easy_strerror(results->curl_result),1884sizeof(curl_errorstr));1885return HTTP_ERROR;1886}1887}
1888
1889int run_one_slot(struct active_request_slot *slot,1890struct slot_results *results)1891{
1892slot->results = results;1893if (!start_active_slot(slot)) {1894xsnprintf(curl_errorstr, sizeof(curl_errorstr),1895"failed to start HTTP request");1896return HTTP_START_FAILED;1897}1898
1899run_active_slot(slot);1900return handle_curl_result(results);1901}
1902
1903struct curl_slist *http_copy_default_headers(void)1904{
1905struct curl_slist *headers = NULL;1906const struct string_list_item *item;1907
1908for_each_string_list_item(item, &extra_http_headers)1909headers = curl_slist_append(headers, item->string);1910
1911return headers;1912}
1913
1914static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf)1915{
1916char *ptr;1917CURLcode ret;1918
1919strbuf_reset(buf);1920ret = curl_easy_getinfo(curl, info, &ptr);1921if (!ret && ptr)1922strbuf_addstr(buf, ptr);1923return ret;1924}
1925
1926/*
1927* Check for and extract a content-type parameter. "raw"
1928* should be positioned at the start of the potential
1929* parameter, with any whitespace already removed.
1930*
1931* "name" is the name of the parameter. The value is appended
1932* to "out".
1933*/
1934static int extract_param(const char *raw, const char *name,1935struct strbuf *out)1936{
1937size_t len = strlen(name);1938
1939if (strncasecmp(raw, name, len))1940return -1;1941raw += len;1942
1943if (*raw != '=')1944return -1;1945raw++;1946
1947while (*raw && !isspace(*raw) && *raw != ';')1948strbuf_addch(out, *raw++);1949return 0;1950}
1951
1952/*
1953* Extract a normalized version of the content type, with any
1954* spaces suppressed, all letters lowercased, and no trailing ";"
1955* or parameters.
1956*
1957* Note that we will silently remove even invalid whitespace. For
1958* example, "text / plain" is specifically forbidden by RFC 2616,
1959* but "text/plain" is the only reasonable output, and this keeps
1960* our code simple.
1961*
1962* If the "charset" argument is not NULL, store the value of any
1963* charset parameter there.
1964*
1965* Example:
1966* "TEXT/PLAIN; charset=utf-8" -> "text/plain", "utf-8"
1967* "text / plain" -> "text/plain"
1968*/
1969static void extract_content_type(struct strbuf *raw, struct strbuf *type,1970struct strbuf *charset)1971{
1972const char *p;1973
1974strbuf_reset(type);1975strbuf_grow(type, raw->len);1976for (p = raw->buf; *p; p++) {1977if (isspace(*p))1978continue;1979if (*p == ';') {1980p++;1981break;1982}1983strbuf_addch(type, tolower(*p));1984}1985
1986if (!charset)1987return;1988
1989strbuf_reset(charset);1990while (*p) {1991while (isspace(*p) || *p == ';')1992p++;1993if (!extract_param(p, "charset", charset))1994return;1995while (*p && !isspace(*p))1996p++;1997}1998
1999if (!charset->len && starts_with(type->buf, "text/"))2000strbuf_addstr(charset, "ISO-8859-1");2001}
2002
2003static void write_accept_language(struct strbuf *buf)2004{
2005/*2006* MAX_DECIMAL_PLACES must not be larger than 3. If it is larger than
2007* that, q-value will be smaller than 0.001, the minimum q-value the
2008* HTTP specification allows. See
2009* https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.1 for q-value.
2010*/
2011const int MAX_DECIMAL_PLACES = 3;2012const int MAX_LANGUAGE_TAGS = 1000;2013const int MAX_ACCEPT_LANGUAGE_HEADER_SIZE = 4000;2014char **language_tags = NULL;2015int num_langs = 0;2016const char *s = get_preferred_languages();2017int i;2018struct strbuf tag = STRBUF_INIT;2019
2020/* Don't add Accept-Language header if no language is preferred. */2021if (!s)2022return;2023
2024/*2025* Split the colon-separated string of preferred languages into
2026* language_tags array.
2027*/
2028do {2029/* collect language tag */2030for (; *s && (isalnum(*s) || *s == '_'); s++)2031strbuf_addch(&tag, *s == '_' ? '-' : *s);2032
2033/* skip .codeset, @modifier and any other unnecessary parts */2034while (*s && *s != ':')2035s++;2036
2037if (tag.len) {2038num_langs++;2039REALLOC_ARRAY(language_tags, num_langs);2040language_tags[num_langs - 1] = strbuf_detach(&tag, NULL);2041if (num_langs >= MAX_LANGUAGE_TAGS - 1) /* -1 for '*' */2042break;2043}2044} while (*s++);2045
2046/* write Accept-Language header into buf */2047if (num_langs) {2048int last_buf_len = 0;2049int max_q;2050int decimal_places;2051char q_format[32];2052
2053/* add '*' */2054REALLOC_ARRAY(language_tags, num_langs + 1);2055language_tags[num_langs++] = xstrdup("*");2056
2057/* compute decimal_places */2058for (max_q = 1, decimal_places = 0;2059max_q < num_langs && decimal_places <= MAX_DECIMAL_PLACES;2060decimal_places++, max_q *= 10)2061;2062
2063xsnprintf(q_format, sizeof(q_format), ";q=0.%%0%dd", decimal_places);2064
2065strbuf_addstr(buf, "Accept-Language: ");2066
2067for (i = 0; i < num_langs; i++) {2068if (i > 0)2069strbuf_addstr(buf, ", ");2070
2071strbuf_addstr(buf, language_tags[i]);2072
2073if (i > 0)2074strbuf_addf(buf, q_format, max_q - i);2075
2076if (buf->len > MAX_ACCEPT_LANGUAGE_HEADER_SIZE) {2077strbuf_remove(buf, last_buf_len, buf->len - last_buf_len);2078break;2079}2080
2081last_buf_len = buf->len;2082}2083}2084
2085for (i = 0; i < num_langs; i++)2086free(language_tags[i]);2087free(language_tags);2088}
2089
2090/*
2091* Get an Accept-Language header which indicates user's preferred languages.
2092*
2093* Examples:
2094* LANGUAGE= -> ""
2095* LANGUAGE=ko:en -> "Accept-Language: ko, en; q=0.9, *; q=0.1"
2096* LANGUAGE=ko_KR.UTF-8:sr@latin -> "Accept-Language: ko-KR, sr; q=0.9, *; q=0.1"
2097* LANGUAGE=ko LANG=en_US.UTF-8 -> "Accept-Language: ko, *; q=0.1"
2098* LANGUAGE= LANG=en_US.UTF-8 -> "Accept-Language: en-US, *; q=0.1"
2099* LANGUAGE= LANG=C -> ""
2100*/
2101const char *http_get_accept_language_header(void)2102{
2103if (!cached_accept_language) {2104struct strbuf buf = STRBUF_INIT;2105write_accept_language(&buf);2106if (buf.len > 0)2107cached_accept_language = strbuf_detach(&buf, NULL);2108}2109
2110return cached_accept_language;2111}
2112
2113static void http_opt_request_remainder(CURL *curl, off_t pos)2114{
2115char buf[128];2116xsnprintf(buf, sizeof(buf), "%"PRIuMAX"-", (uintmax_t)pos);2117curl_easy_setopt(curl, CURLOPT_RANGE, buf);2118}
2119
2120/* http_request() targets */
2121#define HTTP_REQUEST_STRBUF 02122#define HTTP_REQUEST_FILE 12123
2124static int http_request(const char *url,2125void *result, int target,2126const struct http_get_options *options)2127{
2128struct active_request_slot *slot;2129struct slot_results results;2130struct curl_slist *headers = http_copy_default_headers();2131struct strbuf buf = STRBUF_INIT;2132const char *accept_language;2133int ret;2134
2135slot = get_active_slot();2136curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);2137
2138if (!result) {2139curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 1);2140} else {2141curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);2142curl_easy_setopt(slot->curl, CURLOPT_WRITEDATA, result);2143
2144if (target == HTTP_REQUEST_FILE) {2145off_t posn = ftello(result);2146curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,2147fwrite);2148if (posn > 0)2149http_opt_request_remainder(slot->curl, posn);2150} else2151curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION,2152fwrite_buffer);2153}2154
2155curl_easy_setopt(slot->curl, CURLOPT_HEADERFUNCTION, fwrite_wwwauth);2156
2157accept_language = http_get_accept_language_header();2158
2159if (accept_language)2160headers = curl_slist_append(headers, accept_language);2161
2162strbuf_addstr(&buf, "Pragma:");2163if (options && options->no_cache)2164strbuf_addstr(&buf, " no-cache");2165if (options && options->initial_request &&2166http_follow_config == HTTP_FOLLOW_INITIAL)2167curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1);2168
2169headers = curl_slist_append(headers, buf.buf);2170
2171/* Add additional headers here */2172if (options && options->extra_headers) {2173const struct string_list_item *item;2174if (options && options->extra_headers) {2175for_each_string_list_item(item, options->extra_headers) {2176headers = curl_slist_append(headers, item->string);2177}2178}2179}2180
2181headers = http_append_auth_header(&http_auth, headers);2182
2183curl_easy_setopt(slot->curl, CURLOPT_URL, url);2184curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);2185curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");2186curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);2187
2188ret = run_one_slot(slot, &results);2189
2190if (options && options->content_type) {2191struct strbuf raw = STRBUF_INIT;2192curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE, &raw);2193extract_content_type(&raw, options->content_type,2194options->charset);2195strbuf_release(&raw);2196}2197
2198if (options && options->effective_url)2199curlinfo_strbuf(slot->curl, CURLINFO_EFFECTIVE_URL,2200options->effective_url);2201
2202curl_slist_free_all(headers);2203strbuf_release(&buf);2204
2205return ret;2206}
2207
2208/*
2209* Update the "base" url to a more appropriate value, as deduced by
2210* redirects seen when requesting a URL starting with "url".
2211*
2212* The "asked" parameter is a URL that we asked curl to access, and must begin
2213* with "base".
2214*
2215* The "got" parameter is the URL that curl reported to us as where we ended
2216* up.
2217*
2218* Returns 1 if we updated the base url, 0 otherwise.
2219*
2220* Our basic strategy is to compare "base" and "asked" to find the bits
2221* specific to our request. We then strip those bits off of "got" to yield the
2222* new base. So for example, if our base is "http://example.com/foo.git",
2223* and we ask for "http://example.com/foo.git/info/refs", we might end up
2224* with "https://other.example.com/foo.git/info/refs". We would want the
2225* new URL to become "https://other.example.com/foo.git".
2226*
2227* Note that this assumes a sane redirect scheme. It's entirely possible
2228* in the example above to end up at a URL that does not even end in
2229* "info/refs". In such a case we die. There's not much we can do, such a
2230* scheme is unlikely to represent a real git repository, and failing to
2231* rewrite the base opens options for malicious redirects to do funny things.
2232*/
2233static int update_url_from_redirect(struct strbuf *base,2234const char *asked,2235const struct strbuf *got)2236{
2237const char *tail;2238size_t new_len;2239
2240if (!strcmp(asked, got->buf))2241return 0;2242
2243if (!skip_prefix(asked, base->buf, &tail))2244BUG("update_url_from_redirect: %s is not a superset of %s",2245asked, base->buf);2246
2247new_len = got->len;2248if (!strip_suffix_mem(got->buf, &new_len, tail))2249die(_("unable to update url base from redirection:\n"2250" asked for: %s\n"2251" redirect: %s"),2252asked, got->buf);2253
2254strbuf_reset(base);2255strbuf_add(base, got->buf, new_len);2256
2257return 1;2258}
2259
2260static int http_request_reauth(const char *url,2261void *result, int target,2262struct http_get_options *options)2263{
2264int i = 3;2265int ret;2266
2267if (always_auth_proactively())2268credential_fill(&http_auth, 1);2269
2270ret = http_request(url, result, target, options);2271
2272if (ret != HTTP_OK && ret != HTTP_REAUTH)2273return ret;2274
2275if (options && options->effective_url && options->base_url) {2276if (update_url_from_redirect(options->base_url,2277url, options->effective_url)) {2278credential_from_url(&http_auth, options->base_url->buf);2279url = options->effective_url->buf;2280}2281}2282
2283while (ret == HTTP_REAUTH && --i) {2284/*2285* The previous request may have put cruft into our output stream; we
2286* should clear it out before making our next request.
2287*/
2288switch (target) {2289case HTTP_REQUEST_STRBUF:2290strbuf_reset(result);2291break;2292case HTTP_REQUEST_FILE:2293if (fflush(result)) {2294error_errno("unable to flush a file");2295return HTTP_START_FAILED;2296}2297rewind(result);2298if (ftruncate(fileno(result), 0) < 0) {2299error_errno("unable to truncate a file");2300return HTTP_START_FAILED;2301}2302break;2303default:2304BUG("Unknown http_request target");2305}2306
2307credential_fill(&http_auth, 1);2308
2309ret = http_request(url, result, target, options);2310}2311return ret;2312}
2313
2314int http_get_strbuf(const char *url,2315struct strbuf *result,2316struct http_get_options *options)2317{
2318return http_request_reauth(url, result, HTTP_REQUEST_STRBUF, options);2319}
2320
2321/*
2322* Downloads a URL and stores the result in the given file.
2323*
2324* If a previous interrupted download is detected (i.e. a previous temporary
2325* file is still around) the download is resumed.
2326*/
2327int http_get_file(const char *url, const char *filename,2328struct http_get_options *options)2329{
2330int ret;2331struct strbuf tmpfile = STRBUF_INIT;2332FILE *result;2333
2334strbuf_addf(&tmpfile, "%s.temp", filename);2335result = fopen(tmpfile.buf, "a");2336if (!result) {2337error("Unable to open local file %s", tmpfile.buf);2338ret = HTTP_ERROR;2339goto cleanup;2340}2341
2342ret = http_request_reauth(url, result, HTTP_REQUEST_FILE, options);2343fclose(result);2344
2345if (ret == HTTP_OK && finalize_object_file(tmpfile.buf, filename))2346ret = HTTP_ERROR;2347cleanup:2348strbuf_release(&tmpfile);2349return ret;2350}
2351
2352int http_fetch_ref(const char *base, struct ref *ref)2353{
2354struct http_get_options options = {0};2355char *url;2356struct strbuf buffer = STRBUF_INIT;2357int ret = -1;2358
2359options.no_cache = 1;2360
2361url = quote_ref_url(base, ref->name);2362if (http_get_strbuf(url, &buffer, &options) == HTTP_OK) {2363strbuf_rtrim(&buffer);2364if (buffer.len == the_hash_algo->hexsz)2365ret = get_oid_hex(buffer.buf, &ref->old_oid);2366else if (starts_with(buffer.buf, "ref: ")) {2367ref->symref = xstrdup(buffer.buf + 5);2368ret = 0;2369}2370}2371
2372strbuf_release(&buffer);2373free(url);2374return ret;2375}
2376
2377/* Helpers for fetching packs */
2378static char *fetch_pack_index(unsigned char *hash, const char *base_url)2379{
2380char *url, *tmp;2381struct strbuf buf = STRBUF_INIT;2382
2383if (http_is_verbose)2384fprintf(stderr, "Getting index for pack %s\n", hash_to_hex(hash));2385
2386end_url_with_slash(&buf, base_url);2387strbuf_addf(&buf, "objects/pack/pack-%s.idx", hash_to_hex(hash));2388url = strbuf_detach(&buf, NULL);2389
2390strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(hash));2391tmp = strbuf_detach(&buf, NULL);2392
2393if (http_get_file(url, tmp, NULL) != HTTP_OK) {2394error("Unable to get pack index %s", url);2395FREE_AND_NULL(tmp);2396}2397
2398free(url);2399return tmp;2400}
2401
2402static int fetch_and_setup_pack_index(struct packed_git **packs_head,2403unsigned char *sha1, const char *base_url)2404{
2405struct packed_git *new_pack;2406char *tmp_idx = NULL;2407int ret;2408
2409if (has_pack_index(sha1)) {2410new_pack = parse_pack_index(sha1, sha1_pack_index_name(sha1));2411if (!new_pack)2412return -1; /* parse_pack_index() already issued error message */2413goto add_pack;2414}2415
2416tmp_idx = fetch_pack_index(sha1, base_url);2417if (!tmp_idx)2418return -1;2419
2420new_pack = parse_pack_index(sha1, tmp_idx);2421if (!new_pack) {2422unlink(tmp_idx);2423free(tmp_idx);2424
2425return -1; /* parse_pack_index() already issued error message */2426}2427
2428ret = verify_pack_index(new_pack);2429if (!ret) {2430close_pack_index(new_pack);2431ret = finalize_object_file(tmp_idx, sha1_pack_index_name(sha1));2432}2433free(tmp_idx);2434if (ret)2435return -1;2436
2437add_pack:2438new_pack->next = *packs_head;2439*packs_head = new_pack;2440return 0;2441}
2442
2443int http_get_info_packs(const char *base_url, struct packed_git **packs_head)2444{
2445struct http_get_options options = {0};2446int ret = 0;2447char *url;2448const char *data;2449struct strbuf buf = STRBUF_INIT;2450struct object_id oid;2451
2452end_url_with_slash(&buf, base_url);2453strbuf_addstr(&buf, "objects/info/packs");2454url = strbuf_detach(&buf, NULL);2455
2456options.no_cache = 1;2457ret = http_get_strbuf(url, &buf, &options);2458if (ret != HTTP_OK)2459goto cleanup;2460
2461data = buf.buf;2462while (*data) {2463if (skip_prefix(data, "P pack-", &data) &&2464!parse_oid_hex(data, &oid, &data) &&2465skip_prefix(data, ".pack", &data) &&2466(*data == '\n' || *data == '\0')) {2467fetch_and_setup_pack_index(packs_head, oid.hash, base_url);2468} else {2469data = strchrnul(data, '\n');2470}2471if (*data)2472data++; /* skip past newline */2473}2474
2475cleanup:2476free(url);2477return ret;2478}
2479
2480void release_http_pack_request(struct http_pack_request *preq)2481{
2482if (preq->packfile) {2483fclose(preq->packfile);2484preq->packfile = NULL;2485}2486preq->slot = NULL;2487strbuf_release(&preq->tmpfile);2488curl_slist_free_all(preq->headers);2489free(preq->url);2490free(preq);2491}
2492
2493static const char *default_index_pack_args[] =2494{"index-pack", "--stdin", NULL};2495
2496int finish_http_pack_request(struct http_pack_request *preq)2497{
2498struct child_process ip = CHILD_PROCESS_INIT;2499int tmpfile_fd;2500int ret = 0;2501
2502fclose(preq->packfile);2503preq->packfile = NULL;2504
2505tmpfile_fd = xopen(preq->tmpfile.buf, O_RDONLY);2506
2507ip.git_cmd = 1;2508ip.in = tmpfile_fd;2509strvec_pushv(&ip.args, preq->index_pack_args ?2510preq->index_pack_args :2511default_index_pack_args);2512
2513if (preq->preserve_index_pack_stdout)2514ip.out = 0;2515else2516ip.no_stdout = 1;2517
2518if (run_command(&ip)) {2519ret = -1;2520goto cleanup;2521}2522
2523cleanup:2524close(tmpfile_fd);2525unlink(preq->tmpfile.buf);2526return ret;2527}
2528
2529void http_install_packfile(struct packed_git *p,2530struct packed_git **list_to_remove_from)2531{
2532struct packed_git **lst = list_to_remove_from;2533
2534while (*lst != p)2535lst = &((*lst)->next);2536*lst = (*lst)->next;2537
2538install_packed_git(the_repository, p);2539}
2540
2541struct http_pack_request *new_http_pack_request(2542const unsigned char *packed_git_hash, const char *base_url) {2543
2544struct strbuf buf = STRBUF_INIT;2545
2546end_url_with_slash(&buf, base_url);2547strbuf_addf(&buf, "objects/pack/pack-%s.pack",2548hash_to_hex(packed_git_hash));2549return new_direct_http_pack_request(packed_git_hash,2550strbuf_detach(&buf, NULL));2551}
2552
2553struct http_pack_request *new_direct_http_pack_request(2554const unsigned char *packed_git_hash, char *url)2555{
2556off_t prev_posn = 0;2557struct http_pack_request *preq;2558
2559CALLOC_ARRAY(preq, 1);2560strbuf_init(&preq->tmpfile, 0);2561
2562preq->url = url;2563
2564strbuf_addf(&preq->tmpfile, "%s.temp", sha1_pack_name(packed_git_hash));2565preq->packfile = fopen(preq->tmpfile.buf, "a");2566if (!preq->packfile) {2567error("Unable to open local file %s for pack",2568preq->tmpfile.buf);2569goto abort;2570}2571
2572preq->slot = get_active_slot();2573preq->headers = object_request_headers();2574curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEDATA, preq->packfile);2575curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);2576curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);2577curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER, preq->headers);2578
2579/*2580* If there is data present from a previous transfer attempt,
2581* resume where it left off
2582*/
2583prev_posn = ftello(preq->packfile);2584if (prev_posn>0) {2585if (http_is_verbose)2586fprintf(stderr,2587"Resuming fetch of pack %s at byte %"PRIuMAX"\n",2588hash_to_hex(packed_git_hash),2589(uintmax_t)prev_posn);2590http_opt_request_remainder(preq->slot->curl, prev_posn);2591}2592
2593return preq;2594
2595abort:2596strbuf_release(&preq->tmpfile);2597free(preq->url);2598free(preq);2599return NULL;2600}
2601
2602/* Helpers for fetching objects (loose) */
2603static size_t fwrite_sha1_file(char *ptr, size_t eltsize, size_t nmemb,2604void *data)2605{
2606unsigned char expn[4096];2607size_t size = eltsize * nmemb;2608int posn = 0;2609struct http_object_request *freq = data;2610struct active_request_slot *slot = freq->slot;2611
2612if (slot) {2613CURLcode c = curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE,2614&slot->http_code);2615if (c != CURLE_OK)2616BUG("curl_easy_getinfo for HTTP code failed: %s",2617curl_easy_strerror(c));2618if (slot->http_code >= 300)2619return nmemb;2620}2621
2622do {2623ssize_t retval = xwrite(freq->localfile,2624(char *) ptr + posn, size - posn);2625if (retval < 0)2626return posn / eltsize;2627posn += retval;2628} while (posn < size);2629
2630freq->stream.avail_in = size;2631freq->stream.next_in = (void *)ptr;2632do {2633freq->stream.next_out = expn;2634freq->stream.avail_out = sizeof(expn);2635freq->zret = git_inflate(&freq->stream, Z_SYNC_FLUSH);2636the_hash_algo->update_fn(&freq->c, expn,2637sizeof(expn) - freq->stream.avail_out);2638} while (freq->stream.avail_in && freq->zret == Z_OK);2639return nmemb;2640}
2641
2642struct http_object_request *new_http_object_request(const char *base_url,2643const struct object_id *oid)2644{
2645char *hex = oid_to_hex(oid);2646struct strbuf filename = STRBUF_INIT;2647struct strbuf prevfile = STRBUF_INIT;2648int prevlocal;2649char prev_buf[PREV_BUF_SIZE];2650ssize_t prev_read = 0;2651off_t prev_posn = 0;2652struct http_object_request *freq;2653
2654CALLOC_ARRAY(freq, 1);2655strbuf_init(&freq->tmpfile, 0);2656oidcpy(&freq->oid, oid);2657freq->localfile = -1;2658
2659loose_object_path(the_repository, &filename, oid);2660strbuf_addf(&freq->tmpfile, "%s.temp", filename.buf);2661
2662strbuf_addf(&prevfile, "%s.prev", filename.buf);2663unlink_or_warn(prevfile.buf);2664rename(freq->tmpfile.buf, prevfile.buf);2665unlink_or_warn(freq->tmpfile.buf);2666strbuf_release(&filename);2667
2668if (freq->localfile != -1)2669error("fd leakage in start: %d", freq->localfile);2670freq->localfile = open(freq->tmpfile.buf,2671O_WRONLY | O_CREAT | O_EXCL, 0666);2672/*2673* This could have failed due to the "lazy directory creation";
2674* try to mkdir the last path component.
2675*/
2676if (freq->localfile < 0 && errno == ENOENT) {2677char *dir = strrchr(freq->tmpfile.buf, '/');2678if (dir) {2679*dir = 0;2680mkdir(freq->tmpfile.buf, 0777);2681*dir = '/';2682}2683freq->localfile = open(freq->tmpfile.buf,2684O_WRONLY | O_CREAT | O_EXCL, 0666);2685}2686
2687if (freq->localfile < 0) {2688error_errno("Couldn't create temporary file %s",2689freq->tmpfile.buf);2690goto abort;2691}2692
2693git_inflate_init(&freq->stream);2694
2695the_hash_algo->init_fn(&freq->c);2696
2697freq->url = get_remote_object_url(base_url, hex, 0);2698
2699/*2700* If a previous temp file is present, process what was already
2701* fetched.
2702*/
2703prevlocal = open(prevfile.buf, O_RDONLY);2704if (prevlocal != -1) {2705do {2706prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);2707if (prev_read>0) {2708if (fwrite_sha1_file(prev_buf,27091,2710prev_read,2711freq) == prev_read) {2712prev_posn += prev_read;2713} else {2714prev_read = -1;2715}2716}2717} while (prev_read > 0);2718close(prevlocal);2719}2720unlink_or_warn(prevfile.buf);2721strbuf_release(&prevfile);2722
2723/*2724* Reset inflate/SHA1 if there was an error reading the previous temp
2725* file; also rewind to the beginning of the local file.
2726*/
2727if (prev_read == -1) {2728memset(&freq->stream, 0, sizeof(freq->stream));2729git_inflate_init(&freq->stream);2730the_hash_algo->init_fn(&freq->c);2731if (prev_posn>0) {2732prev_posn = 0;2733lseek(freq->localfile, 0, SEEK_SET);2734if (ftruncate(freq->localfile, 0) < 0) {2735error_errno("Couldn't truncate temporary file %s",2736freq->tmpfile.buf);2737goto abort;2738}2739}2740}2741
2742freq->slot = get_active_slot();2743freq->headers = object_request_headers();2744
2745curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEDATA, freq);2746curl_easy_setopt(freq->slot->curl, CURLOPT_FAILONERROR, 0);2747curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);2748curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);2749curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);2750curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, freq->headers);2751
2752/*2753* If we have successfully processed data from a previous fetch
2754* attempt, only fetch the data we don't already have.
2755*/
2756if (prev_posn>0) {2757if (http_is_verbose)2758fprintf(stderr,2759"Resuming fetch of object %s at byte %"PRIuMAX"\n",2760hex, (uintmax_t)prev_posn);2761http_opt_request_remainder(freq->slot->curl, prev_posn);2762}2763
2764return freq;2765
2766abort:2767strbuf_release(&prevfile);2768free(freq->url);2769free(freq);2770return NULL;2771}
2772
2773void process_http_object_request(struct http_object_request *freq)2774{
2775if (!freq->slot)2776return;2777freq->curl_result = freq->slot->curl_result;2778freq->http_code = freq->slot->http_code;2779freq->slot = NULL;2780}
2781
2782int finish_http_object_request(struct http_object_request *freq)2783{
2784struct stat st;2785struct strbuf filename = STRBUF_INIT;2786
2787close(freq->localfile);2788freq->localfile = -1;2789
2790process_http_object_request(freq);2791
2792if (freq->http_code == 416) {2793warning("requested range invalid; we may already have all the data.");2794} else if (freq->curl_result != CURLE_OK) {2795if (stat(freq->tmpfile.buf, &st) == 0)2796if (st.st_size == 0)2797unlink_or_warn(freq->tmpfile.buf);2798return -1;2799}2800
2801git_inflate_end(&freq->stream);2802the_hash_algo->final_oid_fn(&freq->real_oid, &freq->c);2803if (freq->zret != Z_STREAM_END) {2804unlink_or_warn(freq->tmpfile.buf);2805return -1;2806}2807if (!oideq(&freq->oid, &freq->real_oid)) {2808unlink_or_warn(freq->tmpfile.buf);2809return -1;2810}2811loose_object_path(the_repository, &filename, &freq->oid);2812freq->rename = finalize_object_file(freq->tmpfile.buf, filename.buf);2813strbuf_release(&filename);2814
2815return freq->rename;2816}
2817
2818void abort_http_object_request(struct http_object_request *freq)2819{
2820unlink_or_warn(freq->tmpfile.buf);2821
2822release_http_object_request(freq);2823}
2824
2825void release_http_object_request(struct http_object_request *freq)2826{
2827if (freq->localfile != -1) {2828close(freq->localfile);2829freq->localfile = -1;2830}2831FREE_AND_NULL(freq->url);2832if (freq->slot) {2833freq->slot->callback_func = NULL;2834freq->slot->callback_data = NULL;2835release_active_slot(freq->slot);2836freq->slot = NULL;2837}2838curl_slist_free_all(freq->headers);2839strbuf_release(&freq->tmpfile);2840}
2841