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
是可行的。
我有一个虚拟服务器,它发送一个带有 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
是可行的。