forked from extern/httpie-cli
Replaced --ignore-http-status with --check-status.
The default behaviour now is to exit with 0 on HTTP errors unless --check-status is set.
This commit is contained in:
parent
fba3912f2e
commit
2646ebaaed
54
README.rst
54
README.rst
@ -151,6 +151,11 @@ the second one does via ``stdin``::
|
|||||||
|
|
||||||
http https://api.github.com/repos/jkbr/httpie | http httpbin.org/post
|
http https://api.github.com/repos/jkbr/httpie | http httpbin.org/post
|
||||||
|
|
||||||
|
Note that when the output is redirected (like the examples above), HTTPie
|
||||||
|
applies a different set of defaults then for console output. Namely colors
|
||||||
|
aren't used (can be forced with ``--pretty``) and only the response body
|
||||||
|
gets printed (can be overwritten with ``--print``).
|
||||||
|
|
||||||
An alternative to ``stdin`` is to pass a file name whose content will be used
|
An alternative to ``stdin`` is to pass a file name whose content will be used
|
||||||
as the request body. It has the advantage that the ``Content-Type`` header
|
as the request body. It has the advantage that the ``Content-Type`` header
|
||||||
will automatically be set to the appropriate value based on the filename
|
will automatically be set to the appropriate value based on the filename
|
||||||
@ -160,6 +165,25 @@ request will send the verbatim contents of the file with
|
|||||||
|
|
||||||
http PUT httpbin.org/put @/data/file.xml
|
http PUT httpbin.org/put @/data/file.xml
|
||||||
|
|
||||||
|
When using HTTPie from shell scripts you might want to use the
|
||||||
|
``--check-status`` flag. It instructs HTTPie to exit with an error if the
|
||||||
|
HTTP status is one of ``3xx``, ``4xx``, or ``5xx``. The exit status will
|
||||||
|
be ``3`` (unless ``--allow-redirects`` is set), ``4``, or ``5``
|
||||||
|
respectivelly::
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
if http --check-status example.org/health &> /dev/null; then
|
||||||
|
echo 'OK!'
|
||||||
|
else
|
||||||
|
case $? in
|
||||||
|
3) echo 'Unexpected 3xx Redirection!' ;;
|
||||||
|
4) echo '4xx Client Error!' ;;
|
||||||
|
5) echo '5xx Server Error!' ;;
|
||||||
|
*) echo 'Other Error!' ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
Flags
|
Flags
|
||||||
^^^^^
|
^^^^^
|
||||||
@ -170,7 +194,7 @@ 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] [--ignore-http-status] [--auth AUTH]
|
[--style STYLE] [--check-status] [--auth AUTH]
|
||||||
[--auth-type {basic,digest}] [--verify VERIFY] [--proxy PROXY]
|
[--auth-type {basic,digest}] [--verify VERIFY] [--proxy PROXY]
|
||||||
[--allow-redirects] [--timeout TIMEOUT]
|
[--allow-redirects] [--timeout TIMEOUT]
|
||||||
[METHOD] URL [ITEM [ITEM ...]]
|
[METHOD] URL [ITEM [ITEM ...]]
|
||||||
@ -235,14 +259,16 @@ 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
|
--check-status By default, HTTPie exits with 0 when no network or
|
||||||
and exit with 0. By default, HTTPie exits with 0 only
|
other fatal errors occur. This flag instructs HTTPie
|
||||||
if no errors occur and the request is successful. When
|
to also check the HTTP status code and exit with an
|
||||||
the server replies with a 4xx (Client Error) or 5xx
|
error if the status indicates one. When the server
|
||||||
(Server Error) status code, HTTPie exits with 4 or 5
|
replies with a 4xx (Client Error) or 5xx (Server
|
||||||
respectively. If the response is 3xx (Redirect) and
|
Error) status code, HTTPie exits with 4 or 5
|
||||||
--allow-redirects isn't set, then the exit status is
|
respectively. If the response is a 3xx (Redirect) and
|
||||||
3.
|
--allow-redirects hasn't been set, then the exit
|
||||||
|
status is 3. Also an error message is written to
|
||||||
|
stderr if stdout is redirected.
|
||||||
--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}
|
||||||
@ -261,6 +287,7 @@ See ``http -h`` for more details::
|
|||||||
socket.setdefaulttimeout() as fallback).
|
socket.setdefaulttimeout() as fallback).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Contribute
|
Contribute
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
@ -276,8 +303,13 @@ to see if the feature/bug has previously been discussed. Then fork
|
|||||||
develop branch and submit a pull request. Note: Pull requests with tests and
|
develop branch and submit a pull request. Note: Pull requests with tests and
|
||||||
documentation are 53.6% more awesome :)
|
documentation are 53.6% more awesome :)
|
||||||
|
|
||||||
Before a pull requests is submitted, it's a good idea to run the existing
|
To point the ``http`` command to your working copy you can install HTTPie in
|
||||||
suite of tests::
|
the editable mode::
|
||||||
|
|
||||||
|
pip install --editable .
|
||||||
|
|
||||||
|
It's a good idea to run the existing suite of tests before a pull requests is
|
||||||
|
submitted::
|
||||||
|
|
||||||
python setup.py test
|
python setup.py test
|
||||||
|
|
||||||
|
@ -126,17 +126,21 @@ parser.add_argument(
|
|||||||
)
|
)
|
||||||
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--ignore-http-status', default=False, action='store_true',
|
'--check-status', default=False, action='store_true',
|
||||||
help=_('''
|
help=_('''
|
||||||
This flag tells HTTP to ignore the HTTP status code
|
By default, HTTPie exits with 0 when no network or other fatal
|
||||||
and exit with 0.
|
errors occur.
|
||||||
|
|
||||||
|
This flag instructs HTTPie to also check the HTTP status code and
|
||||||
|
exit with an error if the status indicates one.
|
||||||
|
|
||||||
|
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 a 3xx (Redirect) and --allow-redirects
|
||||||
|
hasn't been set, then the exit status is 3.
|
||||||
|
|
||||||
|
Also an error message is written to stderr if stdout is redirected.
|
||||||
|
|
||||||
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.
|
|
||||||
''')
|
''')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -141,12 +141,20 @@ def main(args=sys.argv[1:], env=Environment()):
|
|||||||
"""
|
"""
|
||||||
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)
|
||||||
|
|
||||||
|
status = 0
|
||||||
|
|
||||||
|
if args.check_status:
|
||||||
|
status = get_exist_status(response.status_code,
|
||||||
|
args.allow_redirects)
|
||||||
|
if status and not env.stdout_isatty:
|
||||||
|
err = 'http error: %s %s\n' % (
|
||||||
|
response.raw.status, response.raw.reason)
|
||||||
|
env.stderr.write(err.encode('utf8'))
|
||||||
|
|
||||||
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
|
return status
|
||||||
else:
|
|
||||||
return get_exist_status(
|
|
||||||
response.status_code, args.allow_redirects)
|
|
||||||
|
@ -17,6 +17,8 @@ class Environment(object):
|
|||||||
stdout_isatty = sys.stdout.isatty()
|
stdout_isatty = sys.stdout.isatty()
|
||||||
stdout = sys.stdout
|
stdout = sys.stdout
|
||||||
|
|
||||||
|
stderr = sys.stderr
|
||||||
|
|
||||||
# Can be set to 0 to disable colors completely.
|
# Can be set to 0 to disable colors completely.
|
||||||
colors = 256 if '256color' in os.environ.get('TERM', '') else 88
|
colors = 256 if '256color' in os.environ.get('TERM', '') else 88
|
||||||
|
|
||||||
|
@ -53,10 +53,12 @@ def httpbin(path):
|
|||||||
|
|
||||||
class Response(str):
|
class Response(str):
|
||||||
"""
|
"""
|
||||||
A unicode subclass holding the output of `main()` and the exit status.
|
A unicode subclass holding the output of `main()`, and also
|
||||||
|
the exit status and contents of ``stderr``.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
exit_status = None
|
exit_status = None
|
||||||
|
stderr = None
|
||||||
|
|
||||||
|
|
||||||
def http(*args, **kwargs):
|
def http(*args, **kwargs):
|
||||||
@ -75,17 +77,21 @@ def http(*args, **kwargs):
|
|||||||
)
|
)
|
||||||
|
|
||||||
stdout = kwargs['env'].stdout = tempfile.TemporaryFile()
|
stdout = kwargs['env'].stdout = tempfile.TemporaryFile()
|
||||||
|
stderr = kwargs['env'].stderr = tempfile.TemporaryFile()
|
||||||
|
|
||||||
exit_status = main(args=args, **kwargs)
|
exit_status = main(args=args, **kwargs)
|
||||||
|
|
||||||
stdout.seek(0)
|
stdout.seek(0)
|
||||||
|
stderr.seek(0)
|
||||||
|
|
||||||
response = Response(stdout.read().decode('utf8'))
|
r = Response(stdout.read().decode('utf8'))
|
||||||
response.exit_status = exit_status
|
r.stderr = stderr.read().decode('utf8')
|
||||||
|
r.exit_status = exit_status
|
||||||
|
|
||||||
stdout.close()
|
stdout.close()
|
||||||
|
stderr.close()
|
||||||
|
|
||||||
return response
|
return r
|
||||||
|
|
||||||
|
|
||||||
class BaseTestCase(unittest.TestCase):
|
class BaseTestCase(unittest.TestCase):
|
||||||
@ -155,6 +161,7 @@ class HTTPieTest(BaseTestCase):
|
|||||||
env = Environment(
|
env = Environment(
|
||||||
stdin=open(TEST_FILE_PATH),
|
stdin=open(TEST_FILE_PATH),
|
||||||
stdin_isatty=False,
|
stdin_isatty=False,
|
||||||
|
stdout_isatty=True,
|
||||||
colors=0,
|
colors=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -276,7 +283,10 @@ class AutoContentTypeAndAcceptHeadersTest(BaseTestCase):
|
|||||||
r = http(
|
r = http(
|
||||||
'GET',
|
'GET',
|
||||||
httpbin('/get'),
|
httpbin('/get'),
|
||||||
env=Environment(stdout_isatty=False)
|
env=Environment(
|
||||||
|
stdin_isatty=True,
|
||||||
|
stdout_isatty=False
|
||||||
|
)
|
||||||
)
|
)
|
||||||
self.assertNotIn('HTTP/', r)
|
self.assertNotIn('HTTP/', r)
|
||||||
|
|
||||||
@ -286,7 +296,10 @@ class AutoContentTypeAndAcceptHeadersTest(BaseTestCase):
|
|||||||
'--print=h',
|
'--print=h',
|
||||||
'GET',
|
'GET',
|
||||||
httpbin('/get'),
|
httpbin('/get'),
|
||||||
env=Environment(stdout_isatty=False)
|
env=Environment(
|
||||||
|
stdin_isatty=True,
|
||||||
|
stdout_isatty=False
|
||||||
|
)
|
||||||
)
|
)
|
||||||
self.assertIn('HTTP/1.1 200', r)
|
self.assertIn('HTTP/1.1 200', r)
|
||||||
|
|
||||||
@ -326,6 +339,7 @@ class ImplicitHTTPMethodTest(BaseTestCase):
|
|||||||
env = Environment(
|
env = Environment(
|
||||||
stdin_isatty=False,
|
stdin_isatty=False,
|
||||||
stdin=open(TEST_FILE_PATH),
|
stdin=open(TEST_FILE_PATH),
|
||||||
|
stdout_isatty=True,
|
||||||
colors=0,
|
colors=0,
|
||||||
)
|
)
|
||||||
r = http(
|
r = http(
|
||||||
@ -343,7 +357,10 @@ class PrettyFlagTest(BaseTestCase):
|
|||||||
r = http(
|
r = http(
|
||||||
'GET',
|
'GET',
|
||||||
httpbin('/get'),
|
httpbin('/get'),
|
||||||
env=Environment(stdout_isatty=True),
|
env=Environment(
|
||||||
|
stdin_isatty=True,
|
||||||
|
stdout_isatty=True,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.assertIn(TERMINAL_COLOR_PRESENCE_CHECK, r)
|
self.assertIn(TERMINAL_COLOR_PRESENCE_CHECK, r)
|
||||||
|
|
||||||
@ -359,7 +376,10 @@ class PrettyFlagTest(BaseTestCase):
|
|||||||
'--pretty',
|
'--pretty',
|
||||||
'GET',
|
'GET',
|
||||||
httpbin('/get'),
|
httpbin('/get'),
|
||||||
env=Environment(stdout_isatty=False),
|
env=Environment(
|
||||||
|
stdin_isatty=True,
|
||||||
|
stdout_isatty=False
|
||||||
|
),
|
||||||
)
|
)
|
||||||
self.assertIn(TERMINAL_COLOR_PRESENCE_CHECK, r)
|
self.assertIn(TERMINAL_COLOR_PRESENCE_CHECK, r)
|
||||||
|
|
||||||
@ -508,16 +528,40 @@ class AuthTest(BaseTestCase):
|
|||||||
|
|
||||||
class ExitStatusTest(BaseTestCase):
|
class ExitStatusTest(BaseTestCase):
|
||||||
|
|
||||||
def test_3xx_exits_3(self):
|
def test_ok_response_exits_0(self):
|
||||||
r = http(
|
r = http(
|
||||||
'GET',
|
'GET',
|
||||||
httpbin('/status/301')
|
httpbin('/status/200')
|
||||||
|
)
|
||||||
|
self.assertIn('HTTP/1.1 200', r)
|
||||||
|
self.assertEqual(r.exit_status, 0)
|
||||||
|
|
||||||
|
def test_error_response_exits_0_without_check_status(self):
|
||||||
|
r = http(
|
||||||
|
'GET',
|
||||||
|
httpbin('/status/500')
|
||||||
|
)
|
||||||
|
self.assertIn('HTTP/1.1 500', r)
|
||||||
|
self.assertEqual(r.exit_status, 0)
|
||||||
|
|
||||||
|
def test_3xx_check_status_exits_3_and_stderr_when_stdout_redirected(self):
|
||||||
|
r = http(
|
||||||
|
'--check-status',
|
||||||
|
'--headers', # non-terminal, force headers
|
||||||
|
'GET',
|
||||||
|
httpbin('/status/301'),
|
||||||
|
env=Environment(
|
||||||
|
stdout_isatty=False,
|
||||||
|
stdin_isatty=True,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
self.assertIn('HTTP/1.1 301', r)
|
self.assertIn('HTTP/1.1 301', r)
|
||||||
self.assertEqual(r.exit_status, 3)
|
self.assertEqual(r.exit_status, 3)
|
||||||
|
self.assertIn('301 moved permanently', r.stderr.lower())
|
||||||
|
|
||||||
def test_3xx_redirects_allowed_exits_0(self):
|
def test_3xx_check_status_redirects_allowed_exits_0(self):
|
||||||
r = http(
|
r = http(
|
||||||
|
'--check-status',
|
||||||
'--allow-redirects',
|
'--allow-redirects',
|
||||||
'GET',
|
'GET',
|
||||||
httpbin('/status/301')
|
httpbin('/status/301')
|
||||||
@ -526,31 +570,26 @@ class ExitStatusTest(BaseTestCase):
|
|||||||
self.assertIn('HTTP/1.1 200 OK', r)
|
self.assertIn('HTTP/1.1 200 OK', r)
|
||||||
self.assertEqual(r.exit_status, 0)
|
self.assertEqual(r.exit_status, 0)
|
||||||
|
|
||||||
def test_4xx_exits_4(self):
|
def test_4xx_check_status_exits_4(self):
|
||||||
r = http(
|
r = http(
|
||||||
|
'--check-status',
|
||||||
'GET',
|
'GET',
|
||||||
httpbin('/status/401')
|
httpbin('/status/401')
|
||||||
)
|
)
|
||||||
self.assertIn('HTTP/1.1 401', r)
|
self.assertIn('HTTP/1.1 401', r)
|
||||||
self.assertEqual(r.exit_status, 4)
|
self.assertEqual(r.exit_status, 4)
|
||||||
|
# Also stderr should be empty since stdout isn't redirected.
|
||||||
|
self.assert_(not r.stderr)
|
||||||
|
|
||||||
def test_5xx_exits_5(self):
|
def test_5xx_check_status_exits_5(self):
|
||||||
r = http(
|
r = http(
|
||||||
|
'--check-status',
|
||||||
'GET',
|
'GET',
|
||||||
httpbin('/status/500')
|
httpbin('/status/500')
|
||||||
)
|
)
|
||||||
self.assertIn('HTTP/1.1 500', r)
|
self.assertIn('HTTP/1.1 500', r)
|
||||||
self.assertEqual(r.exit_status, 5)
|
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.
|
||||||
@ -660,7 +699,10 @@ class ArgumentParserTestCase(unittest.TestCase):
|
|||||||
args.url = 'http://example.com/'
|
args.url = 'http://example.com/'
|
||||||
args.items = []
|
args.items = []
|
||||||
|
|
||||||
self.parser._guess_method(args, Environment())
|
self.parser._guess_method(args, Environment(
|
||||||
|
stdin_isatty=True,
|
||||||
|
stdout_isatty=True,
|
||||||
|
))
|
||||||
|
|
||||||
self.assertEquals(args.method, 'GET')
|
self.assertEquals(args.method, 'GET')
|
||||||
self.assertEquals(args.url, 'http://example.com/')
|
self.assertEquals(args.url, 'http://example.com/')
|
||||||
@ -687,7 +729,10 @@ class ArgumentParserTestCase(unittest.TestCase):
|
|||||||
args.url = 'test:header'
|
args.url = 'test:header'
|
||||||
args.items = []
|
args.items = []
|
||||||
|
|
||||||
self.parser._guess_method(args, Environment())
|
self.parser._guess_method(args, Environment(
|
||||||
|
stdin_isatty=True,
|
||||||
|
stdout_isatty=True,
|
||||||
|
))
|
||||||
|
|
||||||
self.assertEquals(args.method, 'GET')
|
self.assertEquals(args.method, 'GET')
|
||||||
self.assertEquals(args.url, 'http://example.com/')
|
self.assertEquals(args.url, 'http://example.com/')
|
||||||
|
Loading…
Reference in New Issue
Block a user