Skip to content

Commit 206b099

Browse files
spearcegitster
authored andcommitted
smart-http: Don't use Expect: 100-Continue
Some HTTP/1.1 servers or proxies don't correctly implement the 100-Continue feature of HTTP/1.1. Its a difficult feature to implement right, and isn't commonly used by browsers, so many developers may not even be aware that their server (or proxy) doesn't honor it. Within the smart HTTP protocol for Git we only use this newer "Expect: 100-Continue" feature to probe for missing authentication before uploading a large payload like a pack file during push. If authentication is necessary, we expect the server to send the 401 Not Authorized response before the bulk data transfer starts, thus saving the client bandwidth during the retry. A different method to probe for working authentication is to send an empty command list (that is just "0000") to $URL/git-receive-pack. or $URL/git-upload-pack. All versions of both receive-pack and upload-pack since the introduction of smart HTTP in Git 1.6.6 cleanly accept just a flush-pkt under --stateless-rpc mode, and exit with success. If HTTP level authentication is successful, the backend will return an empty response, but with HTTP status code 200. This enables the client to continue with the transfer. Signed-off-by: Shawn O. Pearce <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 547e8b9 commit 206b099

File tree

1 file changed

+55
-11
lines changed

1 file changed

+55
-11
lines changed

remote-curl.c

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -363,14 +363,59 @@ static size_t rpc_in(const void *ptr, size_t eltsize,
363363
return size;
364364
}
365365

366+
static int run_slot(struct active_request_slot *slot)
367+
{
368+
int err = 0;
369+
struct slot_results results;
370+
371+
slot->results = &results;
372+
slot->curl_result = curl_easy_perform(slot->curl);
373+
finish_active_slot(slot);
374+
375+
if (results.curl_result != CURLE_OK) {
376+
err |= error("RPC failed; result=%d, HTTP code = %ld",
377+
results.curl_result, results.http_code);
378+
}
379+
380+
return err;
381+
}
382+
383+
static int probe_rpc(struct rpc_state *rpc)
384+
{
385+
struct active_request_slot *slot;
386+
struct curl_slist *headers = NULL;
387+
struct strbuf buf = STRBUF_INIT;
388+
int err;
389+
390+
slot = get_active_slot();
391+
392+
headers = curl_slist_append(headers, rpc->hdr_content_type);
393+
headers = curl_slist_append(headers, rpc->hdr_accept);
394+
395+
curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
396+
curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
397+
curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
398+
curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
399+
curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, "0000");
400+
curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, 4);
401+
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
402+
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
403+
curl_easy_setopt(slot->curl, CURLOPT_FILE, &buf);
404+
405+
err = run_slot(slot);
406+
407+
curl_slist_free_all(headers);
408+
strbuf_release(&buf);
409+
return err;
410+
}
411+
366412
static int post_rpc(struct rpc_state *rpc)
367413
{
368414
struct active_request_slot *slot;
369-
struct slot_results results;
370415
struct curl_slist *headers = NULL;
371416
int use_gzip = rpc->gzip_request;
372417
char *gzip_body = NULL;
373-
int err = 0, large_request = 0;
418+
int err, large_request = 0;
374419

375420
/* Try to load the entire request, if we can fit it into the
376421
* allocated buffer space we can use HTTP/1.0 and avoid the
@@ -393,8 +438,13 @@ static int post_rpc(struct rpc_state *rpc)
393438
rpc->len += n;
394439
}
395440

441+
if (large_request) {
442+
err = probe_rpc(rpc);
443+
if (err)
444+
return err;
445+
}
446+
396447
slot = get_active_slot();
397-
slot->results = &results;
398448

399449
curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
400450
curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
@@ -408,7 +458,7 @@ static int post_rpc(struct rpc_state *rpc)
408458
/* The request body is large and the size cannot be predicted.
409459
* We must use chunked encoding to send it.
410460
*/
411-
headers = curl_slist_append(headers, "Expect: 100-continue");
461+
headers = curl_slist_append(headers, "Expect:");
412462
headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
413463
rpc->initial_buffer = 1;
414464
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
@@ -482,13 +532,7 @@ static int post_rpc(struct rpc_state *rpc)
482532
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
483533
curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
484534

485-
slot->curl_result = curl_easy_perform(slot->curl);
486-
finish_active_slot(slot);
487-
488-
if (results.curl_result != CURLE_OK) {
489-
err |= error("RPC failed; result=%d, HTTP code = %ld",
490-
results.curl_result, results.http_code);
491-
}
535+
err = run_slot(slot);
492536

493537
curl_slist_free_all(headers);
494538
free(gzip_body);

0 commit comments

Comments
 (0)