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