http: VLH, very large header test and fixes

- 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
This commit is contained in:
Stefan Eissing 2023-07-24 15:38:04 +02:00 committed by Daniel Stenberg
parent 3c0a91077c
commit c76df46a19
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
5 changed files with 68 additions and 13 deletions

View 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_PAUSE_BUFFER (64 * 1024 * 1024)
#define DYN_HAXPROXY 2048 #define DYN_HAXPROXY 2048
#define DYN_HTTP_REQUEST (1024*1024) #define DYN_HTTP_REQUEST (1024*1024)
#define DYN_H2_HEADERS (128*1024)
#define DYN_H2_TRAILERS (128*1024)
#define DYN_APRINTF 8000000 #define DYN_APRINTF 8000000
#define DYN_RTSP_REQ_HEADER (64*1024) #define DYN_RTSP_REQ_HEADER (64*1024)
#define DYN_TRAILERS (64*1024) #define DYN_TRAILERS (64*1024)

View File

@ -1308,7 +1308,7 @@ CURLcode Curl_buffer_send(struct dynbuf *in,
|| IS_HTTPS_PROXY(conn->http_proxy.proxytype) || IS_HTTPS_PROXY(conn->http_proxy.proxytype)
#endif #endif
) )
&& conn->httpversion != 20) { && conn->httpversion < 20) {
/* Make sure this doesn't send more body bytes than what the max send /* 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. 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) if(!req->path)
goto out; goto out;
} }
Curl_dynhds_init(&req->headers, 0, DYN_H2_HEADERS); Curl_dynhds_init(&req->headers, 0, DYN_HTTP_REQUEST);
Curl_dynhds_init(&req->trailers, 0, DYN_H2_TRAILERS); Curl_dynhds_init(&req->trailers, 0, DYN_HTTP_REQUEST);
result = CURLE_OK; result = CURLE_OK;
out: out:
@ -4729,8 +4729,8 @@ CURLcode Curl_http_req_make2(struct httpreq **preq,
if(result) if(result)
goto out; goto out;
Curl_dynhds_init(&req->headers, 0, DYN_H2_HEADERS); Curl_dynhds_init(&req->headers, 0, DYN_HTTP_REQUEST);
Curl_dynhds_init(&req->trailers, 0, DYN_H2_TRAILERS); Curl_dynhds_init(&req->trailers, 0, DYN_HTTP_REQUEST);
result = CURLE_OK; result = CURLE_OK;
out: out:
@ -4860,8 +4860,8 @@ CURLcode Curl_http_resp_make(struct http_resp **presp,
if(!resp->description) if(!resp->description)
goto out; goto out;
} }
Curl_dynhds_init(&resp->headers, 0, DYN_H2_HEADERS); Curl_dynhds_init(&resp->headers, 0, DYN_HTTP_REQUEST);
Curl_dynhds_init(&resp->trailers, 0, DYN_H2_TRAILERS); Curl_dynhds_init(&resp->trailers, 0, DYN_HTTP_REQUEST);
result = CURLE_OK; result = CURLE_OK;
out: out:

View File

@ -252,7 +252,7 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
H2_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); 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->resp_hds_len = 0;
stream->bodystarted = FALSE; stream->bodystarted = FALSE;
stream->status_code = -1; 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, /* Call the nghttp2 send loop and flush to write ALL buffered data,
* headers and/or request body completely out to the network */ * headers and/or request body completely out to the network */
result = h2_progress_egress(cf, data); 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; blocked = 1;
} }
else if(result) { else if(result) {
@ -2130,14 +2136,14 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
nwritten = -1; nwritten = -1;
goto out; 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, /* although we wrote everything that nghttp2 wants to send now,
* there is data left in our stream send buffer unwritten. This may * there is data left in our stream send buffer unwritten. This may
* be due to the stream's HTTP/2 flow window being exhausted. */ * be due to the stream's HTTP/2 flow window being exhausted. */
blocked = 1; blocked = 1;
} }
if(blocked) { if(stream && blocked) {
/* Unable to send all data, due to connection blocked or H2 window /* Unable to send all data, due to connection blocked or H2 window
* exhaustion. Data is left in our stream buffer, or nghttp2's internal * exhaustion. Data is left in our stream buffer, or nghttp2's internal
* frame buffer or our network out buffer. */ * frame buffer or our network out buffer. */

View File

@ -1835,6 +1835,8 @@ out:
*err = result; *err = result;
sent = -1; 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); CF_DATA_RESTORE(cf, save);
return sent; return sent;
} }

View File

@ -42,6 +42,7 @@ class TestAuth:
def _class_scope(self, env, httpd, nghttpx): def _class_scope(self, env, httpd, nghttpx):
if env.have_h3(): if env.have_h3():
nghttpx.start_if_needed() nghttpx.start_if_needed()
env.make_data_file(indir=env.gen_dir, fname="data-10m", fsize=10*1024*1024)
httpd.clear_extra_configs() httpd.clear_extra_configs()
httpd.reload() httpd.reload()
@ -79,3 +80,51 @@ class TestAuth:
'--digest', '--user', 'test:test' '--digest', '--user', 'test:test'
]) ])
r.check_response(http_status=200) 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)