Repository metrics
- Stars
- (27,997 stars)
- PR merge metrics
- (Avg merge 8d) (378 merged PRs in 30d)
Description
Title: Envoy concatenates values of duplicated Content-Type header resulting in an invalid response.
Description:
One of our clusters is returning a response with a duplicated Content-Type header, as the following:
HTTP/1.1 200 OK
Content-Length: 0
Content-Type: text/plain
Content-Type: text/plain
Which I believe is invalid according to the HTTP spec, since only one media type is allowed in a response, but it's somehow accepted by the client of our service. However, envoy does not only accept the response but also combines the values into a single header:
HTTP/1.1 200 OK
content-length: 0
content-type: text/plain,text/plain
Which is still wrong and probably even worse than the original upstream's response (and that one does get rejected by the client of our service).
I think envoy should treat the original response as a protocol error and return a 503 to the downstream.
Repro steps:
Tested with envoy v1.11.2
- Start this dummy netcat http upstream server:
# this is using netcat-openbsd
# on alpine: apk add netcat-openbsd
while true; do echo -e "HTTP/1.1 200 OK\nContent-Length: 0\nContent-Type: text/plain\nContent-Type: text/plain\n\n" | nc -l 0.0.0.0 8888; done
- Configure envoy to use that cluster. (see config section below)
- Make a request through envoy:
curl -v http://localhost:8080/
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-length: 0
< content-type: text/plain,text/plain
< x-envoy-upstream-service-time: 0
< date: Thu, 14 Nov 2019 04:19:59 GMT
< server: envoy
<
Admin and Stats Output: no special error counters seem to be incremented.
Config:
static_resources:
listeners:
- name: "ingress_listener"
address:
socket_address:
protocol: "TCP"
address: "0.0.0.0"
port_value: 8080
filter_chains:
- filters:
- name: "envoy.http_connection_manager"
config:
route_config:
name: "ingress_route"
virtual_hosts:
- name: "virtual_host_service"
domains:
- "*"
routes:
- match:
prefix: "/"
route:
cluster: "service"
http_filters:
- name: "envoy.router"
clusters:
- name: "service"
connect_timeout: "0.25s"
type: "LOGICAL_DNS"
load_assignment:
cluster_name: "service"
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: "localhost"
port_value: 8888