Skip to content

Commit

Permalink
smart-http: Don't use Expect: 100-Continue
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
spearce authored and gitster committed Feb 15, 2011
1 parent 547e8b9 commit 206b099
Showing 1 changed file with 55 additions and 11 deletions.
66 changes: 55 additions & 11 deletions remote-curl.c
Original file line number Diff line number Diff line change
Expand Up @@ -363,14 +363,59 @@ static size_t rpc_in(const void *ptr, size_t eltsize,
return size;
}

static int run_slot(struct active_request_slot *slot)
{
int err = 0;
struct slot_results results;

slot->results = &results;
slot->curl_result = curl_easy_perform(slot->curl);
finish_active_slot(slot);

if (results.curl_result != CURLE_OK) {
err |= error("RPC failed; result=%d, HTTP code = %ld",
results.curl_result, results.http_code);
}

return err;
}

static int probe_rpc(struct rpc_state *rpc)
{
struct active_request_slot *slot;
struct curl_slist *headers = NULL;
struct strbuf buf = STRBUF_INIT;
int err;

slot = get_active_slot();

headers = curl_slist_append(headers, rpc->hdr_content_type);
headers = curl_slist_append(headers, rpc->hdr_accept);

curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, "0000");
curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, 4);
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
curl_easy_setopt(slot->curl, CURLOPT_FILE, &buf);

err = run_slot(slot);

curl_slist_free_all(headers);
strbuf_release(&buf);
return err;
}

static int post_rpc(struct rpc_state *rpc)
{
struct active_request_slot *slot;
struct slot_results results;
struct curl_slist *headers = NULL;
int use_gzip = rpc->gzip_request;
char *gzip_body = NULL;
int err = 0, large_request = 0;
int err, large_request = 0;

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

if (large_request) {
err = probe_rpc(rpc);
if (err)
return err;
}

slot = get_active_slot();
slot->results = &results;

curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
Expand All @@ -408,7 +458,7 @@ static int post_rpc(struct rpc_state *rpc)
/* The request body is large and the size cannot be predicted.
* We must use chunked encoding to send it.
*/
headers = curl_slist_append(headers, "Expect: 100-continue");
headers = curl_slist_append(headers, "Expect:");
headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
rpc->initial_buffer = 1;
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
Expand Down Expand Up @@ -482,13 +532,7 @@ static int post_rpc(struct rpc_state *rpc)
curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);

slot->curl_result = curl_easy_perform(slot->curl);
finish_active_slot(slot);

if (results.curl_result != CURLE_OK) {
err |= error("RPC failed; result=%d, HTTP code = %ld",
results.curl_result, results.http_code);
}
err = run_slot(slot);

curl_slist_free_all(headers);
free(gzip_body);
Expand Down

0 comments on commit 206b099

Please sign in to comment.