Skip to content

Commit

Permalink
Allow curl to rewind the RPC read buffer
Browse files Browse the repository at this point in the history
When using multi-pass authentication methods, the curl library may need
to rewind the read buffers used for providing data to HTTP POST, if data
has been output before a 401 error is received.

This is needed only when the first request (when the multi-pass
authentication method isn't initialized and hasn't received its challenge
yet) for a certain curl session is a chunked HTTP POST.

As long as the current rpc read buffer is the first one, we're able to
rewind without need for additional buffering.

The curl library currently starts sending data without waiting for a
response to the Expect: 100-continue header, due to a bug in curl that
exists up to curl version 7.19.7.

If the HTTP server doesn't handle Expect: 100-continue headers properly
(e.g. Lighttpd), the library has to start sending data without knowing
if the request will be successfully authenticated. In this case, this
rewinding solution is not sufficient - the whole request will be sent
before the 401 error is received.

Signed-off-by: Martin Storsjo <martin@martin.st>
Acked-by: Shawn O. Pearce <spearce@spearce.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Martin Storsjö authored and Junio C Hamano committed Dec 1, 2009
1 parent b8ac923 commit 6c81a99
Showing 1 changed file with 30 additions and 0 deletions.
30 changes: 30 additions & 0 deletions remote-curl.c
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ struct rpc_state {
int out;
struct strbuf result;
unsigned gzip_request : 1;
unsigned initial_buffer : 1;
};

static size_t rpc_out(void *ptr, size_t eltsize,
Expand All @@ -300,6 +301,7 @@ static size_t rpc_out(void *ptr, size_t eltsize,
size_t avail = rpc->len - rpc->pos;

if (!avail) {
rpc->initial_buffer = 0;
avail = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
if (!avail)
return 0;
Expand All @@ -314,6 +316,29 @@ static size_t rpc_out(void *ptr, size_t eltsize,
return avail;
}

#ifndef NO_CURL_IOCTL
curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
{
struct rpc_state *rpc = clientp;

switch (cmd) {
case CURLIOCMD_NOP:
return CURLIOE_OK;

case CURLIOCMD_RESTARTREAD:
if (rpc->initial_buffer) {
rpc->pos = 0;
return CURLIOE_OK;
}
fprintf(stderr, "Unable to rewind rpc post data - try increasing http.postBuffer\n");
return CURLIOE_FAILRESTART;

default:
return CURLIOE_UNKNOWNCMD;
}
}
#endif

static size_t rpc_in(const void *ptr, size_t eltsize,
size_t nmemb, void *buffer_)
{
Expand Down Expand Up @@ -370,8 +395,13 @@ static int post_rpc(struct rpc_state *rpc)
*/
headers = curl_slist_append(headers, "Expect: 100-continue");
headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
rpc->initial_buffer = 1;
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc);
#ifndef NO_CURL_IOCTL
curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, rpc_ioctl);
curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, rpc);
#endif
if (options.verbosity > 1) {
fprintf(stderr, "POST %s (chunked)\n", rpc->service_name);
fflush(stderr);
Expand Down

0 comments on commit 6c81a99

Please sign in to comment.