Skip to content

Python - azure.functions.WsgiMiddleware does not support multiple Set-Cookie response headers #107

@bittebak

Description

@bittebak

Bug

When a HttpResponse has multiple headers with the same name, only one header is returned.
Django uses multiple response cookies (e.g. 'csrf' and 'messages').
Multiple Set-Cookie headers can be returned. Set-Cookie is the name (the key) of the Http Response Header.

Context

A Django app running in an Azure Function

package

Name: azure-functions
Version: 1.8.0
Summary: Azure Functions for Python
Home-page: UNKNOWN
Author: Microsoft Corporation
Author-email: [email protected]

Version

Azure function runtime version = ~4

Reproduction

  1. Create a http response with two Set-Cookie headers
    This header has been captured from the app running without the Azure wsgi middleware.
HTTP/1.1 200 OK
Date: Sun, 16 Jan 2022 09:00:59 GMT
Server: WSGIServer/0.2 CPython/3.9.9
Content-Type: text/html; charset=utf-8
X-Frame-Options: DENY
Vary: Cookie
Content-Length: 5206
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Set-Cookie:  messages=""; expires=Thu, 01 Jan 1970 00:00:00 GMT; HttpOnly; Max-Age=0; Path=/; SameSite=Lax
Set-Cookie:  csrftoken=L6coOZOclDhAg2X7geBKbhvJSHPxz9AbgOkoVqNAIsgVe8oxTEPTSPbc2VPSyjwq; expires=Sun, 15 Jan 2023 09:00:59 GMT; Max-Age=31449600; Path=/; SameSite=Lax
  1. Run without azure.functions.WsgiMiddleware
    Two Set-Cookies headers are returned (as depicted above)

  2. Run with azure.functions.WsgiMiddleware.handle

import azure.functions as func
def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    http_response = func.WsgiMiddleware(application).handle(req, context)
    return http_response

Only one header with the name "Set-Cookie" is returned.

Local and remote

This behaviour has been reproduced both locally (while debugging) as on an Azure Function container (icarus-int)

Root cause

Python package 'azure-functions'
azure\functions_http.py

class HttpResponseHeaders(BaseHeaders, collections.abc.MutableMapping):

    def __setitem__(self, key: str, value: str):
        self.__http_headers__[key.lower()] = value

    def __delitem__(self, key: str):
        del self.__http_headers__[key.lower()]

The headers are stored as a dictionary. All keys in a dictionary are unique.

References

https://www.rfc-editor.org/rfc/rfc6265#page-6
Section 3

"Origin servers SHOULD NOT fold multiple Set-Cookie header fields into
a single header field. The usual mechanism for folding HTTP headers
fields (i.e., as defined in [RFC2616]) might change the semantics of
the Set-Cookie header field because the %x2C (",") character is used
by Set-Cookie in a way that conflicts with such folding."

https://www.rfc-editor.org/rfc/rfc6265#section-4.1
"Servers SHOULD NOT include more than one Set-Cookie header field in
the same response with the same cookie-name. (See Section 5.2 for
how user agents handle this case.)"

This means that multiple Set-Cookie headers with different cookie-names are allowed.

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions