git/list[1] front-page[2] threads[3] people[4] search[5] about
 

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

From
Martin Storsjö <martin@martin.st>
Date
Dec 1, 2009, 10:37 UTC
Message-ID
<alpine.DEB.2.00.0912011236360.5582@cone.home.martin.st>
In-Reply-To
<alpine.DEB.2.00.0912011208160.5582@cone.home.martin.st>

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@martin.st>
---
 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
Previous: Martin StorsjöNext: Shawn O. Pearce
Message 14 of 22 in “Add an option for using any HTTP authentication scheme, not only basic”
  1. Add an option for using any HTTP authentication scheme, not only basicMartin Storsjö, Apr 14, 2009
  2. 0/2 http: allow multi-pass authenticationTay Ray Chuan, Nov 27, 2009
  3. 1/2 http: maintain curl sessionsTay Ray Chuan, Nov 27, 2009
  4. 2/2 Add an option for using any HTTP authentication scheme, not only basicTay Ray Chuan, Nov 27, 2009
  5. Martin StorsjöDec 1, 2009
  6. Allow curl to rewind the RPC read bufferMartin Storsjö, Dec 1, 2009
  7. Shawn O. PearceDec 1, 2009
  8. Tay Ray ChuanDec 1, 2009
  9. Shawn O. PearceDec 1, 2009
  10. Martin StorsjöDec 1, 2009
  11. Junio C HamanoDec 1, 2009
  12. Tay Ray ChuanDec 2, 2009
  13. Martin StorsjöDec 2, 2009
  14. Allow curl to rewind the RPC read buffer at any timeMartin Storsjö, Dec 1, 2009
  15. Shawn O. PearceDec 1, 2009
  16. Martin StorsjöDec 1, 2009
  17. Tay Ray ChuanDec 2, 2009
  18. Daniel StenbergDec 1, 2009
  19. Tay Ray ChuanDec 2, 2009
  20. Daniel StenbergDec 2, 2009
  21. Martin StorsjöDec 2, 2009
  22. Daniel StenbergDec 2, 2009

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.