http2: improved on_stream_close/data_done handling

- there seems to be a code path that cleans up easy handles without
  triggering DONE or DETACH events to the connection filters. This
  would explain wh nghttp2 still holds stream user data
- add GOOD check to easy handle used in on_close_callback to
  prevent crashes, ASSERTs in debug builds.
- NULL the stream user data early before submitting RST
- add checks in on_stream_close() to identify UNGOOD easy handles

Reported-by: Hans-Christian Egtvedt
Fixes #10936
Closes #12562
This commit is contained in:
Stefan Eissing 2023-12-19 12:57:40 +01:00 committed by Daniel Stenberg
parent ef2cf58c77
commit 35380273b9
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
3 changed files with 52 additions and 19 deletions

View file

@ -189,6 +189,23 @@ class TestUpload:
r.check_response(count=count, http_status=200)
self.check_download(count, fdata, curl)
# upload large data parallel to a URL that denies uploads
@pytest.mark.parametrize("proto", ['h2', 'h3'])
def test_07_22_upload_parallel_fail(self, env: Env, httpd, nghttpx, repeat, proto):
if proto == 'h3' and not env.have_h3():
pytest.skip("h3 not supported")
if proto == 'h3' and env.curl_uses_lib('msh3'):
pytest.skip("msh3 stalls here")
fdata = os.path.join(env.gen_dir, 'data-10m')
count = 100
curl = CurlClient(env=env)
url = f'https://{env.authority_for(env.domain1, proto)}'\
f'/curltest/tweak?status=400&delay=5ms&chunks=1&body_error=reset&id=[0-{count-1}]'
r = curl.http_upload(urls=[url], data=f'@{fdata}', alpn_proto=proto,
extra_args=['--parallel'])
exp_exit = 92 if proto == 'h2' else 95
r.check_stats(count=count, exitcode=exp_exit)
# PUT 100k
@pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
def test_07_30_put_100k(self, env: Env, httpd, nghttpx, repeat, proto):

View file

@ -247,7 +247,7 @@ class ExecResult:
if exitcode is not None:
for idx, x in enumerate(self.stats):
if 'exitcode' in x:
assert x['exitcode'] == 0, \
assert x['exitcode'] == exitcode, \
f'status #{idx} exitcode: expected {exitcode}, '\
f'got {x["exitcode"]}\n{self.dump_stat(x)}'