]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
http2: minor buffer and error path fixes
authorStefan Eissing <stefan@eissing.org>
Wed, 8 Feb 2023 14:56:57 +0000 (15:56 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 9 Feb 2023 07:38:01 +0000 (08:38 +0100)
- use memory buffer in full available size
- fail receive of reset/errored streams early

pytest:
- make test_05 error cases more reliable

Closes #10444

lib/http2.c
tests/tests-httpd/test_05_errors.py
tests/tests-httpd/testenv/httpd.py

index 778b588413140d4c967d9fe8c7e0c373468fc3b5..d5eed385e2503aa13cdfb0b1e28578912e7b6657 100644 (file)
@@ -1905,8 +1905,9 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
 
   if(stream->memlen) {
     ssize_t retlen = stream->memlen;
-    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] recv: returns %zd",
-                  stream->stream_id, retlen));
+
+    /* TODO: all this buffer handling is very brittle */
+    stream->len += stream->memlen;
     stream->memlen = 0;
 
     if(ctx->pause_stream_id == stream->stream_id) {
@@ -1918,6 +1919,10 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
       Curl_expire(data, 0, EXPIRE_RUN_NOW);
     }
     else if(stream->closed) {
+      if(stream->reset || stream->error) {
+        nread = http2_handle_stream_close(cf, data, stream, err);
+        goto out;
+      }
       /* this stream is closed, trigger a another read ASAP to detect that */
       DEBUGF(LOG_CF(data, cf, "[h2sid=%u] is closed now, run again",
                     stream->stream_id));
@@ -1928,11 +1933,15 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
       drained_transfer(cf, data);
 
     nread = retlen;
+    DEBUGF(LOG_CF(data, cf, "[h2sid=%u] cf_h2_recv -> %zd",
+                  stream->stream_id, nread));
     goto out;
   }
 
-  if(stream->closed)
-    return http2_handle_stream_close(cf, data, stream, err);
+  if(stream->closed) {
+    nread = http2_handle_stream_close(cf, data, stream, err);
+    goto out;
+  }
 
   if(!data->state.drain && Curl_conn_cf_data_pending(cf->next, data)) {
     DEBUGF(LOG_CF(data, cf, "[h2sid=%u] pending data, set drain",
index 10d6786b7b9d4800931f1a3ec3ce920eb1b2cca8..bb6e9217f12b163833e999a4a7fda91793ed8930 100644 (file)
@@ -57,30 +57,34 @@ class TestErrors:
         urln = f'https://{env.authority_for(env.domain1, proto)}' \
                f'/curltest/tweak?id=[0-{count - 1}]'\
                '&chunks=3&chunk_size=16000&body_error=reset'
-        r = curl.http_download(urls=[urln], alpn_proto=proto)
+        r = curl.http_download(urls=[urln], alpn_proto=proto, extra_args=[
+            '--retry', '0'
+        ])
         assert r.exit_code != 0, f'{r}'
         invalid_stats = []
         for idx, s in enumerate(r.stats):
-            if 'exitcode' not in s or s['exitcode'] not in [18, 56]:
+            if 'exitcode' not in s or s['exitcode'] not in [18, 56, 92]:
                 invalid_stats.append(f'request {idx} exit with {s["exitcode"]}')
         assert len(invalid_stats) == 0, f'failed: {invalid_stats}'
 
-    # download 20 file, check that we get CURLE_PARTIAL_FILE for all
+    # download files, check that we get CURLE_PARTIAL_FILE for all
     @pytest.mark.parametrize("proto", ['h2', 'h3'])
     def test_05_02_partial_20(self, env: Env, httpd, nghttpx, repeat,
                               proto):
         if proto == 'h3' and not env.have_h3():
             pytest.skip("h3 not supported")
-        count = 20
+        count = 5
         curl = CurlClient(env=env)
         urln = f'https://{env.authority_for(env.domain1, proto)}' \
                f'/curltest/tweak?id=[0-{count - 1}]'\
                '&chunks=3&chunk_size=16000&body_error=reset'
-        r = curl.http_download(urls=[urln], alpn_proto=proto)
+        r = curl.http_download(urls=[urln], alpn_proto=proto, extra_args=[
+            '--retry', '0', '--parallel',
+        ])
         assert r.exit_code != 0, f'{r}'
         assert len(r.stats) == count, f'did not get all stats: {r}'
         invalid_stats = []
         for idx, s in enumerate(r.stats):
-            if 'exitcode' not in s or s['exitcode'] not in [18, 56]:
-                invalid_stats.append(f'request {idx} exit with {s["exitcode"]}')
+            if 'exitcode' not in s or s['exitcode'] not in [18, 56, 92]:
+                invalid_stats.append(f'request {idx} exit with {s["exitcode"]}\n{s}')
         assert len(invalid_stats) == 0, f'failed: {invalid_stats}'
index 1e9541b519a64a13894629816735952fa04e4112..89a697dbf56f478a1ccfcac1cf5755b4ead23531 100644 (file)
@@ -263,12 +263,12 @@ class Httpd:
             ]))
 
     def _get_log_level(self):
-        if self.env.verbose > 3:
-            return 'trace2'
-        if self.env.verbose > 2:
-            return 'trace1'
-        if self.env.verbose > 1:
-            return 'debug'
+        #if self.env.verbose > 3:
+        #    return 'trace2'
+        #if self.env.verbose > 2:
+        #    return 'trace1'
+        #if self.env.verbose > 1:
+        #    return 'debug'
         return 'info'
 
     def _curltest_conf(self) -> List[str]: