diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 52edadfe..aa942095 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,8 +9,11 @@ This project adheres to `Semantic Versioning `_. ------------------------- * Added ``Content-Type`` of files uploaded in ``multipart/form-data`` requests +* Added ``--show-redirects, -R`` to show intermediate responses with ``--follow`` +* Added ``--max-redirects`` (default 30) * Added ``-A`` as short name for ``--auth-type`` -* Changed the default color style back to ``solarized`` as it supports +* Added ``-F`` as short name for ``--follow`` +* Changed the default color style back to``solarized`` as it supports both the light and dark terminal background mode * Fixed ``--session`` when used with ``--download`` * Fixed handling of ``Content-Type`` with multiple ``+subtype`` parts diff --git a/README.rst b/README.rst index 1bc92b01..5b201aae 100644 --- a/README.rst +++ b/README.rst @@ -623,6 +623,21 @@ Auth Plugins * `httpie-jwt-auth `_: JWTAuth (JSON Web Tokens) +============== +HTTP Redirects +============== + +By default, HTTP redirects are not followed and only the first +response is shown. To instruct HTTPie to follow the ``Location`` header of +``30x`` responses and show the final response instead, use the ``--follow, -F`` option. + +If you additionally wish to see the intermediary requests/responses as well, +use the ``--show-redirects, -R`` option as well. + +To change the default limit of maximum 30 redirects, use the +``--max-redirects=`` option. + + ======= Proxies ======= @@ -1251,6 +1266,7 @@ Also, the ``--timeout`` option allows to overwrite the default 30s timeout: 3) echo 'Unexpected HTTP 3xx Redirection!' ;; 4) echo 'HTTP 4xx Client Error!' ;; 5) echo 'HTTP 5xx Server Error!' ;; + 6) echo 'Exceeded --max-redirects= redirects!' ;; *) echo 'Other Error!' ;; esac fi diff --git a/httpie/__init__.py b/httpie/__init__.py index 7ae07c9e..c4f7b829 100644 --- a/httpie/__init__.py +++ b/httpie/__init__.py @@ -12,6 +12,7 @@ class ExitStatus: OK = 0 ERROR = 1 ERROR_TIMEOUT = 2 + ERROR_TOO_MANY_REDIRECTS = 6 # Used only when requested with --check-status: ERROR_HTTP_3XX = 3 diff --git a/httpie/cli.py b/httpie/cli.py index db3159a4..f03bb46b 100644 --- a/httpie/cli.py +++ b/httpie/cli.py @@ -380,16 +380,6 @@ sessions.add_argument( """ ) -sessions.add_argument( - '--max-redirects', - dest='redirects', - default=30, - help=""" - By default, requests has a limit of 30 redirects. - - """ -) - ####################################################################### # Authentication ####################################################################### @@ -456,15 +446,35 @@ network.add_argument( """ ) network.add_argument( - '--follow', + '--follow', '-F', default=False, action='store_true', help=""" - Set this flag if full redirects are allowed (e.g. re-POST-ing of data at - new Location). + Follow 30x Location redirects. """ ) + +network.add_argument( + '--show-redirects', '-R', + default=False, + action='store_true', + help=""" + Show all responses within the redirect chain (works with --follow). + + """ +) + +network.add_argument( + '--max-redirects', + type=int, + default=30, + help=""" + By default, requests have a limit of 30 redirects (works with --follow). + + """ +) + network.add_argument( '--verify', default='yes', diff --git a/httpie/core.py b/httpie/core.py index 9afe41b3..c0014cea 100644 --- a/httpie/core.py +++ b/httpie/core.py @@ -113,8 +113,12 @@ def main(args=sys.argv[1:], env=Environment(), error=None): last_response = get_response(args, config_dir=env.config.directory) - redirect_chain = last_response.history + [last_response] - for response in redirect_chain: + if args.show_redirects: + responses = last_response.history + [last_response] + else: + responses = [last_response] + + for response in responses: if exit_status != ExitStatus.OK: break @@ -183,7 +187,9 @@ def main(args=sys.argv[1:], env=Environment(), error=None): except requests.Timeout: exit_status = ExitStatus.ERROR_TIMEOUT error('Request timed out (%ss).', args.timeout) - + except requests.TooManyRedirects: + exit_status = ExitStatus.ERROR_TOO_MANY_REDIRECTS + error('Too many redirects (--max-redirects=%s).', args.max_redirects) except Exception as e: # TODO: Better distinction between expected and unexpected errors. # Network errors vs. bugs, etc. diff --git a/tests/fixtures.py b/tests/fixtures/__init__.py similarity index 92% rename from tests/fixtures.py rename to tests/fixtures/__init__.py index 05737c71..294141c1 100644 --- a/tests/fixtures.py +++ b/tests/fixtures/__init__.py @@ -12,7 +12,7 @@ def patharg(path): return path.replace('\\', '\\\\\\') -FIXTURES_ROOT = path.join(path.abspath(path.dirname(__file__)), 'fixtures') +FIXTURES_ROOT = path.join(path.abspath(path.dirname(__file__))) FILE_PATH = path.join(FIXTURES_ROOT, 'test.txt') JSON_FILE_PATH = path.join(FIXTURES_ROOT, 'test.json') BIN_FILE_PATH = path.join(FIXTURES_ROOT, 'test.bin') diff --git a/tests/test_redirects.py b/tests/test_redirects.py new file mode 100644 index 00000000..35cf9b03 --- /dev/null +++ b/tests/test_redirects.py @@ -0,0 +1,22 @@ +"""High-level tests.""" +from httpie import ExitStatus +from utils import http, HTTP_OK + + +class TestRedirects: + + def test_follow_no_show_redirects(self, httpbin): + r = http('--follow', httpbin.url + '/redirect/2') + assert r.count('HTTP/1.1') == 1 + assert HTTP_OK in r + + def test_follow_show_redirects(self, httpbin): + r = http('--follow', '--show-redirects', httpbin.url + '/redirect/2') + assert r.count('HTTP/1.1') == 3 + assert r.count('HTTP/1.1 302 FOUND', 2) + assert HTTP_OK in r + + def test_max_redirects(self, httpbin): + r = http('--max-redirects=2', '--follow', httpbin.url + '/redirect/3', + error_exit_ok=True) + assert r.exit_status == ExitStatus.ERROR_TOO_MANY_REDIRECTS