curl -X OPTIONS -i -v https://csat.innovaccer.com/_version
Setup:
Client -> Nginx HTTP2 -> Server HTTP1.1
Output:
* Trying 35.173.7.7...
* TCP_NODELAY set
* Connected to csat.innovaccer.com (35.173.7.7) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (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-AES128-GCM-SHA256
* ALPN, server accepted to use h2
* Server certificate:
* subject: C=US; ST=California; L=Palo Alto; O=Innovaccer Inc.; CN=*.innovaccer.com
* start date: Jun 25 00:00:00 2019 GMT
* expire date: Jun 24 12:00:00 2021 GMT
* subjectAltName: host "csat.innovaccer.com" matched cert's "*.innovaccer.com"
* issuer: C=US; O=DigiCert Inc; CN=DigiCert SHA2 Secure Server CA
* 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 0x560a8b0e8580)
> OPTIONS /_version HTTP/2
> Host: csat.innovaccer.com
> User-Agent: curl/7.58.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS updated)!
* http2 error: Invalid HTTP header field was received: frame type: 1, stream: 1, name: [content-length], value: [0]
* HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)
* Connection #0 to host csat.innovaccer.com left intact
curl: (92) HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)
Although, when I enforce http1.1 by adding --http1.1 to the command, everything works fine.
Response, when enforcing http1.1:
* Trying 35.173.7.7...
* TCP_NODELAY set
* Connected to csat.innovaccer.com (35.173.7.7) port 443 (#0)
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/ca-certificates.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (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-AES128-GCM-SHA256
* ALPN, server accepted to use http/1.1
* Server certificate:
* subject: C=US; ST=California; L=Palo Alto; O=Innovaccer Inc.; CN=*.innovaccer.com
* start date: Jun 25 00:00:00 2019 GMT
* expire date: Jun 24 12:00:00 2021 GMT
* subjectAltName: host "csat.innovaccer.com" matched cert's "*.innovaccer.com"
* issuer: C=US; O=DigiCert Inc; CN=DigiCert SHA2 Secure Server CA
* SSL certificate verify ok.
> OPTIONS /_version HTTP/1.1
> Host: csat.innovaccer.com
> User-Agent: curl/7.58.0
> Accept: */*
>
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
< Server: nginx
Server: nginx
< Date: Mon, 09 Dec 2019 12:19:25 GMT
Date: Mon, 09 Dec 2019 12:19:25 GMT
< Content-Type: application/octet-stream
Content-Type: application/octet-stream
< Content-Length: 0
Content-Length: 0
< Connection: keep-alive
Connection: keep-alive
< Content-Length: 0
Content-Length: 0
< Content-Type: application/json
Content-Type: application/json
< Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE, PATCH
Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE, PATCH
< Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: *
< Access-Control-Allow-Headers: Authorization, Content-Type, x-customer-id,x-customer-url,x-requested-with,content-type,Cache-Control,Pragma,Date,Authorization,If-Modified-Since
Access-Control-Allow-Headers: Authorization, Content-Type, x-customer-id,x-customer-url,x-requested-with,content-type,Cache-Control,Pragma,Date,Authorization,If-Modified-Since
<
* Connection #0 to host csat.innovaccer.com left intact
NOTE: Content-Length header is present twice in the response, which was added by nginx.
I also understand that HTTP/2 doesn't support Connection headers, but I think it has no problem with Content-Length. I also tried other http2 clients like hyper, and they are fine with Content-Length being returned in the response in the same request..
I expected the above request to work successfully.
curl 7.58.0 (x86_64-pc-linux-gnu) libcurl/7.58.0 OpenSSL/1.1.1 zlib/1.2.11 libidn2/2.0.4 libpsl/0.19.1 (+libidn2/2.0.4) nghttp2/1.30.0 librtmp/2.3
Release-Date: 2018-01-24
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy PSL
Ubuntu 18.04
Right, HTTP/2 doesn't support Connection headers but this is not such a header. HTTP/2 still supports Content-Length: even when set to zero.
What it doesn't support though is setting it to zero and then still trying to send data. Mentioned in RFC 7540 section 8.1.2.6.
I can reproduce this with my current git master using the command line shown above (nghttp2/1.40.0).
The error is caught by nghttp2 already in the HTTP/2 framing layer:
~~~
@tatsuhiro-t can you recall off the top of your head what might make nghttp2 to report this error for Content-Length ?
Found it, the duplicated Content-Length header makes nghttp2 reject this:
https://tools.ietf.org/html/rfc7230#section-3.3.2 allows endpoint to reject the response which contains multiple content-length header fields. Apparently server is misconfigured or broken.
Not a bug, allowed by the RFC.
~
If a message is received that has multiple Content-Length header
fields with field-values consisting of the same decimal value, or a
single Content-Length header field with a field value containing a
list of identical decimal values (e.g., "Content-Length: 42, 42"),
indicating that duplicate Content-Length header fields have been
generated or combined by an upstream message processor, then the
recipient MUST either reject the message as invalid or replace the
duplicated field-values with a single valid Content-Length field
containing that decimal value prior to determining the message body
length or forwarding the message.
~
It would be fairly easy to change nghttp2 to allow duplicates when the value is the same. You would have to discuss that with @tatsuhiro-t in https://github.com/nghttp2/nghttp2/issues
Most helpful comment
https://tools.ietf.org/html/rfc7230#section-3.3.2 allows endpoint to reject the response which contains multiple content-length header fields. Apparently server is misconfigured or broken.