Skip to content

Commit 00090c8

Browse files
committed
http: support sending custom HTTP headers
We introduce a way to send custom HTTP headers with all requests. This allows us, for example, to send an extra token from build agents for temporary access to private repositories. (This is the use case that triggered this patch.) This feature can be used like this: git -c http.extraheader='Secret: sssh!' fetch $URL $REF Note that `curl_easy_setopt(..., CURLOPT_HTTPHEADER, ...)` takes only a single list, overriding any previous call. This means we have to collect _all_ of the headers we want to use into a single list, and feed it to cURL in one shot. Since we already unconditionally set a "pragma" header when initializing the curl handles, we can add our new headers to that list. For callers which override the default header list (like probe_rpc), we provide `http_copy_default_headers()` so they can do the same trick. Big thanks to Jeff King and Junio Hamano for their outstanding help and patient reviews. Signed-off-by: Johannes Schindelin <[email protected]> Reviewed-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]> Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 0f8e831 commit 00090c8

File tree

7 files changed

+61
-10
lines changed

7 files changed

+61
-10
lines changed

Documentation/config.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1654,6 +1654,12 @@ http.emptyAuth::
16541654
a username in the URL, as libcurl normally requires a username for
16551655
authentication.
16561656

1657+
http.extraHeader::
1658+
Pass an additional HTTP header when communicating with a server. If
1659+
more than one such entry exists, all of them are added as extra
1660+
headers. To allow overriding the settings inherited from the system
1661+
config, an empty value will reset the extra headers to the empty list.
1662+
16571663
http.cookieFile::
16581664
File containing previously stored cookie lines which should be used
16591665
in the Git http session, if they match the server. The file format

http-push.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ static void curl_setup_http(CURL *curl, const char *url,
211211
static struct curl_slist *get_dav_token_headers(struct remote_lock *lock, enum dav_header_flag options)
212212
{
213213
struct strbuf buf = STRBUF_INIT;
214-
struct curl_slist *dav_headers = NULL;
214+
struct curl_slist *dav_headers = http_copy_default_headers();
215215

216216
if (options & DAV_HEADER_IF) {
217217
strbuf_addf(&buf, "If: (<%s>)", lock->token);
@@ -417,7 +417,7 @@ static void start_put(struct transfer_request *request)
417417
static void start_move(struct transfer_request *request)
418418
{
419419
struct active_request_slot *slot;
420-
struct curl_slist *dav_headers = NULL;
420+
struct curl_slist *dav_headers = http_copy_default_headers();
421421

422422
slot = get_active_slot();
423423
slot->callback_func = process_response;
@@ -845,7 +845,7 @@ static struct remote_lock *lock_remote(const char *path, long timeout)
845845
char *ep;
846846
char timeout_header[25];
847847
struct remote_lock *lock = NULL;
848-
struct curl_slist *dav_headers = NULL;
848+
struct curl_slist *dav_headers = http_copy_default_headers();
849849
struct xml_ctx ctx;
850850
char *escaped;
851851

@@ -1126,7 +1126,7 @@ static void remote_ls(const char *path, int flags,
11261126
struct slot_results results;
11271127
struct strbuf in_buffer = STRBUF_INIT;
11281128
struct buffer out_buffer = { STRBUF_INIT, 0 };
1129-
struct curl_slist *dav_headers = NULL;
1129+
struct curl_slist *dav_headers = http_copy_default_headers();
11301130
struct xml_ctx ctx;
11311131
struct remote_ls_ctx ls;
11321132

@@ -1204,7 +1204,7 @@ static int locking_available(void)
12041204
struct slot_results results;
12051205
struct strbuf in_buffer = STRBUF_INIT;
12061206
struct buffer out_buffer = { STRBUF_INIT, 0 };
1207-
struct curl_slist *dav_headers = NULL;
1207+
struct curl_slist *dav_headers = http_copy_default_headers();
12081208
struct xml_ctx ctx;
12091209
int lock_flags = 0;
12101210
char *escaped;

http.c

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ static unsigned long http_auth_methods = CURLAUTH_ANY;
114114

115115
static struct curl_slist *pragma_header;
116116
static struct curl_slist *no_pragma_header;
117+
static struct curl_slist *extra_http_headers;
117118

118119
static struct active_request_slot *active_queue_head;
119120

@@ -323,6 +324,19 @@ static int http_options(const char *var, const char *value, void *cb)
323324
#endif
324325
}
325326

327+
if (!strcmp("http.extraheader", var)) {
328+
if (!value) {
329+
return config_error_nonbool(var);
330+
} else if (!*value) {
331+
curl_slist_free_all(extra_http_headers);
332+
extra_http_headers = NULL;
333+
} else {
334+
extra_http_headers =
335+
curl_slist_append(extra_http_headers, value);
336+
}
337+
return 0;
338+
}
339+
326340
/* Fall back on the default ones */
327341
return git_default_config(var, value, cb);
328342
}
@@ -678,8 +692,10 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
678692
if (remote)
679693
var_override(&http_proxy_authmethod, remote->http_proxy_authmethod);
680694

681-
pragma_header = curl_slist_append(pragma_header, "Pragma: no-cache");
682-
no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
695+
pragma_header = curl_slist_append(http_copy_default_headers(),
696+
"Pragma: no-cache");
697+
no_pragma_header = curl_slist_append(http_copy_default_headers(),
698+
"Pragma:");
683699

684700
#ifdef USE_CURL_MULTI
685701
{
@@ -765,6 +781,9 @@ void http_cleanup(void)
765781
#endif
766782
curl_global_cleanup();
767783

784+
curl_slist_free_all(extra_http_headers);
785+
extra_http_headers = NULL;
786+
768787
curl_slist_free_all(pragma_header);
769788
pragma_header = NULL;
770789

@@ -1163,6 +1182,16 @@ int run_one_slot(struct active_request_slot *slot,
11631182
return handle_curl_result(results);
11641183
}
11651184

1185+
struct curl_slist *http_copy_default_headers(void)
1186+
{
1187+
struct curl_slist *headers = NULL, *h;
1188+
1189+
for (h = extra_http_headers; h; h = h->next)
1190+
headers = curl_slist_append(headers, h->data);
1191+
1192+
return headers;
1193+
}
1194+
11661195
static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf)
11671196
{
11681197
char *ptr;
@@ -1380,7 +1409,7 @@ static int http_request(const char *url,
13801409
{
13811410
struct active_request_slot *slot;
13821411
struct slot_results results;
1383-
struct curl_slist *headers = NULL;
1412+
struct curl_slist *headers = http_copy_default_headers();
13841413
struct strbuf buf = STRBUF_INIT;
13851414
const char *accept_language;
13861415
int ret;

http.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ extern void step_active_slots(void);
106106
extern void http_init(struct remote *remote, const char *url,
107107
int proactive_auth);
108108
extern void http_cleanup(void);
109+
extern struct curl_slist *http_copy_default_headers(void);
109110

110111
extern long int git_curl_ipresolve;
111112
extern int active_requests;

remote-curl.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ static int run_slot(struct active_request_slot *slot,
474474
static int probe_rpc(struct rpc_state *rpc, struct slot_results *results)
475475
{
476476
struct active_request_slot *slot;
477-
struct curl_slist *headers = NULL;
477+
struct curl_slist *headers = http_copy_default_headers();
478478
struct strbuf buf = STRBUF_INIT;
479479
int err;
480480

@@ -503,7 +503,7 @@ static int probe_rpc(struct rpc_state *rpc, struct slot_results *results)
503503
static int post_rpc(struct rpc_state *rpc)
504504
{
505505
struct active_request_slot *slot;
506-
struct curl_slist *headers = NULL;
506+
struct curl_slist *headers = http_copy_default_headers();
507507
int use_gzip = rpc->gzip_request;
508508
char *gzip_body = NULL;
509509
size_t gzip_size = 0;

t/lib-httpd/apache.conf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,14 @@ Alias /auth/dumb/ www/auth/dumb/
102102
SetEnv GIT_HTTP_EXPORT_ALL
103103
Header set Set-Cookie name=value
104104
</LocationMatch>
105+
<LocationMatch /smart_headers/>
106+
<RequireAll>
107+
Require expr %{HTTP:x-magic-one} == 'abra'
108+
Require expr %{HTTP:x-magic-two} == 'cadabra'
109+
</RequireAll>
110+
SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
111+
SetEnv GIT_HTTP_EXPORT_ALL
112+
</LocationMatch>
105113
ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
106114
ScriptAlias /broken_smart/ broken-smart-http.sh/
107115
ScriptAlias /error/ error.sh/

t/t5551-http-fetch-smart.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,5 +282,12 @@ test_expect_success EXPENSIVE 'http can handle enormous ref negotiation' '
282282
test_line_count = 100000 tags
283283
'
284284

285+
test_expect_success 'custom http headers' '
286+
test_must_fail git fetch "$HTTPD_URL/smart_headers/repo.git" &&
287+
git -c http.extraheader="x-magic-one: abra" \
288+
-c http.extraheader="x-magic-two: cadabra" \
289+
fetch "$HTTPD_URL/smart_headers/repo.git"
290+
'
291+
285292
stop_httpd
286293
test_done

0 commit comments

Comments
 (0)