Skip to content

Commit 6c81a99

Browse files
mstorsjogitster
authored andcommitted
Allow curl to rewind the RPC read buffer
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 <[email protected]> Acked-by: Shawn O. Pearce <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent b8ac923 commit 6c81a99

File tree

1 file changed

+30
-0
lines changed

1 file changed

+30
-0
lines changed

remote-curl.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ struct rpc_state {
290290
int out;
291291
struct strbuf result;
292292
unsigned gzip_request : 1;
293+
unsigned initial_buffer : 1;
293294
};
294295

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

302303
if (!avail) {
304+
rpc->initial_buffer = 0;
303305
avail = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
304306
if (!avail)
305307
return 0;
@@ -314,6 +316,29 @@ static size_t rpc_out(void *ptr, size_t eltsize,
314316
return avail;
315317
}
316318

319+
#ifndef NO_CURL_IOCTL
320+
curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
321+
{
322+
struct rpc_state *rpc = clientp;
323+
324+
switch (cmd) {
325+
case CURLIOCMD_NOP:
326+
return CURLIOE_OK;
327+
328+
case CURLIOCMD_RESTARTREAD:
329+
if (rpc->initial_buffer) {
330+
rpc->pos = 0;
331+
return CURLIOE_OK;
332+
}
333+
fprintf(stderr, "Unable to rewind rpc post data - try increasing http.postBuffer\n");
334+
return CURLIOE_FAILRESTART;
335+
336+
default:
337+
return CURLIOE_UNKNOWNCMD;
338+
}
339+
}
340+
#endif
341+
317342
static size_t rpc_in(const void *ptr, size_t eltsize,
318343
size_t nmemb, void *buffer_)
319344
{
@@ -370,8 +395,13 @@ static int post_rpc(struct rpc_state *rpc)
370395
*/
371396
headers = curl_slist_append(headers, "Expect: 100-continue");
372397
headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
398+
rpc->initial_buffer = 1;
373399
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
374400
curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc);
401+
#ifndef NO_CURL_IOCTL
402+
curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, rpc_ioctl);
403+
curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, rpc);
404+
#endif
375405
if (options.verbosity > 1) {
376406
fprintf(stderr, "POST %s (chunked)\n", rpc->service_name);
377407
fflush(stderr);

0 commit comments

Comments
 (0)