Skip to content

Compliance with rfc6265 for cookie expires dates #4327

Open
@bob-pasabi

Description

@bob-pasabi

Long story short

Compliance with rfc6265 for cookie expires functionality. Took a while yesterday trying to work out why some client code wasn't working correctly, eventually tracked it down to cookie handling in aiohttp; well in pythons http.cookies. The server was returning an old but should be valid date format.

Expected behaviour

The specific problems I had are related to expires. Digging through the rfc's my current understanding is that expires as defined in rfc6265 states that the date format should be a sane-cookie-date as described in https://tools.ietf.org/html/rfc2616#section-3.3.1

  Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
  Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
  Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format

Problem is that aiohttp doesn't handle all these options and it appears to strip cookies that have two of these formats.

Steps to reproduce

import sys
import http.server
import socketserver

class MainHandler(http.server.BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Set-Cookie', 'verify1=verify1; Path=/;')
        self.send_header('Set-Cookie', 'rfc822=rfc822value; expires=Fri, 06 Nov 2020 08:49:37 GMT; Max-Age=31449600; Path=/;')
        self.send_header('Set-Cookie', 'rfc850=rfc850value; expires=Friday, 06-Nov-20 08:49:37 GMT; Max-Age=31449600; Path=/;')
        self.send_header('Set-Cookie', 'ansic=ansicvalue; expires=Fri Nov  6 08:49:37 2020; Max-Age=31449600; Path=/;')
        self.send_header('Set-Cookie', 'common=commonvalue; expires=Fri, 06-Nov-2020 16:01:51 GMT; Max-Age=31449600; Path=/;')
        self.send_header('Set-Cookie', 'expired=expiredvalue; expires=Fri, 06 Nov 1999 14:18:35 GMT; Path=/;')
        self.end_headers()
        self.wfile.write(b'cookies!')

def main(port):
    with socketserver.TCPServer(("", port), MainHandler) as httpd:
        print("serving at port", port)
        httpd.serve_forever()

if __name__ == '__main__':
    main(int(sys.argv[1]))

This is a simple python server to return the different values, I included the common type with 4 digit years as everywhere seems to use it.

If I run a request with aiohttp

import aiohttp
import asyncio

async def main():
    async with aiohttp.ClientSession() as session:
        async with session.get('http://localhost:8000') as response:
            await response.text()

            for c in response.cookies:
                print(c)
            print('in session')
            for c in session.cookie_jar:
                print(c)

if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

Actual behaviour

I will get output along the lines of

verify1
rfc822
common
expired
in session
Set-Cookie: verify1=verify1; Domain=localhost; Path=/
Set-Cookie: rfc822=rfc822value; Domain=localhost; expires=Fri, 06 Nov 2020 08:49:37 GMT; Max-Age=31449600; Path=/
Set-Cookie: common=commonvalue; Domain=localhost; expires=Fri, 06-Nov-2020 16:01:51 GMT; Max-Age=31449600; Path=/

rfc850 and ansic dates are excluded during response parsing.

I'm not sure if supporting these formats is important to you, I'm going to get the server we are connecting to to switch formats aliviating the problem for me specifically. Just thought I would report it anyway.

OS: MacOSX
Python: 3.7
aiohttp: 3.6.2

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions