mirror of
https://github.com/httpie/cli.git
synced 2024-11-25 17:23:25 +01:00
Combine cookies from original request and session file
Close #932 Co-authored-by: kbanc <katherine.bancoft@gmail.com> Co-authored-by: Gabriel Cruz <gabs.oficial98@gmail.com>
This commit is contained in:
parent
93d07cfe57
commit
9500ce136a
55
README.rst
55
README.rst
@ -530,7 +530,6 @@ Simple example:
|
|||||||
$ http PUT httpbin.org/put name=John email=john@example.org
|
$ http PUT httpbin.org/put name=John email=john@example.org
|
||||||
|
|
||||||
.. code-block:: http
|
.. code-block:: http
|
||||||
|
|
||||||
PUT / HTTP/1.1
|
PUT / HTTP/1.1
|
||||||
Accept: application/json, */*;q=0.5
|
Accept: application/json, */*;q=0.5
|
||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
@ -1745,6 +1744,60 @@ exchange after it has been created, specify the session name via
|
|||||||
# But it is not updated:
|
# But it is not updated:
|
||||||
$ http --session-read-only=./ro-session.json httpbin.org/headers Custom-Header:new-value
|
$ http --session-read-only=./ro-session.json httpbin.org/headers Custom-Header:new-value
|
||||||
|
|
||||||
|
Cookie Storage Behaviour
|
||||||
|
------------------------
|
||||||
|
|
||||||
|
**TL;DR:** Cookie storage priority: Server response > Command line request > Session file
|
||||||
|
|
||||||
|
To set a cookie within a Session there are three options:
|
||||||
|
|
||||||
|
1. Get a `Set-Cookie` header in a response from a server
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ http --session=./session.json httpbin.org/cookie/set?foo=bar
|
||||||
|
|
||||||
|
2. Set the cookie name and value through the command line as seen in `cookies`_
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ http --session=./session.json httpbin.org/headers Cookie:foo=bar
|
||||||
|
|
||||||
|
3. Manually set cookie parameters in the json file of the session
|
||||||
|
|
||||||
|
.. code-block:: json
|
||||||
|
|
||||||
|
{
|
||||||
|
"__meta__": {
|
||||||
|
"about": "HTTPie session file",
|
||||||
|
"help": "https://httpie.org/doc#sessions",
|
||||||
|
"httpie": "2.2.0-dev"
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"password": null,
|
||||||
|
"type": null,
|
||||||
|
"username": null
|
||||||
|
},
|
||||||
|
"cookies": {
|
||||||
|
"foo": {
|
||||||
|
"expires": null,
|
||||||
|
"path": "/",
|
||||||
|
"secure": false,
|
||||||
|
"value": "bar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cookies will be set in the session file with the priority specified above. For example, a cookie
|
||||||
|
set through the command line will overwrite a cookie of the same name stored
|
||||||
|
in the session file. If the server returns a `Set-Cookie` header with a
|
||||||
|
cookie of the same name, the returned cookie will overwrite the preexisting cookie.
|
||||||
|
|
||||||
|
Expired cookies are never stored. If a cookie in a session file expires, it will be removed before
|
||||||
|
sending a new request. If the server expires an existing cookie, it will also be removed from the
|
||||||
|
session file.
|
||||||
|
|
||||||
|
|
||||||
Config
|
Config
|
||||||
======
|
======
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ Persistent, JSON-serialized sessions.
|
|||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from http.cookies import SimpleCookie
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Iterable, Optional, Union
|
from typing import Iterable, Optional, Union
|
||||||
from urllib.parse import urlsplit
|
from urllib.parse import urlsplit
|
||||||
@ -76,7 +78,13 @@ class Session(BaseConfigDict):
|
|||||||
continue # Ignore explicitly unset headers
|
continue # Ignore explicitly unset headers
|
||||||
|
|
||||||
value = value.decode('utf8')
|
value = value.decode('utf8')
|
||||||
if name == 'User-Agent' and value.startswith('HTTPie/'):
|
if name.lower() == 'user-agent' and value.startswith('HTTPie/'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if name.lower() == 'cookie':
|
||||||
|
for cookie_name, morsel in SimpleCookie(value).items():
|
||||||
|
self['cookies'][cookie_name] = {'value': morsel.value}
|
||||||
|
del request_headers[name]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for prefix in SESSION_IGNORED_HEADER_PREFIXES:
|
for prefix in SESSION_IGNORED_HEADER_PREFIXES:
|
||||||
|
@ -35,6 +35,27 @@ class SessionTestBase:
|
|||||||
return MockEnvironment(config_dir=self.config_dir)
|
return MockEnvironment(config_dir=self.config_dir)
|
||||||
|
|
||||||
|
|
||||||
|
class CookieTestBase:
|
||||||
|
def setup_method(self, method):
|
||||||
|
self.config_dir = mk_config_dir()
|
||||||
|
|
||||||
|
orig_session = {
|
||||||
|
'cookies': {
|
||||||
|
'cookie1': {
|
||||||
|
'value': 'foo',
|
||||||
|
},
|
||||||
|
'cookie2': {
|
||||||
|
'value': 'foo',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.session_path = self.config_dir / 'test-session.json'
|
||||||
|
self.session_path.write_text(json.dumps(orig_session))
|
||||||
|
|
||||||
|
def teardown_method(self, method):
|
||||||
|
shutil.rmtree(self.config_dir)
|
||||||
|
|
||||||
|
|
||||||
class TestSessionFlow(SessionTestBase):
|
class TestSessionFlow(SessionTestBase):
|
||||||
"""
|
"""
|
||||||
These tests start with an existing session created in `setup_method()`.
|
These tests start with an existing session created in `setup_method()`.
|
||||||
@ -191,13 +212,7 @@ class TestSession(SessionTestBase):
|
|||||||
os.chdir(cwd)
|
os.chdir(cwd)
|
||||||
|
|
||||||
|
|
||||||
class TestExpiredCookies:
|
class TestExpiredCookies(CookieTestBase):
|
||||||
|
|
||||||
def setup_method(self, method):
|
|
||||||
self.config_dir = mk_config_dir()
|
|
||||||
|
|
||||||
def teardown_method(self, method):
|
|
||||||
shutil.rmtree(self.config_dir)
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
argnames=['initial_cookie', 'expired_cookie'],
|
argnames=['initial_cookie', 'expired_cookie'],
|
||||||
@ -213,29 +228,16 @@ class TestExpiredCookies:
|
|||||||
assert expired_cookie not in session.cookies
|
assert expired_cookie not in session.cookies
|
||||||
|
|
||||||
def test_expired_cookies(self, httpbin):
|
def test_expired_cookies(self, httpbin):
|
||||||
orig_session = {
|
|
||||||
'cookies': {
|
|
||||||
'to_expire': {
|
|
||||||
'value': 'foo'
|
|
||||||
},
|
|
||||||
'to_stay': {
|
|
||||||
'value': 'foo'
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
session_path = self.config_dir / 'test-session.json'
|
|
||||||
session_path.write_text(json.dumps(orig_session))
|
|
||||||
|
|
||||||
r = http(
|
r = http(
|
||||||
'--session', str(session_path),
|
'--session', str(self.session_path),
|
||||||
'--print=H',
|
'--print=H',
|
||||||
httpbin.url + '/cookies/delete?to_expire',
|
httpbin.url + '/cookies/delete?cookie2',
|
||||||
)
|
)
|
||||||
assert 'Cookie: to_expire=foo; to_stay=foo' in r
|
assert 'Cookie: cookie1=foo; cookie2=foo' in r
|
||||||
|
|
||||||
updated_session = json.loads(session_path.read_text())
|
updated_session = json.loads(self.session_path.read_text())
|
||||||
assert 'to_stay' in updated_session['cookies']
|
assert 'cookie1' in updated_session['cookies']
|
||||||
assert 'to_expire' not in updated_session['cookies']
|
assert 'cookie2' not in updated_session['cookies']
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
argnames=['headers', 'now', 'expected_expired'],
|
argnames=['headers', 'now', 'expected_expired'],
|
||||||
@ -277,3 +279,90 @@ class TestExpiredCookies:
|
|||||||
)
|
)
|
||||||
def test_get_expired_cookies_manages_multiple_cookie_headers(self, headers, now, expected_expired):
|
def test_get_expired_cookies_manages_multiple_cookie_headers(self, headers, now, expected_expired):
|
||||||
assert get_expired_cookies(headers, now=now) == expected_expired
|
assert get_expired_cookies(headers, now=now) == expected_expired
|
||||||
|
|
||||||
|
|
||||||
|
class TestCookieStorage(CookieTestBase):
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
argnames=['new_cookies', 'new_cookies_dict', 'expected'],
|
||||||
|
argvalues=[(
|
||||||
|
'new=bar',
|
||||||
|
{'new': 'bar'},
|
||||||
|
'cookie1=foo; cookie2=foo; new=bar'
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'new=bar;chocolate=milk',
|
||||||
|
{'new': 'bar', 'chocolate': 'milk'},
|
||||||
|
'chocolate=milk; cookie1=foo; cookie2=foo; new=bar'
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'new=bar; chocolate=milk',
|
||||||
|
{'new': 'bar', 'chocolate': 'milk'},
|
||||||
|
'chocolate=milk; cookie1=foo; cookie2=foo; new=bar'
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'new=bar;; chocolate=milk;;;',
|
||||||
|
{'new': 'bar', 'chocolate': 'milk'},
|
||||||
|
'cookie1=foo; cookie2=foo; new=bar'
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'new=bar; chocolate=milk;;;',
|
||||||
|
{'new': 'bar', 'chocolate': 'milk'},
|
||||||
|
'chocolate=milk; cookie1=foo; cookie2=foo; new=bar'
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_existing_and_new_cookies_sent_in_request(self, new_cookies, new_cookies_dict, expected, httpbin):
|
||||||
|
r = http(
|
||||||
|
'--session', str(self.session_path),
|
||||||
|
'--print=H',
|
||||||
|
httpbin.url,
|
||||||
|
'Cookie:' + new_cookies,
|
||||||
|
)
|
||||||
|
# Note: cookies in response are in alphabetical order
|
||||||
|
assert 'Cookie: ' + expected in r
|
||||||
|
|
||||||
|
updated_session = json.loads(self.session_path.read_text())
|
||||||
|
for name, value in new_cookies_dict.items():
|
||||||
|
assert name, value in updated_session['cookies']
|
||||||
|
assert 'Cookie' not in updated_session['headers']
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
argnames=['cli_cookie', 'set_cookie', 'expected'],
|
||||||
|
argvalues=[(
|
||||||
|
'',
|
||||||
|
'/cookies/set/cookie1/bar',
|
||||||
|
'bar'
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'cookie1=not_foo',
|
||||||
|
'/cookies/set/cookie1/bar',
|
||||||
|
'bar'
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'cookie1=not_foo',
|
||||||
|
'',
|
||||||
|
'not_foo'
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'foo'
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_cookie_storage_priority(self, cli_cookie, set_cookie, expected, httpbin):
|
||||||
|
"""
|
||||||
|
Expected order of priority for cookie storage in session file:
|
||||||
|
1. set-cookie (from server)
|
||||||
|
2. command line arg
|
||||||
|
3. cookie already stored in session file
|
||||||
|
"""
|
||||||
|
r = http(
|
||||||
|
'--session', str(self.session_path),
|
||||||
|
httpbin.url + set_cookie,
|
||||||
|
'Cookie:' + cli_cookie,
|
||||||
|
)
|
||||||
|
updated_session = json.loads(self.session_path.read_text())
|
||||||
|
|
||||||
|
assert updated_session['cookies']['cookie1']['value'] == expected
|
||||||
|
Loading…
Reference in New Issue
Block a user