Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
bittebak opened this issue Jan 16, 2022 · 10 comments · Fixed by Azure/azure-functions-python-worker#1004

Comments

@bittebak
Copy link

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.

@v-bbalaiagar v-bbalaiagar self-assigned this Jan 18, 2022
@v-bbalaiagar
Copy link

Hi @pragnagopa / @fabiocav, Can you please suggest how to proceed here

@pragnagopa
Copy link
Member

@v-bbalaiagar
Copy link

Transferring this issue to python worker repo for further investigation.

@v-bbalaiagar v-bbalaiagar transferred this issue from Azure/Azure-Functions Jan 19, 2022
@YunchuWang YunchuWang transferred this issue from Azure/azure-functions-python-worker Mar 15, 2022
@Ben-Ruben
Copy link

Having the same issue. Is there any timeline available on if/when this will be available? Or would it be better to rewrite my functions to e.g. Node where setting multiple cookies seems to be available?

@Th3OnlyN00b
Copy link

Th3OnlyN00b commented Oct 4, 2023

Leaving this here for anyone like myself who is searching for how to do this, finds this as the only relevant google search result, and can't figure out how to do it. Here's example code that is working.

response = func.HttpResponse(json.dumps({'code': 'success', 'message': "User logged in"}), status_code=200)
# Headers have to be added by this method as the default is a dict, which doesn't work because dict keys are unique
response.headers.add("Set-Cookie", f"token={token}")
response.headers.add("Set-Cookie", f"id={uuid}")

@aadamsx
Copy link

aadamsx commented Oct 11, 2023

Leaving this here for anyone like myself who is searching for how to do this, finds this as the only relevant google search result, and can't figure out how to do it. Here's example code that is working.

response = func.HttpResponse(json.dumps({'code': 'success', 'message': "User logged in"}), status_code=200)
# Headers have to be added by this method as the default is a dict, which doesn't work because dict keys are unique
response.headers.add("Set-Cookie", f"token={token}")
response.headers.add("Set-Cookie", f"id={uuid}")

Thanks for the code, but this ended up not working. You'll see the Set-Cookies in the header response, but if you'll notice in the cookie's tab, only one cooke is set, and since it's a dictionary, the second Set-Cookie overwrites the values of the first Set-Cookie.

@Th3OnlyN00b
Copy link

Th3OnlyN00b commented Oct 11, 2023

image image

Odd, that works perfectly fine for me. Behind the scenes it's not a dictionary actually, it's a list of tuples as seen in line 1138 of the Headers object file: self._list.append((_key, _value))

(Both of the credentials in the screenshot are now invalid btw, don't worry)

@aadamsx
Copy link

aadamsx commented Oct 11, 2023

Here's what I'm seeing:

image

The client is a stock create react app running locally on https://127.0.0.1:3000 and using Chrome dev tools.

I'm using stock azure functions v4 with no middle where or extra imports -- maybe I need to add something?

So in terms of cookies, both show in the response, but for cookies set, only the first ever shows as valid by chrome. In prior iterations, I would see that the values for the first token would be overwritten by the second.

@Th3OnlyN00b
Copy link

Th3OnlyN00b commented Oct 11, 2023

This is an issue with chrome, not your code. https://stackoverflow.com/questions/63204093/how-to-get-set-multiple-set-cookie-headers-using-fetch-api

Officially, there is no requirement to support multiple Set-Cookie headers in a response, so chrome's auto-parsing of your headers is deleting the second one (but the standard arguably says it could keep either one). If you try this with Postman or any other request client that supports multiple headers of the same name, you'll find you're getting both cookies set. Remember that how the headers are handled is entirely on the client, and you have full control over that in your client code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
9 participants