Skip to content

Commit da59847

Browse files
committed
scope cookies by default
1 parent d89d553 commit da59847

File tree

2 files changed

+87
-6
lines changed

2 files changed

+87
-6
lines changed

src/treq/client.py

+45-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import uuid
44
import warnings
55
from collections.abc import Mapping
6-
from http.cookiejar import CookieJar
6+
from http.cookiejar import CookieJar, Cookie
77
from urllib.parse import quote_plus, urlencode as _urlencode
88

99
from twisted.internet.interfaces import IProtocol
@@ -30,7 +30,7 @@
3030
from treq.auth import add_auth
3131
from treq import multipart
3232
from treq.response import _Response
33-
from requests.cookies import cookiejar_from_dict, merge_cookies
33+
from requests.cookies import merge_cookies
3434

3535

3636
_NOTHING = object()
@@ -43,6 +43,45 @@ def urlencode(query, doseq):
4343
return s
4444

4545

46+
def _scoped_cookiejar_from_dict(url_object, cookie_dict):
47+
"""
48+
Create a CookieJar from a dictionary whose cookies are all scoped to the
49+
given URL's origin.
50+
51+
@note: This does not scope the cookies to any particular path, only the
52+
host, port, and scheme of the given URL.
53+
"""
54+
cookie_jar = CookieJar()
55+
if cookie_dict is None:
56+
return cookie_jar
57+
for k, v in cookie_dict.items():
58+
cookie_jar.set_cookie(
59+
Cookie(
60+
version=0,
61+
name=k,
62+
value=v,
63+
domain=url_object.host,
64+
port=str(url_object.port),
65+
path="",
66+
secure=(url_object.scheme == 'https'),
67+
expires=None,
68+
discard=False,
69+
comment=None,
70+
comment_url=None,
71+
rfc2109=False,
72+
port_specified=(
73+
not (url_object.scheme == "https" and url_object.port == 443)
74+
or (url_object.scheme == "http" and url_object.port == 80)
75+
),
76+
path_specified=False,
77+
domain_specified=False,
78+
domain_initial_dot=False,
79+
rest=[],
80+
)
81+
)
82+
return cookie_jar
83+
84+
4685
class _BodyBufferingProtocol(proxyForInterface(IProtocol)):
4786
def __init__(self, original, buffer, finished):
4887
self.original = original
@@ -98,7 +137,9 @@ class HTTPClient:
98137
def __init__(self, agent, cookiejar=None,
99138
data_to_body_producer=IBodyProducer):
100139
self._agent = agent
101-
self._cookiejar = cookiejar or cookiejar_from_dict({})
140+
if cookiejar is None:
141+
cookiejar = CookieJar()
142+
self._cookiejar = cookiejar
102143
self._data_to_body_producer = data_to_body_producer
103144

104145
def get(self, url, **kwargs):
@@ -195,7 +236,7 @@ def request(
195236
headers.setRawHeaders(b'Content-Type', [contentType])
196237

197238
if not isinstance(cookies, CookieJar):
198-
cookies = cookiejar_from_dict(cookies)
239+
cookies = _scoped_cookiejar_from_dict(parsed_url, cookies)
199240

200241
cookies = merge_cookies(self._cookiejar, cookies)
201242
wrapped_agent = CookieAgent(self._agent, cookies)

src/treq/test/test_testing.py

+42-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44
from functools import partial
55
from inspect import getmembers, isfunction
6+
from json import dumps
67

78
from unittest.mock import ANY
89

@@ -32,6 +33,24 @@ def render(self, request):
3233
return b"I'm a teapot"
3334

3435

36+
class _RedirectResource(Resource):
37+
"""
38+
Resource that redirects to a different domain.
39+
"""
40+
isLeaf = True
41+
def render(self, request):
42+
if b'redirected' not in request.uri:
43+
request.redirect(b'https://example.org/redirected')
44+
return dumps(
45+
{
46+
key.decode("charmap"): [
47+
value.decode("charmap")
48+
for value in values
49+
]
50+
for key, values in
51+
request.requestHeaders.getAllRawHeaders()}
52+
).encode("utf-8")
53+
3554
class _NonResponsiveTestResource(Resource):
3655
"""Resource that returns NOT_DONE_YET and never finishes the request"""
3756
isLeaf = True
@@ -272,8 +291,10 @@ def test_handles_successful_asynchronous_requests_with_streaming(self):
272291

273292
def test_session_persistence_between_requests(self):
274293
"""
275-
Calling request.getSession() in the wrapped resource will return
276-
a session with the same ID, until the sessions are cleaned.
294+
Calling request.getSession() in the wrapped resource will return a
295+
session with the same ID, until the sessions are cleaned; in other
296+
words, cookies are propagated between requests when the result of
297+
C{response.cookies()} is passed to the next request.
277298
"""
278299
rsrc = _SessionIdTestResource()
279300
stub = StubTreq(rsrc)
@@ -304,6 +325,25 @@ def test_session_persistence_between_requests(self):
304325
sid_4 = self.successResultOf(resp.content())
305326
self.assertEqual(sid_3, sid_4)
306327

328+
def test_different_domains(self):
329+
"""
330+
Cookies manually specified as part of a dictionary are not relayed
331+
through redirects.
332+
333+
(This is really more of a test for scoping of cookies within treq
334+
itself, rather than just for testing.)
335+
"""
336+
rsrc = _RedirectResource()
337+
stub = StubTreq(rsrc)
338+
d = stub.request(
339+
"GET", "http://example.com/",
340+
cookies={"not-across-redirect": "nope"}
341+
)
342+
resp = self.successResultOf(d)
343+
received = self.successResultOf(resp.json())
344+
self.assertNotIn('not-across-redirect', received.get('Cookie', [''])[0])
345+
346+
307347

308348
class HasHeadersTests(TestCase):
309349
"""

0 commit comments

Comments
 (0)