diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 37e3b70a..2c59d9ca 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,9 +9,10 @@ This project adheres to `Semantic Versioning `_. `2.5.0-dev`_ (unreleased) ------------------------- -* Fixed ``--continue --download`` with a single byte to be downloaded left. (`#1032`_) * Added ``--raw`` to allow specifying the raw request body without extra processing as an alternative to ``stdin``. (`#534`_) +* Fixed ``--continue --download`` with a single byte to be downloaded left. (`#1032`_) +* Fixed ``--verbose`` HTTP 307 redirects with streamed request body. (`#1088`_) `2.4.0`_ (2021-02-06) @@ -477,6 +478,7 @@ This project adheres to `Semantic Versioning `_. .. _#128: https://github.com/httpie/httpie/issues/128 .. _#201: https://github.com/httpie/httpie/issues/201 .. _#488: https://github.com/httpie/httpie/issues/488 +.. _#534: https://github.com/httpie/httpie/issues/534 .. _#668: https://github.com/httpie/httpie/issues/668 .. _#684: https://github.com/httpie/httpie/issues/684 .. _#718: https://github.com/httpie/httpie/issues/718 @@ -500,3 +502,4 @@ This project adheres to `Semantic Versioning `_. .. _#1026: https://github.com/httpie/httpie/issues/1026 .. _#1029: https://github.com/httpie/httpie/issues/1029 .. _#1032: https://github.com/httpie/httpie/issues/1032 +.. _#1088: https://github.com/httpie/httpie/issues/1088 diff --git a/README.rst b/README.rst index c63c3a0b..e0a36e09 100644 --- a/README.rst +++ b/README.rst @@ -1071,6 +1071,10 @@ and show the final response instead, use the ``--follow, -F`` option: $ http --follow pie.dev/redirect/3 +With ``HTTP 307 Temporary Redirect``, the method and the body of the original request +are reused to perform the redirected request. Otherwise a body-less ``GET`` request is performed. + + Showing intermediary redirect responses --------------------------------------- diff --git a/tests/fixtures/.editorconfig b/tests/fixtures/.editorconfig new file mode 100644 index 00000000..60b05522 --- /dev/null +++ b/tests/fixtures/.editorconfig @@ -0,0 +1,6 @@ +# https://editorconfig.org + +[{*.txt, *.json}] +trim_trailing_whitespace = false +insert_final_newline = false + diff --git a/tests/fixtures/test.json b/tests/fixtures/test.json index b5ea8840..fe547ebc 100644 --- a/tests/fixtures/test.json +++ b/tests/fixtures/test.json @@ -1,4 +1,4 @@ { "name": "Jakub Roztočil", "unicode": "χρυσαφὶ 太陽 เลิศ ♜♞♝♛♚♝♞♜ оживлённым तान्यहानि 有朋" -} +} \ No newline at end of file diff --git a/tests/fixtures/test.txt b/tests/fixtures/test.txt index 21857443..7b953309 100644 --- a/tests/fixtures/test.txt +++ b/tests/fixtures/test.txt @@ -1 +1 @@ -[one line of UTF8-encoded unicode text] χρυσαφὶ 太陽 เลิศ ♜♞♝♛♚♝♞♜ оживлённым तान्यहानि 有朋 ஸ்றீனிவாஸ ٱلرَّحْمـَبنِ +[one line of UTF8-encoded unicode text] χρυσαφὶ 太陽 เลิศ ♜♞♝♛♚♝♞♜ оживлённым तान्यहानि 有朋 ஸ்றீனிவாஸ ٱلرَّحْمـَبنِ \ No newline at end of file diff --git a/tests/test_redirects.py b/tests/test_redirects.py index 9f2648cf..1bf15ef6 100644 --- a/tests/test_redirects.py +++ b/tests/test_redirects.py @@ -2,8 +2,9 @@ import pytest from httpie.status import ExitStatus -from .fixtures import FILE_PATH_ARG +from .fixtures import FILE_PATH_ARG, FILE_CONTENT from .utils import http, HTTP_OK +from .utils.matching import assert_output_matches, Expect, ExpectSequence def test_follow_all_redirects_shown(httpbin): @@ -73,4 +74,11 @@ def test_http_307_allow_redirect_post_verbose(httpbin): '@' + FILE_PATH_ARG) assert r.count('POST /redirect-to') == 1 assert r.count('POST /post') == 1 + assert r.count(FILE_CONTENT) == 3 # two requests + final response contain it assert HTTP_OK in r + assert_output_matches(r, [ + *ExpectSequence.TERMINAL_REQUEST, + Expect.RESPONSE_HEADERS, + Expect.SEPARATOR, + *ExpectSequence.TERMINAL_EXCHANGE, + ]) diff --git a/tests/test_tokens.py b/tests/test_tokens.py index 519b5453..7281b2a3 100644 --- a/tests/test_tokens.py +++ b/tests/test_tokens.py @@ -9,45 +9,10 @@ TODO: cover more scenarios * streamed uploads """ -from .utils.matching import assert_output_matches, Expect +from .utils.matching import assert_output_matches, Expect, ExpectSequence from .utils import http, HTTP_OK, MockEnvironment -RAW_REQUEST = [ - Expect.REQUEST_HEADERS, - Expect.BODY, -] -RAW_RESPONSE = [ - Expect.RESPONSE_HEADERS, - Expect.BODY, -] -RAW_EXCHANGE = [ - *RAW_REQUEST, - Expect.SEPARATOR, # Good choice? - *RAW_RESPONSE, -] -RAW_BODY = [ - Expect.BODY, -] - -TERMINAL_REQUEST = [ - *RAW_REQUEST, - Expect.SEPARATOR, -] -TERMINAL_RESPONSE = [ - *RAW_RESPONSE, - Expect.SEPARATOR, -] -TERMINAL_EXCHANGE = [ - *TERMINAL_REQUEST, - *TERMINAL_RESPONSE, -] -TERMINAL_BODY = [ - RAW_BODY, - Expect.SEPARATOR -] - - def test_headers(): r = http('--print=H', '--offline', 'pie.dev') assert_output_matches(r, [Expect.REQUEST_HEADERS]) @@ -60,17 +25,17 @@ def test_redirected_headers(): def test_terminal_headers_and_body(): r = http('--print=HB', '--offline', 'pie.dev', 'AAA=BBB') - assert_output_matches(r, TERMINAL_REQUEST) + assert_output_matches(r, ExpectSequence.TERMINAL_REQUEST) def test_terminal_request_headers_response_body(httpbin): r = http('--print=Hb', httpbin + '/get') - assert_output_matches(r, TERMINAL_REQUEST) + assert_output_matches(r, ExpectSequence.TERMINAL_REQUEST) def test_raw_request_headers_response_body(httpbin): r = http('--print=Hb', httpbin + '/get', env=MockEnvironment(stdout_isatty=False)) - assert_output_matches(r, RAW_REQUEST) + assert_output_matches(r, ExpectSequence.RAW_REQUEST) def test_terminal_request_headers_response_headers(httpbin): @@ -93,29 +58,29 @@ def test_raw_headers_and_body(): '--print=HB', '--offline', 'pie.dev', 'AAA=BBB', env=MockEnvironment(stdout_isatty=False), ) - assert_output_matches(r, RAW_REQUEST) + assert_output_matches(r, ExpectSequence.RAW_REQUEST) def test_raw_body(): r = http('--print=B', '--offline', 'pie.dev', 'AAA=BBB', env=MockEnvironment(stdout_isatty=False)) - assert_output_matches(r, RAW_BODY) + assert_output_matches(r, ExpectSequence.RAW_BODY) def test_raw_exchange(httpbin): r = http('--verbose', httpbin + '/post', 'a=b', env=MockEnvironment(stdout_isatty=False)) assert HTTP_OK in r - assert_output_matches(r, RAW_EXCHANGE) + assert_output_matches(r, ExpectSequence.RAW_EXCHANGE) def test_terminal_exchange(httpbin): r = http('--verbose', httpbin + '/post', 'a=b') assert HTTP_OK in r - assert_output_matches(r, TERMINAL_EXCHANGE) + assert_output_matches(r, ExpectSequence.TERMINAL_EXCHANGE) def test_headers_multipart_body_separator(): r = http('--print=HB', '--multipart', '--offline', 'pie.dev', 'AAA=BBB') - assert_output_matches(r, TERMINAL_REQUEST) + assert_output_matches(r, ExpectSequence.TERMINAL_REQUEST) def test_redirected_headers_multipart_no_separator(): @@ -123,16 +88,16 @@ def test_redirected_headers_multipart_no_separator(): '--print=HB', '--multipart', '--offline', 'pie.dev', 'AAA=BBB', env=MockEnvironment(stdout_isatty=False), ) - assert_output_matches(r, RAW_REQUEST) + assert_output_matches(r, ExpectSequence.RAW_REQUEST) def test_verbose_chunked(httpbin_with_chunked_support): r = http('--verbose', '--chunked', httpbin_with_chunked_support + '/post', 'hello=world') assert HTTP_OK in r assert 'Transfer-Encoding: chunked' in r - assert_output_matches(r, TERMINAL_EXCHANGE) + assert_output_matches(r, ExpectSequence.TERMINAL_EXCHANGE) def test_request_headers_response_body(httpbin): r = http('--print=Hb', httpbin + '/get') - assert_output_matches(r, TERMINAL_REQUEST) + assert_output_matches(r, ExpectSequence.TERMINAL_REQUEST) diff --git a/tests/utils/matching/__init__.py b/tests/utils/matching/__init__.py index 216f65d4..78720728 100644 --- a/tests/utils/matching/__init__.py +++ b/tests/utils/matching/__init__.py @@ -1,14 +1,20 @@ +""" +Utilities for testing output composition. + +""" from typing import Iterable import pytest -from .parsing import OutputMatchingError, expect_tokens, Expect +from .parsing import OutputMatchingError, expect_tokens +from .tokens import Expect, ExpectSequence __all__ = [ 'assert_output_matches', 'assert_output_does_not_match', 'Expect', + 'ExpectSequence', ] diff --git a/tests/utils/matching/parsing.py b/tests/utils/matching/parsing.py index 53a4b21b..998fe9a4 100644 --- a/tests/utils/matching/parsing.py +++ b/tests/utils/matching/parsing.py @@ -1,22 +1,11 @@ import re from typing import Iterable -from enum import Enum, auto from httpie.output.writer import MESSAGE_SEPARATOR +from .tokens import Expect from ...utils import CRLF -class Expect(Enum): - """ - Predefined token types we can expect in the output. - - """ - REQUEST_HEADERS = auto() - RESPONSE_HEADERS = auto() - BODY = auto() - SEPARATOR = auto() - - SEPARATOR_RE = re.compile(f'^{MESSAGE_SEPARATOR}') diff --git a/tests/utils/matching/tokens.py b/tests/utils/matching/tokens.py new file mode 100644 index 00000000..61bc7234 --- /dev/null +++ b/tests/utils/matching/tokens.py @@ -0,0 +1,51 @@ +from enum import Enum, auto + + +class Expect(Enum): + """ + Predefined token types we can expect in the output. + + """ + REQUEST_HEADERS = auto() + RESPONSE_HEADERS = auto() + BODY = auto() + SEPARATOR = auto() + + +class ExpectSequence: + """ + Standard combined chunks. These predefined requests and responses assume a body. + + """ + RAW_REQUEST = [ + Expect.REQUEST_HEADERS, + Expect.BODY, + ] + RAW_RESPONSE = [ + Expect.RESPONSE_HEADERS, + Expect.BODY, + ] + RAW_EXCHANGE = [ + *RAW_REQUEST, + Expect.SEPARATOR, # Good choice? + *RAW_RESPONSE, + ] + RAW_BODY = [ + Expect.BODY, + ] + TERMINAL_REQUEST = [ + *RAW_REQUEST, + Expect.SEPARATOR, + ] + TERMINAL_RESPONSE = [ + *RAW_RESPONSE, + Expect.SEPARATOR, + ] + TERMINAL_EXCHANGE = [ + *TERMINAL_REQUEST, + *TERMINAL_RESPONSE, + ] + TERMINAL_BODY = [ + RAW_BODY, + Expect.SEPARATOR + ]