From 5acbc904b7356cecf081f33e948e394d660a15b2 Mon Sep 17 00:00:00 2001 From: Jakub Roztocil Date: Sat, 2 Jul 2016 11:50:30 +0200 Subject: [PATCH] Added the ability to unset headers Closes #476 --- CHANGELOG.rst | 2 ++ README.rst | 18 +++++++++++++++++- httpie/input.py | 15 +++++++++++++-- tests/test_httpie.py | 23 +++++++++++++++++++++++ 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 869c2dff..53e3c7cb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -12,6 +12,8 @@ This project adheres to `Semantic Versioning `_. * Added Python 3 as a dependency for Homebrew installations to ensure some of the newer HTTP features work out of the box for macOS users (starting with HTTPie 0.9.4.). +* Added the ability to unset a request header with ``Header:``, and send an + empty value with ``Header;``. `0.9.4`_ (2016-07-01) diff --git a/README.rst b/README.rst index e6a1db76..506d2115 100644 --- a/README.rst +++ b/README.rst @@ -560,7 +560,23 @@ There are a couple of default headers that HTTPie sets: Host: -Any of the default headers can be overwritten. +Any of the default headers can be overwritten and some of them unset. + +To unset a header that has already been specified (such a one of the default +headers), use ``Header:``: + + +.. code-block:: bash + + $ http httpbin.org/headers Accept: User-Agent: + + +To send a header with an empty value, use ``Header;``: + + +.. code-block:: bash + + $ http httpbin.org/headers 'Header;' ============== diff --git a/httpie/input.py b/httpie/input.py index 85cb9a27..d1f6600f 100644 --- a/httpie/input.py +++ b/httpie/input.py @@ -34,6 +34,7 @@ HTTPS = 'https://' # Various separators used in args SEP_HEADERS = ':' +SEP_HEADERS_EMPTY = ';' SEP_CREDENTIALS = ':' SEP_PROXY = ':' SEP_DATA = '=' @@ -67,6 +68,7 @@ SEP_GROUP_RAW_JSON_ITEMS = frozenset([ # Separators allowed in ITEM arguments SEP_GROUP_ALL_ITEMS = frozenset([ SEP_HEADERS, + SEP_HEADERS_EMPTY, SEP_QUERY, SEP_DATA, SEP_DATA_RAW_JSON, @@ -655,11 +657,20 @@ def parse_items(items, data = [] files = [] params = [] - for item in items: value = item.value - if item.sep == SEP_HEADERS: + if value == '': + # No value => unset the header + value = None + target = headers + elif item.sep == SEP_HEADERS_EMPTY: + if item.value: + raise ParseError( + 'Invalid item "%s" ' + '(to specify an empty header use `Header;`)' + % item.orig + ) target = headers elif item.sep == SEP_QUERY: target = params diff --git a/tests/test_httpie.py b/tests/test_httpie.py index dcfb0d76..37cc152c 100644 --- a/tests/test_httpie.py +++ b/tests/test_httpie.py @@ -1,5 +1,7 @@ """High-level tests.""" import pytest + +from httpie.input import ParseError from utils import TestEnvironment, http, HTTP_OK from fixtures import FILE_PATH, FILE_CONTENT @@ -75,6 +77,27 @@ def test_headers(httpbin_both): assert '"Foo": "bar"' in r +def test_headers_unset(httpbin_both): + r = http('GET', httpbin_both + '/headers') + assert 'Accept' in r.json['headers'] # default Accept present + + r = http('GET', httpbin_both + '/headers', 'Accept:') + assert 'Accept' not in r.json['headers'] # default Accept unset + + +def test_headers_empty_value(httpbin_both): + r = http('GET', httpbin_both + '/headers') + assert r.json['headers']['Accept'] # default Accept has value + + r = http('GET', httpbin_both + '/headers', 'Accept;') + assert r.json['headers']['Accept'] == '' # Accept has no value + + +def test_headers_empty_value_with_value_gives_error(httpbin): + with pytest.raises(ParseError): + http('GET', httpbin + '/headers', 'Accept;SYNTAX_ERROR') + + @pytest.mark.skipif( is_py26, reason='the `object_pairs_hook` arg for `json.loads()` is Py>2.6 only'