[python] Cope with duplicate header values (#9205)

There are times when a request can arrive for a Python function with headers as a list. One of those examples is this header `x-vercel-proxied-for` which apparently is set twice. Example:

`[b'x-vercel-proxied-for', [b'207.81.134.243', b'172.71.147.74']]`

I took a quick scan through the other Python server implementations and I don't think any of them manipulate the value of the HTTP headers, the way the ASGI one does so I think we are good there.

To reproduce:

`curl https://..../ -H "foo: bar" -H "foo: bar"`

Will fail.

Fixes: https://github.com/vercel/vercel/issues/9132
This commit is contained in:
Andy McKay
2023-02-03 09:01:02 -08:00
committed by GitHub
parent b5a9408272
commit ba498f3a8e
4 changed files with 50 additions and 1 deletions

View File

@@ -0,0 +1,14 @@
async def app(scope, receive, send):
assert scope["type"] == "http"
await send(
{
"type": "http.response.start",
"status": 200,
}
)
await send(
{
"type": "http.response.body",
"body": b"hello world"
}
)

View File

@@ -0,0 +1,18 @@
const execa = require('execa');
module.exports = async function ({ deploymentUrl, fetch }) {
const probeUrl = `https://${deploymentUrl}`;
const result = await execa('curl', [
probeUrl,
'-s',
'-H',
'foo: bar',
'-H',
'foo: bar',
]);
if (result.stdout.includes('FUNCTION_INVOCATION_FAILED')) {
throw new Error(
'Duplicate headers should not cause a function invocation failure'
);
}
};

View File

@@ -0,0 +1,9 @@
{
"version": 2,
"builds": [
{
"src": "*.py",
"use": "@vercel/python"
}
]
}

View File

@@ -276,6 +276,14 @@ elif 'app' in __vc_variables:
query = url.query.encode()
path = url.path
headers_encoded = []
for k, v in headers.items():
# Cope with repeated headers in the encoding.
if isinstance(v, list):
headers_encoded.append([k.lower().encode(), [i.encode() for i in v]])
else:
headers_encoded.append([k.lower().encode(), v.encode()])
scope = {
'server': (headers.get('host', 'lambda'), headers.get('x-forwarded-port', 80)),
'client': (headers.get(
@@ -285,7 +293,7 @@ elif 'app' in __vc_variables:
'scheme': headers.get('x-forwarded-proto', 'http'),
'root_path': '',
'query_string': query,
'headers': [[k.lower().encode(), v.encode()] for k, v in headers.items()],
'headers': headers_encoded,
'type': 'http',
'http_version': '1.1',
'method': payload['method'],