https://github.com/curl/curl/commit/219302b4e64e2337c50d86056e9af2103b281e7e
From: Stefan Eissing <stefan@eissing.org>
Date: Wed, 9 Apr 2025 11:01:54 +0200
Subject: [PATCH] openssl-quic: fix shutdown when stream not open

Check that h3 stream had been opened before telling nghttp3 to
shut it down.

Fixes #16998
Reported-by: Demi Marie Obenour
Closes #17003
--- a/lib/vquic/curl_osslq.c
+++ b/lib/vquic/curl_osslq.c
@@ -654,7 +654,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
   if(stream) {
     CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] easy handle is done",
                 stream->s.id);
-    if(ctx->h3.conn && !stream->closed) {
+    if(ctx->h3.conn && (stream->s.id >= 0) && !stream->closed) {
       nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id);
       nghttp3_conn_close_stream(ctx->h3.conn, stream->s.id,
                                 NGHTTP3_H3_REQUEST_CANCELLED);
--- a/tests/http/test_01_basic.py
+++ b/tests/http/test_01_basic.py
@@ -242,3 +242,19 @@ def test_01_15_gigalarge_resp_headers(self, env: Env, httpd, proto):
             r.check_exit_code(16)  # CURLE_HTTP2
         else:
             r.check_exit_code(100)  # CURLE_TOO_LARGE
+
+    # http: invalid request headers, GET, issue #16998
+    @pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
+    def test_01_16_inv_req_get(self, env: Env, httpd, proto):
+        if proto == 'h3' and not env.have_h3():
+            pytest.skip("h3 not supported")
+        curl = CurlClient(env=env)
+        url = f'https://{env.authority_for(env.domain1, proto)}/curltest/echo'
+        r = curl.http_get(url=url, alpn_proto=proto, extra_args=[
+            '-H', "a: a\x0ab"
+        ])
+        # on h1, request is sent, h2/h3 reject
+        if proto == 'http/1.1':
+            r.check_exit_code(0)
+        else:
+            r.check_exit_code(43)