You've already forked httpie-cli
mirror of
https://github.com/httpie/cli.git
synced 2025-08-10 22:42:05 +02:00
Implement support for multiple headers with the same name in sessions (#1335)
* Properly remove duplicate Cookie headers * Implement support for multiple headers with the same name in sessions * More testing * Cleanup * Remove duplicated test, cleanup * Fix pycodestyle * CHANGELOG Co-authored-by: Jakub Roztocil <jakub@roztocil.co>
This commit is contained in:
@@ -27,5 +27,5 @@
|
||||
"value": "bar"
|
||||
}
|
||||
],
|
||||
"headers": {}
|
||||
"headers": []
|
||||
}
|
||||
|
@@ -27,5 +27,5 @@
|
||||
"value": "bar"
|
||||
}
|
||||
],
|
||||
"headers": {}
|
||||
"headers": []
|
||||
}
|
||||
|
@@ -26,8 +26,14 @@
|
||||
"value": "bar"
|
||||
}
|
||||
],
|
||||
"headers": {
|
||||
"X-Data": "value",
|
||||
"X-Foo": "bar"
|
||||
}
|
||||
"headers": [
|
||||
{
|
||||
"name": "X-Data",
|
||||
"value": "value"
|
||||
},
|
||||
{
|
||||
"name": "X-Foo",
|
||||
"value": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -10,5 +10,5 @@
|
||||
"username": null
|
||||
},
|
||||
"cookies": [],
|
||||
"headers": {}
|
||||
"headers": []
|
||||
}
|
||||
|
@@ -10,5 +10,5 @@
|
||||
"username": null
|
||||
},
|
||||
"cookies": [],
|
||||
"headers": {}
|
||||
"headers": []
|
||||
}
|
||||
|
14
tests/fixtures/session_data/new/empty_headers_dict.json
vendored
Normal file
14
tests/fixtures/session_data/new/empty_headers_dict.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"__meta__": {
|
||||
"about": "HTTPie session file",
|
||||
"help": "https://httpie.io/docs#sessions",
|
||||
"httpie": "__version__"
|
||||
},
|
||||
"auth": {
|
||||
"password": null,
|
||||
"type": null,
|
||||
"username": null
|
||||
},
|
||||
"cookies": [],
|
||||
"headers": []
|
||||
}
|
14
tests/fixtures/session_data/new/empty_headers_list.json
vendored
Normal file
14
tests/fixtures/session_data/new/empty_headers_list.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"__meta__": {
|
||||
"about": "HTTPie session file",
|
||||
"help": "https://httpie.io/docs#sessions",
|
||||
"httpie": "__version__"
|
||||
},
|
||||
"auth": {
|
||||
"password": null,
|
||||
"type": null,
|
||||
"username": null
|
||||
},
|
||||
"cookies": [],
|
||||
"headers": []
|
||||
}
|
40
tests/fixtures/session_data/new/headers_cookies_dict_mixed.json
vendored
Normal file
40
tests/fixtures/session_data/new/headers_cookies_dict_mixed.json
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"__meta__": {
|
||||
"about": "HTTPie session file",
|
||||
"help": "https://httpie.io/docs#sessions",
|
||||
"httpie": "__version__"
|
||||
},
|
||||
"auth": {
|
||||
"password": null,
|
||||
"type": null,
|
||||
"username": null
|
||||
},
|
||||
"cookies": [
|
||||
{
|
||||
"domain": __host__,
|
||||
"expires": null,
|
||||
"name": "baz",
|
||||
"path": "/",
|
||||
"secure": false,
|
||||
"value": "quux"
|
||||
},
|
||||
{
|
||||
"domain": __host__,
|
||||
"expires": null,
|
||||
"name": "foo",
|
||||
"path": "/",
|
||||
"secure": false,
|
||||
"value": "bar"
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"name": "X-Data",
|
||||
"value": "value"
|
||||
},
|
||||
{
|
||||
"name": "X-Foo",
|
||||
"value": "bar"
|
||||
}
|
||||
]
|
||||
}
|
23
tests/fixtures/session_data/new/headers_dict.json
vendored
Normal file
23
tests/fixtures/session_data/new/headers_dict.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"__meta__": {
|
||||
"about": "HTTPie session file",
|
||||
"help": "https://httpie.io/docs#sessions",
|
||||
"httpie": "__version__"
|
||||
},
|
||||
"auth": {
|
||||
"password": null,
|
||||
"type": null,
|
||||
"username": null
|
||||
},
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "foo",
|
||||
"value": "bar"
|
||||
},
|
||||
{
|
||||
"name": "baz",
|
||||
"value": "quux"
|
||||
}
|
||||
]
|
||||
}
|
39
tests/fixtures/session_data/new/headers_dict_extras.json
vendored
Normal file
39
tests/fixtures/session_data/new/headers_dict_extras.json
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"__meta__": {
|
||||
"about": "HTTPie session file",
|
||||
"help": "https://httpie.io/docs#sessions",
|
||||
"httpie": "__version__"
|
||||
},
|
||||
"auth": {
|
||||
"raw_auth": "foo:bar",
|
||||
"type": "basic"
|
||||
},
|
||||
"cookies": [
|
||||
{
|
||||
"domain": null,
|
||||
"expires": null,
|
||||
"name": "baz",
|
||||
"path": "/",
|
||||
"secure": false,
|
||||
"value": "quux"
|
||||
},
|
||||
{
|
||||
"domain": null,
|
||||
"expires": null,
|
||||
"name": "foo",
|
||||
"path": "/",
|
||||
"secure": false,
|
||||
"value": "bar"
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"name": "X-Data",
|
||||
"value": "value"
|
||||
},
|
||||
{
|
||||
"name": "X-Foo",
|
||||
"value": "bar"
|
||||
}
|
||||
]
|
||||
}
|
23
tests/fixtures/session_data/new/headers_list.json
vendored
Normal file
23
tests/fixtures/session_data/new/headers_list.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"__meta__": {
|
||||
"about": "HTTPie session file",
|
||||
"help": "https://httpie.io/docs#sessions",
|
||||
"httpie": "3.2.0"
|
||||
},
|
||||
"auth": {
|
||||
"password": null,
|
||||
"type": null,
|
||||
"username": null
|
||||
},
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "X-Data",
|
||||
"value": "value"
|
||||
},
|
||||
{
|
||||
"name": "X-Foo",
|
||||
"value": "bar"
|
||||
}
|
||||
]
|
||||
}
|
@@ -23,5 +23,5 @@
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
"headers": {}
|
||||
"headers": []
|
||||
}
|
||||
|
@@ -23,5 +23,5 @@
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
"headers": {}
|
||||
"headers": []
|
||||
}
|
||||
|
@@ -22,8 +22,14 @@
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"X-Data": "value",
|
||||
"X-Foo": "bar"
|
||||
}
|
||||
"headers": [
|
||||
{
|
||||
"name": "X-Data",
|
||||
"value": "value"
|
||||
},
|
||||
{
|
||||
"name": "X-Foo",
|
||||
"value": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@@ -10,5 +10,5 @@
|
||||
"username": null
|
||||
},
|
||||
"cookies": {},
|
||||
"headers": {}
|
||||
"headers": []
|
||||
}
|
||||
|
@@ -10,5 +10,5 @@
|
||||
"username": null
|
||||
},
|
||||
"cookies": [],
|
||||
"headers": {}
|
||||
"headers": []
|
||||
}
|
||||
|
14
tests/fixtures/session_data/old/empty_headers_dict.json
vendored
Normal file
14
tests/fixtures/session_data/old/empty_headers_dict.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"__meta__": {
|
||||
"about": "HTTPie session file",
|
||||
"help": "https://httpie.io/docs#sessions",
|
||||
"httpie": "3.0.2"
|
||||
},
|
||||
"auth": {
|
||||
"password": null,
|
||||
"type": null,
|
||||
"username": null
|
||||
},
|
||||
"cookies": [],
|
||||
"headers": {}
|
||||
}
|
14
tests/fixtures/session_data/old/empty_headers_list.json
vendored
Normal file
14
tests/fixtures/session_data/old/empty_headers_list.json
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"__meta__": {
|
||||
"about": "HTTPie session file",
|
||||
"help": "https://httpie.io/docs#sessions",
|
||||
"httpie": "3.0.2"
|
||||
},
|
||||
"auth": {
|
||||
"password": null,
|
||||
"type": null,
|
||||
"username": null
|
||||
},
|
||||
"cookies": [],
|
||||
"headers": []
|
||||
}
|
30
tests/fixtures/session_data/old/headers_cookies_dict_mixed.json
vendored
Normal file
30
tests/fixtures/session_data/old/headers_cookies_dict_mixed.json
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"__meta__": {
|
||||
"about": "HTTPie session file",
|
||||
"help": "https://httpie.io/docs#sessions",
|
||||
"httpie": "3.0.2"
|
||||
},
|
||||
"auth": {
|
||||
"password": null,
|
||||
"type": null,
|
||||
"username": null
|
||||
},
|
||||
"cookies": {
|
||||
"baz": {
|
||||
"expires": null,
|
||||
"path": "/",
|
||||
"secure": false,
|
||||
"value": "quux"
|
||||
},
|
||||
"foo": {
|
||||
"expires": null,
|
||||
"path": "/",
|
||||
"secure": false,
|
||||
"value": "bar"
|
||||
}
|
||||
},
|
||||
"headers": {
|
||||
"X-Data": "value",
|
||||
"X-Foo": "bar"
|
||||
}
|
||||
}
|
17
tests/fixtures/session_data/old/headers_dict.json
vendored
Normal file
17
tests/fixtures/session_data/old/headers_dict.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"__meta__": {
|
||||
"about": "HTTPie session file",
|
||||
"help": "https://httpie.io/docs#sessions",
|
||||
"httpie": "3.1.0"
|
||||
},
|
||||
"auth": {
|
||||
"password": null,
|
||||
"type": null,
|
||||
"username": null
|
||||
},
|
||||
"cookies": [],
|
||||
"headers": {
|
||||
"foo": "bar",
|
||||
"baz": "quux"
|
||||
}
|
||||
}
|
33
tests/fixtures/session_data/old/headers_dict_extras.json
vendored
Normal file
33
tests/fixtures/session_data/old/headers_dict_extras.json
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"__meta__": {
|
||||
"about": "HTTPie session file",
|
||||
"help": "https://httpie.io/docs#sessions",
|
||||
"httpie": "3.1.0"
|
||||
},
|
||||
"auth": {
|
||||
"raw_auth": "foo:bar",
|
||||
"type": "basic"
|
||||
},
|
||||
"cookies": [
|
||||
{
|
||||
"domain": null,
|
||||
"name": "baz",
|
||||
"expires": null,
|
||||
"path": "/",
|
||||
"secure": false,
|
||||
"value": "quux"
|
||||
},
|
||||
{
|
||||
"domain": null,
|
||||
"name": "foo",
|
||||
"expires": null,
|
||||
"path": "/",
|
||||
"secure": false,
|
||||
"value": "bar"
|
||||
}
|
||||
],
|
||||
"headers": {
|
||||
"X-Data": "value",
|
||||
"X-Foo": "bar"
|
||||
}
|
||||
}
|
23
tests/fixtures/session_data/old/headers_list.json
vendored
Normal file
23
tests/fixtures/session_data/old/headers_list.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"__meta__": {
|
||||
"about": "HTTPie session file",
|
||||
"help": "https://httpie.io/docs#sessions",
|
||||
"httpie": "3.2.0"
|
||||
},
|
||||
"auth": {
|
||||
"password": null,
|
||||
"type": null,
|
||||
"username": null
|
||||
},
|
||||
"cookies": [],
|
||||
"headers": [
|
||||
{
|
||||
"name": "X-Data",
|
||||
"value": "value"
|
||||
},
|
||||
{
|
||||
"name": "X-Foo",
|
||||
"value": "bar"
|
||||
}
|
||||
]
|
||||
}
|
@@ -664,7 +664,7 @@ def test_old_session_cookie_layout_loading(basic_session, httpbin, mock_env):
|
||||
@pytest.mark.parametrize('layout_type', [
|
||||
dict, list
|
||||
])
|
||||
def test_session_cookie_layout_preservance(basic_session, mock_env, layout_type):
|
||||
def test_session_cookie_layout_preservation(basic_session, mock_env, layout_type):
|
||||
with open_session(basic_session, mock_env) as session:
|
||||
session['cookies'] = layout_type()
|
||||
session.cookies.set('foo', 'bar')
|
||||
@@ -677,7 +677,7 @@ def test_session_cookie_layout_preservance(basic_session, mock_env, layout_type)
|
||||
@pytest.mark.parametrize('layout_type', [
|
||||
dict, list
|
||||
])
|
||||
def test_session_cookie_layout_preservance_on_new_cookies(basic_session, httpbin, mock_env, layout_type):
|
||||
def test_session_cookie_layout_preservation_on_new_cookies(basic_session, httpbin, mock_env, layout_type):
|
||||
with open_session(basic_session, mock_env) as session:
|
||||
session['cookies'] = layout_type()
|
||||
session.cookies.set('foo', 'bar')
|
||||
@@ -690,3 +690,113 @@ def test_session_cookie_layout_preservance_on_new_cookies(basic_session, httpbin
|
||||
|
||||
with open_session(basic_session, mock_env, read_only=True) as session:
|
||||
assert isinstance(session['cookies'], layout_type)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('headers, expect_warning', [
|
||||
# Old-style header format
|
||||
(
|
||||
{},
|
||||
False
|
||||
),
|
||||
(
|
||||
{'Foo': 'bar'},
|
||||
True
|
||||
),
|
||||
# New style header format
|
||||
(
|
||||
[],
|
||||
False
|
||||
),
|
||||
(
|
||||
[{'name': 'Foo', 'value': 'Bar'}],
|
||||
False
|
||||
),
|
||||
])
|
||||
def test_headers_old_layout_warning(basic_session, mock_env, headers, expect_warning):
|
||||
with open_raw_session(basic_session) as raw_session:
|
||||
raw_session['headers'] = headers
|
||||
|
||||
with open_session(basic_session, mock_env, read_only=True):
|
||||
warning = b'Outdated layout detected'
|
||||
stderr = read_stderr(mock_env)
|
||||
|
||||
if expect_warning:
|
||||
assert warning in stderr
|
||||
else:
|
||||
assert warning not in stderr
|
||||
|
||||
|
||||
def test_outdated_layout_mixed(basic_session, mock_env):
|
||||
with open_raw_session(basic_session) as raw_session:
|
||||
raw_session['headers'] = {'Foo': 'Bar'}
|
||||
raw_session['cookies'] = {
|
||||
'cookie': {
|
||||
'value': 'value'
|
||||
}
|
||||
}
|
||||
|
||||
with open_session(basic_session, mock_env, read_only=True):
|
||||
stderr = read_stderr(mock_env)
|
||||
# We should only see 1 warning.
|
||||
assert stderr.count(b'Outdated layout') == 1
|
||||
|
||||
|
||||
def test_old_session_header_layout_loading(basic_session, httpbin, mock_env):
|
||||
with open_session(basic_session, mock_env) as session:
|
||||
# Use the old layout & set a header
|
||||
session['headers'] = {}
|
||||
session._headers.add('Foo', 'Bar')
|
||||
|
||||
response = http(
|
||||
'--session', str(basic_session),
|
||||
httpbin + '/get'
|
||||
)
|
||||
assert response.json['headers']['Foo'] == 'Bar'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('layout_type', [
|
||||
dict, list
|
||||
])
|
||||
def test_session_header_layout_preservation(basic_session, mock_env, layout_type):
|
||||
with open_session(basic_session, mock_env) as session:
|
||||
session['headers'] = layout_type()
|
||||
session._headers.add('Foo', 'Bar')
|
||||
|
||||
with open_session(basic_session, mock_env, read_only=True) as session:
|
||||
assert isinstance(session['headers'], layout_type)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('layout_type', [
|
||||
dict, list
|
||||
])
|
||||
def test_session_header_layout_preservation_on_new_headers(basic_session, httpbin, mock_env, layout_type):
|
||||
with open_session(basic_session, mock_env) as session:
|
||||
session['headers'] = layout_type()
|
||||
session._headers.add('Foo', 'Bar')
|
||||
|
||||
http(
|
||||
'--session', str(basic_session),
|
||||
httpbin + '/get',
|
||||
'Baz:Quux'
|
||||
)
|
||||
|
||||
with open_session(basic_session, mock_env, read_only=True) as session:
|
||||
assert isinstance(session['headers'], layout_type)
|
||||
|
||||
|
||||
def test_session_multiple_headers_with_same_name(basic_session, httpbin):
|
||||
http(
|
||||
'--session', str(basic_session),
|
||||
httpbin + '/get',
|
||||
'Foo:bar',
|
||||
'Foo:baz',
|
||||
'Foo:bar'
|
||||
)
|
||||
|
||||
r = http(
|
||||
'--offline',
|
||||
'--session', str(basic_session),
|
||||
httpbin + '/get',
|
||||
)
|
||||
assert r.count('Foo: bar') == 2
|
||||
assert 'Foo: baz' in r
|
||||
|
Reference in New Issue
Block a user