mod_h2: HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR
Hi there We are seeing the following problem:
- When trying to do a HEAD on a PHP-File we get n “PROTOCOL_ERROR(0x01) / HTTP/2 stream 1 was not closed cleanly” error message.
- GET works fine.
- Only happens with HEAD to PHP-Files. HEAD to HTML Files for example works fine.
- Happens with curl but also with nghttp-Client…
- Does not happen with nginx
- Apache v2.4.34 / libnghttp2 v1.32.0 / curl v7.61.0 / FreeBSD 10.4
Thanks for taking a look at it.
curl HEAD output:
$ curl -4 --http2 -I -v https://danii.ch/phpinfo.php
* Trying x.x.x.x...
* TCP_NODELAY set
* Connected to danii.ch (x.x.x.x) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=www.danii.ch
* start date: May 5 00:00:00 2018 GMT
* expire date: Nov 20 12:00:00 2018 GMT
* subjectAltName: host "danii.ch" matched cert's "danii.ch"
* issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=Encryption Everywhere DV TLS CA - G2
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x561a794c7f90)
> HEAD /phpinfo.php HTTP/2
> Host: danii.ch
> User-Agent: curl/7.61.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
* HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)
* Connection #0 to host danii.ch left intact
curl: (92) HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)
curl GET output:
* Trying x.x.x.x...
* TCP_NODELAY set
* Connected to danii.ch (x.x.x.x) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: none
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Client hello (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=www.danii.ch
* start date: May 5 00:00:00 2018 GMT
* expire date: Nov 20 12:00:00 2018 GMT
* subjectAltName: host "danii.ch" matched cert's "danii.ch"
* issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=Encryption Everywhere DV TLS CA - G2
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x5613edf1df90)
> GET /phpinfo.php HTTP/2
> Host: danii.ch
> User-Agent: curl/7.61.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 100)!
< HTTP/2 200
HTTP/2 200
< date: Mon, 06 Aug 2018 13:39:32 GMT
date: Mon, 06 Aug 2018 13:39:32 GMT
< server: Apache/2.4
server: Apache/2.4
< content-type: text/html; charset=UTF-8
content-type: text/html; charset=UTF-8
<
* Connection #0 to host danii.ch left intact
HEAD via Google Chrome Console:
var http = new XMLHttpRequest();
http.open("HEAD", "https://prognolite.com/header.php");
http.send(); console.log(http);
Failed to load resource: net::ERR_SPDY_PROTOCOL_ERROR
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 37 (17 by maintainers)
Commits related to this issue
- * Adding defensive code for stream EOS handling, in case the request handler somehow missed to signal it the normal way. Should address #167 and #170. — committed to icing/mod_h2 by deleted user 6 years ago
- mod_http2: adding defensive code for stream EOS handling, in case the request handler missed to signal it the normal way (eos buckets). Addresses github issues https://github.com/icing/mod_... — committed to apache/httpd by icing 6 years ago
- Merge r1843426 from trunk: mod_http2: adding defensive code for stream EOS handling, in case the request handler missed to signal it the normal way (eos buckets). Addresses github issues h... — committed to apache/httpd by jimjag 6 years ago
- * Fixed issue where mod_proxy_http2 would trigger an invalid internal state when retrying requests. * applied patch for #167 provided by Michael Kaufmann (@mkauf) about errors on HEAD requests (h... — committed to icing/mod_h2 by deleted user 5 years ago
- *) mod_http2: Configuration directoves H2Push and H2Upgrade can now be specified per Location/Directory, e.g. disabling PUSH for a specific set of resources. [Stefan Eissing] *) mod_http2: ... — committed to apache/httpd by icing 5 years ago
- Merge of 1849296,1852038,1852101,1852339,1853171,1853967,1854365,1854963,1854964,1855295,1855411 from trunk: *) mod_http2: when SSL renegotiation is inhibited and a 403 ErrorDocument is in pla... — committed to apache/httpd by icing 5 years ago
- Merge r1843426 from trunk: mod_http2: adding defensive code for stream EOS handling, in case the request handler missed to signal it the normal way (eos buckets). Addresses github issues h... — committed to apache/httpd by jimjag 6 years ago
- Merge of 1849296,1852038,1852101,1852339,1853171,1853967,1854365,1854963,1854964,1855295,1855411 from trunk: *) mod_http2: when SSL renegotiation is inhibited and a 403 ErrorDocument is in pla... — committed to apache/httpd by icing 5 years ago
- mod_http2: adding defensive code for stream EOS handling, in case the request handler missed to signal it the normal way (eos buckets). Addresses github issues https://github.com/icing/mod_... — committed to apache/httpd by icing 6 years ago
- *) mod_http2: Configuration directoves H2Push and H2Upgrade can now be specified per Location/Directory, e.g. disabling PUSH for a specific set of resources. [Stefan Eissing] *) mod_http2: ... — committed to apache/httpd by icing 5 years ago
Thanks a lot @icing and @mkauf! A backport to 2.4.x would be awesome.
Proposed in r1854966.
Correct, thanks for the reminder. I have a bunch of other changes which are to be merged, but I should do this one independently.
@mkauf, finding all my mistakes… 😃
I’ll make a version tomorrow with this change and, if verified by the folks here, bring it into the Apache subversion space.
The bug occurs if the client performs a HEAD request and if the content generator module calls
ap_rflush()
before generating body data. mod_h2 then inserts the headers and tries to remove body buckets (there are none). This code has a bug, and it removes the headers that it just inserted.This bugfix works for me:
I added
AP_STATUS_IS_HEADER_ONLY()
to remove body buckets if the HTTP status code implies that the response has no body (204 No Content / 304 Not Modified). I think that’s necessary because Apache does it for HTTP 1.x inap_http_header_filter()
.