]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
http: VLH, very large header test and fixes
authorStefan Eissing <stefan@eissing.org>
Mon, 24 Jul 2023 13:38:04 +0000 (15:38 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 24 Jul 2023 17:25:20 +0000 (19:25 +0200)
- adding tests using very large passwords in auth
- fixes general http sending to treat h3 like h2, and
  not like http1.1
- eliminate H2_HEADER max definitions and use the commmon
  DYN_HTTP_REQUEST everywhere, different limits do not help
- fix http2 handling of requests denied by nghttp2 on send
  to immediately report the refused stream

Closes #11509

lib/dynbuf.h
lib/http.c
lib/http2.c
lib/vquic/curl_ngtcp2.c
tests/http/test_14_auth.py

index 57ad62b22b3ea5b4d205be9ceb4a3d9ef09208ee..6291eabd37de9ac4652e2df5d2f84a4220a99660 100644 (file)
@@ -81,8 +81,6 @@ int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save);
 #define DYN_PAUSE_BUFFER    (64 * 1024 * 1024)
 #define DYN_HAXPROXY        2048
 #define DYN_HTTP_REQUEST    (1024*1024)
-#define DYN_H2_HEADERS      (128*1024)
-#define DYN_H2_TRAILERS     (128*1024)
 #define DYN_APRINTF         8000000
 #define DYN_RTSP_REQ_HEADER (64*1024)
 #define DYN_TRAILERS        (64*1024)
index e04028b3fec54c25adf2afcbe7a028661619e5f8..e611d2789552887f47da89d1fdec5f2eb3dbb15f 100644 (file)
@@ -1308,7 +1308,7 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
       || IS_HTTPS_PROXY(conn->http_proxy.proxytype)
 #endif
        )
-     && conn->httpversion != 20) {
+     && conn->httpversion < 20) {
     /* Make sure this doesn't send more body bytes than what the max send
        speed says. The request bytes do not count to the max speed.
     */
@@ -4571,8 +4571,8 @@ CURLcode Curl_http_req_make(struct httpreq **preq,
     if(!req->path)
       goto out;
   }
-  Curl_dynhds_init(&req->headers, 0, DYN_H2_HEADERS);
-  Curl_dynhds_init(&req->trailers, 0, DYN_H2_TRAILERS);
+  Curl_dynhds_init(&req->headers, 0, DYN_HTTP_REQUEST);
+  Curl_dynhds_init(&req->trailers, 0, DYN_HTTP_REQUEST);
   result = CURLE_OK;
 
 out:
@@ -4729,8 +4729,8 @@ CURLcode Curl_http_req_make2(struct httpreq **preq,
   if(result)
     goto out;
 
-  Curl_dynhds_init(&req->headers, 0, DYN_H2_HEADERS);
-  Curl_dynhds_init(&req->trailers, 0, DYN_H2_TRAILERS);
+  Curl_dynhds_init(&req->headers, 0, DYN_HTTP_REQUEST);
+  Curl_dynhds_init(&req->trailers, 0, DYN_HTTP_REQUEST);
   result = CURLE_OK;
 
 out:
@@ -4860,8 +4860,8 @@ CURLcode Curl_http_resp_make(struct http_resp **presp,
     if(!resp->description)
       goto out;
   }
-  Curl_dynhds_init(&resp->headers, 0, DYN_H2_HEADERS);
-  Curl_dynhds_init(&resp->trailers, 0, DYN_H2_TRAILERS);
+  Curl_dynhds_init(&resp->headers, 0, DYN_HTTP_REQUEST);
+  Curl_dynhds_init(&resp->trailers, 0, DYN_HTTP_REQUEST);
   result = CURLE_OK;
 
 out:
index 64a066b84f68845529fd2ad7f6773e3f6c81cf6c..6c09ec1a1f75274d03608937cf0bc7c5beb84d42 100644 (file)
@@ -252,7 +252,7 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
                   H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
   Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
                   H2_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
-  Curl_dynhds_init(&stream->resp_trailers, 0, DYN_H2_TRAILERS);
+  Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
   stream->resp_hds_len = 0;
   stream->bodystarted = FALSE;
   stream->status_code = -1;
@@ -2122,7 +2122,13 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
   /* Call the nghttp2 send loop and flush to write ALL buffered data,
    * headers and/or request body completely out to the network */
   result = h2_progress_egress(cf, data);
-  if(result == CURLE_AGAIN) {
+  /* if the stream has been closed in egress handling (nghttp2 does that
+   * when it does not like the headers, for example */
+  if(stream && stream->closed) {
+    nwritten = http2_handle_stream_close(cf, data, stream, err);
+    goto out;
+  }
+  else if(result == CURLE_AGAIN) {
     blocked = 1;
   }
   else if(result) {
@@ -2130,14 +2136,14 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
     nwritten = -1;
     goto out;
   }
-  else if(!Curl_bufq_is_empty(&stream->sendbuf)) {
+  else if(stream && !Curl_bufq_is_empty(&stream->sendbuf)) {
     /* although we wrote everything that nghttp2 wants to send now,
      * there is data left in our stream send buffer unwritten. This may
      * be due to the stream's HTTP/2 flow window being exhausted. */
     blocked = 1;
   }
 
-  if(blocked) {
+  if(stream && blocked) {
     /* Unable to send all data, due to connection blocked or H2 window
      * exhaustion. Data is left in our stream buffer, or nghttp2's internal
      * frame buffer or our network out buffer. */
index 1d68fc0f1a27b6809ab7e6d85c6ff2b3057ab5d6..a430aa119aaba31585174b8f311e9155e76915a8 100644 (file)
@@ -1835,6 +1835,8 @@ out:
     *err = result;
     sent = -1;
   }
+  DEBUGF(LOG_CF(data, cf, "[h3sid=%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
+                stream? stream->id : -1, len, sent, *err));
   CF_DATA_RESTORE(cf, save);
   return sent;
 }
index 23acb35989684bd14d0a5da9ae173d9de3c96599..d962cedb3c9a277423663e8bf1b1ad4733fb8338 100644 (file)
@@ -42,6 +42,7 @@ class TestAuth:
     def _class_scope(self, env, httpd, nghttpx):
         if env.have_h3():
             nghttpx.start_if_needed()
+        env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024)
         httpd.clear_extra_configs()
         httpd.reload()
 
@@ -79,3 +80,51 @@ class TestAuth:
             '--digest', '--user', 'test:test'
         ])
         r.check_response(http_status=200)
+
+    # PUT data, digest auth large pw
+    @pytest.mark.parametrize("proto", ['h2', 'h3'])
+    def test_14_04_digest_large_pw(self, env: Env, httpd, nghttpx, repeat, proto):
+        if proto == 'h3' and not env.have_h3():
+            pytest.skip("h3 not supported")
+        data='0123456789'
+        password = 'x' * 65535
+        curl = CurlClient(env=env)
+        url = f'https://{env.authority_for(env.domain1, proto)}/restricted/digest/data.json'
+        r = curl.http_upload(urls=[url], data=data, alpn_proto=proto, extra_args=[
+            '--digest', '--user', f'test:{password}'
+        ])
+        # digest does not submit the password, but a hash of it, so all
+        # works and, since the pw is not correct, we get a 401
+        r.check_response(http_status=401)
+
+    # PUT data, basic auth large pw
+    @pytest.mark.parametrize("proto", ['h2', 'h3'])
+    def test_14_05_basic_large_pw(self, env: Env, httpd, nghttpx, repeat, proto):
+        if proto == 'h3' and not env.have_h3():
+            pytest.skip("h3 not supported")
+        # just large enought that nghttp2 will submit
+        password = 'x' * (47 * 1024)
+        fdata = os.path.join(env.gen_dir, 'data-10m')
+        curl = CurlClient(env=env)
+        url = f'https://{env.authority_for(env.domain1, proto)}/restricted/digest/data.json'
+        r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto, extra_args=[
+            '--basic', '--user', f'test:{password}'
+        ])
+        # but apache denies on length limit
+        r.check_response(http_status=431)
+
+    # PUT data, basic auth with very large pw
+    @pytest.mark.parametrize("proto", ['h2', 'h3'])
+    def test_14_06_basic_very_large_pw(self, env: Env, httpd, nghttpx, repeat, proto):
+        if proto == 'h3' and not env.have_h3():
+            pytest.skip("h3 not supported")
+        data='0123456789'
+        password = 'x' * (64 * 1024)
+        fdata = os.path.join(env.gen_dir, 'data-10m')
+        curl = CurlClient(env=env)
+        url = f'https://{env.authority_for(env.domain1, proto)}/restricted/digest/data.json'
+        r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto, extra_args=[
+            '--basic', '--user', f'test:{password}'
+        ])
+        # request was never sent
+        r.check_response(exitcode=55, http_status=0)