[PATCH/RFC] Allow curl to rewind the RPC read buffer at any time

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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 solution buffers all data read by the curl library, in order to allow
it to rewind the reading buffer at any time later.

If communicating with a HTTP server that doesn't support the
Expect: 100-continue headers, all HTTP POST data will be sent before
the server replies with the 401 error containing the authentication
challenge.

The buffering is enabled only if the rpc_service function allocates a
buffer - this should perhaps be limited to the cases where http.authAny
is enabled.

Signed-off-by: Martin Storsjo <martin@xxxxxxxxx>
---
 remote-curl.c |   53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 53 insertions(+), 0 deletions(-)

diff --git a/remote-curl.c b/remote-curl.c
index a331bae..c1c5ccd 100644
--- a/remote-curl.c
+++ b/remote-curl.c
@@ -286,6 +286,10 @@ struct rpc_state {
 	size_t alloc;
 	size_t len;
 	size_t pos;
+	char *rewind_buf;
+	size_t rewind_buf_size;
+	size_t rewind_buf_write_pos;
+	size_t rewind_buf_read_pos;
 	int in;
 	int out;
 	struct strbuf result;
@@ -299,12 +303,28 @@ static size_t rpc_out(void *ptr, size_t eltsize,
 	struct rpc_state *rpc = buffer_;
 	size_t avail = rpc->len - rpc->pos;
 
+	if (rpc->rewind_buf && rpc->rewind_buf_read_pos < rpc->rewind_buf_write_pos) {
+		avail = rpc->rewind_buf_write_pos - rpc->rewind_buf_read_pos;
+		if (max < avail)
+			avail = max;
+		memcpy(ptr, rpc->rewind_buf + rpc->rewind_buf_read_pos, avail);
+		rpc->rewind_buf_read_pos += avail;
+		return avail;
+	}
+
 	if (!avail) {
 		avail = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
 		if (!avail)
 			return 0;
 		rpc->pos = 0;
 		rpc->len = avail;
+
+		if (rpc->rewind_buf) {
+			ALLOC_GROW(rpc->rewind_buf, rpc->rewind_buf_write_pos + avail, rpc->rewind_buf_size);
+			memcpy(rpc->rewind_buf + rpc->rewind_buf_write_pos, rpc->buf, avail);
+			rpc->rewind_buf_write_pos += avail;
+			rpc->rewind_buf_read_pos += avail;
+		}
 	}
 
 	if (max < avail)
@@ -314,6 +334,26 @@ 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:
+		rpc->rewind_buf_read_pos = 0;
+		rpc->pos = rpc->len;
+		return CURLIOE_OK;
+
+	default:
+		return CURLIOE_UNKNOWNCMD;
+	}
+}
+#endif
+
 static size_t rpc_in(const void *ptr, size_t eltsize,
 		size_t nmemb, void *buffer_)
 {
@@ -370,8 +410,18 @@ static int post_rpc(struct rpc_state *rpc)
 		 */
 		headers = curl_slist_append(headers, "Expect: 100-continue");
 		headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
+		if (rpc->rewind_buf) {
+			ALLOC_GROW(rpc->rewind_buf, rpc->rewind_buf_write_pos + rpc->len, rpc->rewind_buf_size);
+			memcpy(rpc->rewind_buf, rpc->buf, rpc->len);
+			rpc->rewind_buf_read_pos = rpc->len;
+			rpc->rewind_buf_write_pos = rpc->len;
+		}
 		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);
@@ -472,6 +522,8 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
 	rpc->buf = xmalloc(rpc->alloc);
 	rpc->in = client.in;
 	rpc->out = client.out;
+	rpc->rewind_buf_size = 100;
+	rpc->rewind_buf = xmalloc(rpc->rewind_buf_size);
 	strbuf_init(&rpc->result, 0);
 
 	strbuf_addf(&buf, "%s/%s", url, svc);
@@ -503,6 +555,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
 	free(rpc->hdr_content_type);
 	free(rpc->hdr_accept);
 	free(rpc->buf);
+	free(rpc->rewind_buf);
 	strbuf_release(&buf);
 	return err;
 }
-- 
1.6.4.4

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]