diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 8c37eafa68..c70698d5c5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,7 +14,8 @@ This project adheres to `Semantic Versioning `_. to use for HTTPS requests. * Added JSON detection with ``--json, -j`` to work around incorrect ``Content-Type`` -* Added ``--show-redirects, -R`` to show intermediate responses with ``--follow`` +* Added ``--all`` to show intermediate responses such as redirects (with ``--follow``) +* Added ``--print-others, -P WHAT`` * Added ``--max-redirects`` (default 30) * Added ``-A`` as short name for ``--auth-type`` * Added ``-F`` as short name for ``--follow`` diff --git a/README.rst b/README.rst index 9941607384..50ab6937b3 100644 --- a/README.rst +++ b/README.rst @@ -636,7 +636,7 @@ 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, -then use the ``--show-redirects, -R`` option as well. +then use the ``--all`` option as well. To change the default limit of maximum 30 redirects, use the ``--max-redirects=`` option. @@ -644,7 +644,7 @@ To change the default limit of maximum 30 redirects, use the .. code-block:: bash - $ http --follow --show-redirects --max-redirects=5 httpbin.org/redirect/3 + $ http --follow --all --max-redirects=5 httpbin.org/redirect/3 ======= @@ -771,8 +771,8 @@ You can use the following command to test SNI support: Output Options ============== -By default, HTTPie outputs the whole response message (headers as well as the -body). +By default, HTTPie only outputs the final response and whole response message +is printed (headers as well as the body). You can control what should be printed via several options: @@ -780,6 +780,7 @@ You can control what should be printed via several options: ``--headers, -h`` Only the response headers are printed. ``--body, -b`` Only the response body is printed. ``--verbose, -v`` Print the whole HTTP exchange (request and response). + This option also enables ``--all`` (see bellow). ``--print, -p`` Selects parts of the HTTP exchange. ================= ===================================================== @@ -833,6 +834,33 @@ Print request and response headers: $ http --print=Hh PUT httpbin.org/put hello=world +--------------------------------------- +Viewing Intermediary Requests/Responses +--------------------------------------- + +If you'd like to see any intermediary requests/responses together with the +final one, then use the ``--all`` option. Intermediary requests include +followed redirects (with ``--follow``), the first unauthorized request when +Digest auth is used (``--auth=digest``), etc. They are by default also +formatted according to ``--print, -p`` (and its shortcuts described above), +which can be customized with ``--print-others, -P`` which takes the same +arguments as ``--print, -p`` but applies to the intermediary requests only. + + +View all responses that lead to the final one: + +.. code-block:: bash + + $ http --all --follow httpbin.org/redirect/3 + + +Print the final and the intermediary requests/responses differently: + +.. code-block:: bash + + $ http --all --follow --print=hH --print-others=H httpbin.org/redirect/3 + + ------------------------- Conditional Body Download ------------------------- diff --git a/httpie/cli.py b/httpie/cli.py index f964b23318..5ddfe47209 100644 --- a/httpie/cli.py +++ b/httpie/cli.py @@ -250,17 +250,6 @@ def _split_lines(self, text, width): default=OUTPUT_OPTIONS_DEFAULT, ) ) -output_options.add_argument( - '--verbose', '-v', - dest='output_options', - action='store_const', - const=''.join(OUTPUT_OPTIONS), - help=""" - Print the whole request as well as the response. Shortcut for --print={0}. - - """ - .format(''.join(OUTPUT_OPTIONS)) -) output_options.add_argument( '--headers', '-h', dest='output_options', @@ -284,6 +273,42 @@ def _split_lines(self, text, width): .format(OUT_RESP_BODY) ) +output_options.add_argument( + '--verbose', '-v', + dest='verbose', + action='store_true', + help=""" + Verbose output. Print the whole request as well as the response. Also print + any intermediary requests/responses (such as redirects). + It's a shortcut for: --all --print={0} + + """ + .format(''.join(OUTPUT_OPTIONS)) +) +output_options.add_argument( + '--all', + default=False, + action='store_true', + help=""" + By default, only the final request/response is shown. Use this flag to show + any intermediary requests/responses as well. Intermediary requests include + followed redirects (with --follow), the first unauthorized request when + Digest auth is used (--auth=digest), etc. + + """ +) +output_options.add_argument( + '--print-others', '-P', + dest='output_options_others', + metavar='WHAT', + help=""" + The same as --print, -p but applies only to intermediary requests/responses + (such as redirects) when their inclusion is enabled with --all. If this + options is not specified, then they are formatted the same way as the final + response. + + """ +) output_options.add_argument( '--stream', '-S', action='store_true', @@ -454,16 +479,6 @@ def _split_lines(self, text, width): """ ) -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, diff --git a/httpie/core.py b/httpie/core.py index 4c224b8a10..c3c9b33acc 100644 --- a/httpie/core.py +++ b/httpie/core.py @@ -96,11 +96,11 @@ def program(args, env, log_error): ) downloader.pre_request(args.headers) - last_response = get_response(args, config_dir=env.config.directory) - if args.show_redirects: - responses = last_response.history + [last_response] + final_response = get_response(args, config_dir=env.config.directory) + if args.all: + responses = final_response.history + [final_response] else: - responses = [last_response] + responses = [final_response] for response in responses: @@ -121,6 +121,11 @@ def program(args, env, log_error): env=env, request=response.request, response=response, + output_options=( + args.output_options + if response is final_response + else args.output_options_others + ) ), # NOTE: `env.stdout` will in fact be `stderr` with `--download` 'outfile': env.stdout, @@ -140,7 +145,7 @@ def program(args, env, log_error): if downloader and exit_status == ExitStatus.OK: # Last response body download. - download_stream, download_to = downloader.start(last_response) + download_stream, download_to = downloader.start(final_response) write_stream( stream=download_stream, outfile=download_to, diff --git a/httpie/input.py b/httpie/input.py index c622b65b4d..39c112d398 100644 --- a/httpie/input.py +++ b/httpie/input.py @@ -359,18 +359,32 @@ def _process_output_options(self): The default output options are stdout-type-sensitive. """ - if not self.args.output_options: - self.args.output_options = ( - OUTPUT_OPTIONS_DEFAULT - if self.env.stdout_isatty - else OUTPUT_OPTIONS_DEFAULT_STDOUT_REDIRECTED - ) + def check_options(value, option): + unknown = set(value) - OUTPUT_OPTIONS + if unknown: + self.error('Unknown output options: {0}={1}'.format( + option, + ','.join(unknown) + )) + + if self.args.verbose: + self.args.all = True + + if self.args.output_options is None: + if self.args.verbose: + self.args.output_options = ''.join(OUTPUT_OPTIONS) + else: + self.args.output_options = ( + OUTPUT_OPTIONS_DEFAULT + if self.env.stdout_isatty + else OUTPUT_OPTIONS_DEFAULT_STDOUT_REDIRECTED + ) - unknown_output_options = set(self.args.output_options) - OUTPUT_OPTIONS - if unknown_output_options: - self.error( - 'Unknown output options: %s' % ','.join(unknown_output_options) - ) + if self.args.output_options_others is None: + self.args.output_options_others = self.args.output_options + + check_options(self.args.output_options, '--print') + check_options(self.args.output_options_others, '--print-others') if self.args.download and OUT_RESP_BODY in self.args.output_options: # Response body is always downloaded with --download and it goes diff --git a/httpie/output/streams.py b/httpie/output/streams.py index b22698142c..3483a6d3ce 100644 --- a/httpie/output/streams.py +++ b/httpie/output/streams.py @@ -55,15 +55,15 @@ def write_stream_with_colors_win_py3(stream, outfile, flush): outfile.flush() -def build_output_stream(args, env, request, response): +def build_output_stream(args, env, request, response, output_options): """Build and return a chain of iterators over the `request`-`response` exchange each of which yields `bytes` chunks. """ - req_h = OUT_REQ_HEAD in args.output_options - req_b = OUT_REQ_BODY in args.output_options - resp_h = OUT_RESP_HEAD in args.output_options - resp_b = OUT_RESP_BODY in args.output_options + req_h = OUT_REQ_HEAD in output_options + req_b = OUT_REQ_BODY in output_options + resp_h = OUT_RESP_HEAD in output_options + resp_b = OUT_RESP_BODY in output_options req = req_h or req_b resp = resp_h or resp_b diff --git a/tests/test_redirects.py b/tests/test_redirects.py index e96ea8e5f1..579f5c6c2a 100644 --- a/tests/test_redirects.py +++ b/tests/test_redirects.py @@ -3,16 +3,38 @@ from utils import http, HTTP_OK -def test_follow_no_show_redirects(httpbin): +def test_follow_all_redirects_shown(httpbin): + r = http('--follow', '--all', 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_follow_without_all_redirects_hidden(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(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) +def test_follow_all_output_options_used_for_redirects(httpbin): + r = http('--check-status', + '--follow', + '--all', + '--print=H', + httpbin.url + '/redirect/2') + assert r.count('GET /') == 3 + assert HTTP_OK not in r + + +def test_follow_redirect_output_options(httpbin): + r = http('--check-status', + '--follow', + '--all', + '--print=h', + '--print-others=H', + httpbin.url + '/redirect/2') + assert r.count('GET /') == 2 + assert 'HTTP/1.1 302 FOUND' not in r assert HTTP_OK in r