]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
mime: fix unpausing of readers
authorStefan Eissing <stefan@eissing.org>
Tue, 21 Oct 2025 11:51:10 +0000 (13:51 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 21 Oct 2025 14:30:47 +0000 (16:30 +0200)
When unpausing a transfer, check if the reader pause state differs
in addition to the "keepon" flags.

Reported-by: 包布丁
Fixes #18848
Closes #19178

lib/easy.c
lib/sendf.c
tests/http/test_07_upload.py
tests/http/testenv/httpd.py
tests/http/testenv/mod_curltest/mod_curltest.c
tests/libtest/cli_hx_upload.c

index 793a18f33eb88090ca72bcb56479d19ade337ce0..4236a01d874450f10750b8c2017372c87e2a6d5a 100644 (file)
@@ -1165,7 +1165,8 @@ CURLcode curl_easy_pause(CURL *d, int action)
   send_paused = Curl_xfer_send_is_paused(data);
   send_paused_new = (action & CURLPAUSE_SEND);
 
-  if(send_paused != send_paused_new) {
+  if((send_paused != send_paused_new) ||
+     (send_paused_new != Curl_creader_is_paused(data))) {
     changed = TRUE;
     result = Curl_1st_err(result, Curl_xfer_pause_send(data, send_paused_new));
   }
index c6d84127620b3f01c25bfbee3187d03791a0dea1..7307f872dfaa469f0b29906a097d0c0e90ad44b2 100644 (file)
@@ -1442,6 +1442,7 @@ CURLcode Curl_creader_unpause(struct Curl_easy *data)
 
   while(reader) {
     result = reader->crt->cntrl(data, reader, CURL_CRCNTRL_UNPAUSE);
+    CURL_TRC_READ(data, "unpausing %s -> %d", reader->crt->name, result);
     if(result)
       break;
     reader = reader->next;
index d17d3a44ad045b7feb8347da31693e48110086c7..c2377d41e422cefb13b3ca28d42726aa3d8cd602 100644 (file)
@@ -671,6 +671,25 @@ class TestUpload:
         ])
         r.check_stats(count=1, http_status=200, exitcode=0)
 
+    @pytest.mark.parametrize("proto", ['http/1.1'])
+    def test_07_63_upload_exp100_paused(self, env: Env, httpd, nghttpx, proto):
+        read_delay = 1
+        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo?id=[0-0]'\
+            f'&read_delay={read_delay}s'
+        upload_size = 128 * 1024
+        client = LocalClient(name='cli_hx_upload', env=env)
+        if not client.exists():
+            pytest.skip(f'example client not built: {client.name}')
+        r = client.run(args=[
+             '-n', '1',
+             '-S', f'{upload_size}',
+             '-P', '1',
+             '-M', 'MIME',
+             '-r', f'{env.domain1}:{env.port_for(proto)}:127.0.0.1',
+             '-V', proto, url
+        ])
+        r.check_exit_code(0)
+
     # nghttpx is the only server we have that supports TLS early data and
     # has a limit of 16k it announces
     @pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx")
index d9f535dd22d2fe9db34b0dc973b343148dc3f8b0..740237abc4e62b4c4e81b76d6ef82c50a5e16678 100644 (file)
@@ -575,11 +575,13 @@ class Httpd:
             return
         local_dir = os.path.dirname(inspect.getfile(Httpd))
         out_dir = os.path.join(self.env.gen_dir, 'mod_curltest')
+        in_source = os.path.join(local_dir, 'mod_curltest/mod_curltest.c')
         out_source = os.path.join(out_dir, 'mod_curltest.c')
         if not os.path.exists(out_dir):
             os.mkdir(out_dir)
-        if not os.path.exists(out_source):
-            shutil.copy(os.path.join(local_dir, 'mod_curltest/mod_curltest.c'), out_source)
+        if not os.path.exists(out_source) or \
+                os.stat(in_source).st_mtime > os.stat(out_source).st_mtime:
+            shutil.copy(in_source, out_source)
         p = subprocess.run([
             self.env.apxs, '-c', out_source
         ], capture_output=True, cwd=out_dir)
index e354e5a469098878a1cbfe883d739bda10f678ce..17d0688ace68df3a4acca9b18ed4f8959af40a17 100644 (file)
@@ -188,6 +188,7 @@ static int curltest_echo_handler(request_rec *r)
   char buffer[8192];
   const char *ct;
   apr_off_t die_after_len = -1, total_read_len = 0;
+  apr_time_t read_delay = 0;
   int just_die = 0, die_after_100 = 0;
   long l;
 
@@ -221,6 +222,12 @@ static int curltest_echo_handler(request_rec *r)
           die_after_100 = 1;
           continue;
         }
+        else if(!strcmp("read_delay", arg)) {
+          rv = duration_parse(&read_delay, val, "s");
+          if(APR_SUCCESS == rv) {
+            continue;
+          }
+        }
       }
     }
   }
@@ -258,6 +265,12 @@ static int curltest_echo_handler(request_rec *r)
     apr_table_setn(r->headers_out, "Request-TE",
                    apr_table_get(r->headers_in, "TE"));
 
+  if(read_delay) {
+    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+                  "put_handler: read_delay");
+    apr_sleep(read_delay);
+  }
+
   bb = apr_brigade_create(r->pool, c->bucket_alloc);
   /* copy any request body into the response */
   rv = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK);
@@ -637,6 +650,8 @@ static int curltest_put_handler(request_rec *r)
   ap_set_content_type(r, ct ? ct : "text/plain");
 
   if(read_delay) {
+    ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, r,
+                  "put_handler: read_delay");
     apr_sleep(read_delay);
   }
   bb = apr_brigade_create(r->pool, c->bucket_alloc);
index a0a6b95db84782b3f8f0ab70d0aee6cf57bba2dc..5d9c2f26f8b34c317f37cca0c9c2e7f9d4af7244 100644 (file)
@@ -33,6 +33,7 @@ struct transfer_u {
   CURL *easy;
   const char *method;
   char filename[128];
+  curl_mime *mime;
   FILE *out;
   curl_off_t send_total;
   curl_off_t recv_size;
@@ -158,18 +159,28 @@ static int setup_hx_upload(CURL *hnd, const char *url, struct transfer_u *t,
   if(use_earlydata)
     curl_easy_setopt(hnd, CURLOPT_SSL_OPTIONS, CURLSSLOPT_EARLYDATA);
 
-  if(!t->method || !strcmp("PUT", t->method))
-    curl_easy_setopt(hnd, CURLOPT_UPLOAD, 1L);
-  else if(!strcmp("POST", t->method))
-    curl_easy_setopt(hnd, CURLOPT_POST, 1L);
+  if(!strcmp("MIME", t->method)) {
+    curl_mimepart *part;
+    t->mime = curl_mime_init(hnd);
+    part = curl_mime_addpart(t->mime);
+    curl_mime_name(part, "file");
+    curl_mime_data_cb(part, -1, my_read_cb, NULL, NULL, t);
+    curl_easy_setopt(hnd, CURLOPT_MIMEPOST, t->mime);
+  }
   else {
-    curl_mfprintf(stderr, "unsupported method '%s'\n", t->method);
-    return 1;
+    if(!t->method || !strcmp("PUT", t->method))
+      curl_easy_setopt(hnd, CURLOPT_UPLOAD, 1L);
+    else if(!strcmp("POST", t->method))
+      curl_easy_setopt(hnd, CURLOPT_POST, 1L);
+    else {
+      curl_mfprintf(stderr, "unsupported method '%s'\n", t->method);
+      return 1;
+    }
+    curl_easy_setopt(hnd, CURLOPT_READFUNCTION, my_read_cb);
+    curl_easy_setopt(hnd, CURLOPT_READDATA, t);
+    if(announce_length)
+      curl_easy_setopt(hnd, CURLOPT_INFILESIZE_LARGE, t->send_total);
   }
-  curl_easy_setopt(hnd, CURLOPT_READFUNCTION, my_read_cb);
-  curl_easy_setopt(hnd, CURLOPT_READDATA, t);
-  if(announce_length)
-    curl_easy_setopt(hnd, CURLOPT_INFILESIZE_LARGE, t->send_total);
 
   curl_easy_setopt(hnd, CURLOPT_NOPROGRESS, 0L);
   curl_easy_setopt(hnd, CURLOPT_XFERINFOFUNCTION, my_progress_u_cb);
@@ -488,6 +499,7 @@ static CURLcode test_cli_hx_upload(const char *URL)
 
     } while(active_transfers); /* as long as we have transfers going */
 
+    curl_mfprintf(stderr, "all transfers done, cleanup multi\n");
     curl_multi_cleanup(multi_handle);
   }
 
@@ -501,9 +513,13 @@ static CURLcode test_cli_hx_upload(const char *URL)
       curl_easy_cleanup(t->easy);
       t->easy = NULL;
     }
+    if(t->mime) {
+      curl_mime_free(t->mime);
+    }
   }
   free(transfer_u);
   curl_share_cleanup(share);
+  curl_slist_free_all(host);
 
   return CURLE_OK;
 }