diff mbox series

[05/13] channel_curl: add fifo to read callback

Message ID 20231120154731.44988-6-stefano.babic@swupdate.org
State Accepted
Headers show
Series Native Docker Support | expand

Commit Message

Stefano Babic Nov. 20, 2023, 3:47 p.m. UTC
In case of upload, the data is read from a buffer that must be allocated
and filled by the caller (request_body field in channel_data_t). This is
not suitable in case a large amount of data should be upload, for
example in case a big image should be transferred.

Let the callback to read from a fifo that must be created by the caller.
The caller writes then into the FIFO, and the callback consumes the data
by reading from the FIFO.

Signed-off-by: Stefano Babic <stefano.babic@swupdate.org>
---
 corelib/channel_curl.c | 58 ++++++++++++++++++++++++++++++++----------
 include/channel_curl.h |  5 ++++
 2 files changed, 49 insertions(+), 14 deletions(-)
diff mbox series

Patch

diff --git a/corelib/channel_curl.c b/corelib/channel_curl.c
index 2da49ce3..4a209bcd 100644
--- a/corelib/channel_curl.c
+++ b/corelib/channel_curl.c
@@ -891,19 +891,41 @@  cleanup:
 static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *data)
 {
 	channel_data_t *channel_data = (channel_data_t *)data;
-	unsigned int bytes;
-	size_t n;
+	ssize_t bytes;
+	size_t n = 0;
+	int ret;
 
-	/* Check data to be sent */
-	bytes = strlen(channel_data->request_body) - channel_data->offs;
+	/*
+	 * Check if data is stored in a buffer or should be read
+	 * form the input pipe
+	 */
+	if (channel_data->request_body) {
+		/* Check data to be sent */
+		bytes = strlen(channel_data->request_body) - channel_data->offs;
 
-	if (!bytes)
-		return 0;
+		if (!bytes)
+			return 0;
+
+		n = min(bytes, size * nmemb);
 
-	n = min(bytes, size * nmemb);
+		memcpy(ptr, &channel_data->request_body[channel_data->offs], n);
+		channel_data->offs += n;
+	} else {
+		bytes = nmemb * size;
+		ret = read(channel_data->read_fifo, ptr, bytes);
+		if (ret < 0) {
+			if (errno == EAGAIN) {
+				TRACE("READ EAGAIN");
+				bytes = 0;
+			} else {
+				ERROR("Cannot read from FIFO");
+				return CURL_READFUNC_ABORT;
+			}
+		} else
+			bytes = ret;
 
-	memcpy(ptr, &channel_data->request_body[channel_data->offs], n);
-	channel_data->offs += n;
+		n = bytes / nmemb;
+	}
 
 	return n;
 }
@@ -992,6 +1014,16 @@  static channel_op_res_t parse_reply(channel_data_t *channel_data, output_data_t
 	return CHANNEL_OK;
 }
 
+static CURLcode channel_set_read_callback(channel_curl_t *handle, channel_data_t *channel_data)
+{
+
+	return curl_easy_setopt(handle, CURLOPT_READFUNCTION, read_callback) ||
+		(channel_data->request_body ?
+			curl_easy_setopt(handle, CURLOPT_INFILESIZE_LARGE,
+				(curl_off_t)strlen(channel_data->request_body)) : 0) ||
+		curl_easy_setopt(handle, CURLOPT_READDATA, channel_data);
+}
+
 static channel_op_res_t channel_post_method(channel_t *this, void *data, int method)
 {
 	channel_curl_t *channel_curl = this->priv;
@@ -1031,6 +1063,8 @@  static channel_op_res_t channel_post_method(channel_t *this, void *data, int met
 		curl_result |= curl_easy_setopt(channel_curl->handle,
 					       CURLOPT_POSTFIELDS,
 					       channel_data->request_body);
+		if (channel_data->read_fifo)
+			curl_result |= channel_set_read_callback(channel_curl->handle, channel_data);
 		break;
 
 	case CHANNEL_PUT:
@@ -1040,11 +1074,7 @@  static channel_op_res_t channel_post_method(channel_t *this, void *data, int met
 						#else
 						CURLOPT_PUT,
 						#endif
-						1L) ||
-			curl_easy_setopt(channel_curl->handle, CURLOPT_READFUNCTION, read_callback) ||
-			curl_easy_setopt(channel_curl->handle, CURLOPT_INFILESIZE_LARGE,
-					(curl_off_t)strlen(channel_data->request_body)) ||
-			curl_easy_setopt(channel_curl->handle, CURLOPT_READDATA, channel_data);
+						1L) || channel_set_read_callback(channel_curl->handle, channel_data);
 		break;
 	}
 
diff --git a/include/channel_curl.h b/include/channel_curl.h
index 1eefbb1c..bf7a508f 100644
--- a/include/channel_curl.h
+++ b/include/channel_curl.h
@@ -68,6 +68,11 @@  typedef struct {
 	bool nofollow;
 	size_t (*dwlwrdata)(char *streamdata, size_t size, size_t nmemb,
 				   void *data);
+	/*
+	 * read_fifo is used as alternative to request_body buffer to push data
+	 * in case of large data. This lets push a stream instead of a buffer.
+	 */
+	int read_fifo;
 	size_t (*headers)(char *streamdata, size_t size, size_t nmemb,
 				   void *data);
 	struct swupdate_digest *dgst;