aiohttp.ClientSession 未使用 'expires' 键设置 cookie

aiohttp.ClientSession does not set cookie with 'expires' key

我有一个虚拟服务器,它发送一个带有 Set-Cookie 的 cookie,我用 aiohttp.ClienSession.

调用它

当我发送一个带有名称和值的简单 cookie 时,会话将它存储在它的 cookie 罐中。 但是,如果我添加 expires 键,则根本不会存储 cookie。

此行为仅发生在 expires 键上,而不是其他标准键。 此外,当我改用 requests.Session 时,cookie 已正确存储。

我发送 cookie 的方式有问题吗?


服务器代码如下:

import flask

app = flask.Flask(__name__)

@app.route("/cookie/<name>/<value>")
def send_cookie(name, value):
    cookie = f"{name}={value}"
    response = flask.Response(status=200, headers={"Set-Cookie": cookie})
    return response

@app.route("/cookie/expires/<name>/<value>")
def send_expiring_cookie(name, value):
    cookie = f"{name}={value}; expires=Wed, 15 Jan 2020 09:45:07 -0000"
    response = flask.Response(status=200, headers={"Set-Cookie": cookie})
    return response

app.run("localhost")

aiohttp.ClientSession:

import asyncio as aio
import aiohttp

async def main():
    async with aiohttp.ClientSession() as session:
        response = await session.get("http://localhost:5000/cookie/hello/world")
        print(session.cookie_jar._cookies)
        # defaultdict(<class 'http.cookies.SimpleCookie'>, {'localhost': <SimpleCookie: hello='world'>})

    async with aiohttp.ClientSession() as session:
        response = await session.get("http://localhost:5000/cookie/expires/hello/world")
        print(session.cookie_jar._cookies)
        # defaultdict(<class 'http.cookies.SimpleCookie'>, {})

loop = aio.get_event_loop()
loop.run_until_complete(main())

requests.Session:

import requests

with requests.Session() as session:
    session.get("http://localhost:5000/cookie/hello/world")
    print(session.cookies)
    # <RequestsCookieJar[<Cookie hello=world for localhost.local/cookie/hello>]>

with requests.Session() as session:
    session.get("http://localhost:5000/cookie/expires/hello/world")
    print(session.cookies)
    # <RequestsCookieJar[<Cookie hello=world for localhost.local/cookie/expires/hello>]

首先,感谢您提供如此出色的问题示例!

我设法让你的解决方案通过转义过期字段中的空格来存储 cookie,如下所示:

import re

import flask

app = flask.Flask(__name__)

@app.route("/cookie/<name>/<value>")
def send_cookie(name, value):
    cookie = f"{name}={value}"
    response = flask.Response(status=200, headers={"Set-Cookie": cookie})
    return response

@app.route("/cookie/expires/<name>/<value>")
def send_expiring_cookie(name, value):
    exp_date = re.escape("Wed, 15 Jan 2020 09:45:07 -0000")
    cookie = f"{name}={value}; expires={exp_date}"
    response = flask.Response(status=200, headers={"Set-Cookie": cookie})
    return response

app.run("localhost")

我认为问题出在 aiohttp 解析日期的方式上。我不知道他们声称什么应该起作用,但它肯定是使用正则表达式来解析日期,所以它需要被转义是有道理的。

没有设置cookie的原因是没有解析成功。 aiohttp 模块依赖于标准库中的 http.cookies 及其 SimpleCookie class.

当响应到达时,它的 headers 被检查是否有 Set-Cookie 键,并且与之关联的值用于实例化 SimpleCookie。 后者有一个 load 方法,它解析 cookie 字符串并提取 cookie 信息。 为此,cookie 字符串与正则表达式 _CookiePattern 匹配,该正则表达式在同一模块的第 434 行(在标记 v3.7.2 上)定义:

_CookiePattern = re.compile(r"""
    \s*                            # Optional whitespace at start of cookie
    (?P<key>                       # Start of group 'key'
    [""" + _LegalKeyChars + r"""]+?   # Any word of at least one letter
    )                              # End of group 'key'
    (                              # Optional group: there may not be a value.
    \s*=\s*                          # Equal Sign
    (?P<val>                         # Start of group 'val'
    "(?:[^\"]|\.)*"                  # Any doublequoted string
    |                                  # or
    \w{3},\s[\w\d\s-]{9,11}\s[\d:]{8}\sGMT  # Special case for "expires" attr
    |                                  # or
    [""" + _LegalValueChars + r"""]*      # Any word or empty string
    )                                # End of group 'val'
    )?                             # End of optional value group
    \s*                            # Any number of spaces.
    (\s+|;|$)                      # Ending either at space, semicolon, or EOS.
    """, re.ASCII | re.VERBOSE)    # re.ASCII may be removed if safe.

从这个模式中,我们可以看出 expires 属性的预期模式是:

\w{3},\s[\w\d\s-]{9,11}\s[\d:]{8}\sGMT

此模式实现了(以一种有些复杂且不绝对准确但仍然很短的方式)由 MDN docs 指定的日期格式。

因此,我的 expires 字段为 expires=Wed, 15 Jan 2020 09:45:07 -0000 的 cookie 自然会被忽略,因为时区表示为 -0000 而预期为 GMT。 顺便说一下,MDN docs 是这样说的:

GMT

Greenwich Mean Time. HTTP dates are always expressed in GMT, never in local time.

结论是我的 cookie 格式不正确。 毫不奇怪,用 GMT 替换 -0000 是可行的。