@matheusbsbs / matheus-bagliano / commits / 6c81a99082
Fork 0

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 <martin@martin.st> Acked-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Martin Storsjö committed Dec 1, 2009 at 12:33 UTC 6c81a9908206799ccbc9a17bde17f1d766a8eef4
1 file changed +30
remote-curl.c
+30
@@ -290,6 +290,7 @@ struct rpc_state {
290 int out;
291 struct strbuf result;
292 unsigned gzip_request : 1;
293 + unsigned initial_buffer : 1;
294 };
295
296 static size_t rpc_out(void *ptr, size_t eltsize,
@@ -300,6 +301,7 @@ static size_t rpc_out(void *ptr, size_t eltsize,
301 size_t avail = rpc->len - rpc->pos;
302
303 if (!avail) {
304 + rpc->initial_buffer = 0;
305 avail = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
306 if (!avail)
307 return 0;
@@ -314,6 +316,29 @@ static size_t rpc_out(void *ptr, size_t eltsize,
316 return avail;
317 }
318
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 +
342 static size_t rpc_in(const void *ptr, size_t eltsize,
343 size_t nmemb, void *buffer_)
344 {
@@ -370,8 +395,13 @@ static int post_rpc(struct rpc_state *rpc)
395 */
396 headers = curl_slist_append(headers, "Expect: 100-continue");
397 headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
398 + rpc->initial_buffer = 1;
399 curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
400 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
405 if (options.verbosity > 1) {
406 fprintf(stderr, "POST %s (chunked)\n", rpc->service_name);
407 fflush(stderr);