Added exit codes for HTTP 3xx, 4xx, 5xx (3, 4, 5).

Also added `--ignore-http-status` to ensure 0 exit status.

HTTP 3xx result in 0 exit status when `--allow-redirects` is set.
This commit is contained in:
Jakub Roztocil 2012-07-23 19:35:44 +02:00
parent 0a673613ef
commit 0572158ba1
6 changed files with 123 additions and 13 deletions

View File

@ -170,9 +170,9 @@ See ``http -h`` for more details::
usage: http [-h] [--version] [--json | --form] [--traceback] usage: http [-h] [--version] [--json | --form] [--traceback]
[--pretty | --ugly] [--pretty | --ugly]
[--print OUTPUT_OPTIONS | --verbose | --headers | --body] [--print OUTPUT_OPTIONS | --verbose | --headers | --body]
[--style STYLE] [--auth AUTH] [--auth-type {basic,digest}] [--style STYLE] [--ignore-http-status] [--auth AUTH]
[--verify VERIFY] [--proxy PROXY] [--allow-redirects] [--auth-type {basic,digest}] [--verify VERIFY] [--proxy PROXY]
[--timeout TIMEOUT] [--allow-redirects] [--timeout TIMEOUT]
[METHOD] URL [ITEM [ITEM ...]] [METHOD] URL [ITEM [ITEM ...]]
HTTPie - cURL for humans. <http://httpie.org> HTTPie - cURL for humans. <http://httpie.org>
@ -235,6 +235,14 @@ See ``http -h`` for more details::
make sure that the $TERM environment variable is set make sure that the $TERM environment variable is set
to "xterm-256color" or similar (e.g., via `export TERM to "xterm-256color" or similar (e.g., via `export TERM
=xterm-256color' in your ~/.bashrc). =xterm-256color' in your ~/.bashrc).
--ignore-http-status This flag tells HTTP to ignore the HTTP status code
and exit with 0. By default, HTTPie exits with 0 only
if no errors occur and the request is successful. When
the server replies with a 4xx (Client Error) or 5xx
(Server Error) status code, HTTPie exits with 4 or 5
respectively. If the response is 3xx (Redirect) and
--allow-redirects isn't set, then the exit status is
3.
--auth AUTH, -a AUTH username:password. If only the username is provided --auth AUTH, -a AUTH username:password. If only the username is provided
(-a username), HTTPie will prompt for the password. (-a username), HTTPie will prompt for the password.
--auth-type {basic,digest} --auth-type {basic,digest}
@ -287,6 +295,7 @@ Changelog
--------- ---------
* `0.2.6dev <https://github.com/jkbr/httpie/compare/0.2.5...master>`_ * `0.2.6dev <https://github.com/jkbr/httpie/compare/0.2.5...master>`_
* Added exit status codes for HTTP 3xx, 4xx and 5xx (3, 4, 5).
* If the output is piped to another program or redirected to a file, * If the output is piped to another program or redirected to a file,
the new default behaviour is to only print the response body. the new default behaviour is to only print the response body.
(It can still be overriden via the ``--print`` flag.) (It can still be overriden via the ``--print`` flag.)

View File

@ -3,8 +3,9 @@
The main entry point. Invoke as `http' or `python -m httpie'. The main entry point. Invoke as `http' or `python -m httpie'.
""" """
import sys
from .core import main from .core import main
if __name__ == '__main__': if __name__ == '__main__':
main() sys.exit(main())

View File

@ -125,6 +125,21 @@ parser.add_argument(
''') % ', '.join(sorted(AVAILABLE_STYLES)) ''') % ', '.join(sorted(AVAILABLE_STYLES))
) )
parser.add_argument(
'--ignore-http-status', default=False, action='store_true',
help=_('''
This flag tells HTTP to ignore the HTTP status code
and exit with 0.
By default, HTTPie exits with 0 only if no errors occur
and the request is successful. When the server
replies with a 4xx (Client Error) or 5xx (Server Error)
status code, HTTPie exits with 4 or 5 respectively.
If the response is 3xx (Redirect) and --allow-redirects
isn't set, then the exit status is 3.
''')
)
# ``requests.request`` keyword arguments. # ``requests.request`` keyword arguments.
parser.add_argument( parser.add_argument(
'--auth', '-a', type=cliparse.AuthCredentialsType(cliparse.SEP_COMMON), '--auth', '-a', type=cliparse.AuthCredentialsType(cliparse.SEP_COMMON),

View File

@ -114,10 +114,39 @@ def get_output(args, env, response):
return ''.join(buf) return ''.join(buf)
def get_exist_status(code, allow_redirects=False):
"""
Translate HTTP status code to exit status.
"""
if 300 <= code <= 399 and not allow_redirects:
# Redirect
return 3
elif 400 <= code <= 499:
# Client Error
return 4
elif 500 <= code <= 599:
# Server Error
return 5
else:
return 0
def main(args=sys.argv[1:], env=Environment()): def main(args=sys.argv[1:], env=Environment()):
"""
Run the main program and write the output to ``env.stdout``.
Return exit status.
"""
args = cli.parser.parse_args(args=args, env=env) args = cli.parser.parse_args(args=args, env=env)
response = get_response(args) response = get_response(args)
output = get_output(args, env, response) output = get_output(args, env, response)
output_bytes = output.encode('utf8') output_bytes = output.encode('utf8')
f = getattr(env.stdout, 'buffer', env.stdout) f = getattr(env.stdout, 'buffer', env.stdout)
f.write(output_bytes) f.write(output_bytes)
if args.ignore_http_status:
return 0
else:
return get_exist_status(
response.status_code, args.allow_redirects)

View File

@ -2,7 +2,6 @@
Colorizing of HTTP messages and content processing. Colorizing of HTTP messages and content processing.
""" """
import os
import re import re
import json import json
import pygments import pygments

View File

@ -21,8 +21,7 @@ import json
import tempfile import tempfile
import unittest import unittest
import argparse import argparse
from requests import Response from requests.compat import is_py26, is_py3, str
from requests.compat import is_py26, is_py3
################################################################# #################################################################
@ -51,28 +50,40 @@ def httpbin(path):
return HTTPBIN_URL + path return HTTPBIN_URL + path
class Response(str):
"""
A unicode subclass holding the output of `main()` and the exit status.
"""
exit_status = None
def http(*args, **kwargs): def http(*args, **kwargs):
""" """
Invoke `httpie.__main__.main` with `args` and `kwargs`, Invoke `httpie.core.main()` with `args` and `kwargs`,
and return a unicode response. and return a unicode response.
""" """
if 'env' not in kwargs: if 'env' not in kwargs:
# Ensure that we have terminal by default # Ensure that we have terminal by default (needed for Travis).
# (needed for Travis).
kwargs['env'] = Environment( kwargs['env'] = Environment(
colors=0, colors=0,
stdin_isatty=True, stdin_isatty=True,
stdout_isatty=True, stdout_isatty=True,
) )
stdout = kwargs['env'].stdout = tempfile.TemporaryFile() stdout = kwargs['env'].stdout = tempfile.TemporaryFile()
main(args=args, **kwargs)
exit_status = main(args=args, **kwargs)
stdout.seek(0) stdout.seek(0)
response = stdout.read().decode('utf8')
response = Response(stdout.read().decode('utf8'))
response.exit_status = exit_status
stdout.close() stdout.close()
return response return response
@ -494,6 +505,52 @@ class AuthTest(BaseTestCase):
self.assertIn('"user": "user"', r) self.assertIn('"user": "user"', r)
class ExitStatusTest(BaseTestCase):
def test_3xx_exits_3(self):
r = http(
'GET',
httpbin('/status/301')
)
self.assertIn('HTTP/1.1 301', r)
self.assertEqual(r.exit_status, 3)
def test_3xx_redirects_allowed_exits_0(self):
r = http(
'--allow-redirects',
'GET',
httpbin('/status/301')
)
# The redirect will be followed so 200 is expected.
self.assertIn('HTTP/1.1 200 OK', r)
self.assertEqual(r.exit_status, 0)
def test_4xx_exits_4(self):
r = http(
'GET',
httpbin('/status/401')
)
self.assertIn('HTTP/1.1 401', r)
self.assertEqual(r.exit_status, 4)
def test_5xx_exits_5(self):
r = http(
'GET',
httpbin('/status/500')
)
self.assertIn('HTTP/1.1 500', r)
self.assertEqual(r.exit_status, 5)
def test_ignore_http_status_exits_0(self):
r = http(
'--ignore-http-status',
'GET',
httpbin('/status/500')
)
self.assertIn('HTTP/1.1 500', r)
self.assertEqual(r.exit_status, 0)
################################################################# #################################################################
# CLI argument parsing related tests. # CLI argument parsing related tests.
################################################################# #################################################################