mirror of
https://github.com/httpie/cli.git
synced 2024-11-24 08:22:22 +02:00
Clean up.
This commit is contained in:
parent
40948dbd2e
commit
c2d70e2bb1
103
README.rst
103
README.rst
@ -1,19 +1,28 @@
|
||||
HTTPie: cURL for humans
|
||||
=======================
|
||||
|
||||
**HTTPie is a CLI HTTP utility** built out of frustration with existing tools. The goal is to make CLI interaction with HTTP-based services as human-friendly as possible.
|
||||
**HTTPie is a CLI HTTP utility** built out of frustration with existing tools.
|
||||
The goal is to make CLI interaction with HTTP-based services as
|
||||
human-friendly as possible.
|
||||
|
||||
HTTPie does so by providing an ``http`` command that allows for issuing arbitrary HTTP requests using a **simple and natural syntax** and displaying **colorized responses**:
|
||||
HTTPie does so by providing an ``http`` command that allows for issuing
|
||||
arbitrary HTTP requests using a **simple and natural syntax** and displaying
|
||||
**colorized responses**:
|
||||
|
||||
.. image:: https://github.com/jkbr/httpie/raw/master/httpie.png
|
||||
:alt: HTTPie compared to cURL
|
||||
|
||||
Under the hood, HTTPie uses the excellent `Requests <http://python-requests.org>`_ and `Pygments <http://pygments.org/>`_ Python libraries. Python 2.6+ is supported (including 3.x).
|
||||
Under the hood, HTTPie uses the excellent
|
||||
`Requests <http://python-requests.org>`_ and
|
||||
`Pygments <http://pygments.org/>`_ Python libraries. Python 2.6+ is supported
|
||||
(including 3.x).
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
The latest **stable version** of HTTPie can always be installed (or updated to) via `pip <http://www.pip-installer.org/en/latest/index.html>`_::
|
||||
The latest **stable version** of HTTPie can always be installed
|
||||
(or updated to) via
|
||||
`pip <http://www.pip-installer.org/en/latest/index.html>`_::
|
||||
|
||||
pip install -U httpie
|
||||
|
||||
@ -29,7 +38,9 @@ Or, you can install the **development version** directly from GitHub:
|
||||
pip install -U https://github.com/jkbr/httpie/tarball/master
|
||||
|
||||
|
||||
There are packages available for `Ubuntu <http://packages.ubuntu.com/quantal/httpie>`_ and `Debian <http://packages.debian.org/wheezy/httpie>`_.
|
||||
There are packages available for
|
||||
`Ubuntu <http://packages.ubuntu.com/quantal/httpie>`_ and
|
||||
`Debian <http://packages.debian.org/wheezy/httpie>`_.
|
||||
|
||||
|
||||
Usage
|
||||
@ -46,16 +57,25 @@ Synopsis::
|
||||
There are four types of key-value pair items available:
|
||||
|
||||
Headers (``Name:Value``)
|
||||
Arbitrary HTTP headers. The ``:`` character is used to separate a header's name from its value, e.g., ``X-API-Token:123``.
|
||||
Arbitrary HTTP headers. The ``:`` character is used to separate a header's
|
||||
name from its value, e.g., ``X-API-Token:123``.
|
||||
|
||||
Simple data fields (``field=value``)
|
||||
Data items are included in the request body. Depending on the ``Content-Type``, they are automatically serialized as a JSON ``Object`` (default) or ``application/x-www-form-urlencoded`` (the ``-f`` flag). Data items use ``=`` as the separator, e.g., ``hello=world``.
|
||||
Data items are included in the request body. Depending on the
|
||||
``Content-Type``, they are automatically serialized as a JSON ``Object``
|
||||
(default) or ``application/x-www-form-urlencoded`` (the ``-f`` flag).
|
||||
Data items use ``=`` as the separator, e.g., ``hello=world``.
|
||||
|
||||
Raw JSON fields (``field:=value``)
|
||||
This item type is needed when ``Content-Type`` is JSON and a field's value is a ``Boolean``, ``Number``, nested ``Object`` or an ``Array``, because simple data items are always serialized as ``String``. E.g. ``pies:=[1,2,3]``.
|
||||
This item type is needed when ``Content-Type`` is JSON and a field's value
|
||||
is a ``Boolean``, ``Number``, nested ``Object`` or an ``Array``, because
|
||||
simple data items are always serialized as ``String``.
|
||||
E.g. ``pies:=[1,2,3]``.
|
||||
|
||||
File fields (``field@/path/to/file``)
|
||||
Only available with ``-f`` / ``--form``. Use ``@`` as the separator, e.g., ``screenshot@/path/to/file.png``. The presence of a file field results into a ``multipart/form-data`` request.
|
||||
Only available with ``-f`` / ``--form``. Use ``@`` as the separator, e.g.,
|
||||
``screenshot@/path/to/file.png``. The presence of a file field results into
|
||||
a ``multipart/form-data`` request.
|
||||
|
||||
|
||||
Examples
|
||||
@ -75,7 +95,8 @@ The following request is issued::
|
||||
{"name": "John", "email": "john@example.org", "age": 29}
|
||||
|
||||
|
||||
It can easily be changed to a **form** request using the ``-f`` (or ``--form``) flag, which produces::
|
||||
It can easily be changed to a **form** request using the ``-f``
|
||||
(or ``--form``) flag, which produces::
|
||||
|
||||
PATCH /person/1 HTTP/1.1
|
||||
User-Agent: HTTPie/0.1
|
||||
@ -84,7 +105,9 @@ It can easily be changed to a **form** request using the ``-f`` (or ``--form``)
|
||||
|
||||
age=29&name=John&email=john%40example.org
|
||||
|
||||
It is also possible to send ``multipart/form-data`` requests, i.e., to simulate a **file upload form** submission. It is done using the ``--form`` / ``-f`` flag and passing one or more file fields::
|
||||
It is also possible to send ``multipart/form-data`` requests, i.e., to
|
||||
simulate a **file upload form** submission. It is done using the
|
||||
``--form`` / ``-f`` flag and passing one or more file fields::
|
||||
|
||||
http -f POST example.com/jobs name=John cv@~/Documents/cv.pdf
|
||||
|
||||
@ -95,28 +118,38 @@ The above will send the same request as if the following HTML form were submitte
|
||||
<input type="file" name="cv" />
|
||||
</form>
|
||||
|
||||
A whole request body can be passed in via **``stdin``** instead, in which case it will be used with no further processing::
|
||||
A whole request body can be passed in via **``stdin``** instead, in which
|
||||
case it will be used with no further processing::
|
||||
|
||||
echo '{"name": "John"}' | http PATCH example.com/person/1 X-API-Token:123
|
||||
# Or:
|
||||
http POST example.com/person/1 X-API-Token:123 < person.json
|
||||
|
||||
That can be used for **piping services together**. The following example ``GET``-s JSON data from the Github API and ``POST``-s it to httpbin.org::
|
||||
That can be used for **piping services together**. The following example
|
||||
``GET``-s JSON data from the Github API and ``POST``-s it to httpbin.org::
|
||||
|
||||
http -b GET https://api.github.com/repos/jkbr/httpie | http POST httpbin.org/post
|
||||
|
||||
The above can be further simplified by omitting ``GET`` and ``POST`` because they are both default here. The first command has no request data, whereas the second one does via ``stdin``::
|
||||
The above can be further simplified by omitting ``GET`` and ``POST`` because
|
||||
they are both default here. The first command has no request data, whereas
|
||||
the second one does via ``stdin``::
|
||||
|
||||
http -b https://api.github.com/repos/jkbr/httpie | http httpbin.org/post
|
||||
|
||||
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 will automatically be set to the appropriate value based on the filename extension (using the ``mimetypes`` module). Therefore, the following will request will send the verbatim contents of the file with ``Content-Type: application/xml``::
|
||||
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
|
||||
will automatically be set to the appropriate value based on the filename
|
||||
extension (using the ``mimetypes`` module). Therefore, the following will
|
||||
request will send the verbatim contents of the file with
|
||||
``Content-Type: application/xml``::
|
||||
|
||||
http PUT httpbin.org/put @/data/file.xml
|
||||
|
||||
|
||||
Flags
|
||||
^^^^^
|
||||
Most of the flags mirror the arguments understood by ``requests.request``. See ``http -h`` for more details::
|
||||
Most of the flags mirror the arguments understood by ``requests.request``.
|
||||
See ``http -h`` for more details::
|
||||
|
||||
$ http --help
|
||||
usage: http [-h] [--version] [--json | --form] [--traceback]
|
||||
@ -206,15 +239,24 @@ Contribute
|
||||
|
||||
`View contributors on GitHub <https://github.com/jkbr/httpie/contributors>`_.
|
||||
|
||||
If you have found a bug or have a feature request, the `issue tracker <https://github.com/jkbr/httpie/issues?state=open>`_ is the place to start a discussion about it.
|
||||
If you have found a bug or have a feature request, the
|
||||
`issue tracker <https://github.com/jkbr/httpie/issues?state=open>`_ is the
|
||||
place to start a discussion about it.
|
||||
|
||||
To contribute code or documentation, please first browse the existing issues to see if the feature/bug has previously been discussed. Then fork `the repository <https://github.com/jkbr/httpie>`_, make changes in your develop branch and submit a pull request. Note: Pull requests with tests and documentation are 53.6% more awesome :)
|
||||
To contribute code or documentation, please first browse the existing issues
|
||||
to see if the feature/bug has previously been discussed. Then fork
|
||||
`the repository <https://github.com/jkbr/httpie>`_, make changes in your
|
||||
develop branch and submit a pull request. Note: Pull requests with tests and
|
||||
documentation are 53.6% more awesome :)
|
||||
|
||||
Before a pull requests is submitted, it's a good idea to run the existing suite of tests::
|
||||
Before a pull requests is submitted, it's a good idea to run the existing
|
||||
suite of tests::
|
||||
|
||||
python setup.py test
|
||||
|
||||
`Tox <http://tox.testrun.org/>`_ can used to conveniently run tests in all of the `supported Python environments <https://github.com/jkbr/httpie/blob/master/tox.ini>`_::
|
||||
`Tox <http://tox.testrun.org/>`_ can used to conveniently run tests in all of
|
||||
the
|
||||
`supported Python environments <https://github.com/jkbr/httpie/blob/master/tox.ini>`_::
|
||||
|
||||
# Install tox
|
||||
pip install tox
|
||||
@ -228,23 +270,30 @@ Changelog
|
||||
* `0.2.6dev <https://github.com/jkbr/httpie/compare/0.2.5...master>`_
|
||||
* Added support for terminal colors under Windows.
|
||||
* `0.2.5 <https://github.com/jkbr/httpie/compare/0.2.2...0.2.5>`_ (2012-07-17)
|
||||
* Unicode characters in prettified JSON now don't get escaped to improve readability.
|
||||
* Unicode characters in prettified JSON now don't get escaped for
|
||||
improved readability.
|
||||
* --auth now prompts for a password if only a username provided.
|
||||
* Added support for request payloads from a file path with automatic ``Content-Type`` (``http URL @/path``).
|
||||
* Fixed missing query string when displaing the request headers via ``--verbose``.
|
||||
* Added support for request payloads from a file path with automatic
|
||||
``Content-Type`` (``http URL @/path``).
|
||||
* Fixed missing query string when displaing the request headers via
|
||||
``--verbose``.
|
||||
* Fixed Content-Type for requests with no data.
|
||||
* `0.2.2 <https://github.com/jkbr/httpie/compare/0.2.1...0.2.2>`_ (2012-06-24)
|
||||
* The ``METHOD`` positional argument can now be omitted (defaults to ``GET``, or to ``POST`` with data).
|
||||
* The ``METHOD`` positional argument can now be omitted (defaults to
|
||||
``GET``, or to ``POST`` with data).
|
||||
* Fixed --verbose --form.
|
||||
* Added support for `Tox <http://tox.testrun.org/>`_.
|
||||
* `0.2.1 <https://github.com/jkbr/httpie/compare/0.2.0...0.2.1>`_ (2012-06-13)
|
||||
* Added compatibility with ``requests-0.12.1``.
|
||||
* Dropped custom JSON and HTTP lexers in favor of the ones newly included in ``pygments-1.5``.
|
||||
* Dropped custom JSON and HTTP lexers in favor of the ones newly included
|
||||
in ``pygments-1.5``.
|
||||
* `0.2.0 <https://github.com/jkbr/httpie/compare/0.1.6...0.2.0>`_ (2012-04-25)
|
||||
* Added Python 3 support.
|
||||
* Added the ability to print the HTTP request as well as the response (see ``--print`` and ``--verbose``).
|
||||
* Added the ability to print the HTTP request as well as the response
|
||||
(see ``--print`` and ``--verbose``).
|
||||
* Added support for Digest authentication.
|
||||
* Added file upload support (``http -f POST file_field_name@/path/to/file``).
|
||||
* Added file upload support
|
||||
(``http -f POST file_field_name@/path/to/file``).
|
||||
* Improved syntax highlighting for JSON.
|
||||
* Added support for field name escaping.
|
||||
* Many bug fixes.
|
||||
|
@ -25,7 +25,8 @@ group_type = parser.add_mutually_exclusive_group(required=False)
|
||||
group_type.add_argument(
|
||||
'--json', '-j', action='store_true',
|
||||
help=_('''
|
||||
(default) Data items from the command line are serialized as a JSON object.
|
||||
(default) Data items from the command
|
||||
line are serialized as a JSON object.
|
||||
The Content-Type and Accept headers
|
||||
are set to application/json (if not specified).
|
||||
''')
|
||||
@ -34,8 +35,10 @@ group_type.add_argument(
|
||||
'--form', '-f', action='store_true',
|
||||
help=_('''
|
||||
Data items from the command line are serialized as form fields.
|
||||
The Content-Type is set to application/x-www-form-urlencoded (if not specified).
|
||||
The presence of any file fields results into a multipart/form-data request.
|
||||
The Content-Type is set to application/x-www-form-urlencoded
|
||||
(if not specified).
|
||||
The presence of any file fields results
|
||||
into a multipart/form-data request.
|
||||
''')
|
||||
)
|
||||
|
||||
@ -126,13 +129,17 @@ parser.add_argument(
|
||||
'--auth', '-a', type=cliparse.AuthCredentialsType(cliparse.SEP_COMMON),
|
||||
help=_('''
|
||||
username:password.
|
||||
If only the username is provided (-a username), HTTPie will prompt for the password.
|
||||
If only the username is provided (-a username),
|
||||
HTTPie will prompt for the password.
|
||||
'''),
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
'--auth-type', choices=['basic', 'digest'],
|
||||
help=_('The authentication mechanism to be used. Defaults to "basic".')
|
||||
help=_('''
|
||||
The authentication mechanism to be used.
|
||||
Defaults to "basic".
|
||||
''')
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@ -179,8 +186,9 @@ parser.add_argument(
|
||||
help=_('''
|
||||
The HTTP method to be used for the request
|
||||
(GET, POST, PUT, DELETE, PATCH, ...).
|
||||
If this argument is omitted, then HTTPie will guess the HTTP method.
|
||||
If there is some data to be sent, then it will be POST, otherwise GET.
|
||||
If this argument is omitted, then HTTPie
|
||||
will guess the HTTP method. If there is some
|
||||
data to be sent, then it will be POST, otherwise GET.
|
||||
''')
|
||||
)
|
||||
parser.add_argument(
|
||||
@ -200,11 +208,12 @@ parser.add_argument(
|
||||
cliparse.SEP_FILES
|
||||
),
|
||||
help=_('''
|
||||
A key-value pair whose type is defined by the separator used. It can be an
|
||||
HTTP header (header:value),
|
||||
A key-value pair whose type is defined by the
|
||||
separator used. It can be an HTTP header (header:value),
|
||||
a data field to be used in the request body (field_name=value),
|
||||
a raw JSON data field (field_name:=value),
|
||||
or a file field (field_name@/path/to/file).
|
||||
You can use a backslash to escape a colliding separator in the field name.
|
||||
You can use a backslash to escape a colliding
|
||||
separator in the field name.
|
||||
''')
|
||||
)
|
||||
|
@ -155,9 +155,12 @@ class Parser(argparse.ArgumentParser):
|
||||
args.headers['Content-Type'] = content_type
|
||||
|
||||
def _validate_output_options(self, args):
|
||||
unknown_output_options = set(args.output_options) - set(OUTPUT_OPTIONS)
|
||||
if unknown_output_options:
|
||||
self.error('Unknown output options: %s' % ','.join(unknown_output_options))
|
||||
unknown = set(args.output_options) - set(OUTPUT_OPTIONS)
|
||||
if unknown:
|
||||
self.error(
|
||||
'Unknown output options: %s' %
|
||||
','.join(unknown)
|
||||
)
|
||||
|
||||
def _validate_auth_options(self, args):
|
||||
if args.auth_type and not args.auth:
|
||||
|
@ -45,7 +45,8 @@ class PrettyHttp(object):
|
||||
|
||||
def body(self, content, content_type):
|
||||
content_type = content_type.split(';')[0]
|
||||
application_match = re.match(application_content_type_re, content_type)
|
||||
application_match = re.match(application_content_type_re,
|
||||
content_type)
|
||||
if application_match:
|
||||
# Strip vendor and extensions from Content-Type
|
||||
vendor, extension = application_match.groups()
|
||||
|
@ -156,10 +156,15 @@ class AutoContentTypeAndAcceptHeadersTest(BaseTestCase):
|
||||
def test_POST_form_auto_Content_Type(self):
|
||||
r = http('-f', 'POST', 'http://httpbin.org/post')
|
||||
self.assertIn('HTTP/1.1 200', r)
|
||||
self.assertIn('"Content-Type": "application/x-www-form-urlencoded; charset=utf-8"', r)
|
||||
self.assertIn(
|
||||
'"Content-Type":'
|
||||
' "application/x-www-form-urlencoded; charset=utf-8"',
|
||||
r
|
||||
)
|
||||
|
||||
def test_POST_form_Content_Type_override(self):
|
||||
r = http('-f', 'POST', 'http://httpbin.org/post', 'Content-Type:application/xml')
|
||||
r = http('-f', 'POST', 'http://httpbin.org/post',
|
||||
'Content-Type:application/xml')
|
||||
self.assertIn('HTTP/1.1 200', r)
|
||||
self.assertIn('"Content-Type": "application/xml"', r)
|
||||
|
||||
@ -203,24 +208,46 @@ class PrettyFlagTest(BaseTestCase):
|
||||
self.assertNotIn(TERMINAL_COLOR_PRESENCE_CHECK, r)
|
||||
|
||||
def test_force_pretty(self):
|
||||
r = http('--pretty', 'GET', 'http://httpbin.org/get', stdout_isatty=False)
|
||||
r = http(
|
||||
'--pretty',
|
||||
'GET',
|
||||
'http://httpbin.org/get',
|
||||
stdout_isatty=False
|
||||
)
|
||||
self.assertIn(TERMINAL_COLOR_PRESENCE_CHECK, r)
|
||||
|
||||
def test_force_ugly(self):
|
||||
r = http('--ugly', 'GET', 'http://httpbin.org/get', stdout_isatty=True)
|
||||
r = http(
|
||||
'--ugly',
|
||||
'GET',
|
||||
'http://httpbin.org/get',
|
||||
stdout_isatty=True
|
||||
)
|
||||
self.assertNotIn(TERMINAL_COLOR_PRESENCE_CHECK, r)
|
||||
|
||||
|
||||
class VerboseFlagTest(BaseTestCase):
|
||||
|
||||
def test_verbose(self):
|
||||
r = http('--verbose', 'GET', 'http://httpbin.org/get', 'test-header:__test__')
|
||||
r = http(
|
||||
'--verbose',
|
||||
'GET',
|
||||
'http://httpbin.org/get',
|
||||
'test-header:__test__'
|
||||
)
|
||||
self.assertIn('HTTP/1.1 200', r)
|
||||
self.assertEqual(r.count('__test__'), 2)
|
||||
|
||||
def test_verbose_form(self):
|
||||
# https://github.com/jkbr/httpie/issues/53
|
||||
r = http('--verbose', '--form', 'POST', 'http://httpbin.org/post', 'foo=bar', 'baz=bar')
|
||||
r = http(
|
||||
'--verbose',
|
||||
'--form',
|
||||
'POST',
|
||||
'http://httpbin.org/post',
|
||||
'foo=bar',
|
||||
'baz=bar'
|
||||
)
|
||||
self.assertIn('HTTP/1.1 200', r)
|
||||
self.assertIn('foo=bar&baz=bar', r)
|
||||
|
||||
@ -253,7 +280,12 @@ class RequestBodyFromFilePathTest(BaseTestCase):
|
||||
self.assertIn('"Content-Type": "text/plain"', r)
|
||||
|
||||
def test_request_body_from_file_by_path_with_explicit_content_type(self):
|
||||
r = http('POST', 'http://httpbin.org/post', '@' + TEST_FILE_PATH, 'Content-Type:x-foo/bar')
|
||||
r = http(
|
||||
'POST',
|
||||
'http://httpbin.org/post',
|
||||
'@' + TEST_FILE_PATH,
|
||||
'Content-Type:x-foo/bar'
|
||||
)
|
||||
self.assertIn('HTTP/1.1 200', r)
|
||||
self.assertIn(TEST_FILE_CONTENT, r)
|
||||
self.assertIn('"Content-Type": "x-foo/bar"', r)
|
||||
@ -413,7 +445,8 @@ class ArgumentParserTestCase(unittest.TestCase):
|
||||
self.assertEquals(args.url, 'http://example.com/')
|
||||
self.assertEquals(
|
||||
args.items,
|
||||
[cliparse.KeyValue(key='data', value='field', sep='=', orig='data=field')])
|
||||
[cliparse.KeyValue(
|
||||
key='data', value='field', sep='=', orig='data=field')])
|
||||
|
||||
def test_guess_when_method_set_but_invalid_and_header_field(self):
|
||||
args = argparse.Namespace()
|
||||
@ -427,21 +460,25 @@ class ArgumentParserTestCase(unittest.TestCase):
|
||||
self.assertEquals(args.url, 'http://example.com/')
|
||||
self.assertEquals(
|
||||
args.items,
|
||||
[cliparse.KeyValue(key='test', value='header', sep=':', orig='test:header')])
|
||||
[cliparse.KeyValue(
|
||||
key='test', value='header', sep=':', orig='test:header')])
|
||||
|
||||
def test_guess_when_method_set_but_invalid_and_item_exists(self):
|
||||
args = argparse.Namespace()
|
||||
args.method = 'http://example.com/'
|
||||
args.url = 'new_item=a'
|
||||
args.items = [
|
||||
cliparse.KeyValue(key='old_item', value='b', sep='=', orig='old_item=b')
|
||||
cliparse.KeyValue(
|
||||
key='old_item', value='b', sep='=', orig='old_item=b')
|
||||
]
|
||||
|
||||
self.parser._guess_method(args)
|
||||
|
||||
self.assertEquals(args.items, [
|
||||
cliparse.KeyValue(key='new_item', value='a', sep='=', orig='new_item=a'),
|
||||
cliparse.KeyValue(key='old_item', value='b', sep='=', orig='old_item=b'),
|
||||
cliparse.KeyValue(
|
||||
key='new_item', value='a', sep='=', orig='new_item=a'),
|
||||
cliparse.KeyValue(key
|
||||
='old_item', value='b', sep='=', orig='old_item=b'),
|
||||
])
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user