cloudflare / cloudflare/quiche

HTTP/3 incorect headers handling (according to RFC):

Open
#838 2 comments 0 reactions 0 assignees View on GitHub
nginx
Dominant language
Rust
Stars
11.8k
Forks
1.1k
Avg merge
21h 9m
Merged PRs (30d)
6

Description

Hi, i discovered that quiche implementation doesn't entirely follows rfc in terms of headers: Upgrade, Transfer-encoding: chunked, modifying Content-length header.
RFC says:
Transfer-encoding: chunked
```
The "chunked" transfer encoding defined in Section 7.1 of [HTTP11]
MUST NOT be used.

...

The only exception to this is the TE header field, which MAY be
present in an HTTP/3 request header; when it is, it MUST NOT contain
any value other than "trailers".
```
Content-Length:

```
A request or response that includes a payload body can include a
Content-Length header field. A request or response is also malformed
if the value of a content-length header field does not equal the sum of the DATA frame payload lengths that form the body
```
Upgrade:

```
HTTP/3 does not support the HTTP Upgrade mechanism (Section 9.9 of
[HTTP11]) or 101 (Switching Protocols) informational status code
(Section 9.2.2 of [SEMANTICS]).
```
I created comparation table of google.com cloudflare-quic.com and my server with nginx+quic (quic 0.7.0) responses:

  | Upgrade: (http3) | Upgrade: (http1.1) | Transfer-encoding: (http3) | Transfer-encoding: (http1.1) | Content-length >: (http3) | Content-length >: (http1.1) | Content-length <: (http3) | Content-length <: (http1.1) | Content-length =: (http3) | Content-length =: (http1.1) | Content-length xxx: (http3) | Content-length xxx: (http1.1)
-- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | --
google.com | HTTP/3 400 | HTTP/1.1 200 OK | hangs | HTTP/1.1 405 Method Not Allowed | HTTP/3 400 | hangs | HTTP/3 400 | HTTP/1.1 405 Method Not Allowed | HTTP/3 405 | HTTP/1.1 405 Method Not Allowed | HTTP/3 400 | Error 400 (Bad Request)
cloudflare-quic.com | HTTP/3 200 | HTTP/1.1 200 OK | hangs | HTTP/1.1 200 OK | HTTP/3 400 | hangs | HTTP/3 400 | HTTP/1.1 200 OK | HTTP/3 200 | HTTP/1.1 200 OK | HTTP/3 400 | HTTP/1.1 400 Bad Request
my_domain | HTTP/3 200 | HTTP/1.1 200 OK | hangs | HTTP/1.1 200 OK | hangs | hangs | hangs | HTTP/1.1 200 OK | HTTP/3 200 | HTTP/1.1 200 OK | HTTP/3 400 | HTTP/1.1 400 Bad Request

in this table let's assume google.com response 405 is 200/OK response.
Upgrade: google.com response is 400 Bad request, but Quiche implementation ignores this header. (is it ok with rfc?)
Transfer-encoding: hangs on all implementations, but rfc says it should be treated as malwared request so I belive server response should be 400.
Content-length with greater value is treated as bad request in google.com and cloudflare-quic.com server, but only on my nginx+quic server it will hang (is it nginx patch issue?)
Content-length with smaller value is treated as bad request only in google implementation. Quiche implementation truncates data frame, but it should be treated as bad request.

How i tested this:

```
curl --http3 -vvv https:/// -H "Connection: Upgrade;" -H "Upgrade: h2c"
curl --http3 -vvv https:/// -H "Content-Type: application/x-www-form-urlencoded" -H "Transfer-encoding: chunked" --data "test"
curl --http3 -vvv https:/// -H "Content-Length: 10" --data "test"
curl --http3 -vvv https:/// -H "Content-Length: 1" --data "test"
curl --http3 -vvv https:/// -H "Content-Length: 4" --data "test"
curl --http3 -vvv https:/// -H "Content-Length: xxx" --data "test"
```
Last interesting note about content-length:
when cloudflare-quic.com gets malwared content-length header will return different responses, i.e:

```
> content-length: 1
> content-type: application/x-www-form-urlencoded
>
} [4 bytes data]
< HTTP/3 400
< server: nginx
< date: Thu, 04 Feb 2021 10:38:43 GMT
< content-type: text/html
< content-length: 155
< cf-ray: 61c3c37eddd2166d-WAW
<
```
but correct content-length response is:

```
> content-length: 4
> content-type: application/x-www-form-urlencoded
>
} [4 bytes data]
< HTTP/3 200
< date: Thu, 04 Feb 2021 10:39:16 GMT
< content-type: text/html
< content-length: 109425
< set-cookie: __cfduid=dbbf02f7232794bf8d7c19597e7f3d9a71612435156; expires=Sat, 06-Mar-21 10:39:16 GMT; path=/; domain=.cloudflare-quic.com; HttpOnly; SameSite=Lax; Secure
< cf-request-id: 080e39072a00001661e609c000000001
< expect-ct: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
< server: cloudflare
< cf-ray: 61c3c451dc241661-WAW
< alt-svc: h3-27=":443"; ma=86400, h3-28=":443"; ma=86400, h3-29=":443"; ma=86400
<
```
My configuration:

```
curl 7.75.0-DEV (x86_64-pc-linux-gnu) libcurl/7.75.0-DEV BoringSSL zlib/1.2.11 libidn2/2.0.5 quiche/0.7.0
Release-Date: [unreleased]
Protocols: dict file ftp ftps gopher gophers http https imap imaps mqtt pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: alt-svc AsynchDNS HTTP3 HTTPS-proxy IDN IPv6 Largefile libz NTLM NTLM_WB SSL UnixSockets
```
Nginx 1.16.1 with latest quiche patch.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.