]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
vtls: revert "receive max buffer" + add test case
authorStefan Eissing <stefan@eissing.org>
Thu, 1 Feb 2024 17:15:50 +0000 (18:15 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 7 Feb 2024 13:46:41 +0000 (14:46 +0100)
- add test_05_04 for requests using http/1.0, http/1.1 and h2 against an
  Apache resource that does an unclean TLS shutdown.
- revert special workarund in openssl.c for suppressing shutdown errors
  on multiplexed connections
- vlts.c restore to its state before 9a90c9dd64d2f03601833a70786d485851bd1b53

Fixes #12885
Fixes #12844

Closes #12848

lib/vtls/vtls.c
tests/http/test_05_errors.py
tests/http/testenv/httpd.py
tests/http/testenv/mod_curltest/mod_curltest.c

index e928ba5d079e6e3fb44cdff47c5503847ace8f7b..f654a9749cd258a5f0a8e93b035081fc7e081140 100644 (file)
@@ -1715,32 +1715,17 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
 {
   struct cf_call_data save;
   ssize_t nread;
-  size_t ntotal = 0;
 
   CF_DATA_SAVE(save, cf, data);
   *err = CURLE_OK;
-  /* Do receive until we fill the buffer somehwhat or EGAIN, error or EOF */
-  while(!ntotal || (len - ntotal) > (4*1024)) {
+  nread = Curl_ssl->recv_plain(cf, data, buf, len, err);
+  if(nread > 0) {
+    DEBUGASSERT((size_t)nread <= len);
+  }
+  else if(nread == 0) {
+    /* eof */
     *err = CURLE_OK;
-    nread = Curl_ssl->recv_plain(cf, data, buf + ntotal, len - ntotal, err);
-    if(nread < 0) {
-      if(*err == CURLE_AGAIN && ntotal > 0) {
-        /* we EAGAINed after having reed data, return the success amount */
-        *err = CURLE_OK;
-        break;
-      }
-      /* we have a an error to report */
-      goto out;
-    }
-    else if(nread == 0) {
-      /* eof */
-      break;
-    }
-    ntotal += (size_t)nread;
-    DEBUGASSERT((size_t)ntotal <= len);
   }
-  nread = (ssize_t)ntotal;
-out:
   CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len,
               nread, *err);
   CF_DATA_RESTORE(cf, save);
index b59f3f177a70c2cee0bd55a98b82aef0e8c1076a..68e49f7362a903b9794e573d4859c654973ae06e 100644 (file)
@@ -108,3 +108,30 @@ class TestErrors:
         r.check_response(http_status=200, count=1)
         # check that we did a downgrade
         assert r.stats[0]['http_version'] == '1.1', r.dump_logs()
+
+    # On the URL used here, Apache is doing an "unclean" TLS shutdown,
+    # meaning it sends no shutdown notice and just closes TCP.
+    # The HTTP response delivers a body without Content-Length. We expect:
+    # - http/1.0 to fail since it relies on a clean connection close to
+    #   detect the end of the body
+    # - http/1.1 to work since it will used "chunked" transfer encoding
+    #   and stop receiving when that signals the end
+    # - h2 to work since it will signal the end of the response before
+    #   and not see the "unclean" close either
+    @pytest.mark.parametrize("proto", ['http/1.0', 'http/1.1', 'h2'])
+    def test_05_04_unclean_tls_shutdown(self, env: Env, httpd, nghttpx, repeat, proto):
+        if proto == 'h3' and not env.have_h3():
+            pytest.skip("h3 not supported")
+        count = 10 if proto == 'h2' else 1
+        curl = CurlClient(env=env)
+        url = f'https://{env.authority_for(env.domain1, proto)}'\
+                f'/curltest/shutdown_unclean?id=[0-{count-1}]&chunks=4'
+        r = curl.http_download(urls=[url], alpn_proto=proto, extra_args=[
+            '--parallel',
+        ])
+        if proto == 'http/1.0':
+            r.check_exit_code(56)
+        else:
+            r.check_exit_code(0)
+            r.check_response(http_status=200, count=count)
+
index 79497c5b305a7aa31545026493ab66ec9e58bfb3..c04c22699a62c4b20a4854abb4027a4c8c44f158 100644 (file)
@@ -47,7 +47,7 @@ class Httpd:
         'authn_core', 'authn_file',
         'authz_user', 'authz_core', 'authz_host',
         'auth_basic', 'auth_digest',
-        'alias', 'env', 'filter', 'headers', 'mime',
+        'alias', 'env', 'filter', 'headers', 'mime', 'setenvif',
         'socache_shmcb',
         'rewrite', 'http2', 'ssl', 'proxy', 'proxy_http', 'proxy_connect',
         'mpm_event',
@@ -389,6 +389,11 @@ class Httpd:
                 f'    <Location /curltest/1_1>',
                 f'      SetHandler curltest-1_1-required',
                 f'    </Location>',
+                f'    <Location /curltest/shutdown_unclean>',
+                f'      SetHandler curltest-tweak',
+                f'      SetEnv force-response-1.0 1',
+                f'    </Location>',
+                f'    SetEnvIf Request_URI "/shutdown_unclean" ssl-unclean=1',
             ])
         if self._auth_digest:
             lines.extend([
index ff1983d17fd5dc1d51c072c45363c6324aad5523..a066be5222893d14d1a11af231fc75d314485e24 100644 (file)
@@ -347,7 +347,7 @@ static int curltest_tweak_handler(request_rec *r)
                 "request, %s", r->args? r->args : "(no args)");
   r->status = http_status;
   r->clength = -1;
-  r->chunked = 1;
+  r->chunked = (r->proto_num >= HTTP_VERSION(1,1));
   apr_table_setn(r->headers_out, "request-id", request_id);
   apr_table_unset(r->headers_out, "Content-Length");
   /* Discourage content-encodings */