libcurl version: 7.52.1-5+deb9u6

My applications goal: Use pause/unpause in libcurl to ensure a slow client can 
push back to a fast server so that it slows down while also keeping buffered 
data in curl for the transfer to a minimum. I want to use the 
culr_multi_interface so that my application can decide when to transfer data 
and work with many transfers at a time.

Operations performed:
My application calls curl_multi_perform on a multi-handle that has a single 
easy-handle ‘add’ed to it. The WRITEFUNCTION gets called to consume data being 
transferred for a HTTPS GET request. The write function returns 
CURL_WRITEFUNC_PAUSE to request pause. As data is consumed, in another thread, 
internal data structures are updated and the curl processing thread calls 
curl_easy_unpause to unpause the transfer on an easy handle that is already 
added to the only multi handle being used by the thread. The curl processing 
thread also calls curl_multi_perform immediately afterwards. Both 
curl_easy_pause and curl_multi_perform transfer data.

Behavior observed:
Libcurl stops calling the WRITEFUNCTION after pause is requested. However, 
libcurl keeps receiving data on the connection and buffers it. At some point, 
when transferring 1GB objects, memory allocation fails and libcurl returns 
CURLE_OUT_OF_MEMORY.

The above behavior is seen only when using HTTP2.
When using HTTP1.1 to talk to the server, the data is received as transfer is 
resumed after pauses.

My guess is that the buffered data can get quite large and memory allocation 
fails. It also defeats the attempt to control flow of data and resource 
consumption at the client.

Questions:
1. The code in lib/transfer.c:Curl_readwrite does seem to avoid reading from 
connection when KEEP_RECV_PAUSE is set. Is the intent of the pause 
functionality to stop reading from socket when WRITING is paused?
2. Is pausing/un-pausing the right way for applications to control the flow of 
data between libcurl and http server? Is there any other mechanism?
3. My understanding is that curl_multi_perform ultimately calls 
lib/transfer.c:readwrite_data to read data off the connection from http server 
and delivers to WRITEFUNCTION using Curl_client_write. When WRITEFUNC requests 
pause, this state is set in the easy-handle but readwrite_data does not check 
this state and continues to read data from socket but cannot deliver to the 
WRITEFUNC. If this is indeed a bug, should readwrite_data check the 
KEEP_RECV_PAUSE flag in the easy handle and stop reading?
4. Curl_easy_pause may call the writefunc hander synchronously. This goes 
against the intent of using multi-interface where curl_multi_perform is used to 
move all data. When using multi interface, is curl_easy_unpause the right way 
to un-pause paused transfers?
5. Why does maxloops=100 in readwrite_data not prevent it from stopping at 
reading 100 times from the connection. I sometimes see more than a 1000 
invocations of debugfunction that indicate there were 1000 reads of data from 
the connection. Does the 100 reads somehow transform to 1000+ invocation of 
debug function? Note that the content I am reading is not compressed.
6. Does this have something to do with the way curl interfaces with nghttp2 for 
http2?

------------

A change(lib/transfer.c:readwrite_data(…)) to stop reading from a connection 
when the WRITEFUNCTION has requested pause is:

diff --git a/lib/transfer.c b/lib/transfer.c
index b73f94d9e..d9ef1e394 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -844,6 +844,11 @@ static CURLcode readwrite_data(struct Curl_easy *data,
       k->keepon &= ~KEEP_RECV;
     }

+    if(k->keepon & KEEP_RECV_PAUSE) {
+      /* this is a paused transfer */
+      break;
+    }
+
   } while(data_pending(conn) && maxloops--);

   if(maxloops <= 0) {
------------------------------------------

I understand this is against the limit of maxloops=100.
Are there other gotchas with this approach or is this against the principle of 
what is being done in this code path?

-Dheeraj Sangamkar
-------------------------------------------------------------------
Unsubscribe: https://cool.haxx.se/list/listinfo/curl-library
Etiquette:   https://curl.haxx.se/mail/etiquette.html

Reply via email to