mirror of
https://github.com/httpie/cli.git
synced 2025-08-17 22:59:42 +02:00
Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
8e96238323 | ||
|
8a9206eceb | ||
|
8ac3c5961c | ||
|
487c7a9221 | ||
|
6d65668355 | ||
|
3e5115e4a2 | ||
|
2b8b572f22 | ||
|
af737fd338 | ||
|
ee375b6942 | ||
|
becb63de9a | ||
|
86c8abc485 | ||
|
8f6bee9196 | ||
|
9c2c058ae5 | ||
|
6238b59e72 | ||
|
702c21aa91 | ||
|
aab5cd9da0 | ||
|
8c0f0b578c | ||
|
bb4881a873 | ||
|
3a1726b4ed | ||
|
e1fa57d228 | ||
|
bfc64bce21 | ||
|
595dc51b2d | ||
|
83fa772247 | ||
|
49a0fb6e0f | ||
|
41e822ca2f | ||
|
1124d68946 | ||
|
c3735d0422 | ||
|
364b91cbc4 | ||
|
c8e06b55e1 | ||
|
5acbc904b7 | ||
|
0c7c248dce | ||
|
caf60cbc65 | ||
|
2b0e642842 | ||
|
e25948f6a0 | ||
|
ec245a1e80 | ||
|
6259b5dd3b |
19
.travis.yml
19
.travis.yml
@@ -15,7 +15,8 @@ python:
|
|||||||
- pypy
|
- pypy
|
||||||
- 3.4
|
- 3.4
|
||||||
- 3.5
|
- 3.5
|
||||||
- pypy3
|
# Currently fails because of a Flask issue
|
||||||
|
# - pypy3
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
|
|
||||||
@@ -44,6 +45,11 @@ matrix:
|
|||||||
- TOXENV=py35
|
- TOXENV=py35
|
||||||
- BREW_INSTALL=python3
|
- BREW_INSTALL=python3
|
||||||
|
|
||||||
|
# Python Codestyle
|
||||||
|
- os: linux
|
||||||
|
python: 3.5
|
||||||
|
env: CODESTYLE=true
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- |
|
- |
|
||||||
if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
|
if [[ $TRAVIS_OS_NAME == 'osx' ]]; then
|
||||||
@@ -53,11 +59,20 @@ install:
|
|||||||
fi
|
fi
|
||||||
sudo pip install tox
|
sudo pip install tox
|
||||||
fi
|
fi
|
||||||
|
if [[ $CODESTYLE ]]; then
|
||||||
|
pip install pycodestyle
|
||||||
|
fi
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- |
|
- |
|
||||||
if [[ $TRAVIS_OS_NAME == 'linux' ]]; then
|
if [[ $TRAVIS_OS_NAME == 'linux' ]]; then
|
||||||
make
|
if [[ $CODESTYLE ]]; then
|
||||||
|
# 241 - multiple spaces after ‘,’
|
||||||
|
# 501 - line too long
|
||||||
|
pycodestyle --ignore=E241,E501
|
||||||
|
else
|
||||||
|
make
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
PATH="/usr/local/bin:$PATH" tox -e "$TOXENV"
|
PATH="/usr/local/bin:$PATH" tox -e "$TOXENV"
|
||||||
fi
|
fi
|
||||||
|
@@ -8,7 +8,7 @@ HTTPie authors
|
|||||||
Patches and ideas
|
Patches and ideas
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
`Complete list of contributors on GitHib <https://github.com/jkbrzt/httpie/graphs/contributors>`_
|
`Complete list of contributors on GitHub <https://github.com/jkbrzt/httpie/graphs/contributors>`_
|
||||||
|
|
||||||
* `Cláudia T. Delgado <https://github.com/claudiatd>`_ (logo)
|
* `Cláudia T. Delgado <https://github.com/claudiatd>`_ (logo)
|
||||||
* `Hank Gay <https://github.com/gthank>`_
|
* `Hank Gay <https://github.com/gthank>`_
|
||||||
|
@@ -10,6 +10,27 @@ This project adheres to `Semantic Versioning <http://semver.org/>`_.
|
|||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
|
|
||||||
|
`0.9.6`_ (2016-08-13)
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
* Added Python 3 as a dependency for Homebrew installations
|
||||||
|
to ensure some of the newer HTTP features work out of the box
|
||||||
|
for macOS users (starting with HTTPie 0.9.4.).
|
||||||
|
* Added the ability to unset a request header with ``Header:``, and send an
|
||||||
|
empty value with ``Header;``.
|
||||||
|
* Added ``--default-scheme <URL_SCHEME>`` to enable things like
|
||||||
|
``$ alias https='http --default-scheme=https``.
|
||||||
|
* Added ``-I`` as a shortcut for ``--ignore-stdin``.
|
||||||
|
* Added fish shell completion (located in ``extras/httpie-completion.fish``
|
||||||
|
in the Github repo).
|
||||||
|
* Updated ``requests`` to 2.10.0 so that SOCKS support can be added via
|
||||||
|
``pip install requests[socks]``.
|
||||||
|
* Changed the default JSON ``Accept`` header from ``application/json``
|
||||||
|
to ``application/json, */*``.
|
||||||
|
* Changed the pre-processing of request HTTP headers so that any leading
|
||||||
|
and trailing whitespace is removed.
|
||||||
|
|
||||||
|
|
||||||
`0.9.4`_ (2016-07-01)
|
`0.9.4`_ (2016-07-01)
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
@@ -289,4 +310,5 @@ This project adheres to `Semantic Versioning <http://semver.org/>`_.
|
|||||||
.. _0.9.2: https://github.com/jkbrzt/httpie/compare/0.9.1...0.9.2
|
.. _0.9.2: https://github.com/jkbrzt/httpie/compare/0.9.1...0.9.2
|
||||||
.. _0.9.3: https://github.com/jkbrzt/httpie/compare/0.9.2...0.9.3
|
.. _0.9.3: https://github.com/jkbrzt/httpie/compare/0.9.2...0.9.3
|
||||||
.. _0.9.4: https://github.com/jkbrzt/httpie/compare/0.9.3...0.9.4
|
.. _0.9.4: https://github.com/jkbrzt/httpie/compare/0.9.3...0.9.4
|
||||||
|
.. _0.9.6: https://github.com/jkbrzt/httpie/compare/0.9.3...0.9.6
|
||||||
.. _1.0.0-dev: https://github.com/jkbrzt/httpie/compare/0.9.4...master
|
.. _1.0.0-dev: https://github.com/jkbrzt/httpie/compare/0.9.4...master
|
||||||
|
10
Makefile
10
Makefile
@@ -57,10 +57,12 @@ test-bdist-wheel: clean uninstall-httpie
|
|||||||
test-all: uninstall-all clean init test test-tox test-dist
|
test-all: uninstall-all clean init test test-tox test-dist
|
||||||
|
|
||||||
|
|
||||||
publish: test-all
|
publish: test-all publish-no-test
|
||||||
|
|
||||||
|
publish-no-test:
|
||||||
@echo $(TAG)Testing wheel build an installation$(END)
|
@echo $(TAG)Testing wheel build an installation$(END)
|
||||||
@echo "$(VERSION)"
|
@echo "$(VERSION)"
|
||||||
@echo "$(VERSION)" | grep -q "dev" && echo "!!!Not publishing dev version!!!" && exit 1
|
@echo "$(VERSION)" | grep -q "dev" && echo '!!!Not publishing dev version!!!' && exit 1 || echo ok
|
||||||
python setup.py register
|
python setup.py register
|
||||||
python setup.py sdist upload
|
python setup.py sdist upload
|
||||||
python setup.py bdist_wheel upload
|
python setup.py bdist_wheel upload
|
||||||
@@ -92,3 +94,7 @@ uninstall-all: uninstall-httpie
|
|||||||
|
|
||||||
@echo $(TAG)Uninstalling development requirements$(END)
|
@echo $(TAG)Uninstalling development requirements$(END)
|
||||||
- pip uninstall --yes -r $(REQUIREMENTS)
|
- pip uninstall --yes -r $(REQUIREMENTS)
|
||||||
|
|
||||||
|
|
||||||
|
homebrew-formula-vars:
|
||||||
|
extras/get-homebrew-formula-vars.py
|
||||||
|
139
README.rst
139
README.rst
@@ -35,7 +35,7 @@ HTTPie is written in Python, and under the hood it uses the excellent
|
|||||||
|
|
||||||
|
|
||||||
=============
|
=============
|
||||||
Main Features
|
Main features
|
||||||
=============
|
=============
|
||||||
|
|
||||||
* Expressive and intuitive syntax
|
* Expressive and intuitive syntax
|
||||||
@@ -117,6 +117,17 @@ The **latest development version** can be installed directly from GitHub:
|
|||||||
$ pip install --upgrade https://github.com/jkbrzt/httpie/archive/master.tar.gz
|
$ pip install --upgrade https://github.com/jkbrzt/httpie/archive/master.tar.gz
|
||||||
|
|
||||||
|
|
||||||
|
--------------
|
||||||
|
Python version
|
||||||
|
--------------
|
||||||
|
|
||||||
|
Although Python 2.6 and 2.7 are supported as well, it is recommended to install
|
||||||
|
HTTPie against the latest Python 3.x whenever possible. That will ensure that
|
||||||
|
some of the newer HTTP features, such as `SNI (Server Name Indication)`_,
|
||||||
|
work out of the box.
|
||||||
|
Python 3 is the default for Homebrew installations starting with version 0.9.4.
|
||||||
|
To see which version HTTPie uses, run ``http --debug``.
|
||||||
|
|
||||||
|
|
||||||
=====
|
=====
|
||||||
Usage
|
Usage
|
||||||
@@ -172,7 +183,7 @@ with `authentication`_:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http -a USERNAME POST https://api.github.com/repos/jkbrzt/httpie/issues/83/comments body='HTTPie is awesome!'
|
$ http -a USERNAME POST https://api.github.com/repos/jkbrzt/httpie/issues/83/comments body='HTTPie is awesome! :heart:'
|
||||||
|
|
||||||
|
|
||||||
Upload a file using `redirected input`_:
|
Upload a file using `redirected input`_:
|
||||||
@@ -220,7 +231,7 @@ advanced usage, and also features additional examples.*
|
|||||||
|
|
||||||
|
|
||||||
===========
|
===========
|
||||||
HTTP Method
|
HTTP method
|
||||||
===========
|
===========
|
||||||
|
|
||||||
The name of the HTTP method comes right before the URL argument:
|
The name of the HTTP method comes right before the URL argument:
|
||||||
@@ -302,9 +313,16 @@ this command:
|
|||||||
|
|
||||||
GET /?search=HTTPie+logo&tbm=isch HTTP/1.1
|
GET /?search=HTTPie+logo&tbm=isch HTTP/1.1
|
||||||
|
|
||||||
|
You can use the ``--default-scheme <URL_SCHEME>`` option to create
|
||||||
|
shortcuts for other protocols than HTTP:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ alias https='http --default-scheme=https'
|
||||||
|
|
||||||
|
|
||||||
=============
|
=============
|
||||||
Request Items
|
Request items
|
||||||
=============
|
=============
|
||||||
|
|
||||||
There are a few different *request item* types that provide a
|
There are a few different *request item* types that provide a
|
||||||
@@ -385,13 +403,13 @@ both of which can be overwritten:
|
|||||||
|
|
||||||
================ =======================================
|
================ =======================================
|
||||||
``Content-Type`` ``application/json``
|
``Content-Type`` ``application/json``
|
||||||
``Accept`` ``application/json``
|
``Accept`` ``application/json, */*``
|
||||||
================ =======================================
|
================ =======================================
|
||||||
|
|
||||||
You can use ``--json, -j`` to explicitly set ``Accept``
|
You can use ``--json, -j`` to explicitly set ``Accept``
|
||||||
to ``application/json`` regardless of whether you are sending data
|
to ``application/json`` regardless of whether you are sending data
|
||||||
(it's a shortcut for setting the header via the usual header notation –
|
(it's a shortcut for setting the header via the usual header notation –
|
||||||
``http url Accept:application/json``). Additionally,
|
``http url Accept:application/json, */*``). Additionally,
|
||||||
HTTPie will try to detect JSON responses even when the
|
HTTPie will try to detect JSON responses even when the
|
||||||
``Content-Type`` is incorrectly ``text/plain`` or unknown.
|
``Content-Type`` is incorrectly ``text/plain`` or unknown.
|
||||||
|
|
||||||
@@ -404,7 +422,7 @@ Simple example:
|
|||||||
.. code-block:: http
|
.. code-block:: http
|
||||||
|
|
||||||
PUT / HTTP/1.1
|
PUT / HTTP/1.1
|
||||||
Accept: application/json
|
Accept: application/json, */*
|
||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Host: example.org
|
Host: example.org
|
||||||
@@ -431,7 +449,7 @@ fields using ``=@`` and ``:=@``:
|
|||||||
.. code-block:: http
|
.. code-block:: http
|
||||||
|
|
||||||
PUT /person/1 HTTP/1.1
|
PUT /person/1 HTTP/1.1
|
||||||
Accept: application/json
|
Accept: application/json, */*
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Host: api.example.com
|
Host: api.example.com
|
||||||
|
|
||||||
@@ -471,7 +489,7 @@ via the `config`_ file.
|
|||||||
|
|
||||||
|
|
||||||
-------------
|
-------------
|
||||||
Regular Forms
|
Regular forms
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
@@ -489,7 +507,7 @@ Regular Forms
|
|||||||
|
|
||||||
|
|
||||||
-----------------
|
-----------------
|
||||||
File Upload Forms
|
File upload forms
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
If one or more file fields is present, the serialization and content type is
|
If one or more file fields is present, the serialization and content type is
|
||||||
@@ -515,7 +533,7 @@ Note that ``@`` is used to simulate a file upload form field, whereas
|
|||||||
|
|
||||||
|
|
||||||
============
|
============
|
||||||
HTTP Headers
|
HTTP headers
|
||||||
============
|
============
|
||||||
|
|
||||||
To set custom headers you can use the ``Header:Value`` notation:
|
To set custom headers you can use the ``Header:Value`` notation:
|
||||||
@@ -549,7 +567,23 @@ There are a couple of default headers that HTTPie sets:
|
|||||||
Host: <taken-from-URL>
|
Host: <taken-from-URL>
|
||||||
|
|
||||||
|
|
||||||
Any of the default headers can be overwritten.
|
Any of the default headers can be overwritten and some of them unset.
|
||||||
|
|
||||||
|
To unset a header that has already been specified (such a one of the default
|
||||||
|
headers), use ``Header:``:
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ http httpbin.org/headers Accept: User-Agent:
|
||||||
|
|
||||||
|
|
||||||
|
To send a header with an empty value, use ``Header;``:
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ http httpbin.org/headers 'Header;'
|
||||||
|
|
||||||
|
|
||||||
==============
|
==============
|
||||||
@@ -614,7 +648,7 @@ Authorization information from your ``~/.netrc`` file is honored as well:
|
|||||||
|
|
||||||
|
|
||||||
------------
|
------------
|
||||||
Auth Plugins
|
Auth plugins
|
||||||
------------
|
------------
|
||||||
|
|
||||||
* `httpie-oauth <https://github.com/jkbrzt/httpie-oauth>`_: OAuth
|
* `httpie-oauth <https://github.com/jkbrzt/httpie-oauth>`_: OAuth
|
||||||
@@ -628,7 +662,7 @@ Auth Plugins
|
|||||||
|
|
||||||
|
|
||||||
==============
|
==============
|
||||||
HTTP Redirects
|
HTTP redirects
|
||||||
==============
|
==============
|
||||||
|
|
||||||
By default, HTTP redirects are not followed and only the first
|
By default, HTTP redirects are not followed and only the first
|
||||||
@@ -679,6 +713,24 @@ In your ``~/.bash_profile``:
|
|||||||
export NO_PROXY=localhost,example.com
|
export NO_PROXY=localhost,example.com
|
||||||
|
|
||||||
|
|
||||||
|
-----
|
||||||
|
SOCKS
|
||||||
|
-----
|
||||||
|
|
||||||
|
To enable SOCKS proxy support please install ``requests[socks]`` using ``pip``:
|
||||||
|
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ pip install -U requests[socks]
|
||||||
|
|
||||||
|
Usage is the same as for other types of `proxies`_:
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
$ http --proxy=http:socks5://user:pass@host:port --proxy=https:socks5://user:pass@host:port example.org
|
||||||
|
|
||||||
|
|
||||||
=====
|
=====
|
||||||
HTTPS
|
HTTPS
|
||||||
=====
|
=====
|
||||||
@@ -750,8 +802,8 @@ available set of protocols may vary depending on your OpenSSL installation.)
|
|||||||
SNI (Server Name Indication)
|
SNI (Server Name Indication)
|
||||||
----------------------------
|
----------------------------
|
||||||
|
|
||||||
If you use HTTPie with Python < 2.7.9
|
If you use HTTPie with `Python version`_ lower than 2.7.9
|
||||||
(can be verified with ``python --version``) and need to talk to servers that
|
(can be verified with ``http --debug``) and need to talk to servers that
|
||||||
use **SNI (Server Name Indication)** you need to install some additional
|
use **SNI (Server Name Indication)** you need to install some additional
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|
||||||
@@ -768,7 +820,7 @@ You can use the following command to test SNI support:
|
|||||||
|
|
||||||
|
|
||||||
==============
|
==============
|
||||||
Output Options
|
Output options
|
||||||
==============
|
==============
|
||||||
|
|
||||||
By default, HTTPie only outputs the final response and the whole response
|
By default, HTTPie only outputs the final response and the whole response
|
||||||
@@ -791,7 +843,7 @@ documentation examples:
|
|||||||
|
|
||||||
$ http --verbose PUT httpbin.org/put hello=world
|
$ http --verbose PUT httpbin.org/put hello=world
|
||||||
PUT /put HTTP/1.1
|
PUT /put HTTP/1.1
|
||||||
Accept: application/json
|
Accept: application/json, */*
|
||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Host: httpbin.org
|
Host: httpbin.org
|
||||||
@@ -835,11 +887,11 @@ Print request and response headers:
|
|||||||
|
|
||||||
|
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
Viewing Intermediary Requests/Responses
|
Viewing intermediary requests/responses
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
To see *all* the HTTP communication, i.e. the final request/resposne as
|
To see *all* the HTTP communication, i.e. the final request/response as
|
||||||
well as any possible intermediary requests/responses, use the **``--all``**
|
well as any possible intermediary requests/responses, use the ``--all``
|
||||||
option. The intermediary HTTP communication include followed redirects
|
option. The intermediary HTTP communication include followed redirects
|
||||||
(with ``--follow``), the first unauthorized request when HTTP digest
|
(with ``--follow``), the first unauthorized request when HTTP digest
|
||||||
authentication is used (``--auth=digest``), etc.
|
authentication is used (``--auth=digest``), etc.
|
||||||
@@ -863,7 +915,7 @@ arguments as ``--print, -p`` but applies to the intermediary requests only.
|
|||||||
|
|
||||||
|
|
||||||
-------------------------
|
-------------------------
|
||||||
Conditional Body Download
|
Conditional body download
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
As an optimization, the response body is downloaded from the server
|
As an optimization, the response body is downloaded from the server
|
||||||
@@ -961,9 +1013,9 @@ To prevent HTTPie from reading ``stdin`` data you can use the
|
|||||||
``--ignore-stdin`` option.
|
``--ignore-stdin`` option.
|
||||||
|
|
||||||
|
|
||||||
-------------------------
|
----------------------------
|
||||||
Body Data From a Filename
|
Request data from a filename
|
||||||
-------------------------
|
----------------------------
|
||||||
|
|
||||||
**An alternative to redirected** ``stdin`` is specifying a filename (as
|
**An alternative to redirected** ``stdin`` is specifying a filename (as
|
||||||
``@/path/to/file``) whose content is used as if it came from ``stdin``.
|
``@/path/to/file``) whose content is used as if it came from ``stdin``.
|
||||||
@@ -979,7 +1031,7 @@ verbatim contents of that XML file with ``Content-Type: application/xml``:
|
|||||||
|
|
||||||
|
|
||||||
===============
|
===============
|
||||||
Terminal Output
|
Terminal output
|
||||||
===============
|
===============
|
||||||
|
|
||||||
HTTPie does several things by default in order to make its terminal output
|
HTTPie does several things by default in order to make its terminal output
|
||||||
@@ -987,7 +1039,7 @@ easy to read.
|
|||||||
|
|
||||||
|
|
||||||
---------------------
|
---------------------
|
||||||
Colors and Formatting
|
Colors and formatting
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
Syntax highlighting is applied to HTTP headers and bodies (where it makes
|
Syntax highlighting is applied to HTTP headers and bodies (where it makes
|
||||||
@@ -1042,7 +1094,7 @@ You will nearly instantly see something like this:
|
|||||||
|
|
||||||
|
|
||||||
=================
|
=================
|
||||||
Redirected Output
|
Redirected output
|
||||||
=================
|
=================
|
||||||
|
|
||||||
HTTPie uses **different defaults** for redirected output than for
|
HTTPie uses **different defaults** for redirected output than for
|
||||||
@@ -1093,7 +1145,7 @@ by adding the following to your ``~/.bash_profile``:
|
|||||||
|
|
||||||
|
|
||||||
=============
|
=============
|
||||||
Download Mode
|
Download mode
|
||||||
=============
|
=============
|
||||||
|
|
||||||
HTTPie features a download mode in which it acts similarly to ``wget``.
|
HTTPie features a download mode in which it acts similarly to ``wget``.
|
||||||
@@ -1150,7 +1202,7 @@ Other notes:
|
|||||||
|
|
||||||
|
|
||||||
==================
|
==================
|
||||||
Streamed Responses
|
Streamed responses
|
||||||
==================
|
==================
|
||||||
|
|
||||||
Responses are downloaded and printed in chunks, which allows for streaming
|
Responses are downloaded and printed in chunks, which allows for streaming
|
||||||
@@ -1197,7 +1249,7 @@ ones starting with ``Content-`` or ``If-``), authorization, and cookies
|
|||||||
to the same host.
|
to the same host.
|
||||||
|
|
||||||
--------------
|
--------------
|
||||||
Named Sessions
|
Named sessions
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
Create a new session named ``user1`` for ``example.org``:
|
Create a new session named ``user1`` for ``example.org``:
|
||||||
@@ -1228,7 +1280,7 @@ Named sessions' data is stored in JSON files in the directory
|
|||||||
(``%APPDATA%\httpie\sessions\<host>\<name>.json`` on Windows).
|
(``%APPDATA%\httpie\sessions\<host>\<name>.json`` on Windows).
|
||||||
|
|
||||||
------------------
|
------------------
|
||||||
Anonymous Sessions
|
Anonymous sessions
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
Instead of a name, you can also directly specify a path to a session file. This
|
Instead of a name, you can also directly specify a path to a session file. This
|
||||||
@@ -1322,7 +1374,7 @@ Also, the ``--timeout`` option allows to overwrite the default 30s timeout:
|
|||||||
|
|
||||||
|
|
||||||
================
|
================
|
||||||
Interface Design
|
Interface design
|
||||||
================
|
================
|
||||||
|
|
||||||
The syntax of the command arguments closely corresponds to the actual HTTP
|
The syntax of the command arguments closely corresponds to the actual HTTP
|
||||||
@@ -1396,28 +1448,39 @@ have contributed.
|
|||||||
Logo
|
Logo
|
||||||
====
|
====
|
||||||
|
|
||||||
Please see `claudiatd/httpie-artwork`_
|
See `claudiatd/httpie-artwork`_
|
||||||
|
|
||||||
|
|
||||||
==========
|
==========
|
||||||
Contribute
|
Contribute
|
||||||
==========
|
==========
|
||||||
|
|
||||||
Please see `CONTRIBUTING <https://github.com/jkbrzt/httpie/blob/master/CONTRIBUTING.rst>`_.
|
See `CONTRIBUTING <https://github.com/jkbrzt/httpie/blob/master/CONTRIBUTING.rst>`_.
|
||||||
|
|
||||||
|
|
||||||
==========
|
==========
|
||||||
Change Log
|
Change log
|
||||||
==========
|
==========
|
||||||
|
|
||||||
Please see `CHANGELOG <https://github.com/jkbrzt/httpie/blob/master/CHANGELOG.rst>`_.
|
See `CHANGELOG <https://github.com/jkbrzt/httpie/blob/master/CHANGELOG.rst>`_.
|
||||||
|
|
||||||
|
|
||||||
=======
|
=======
|
||||||
Licence
|
Licence
|
||||||
=======
|
=======
|
||||||
|
|
||||||
Please see `LICENSE <https://github.com/jkbrzt/httpie/blob/master/LICENSE>`_.
|
See `LICENSE <https://github.com/jkbrzt/httpie/blob/master/LICENSE>`_.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
================
|
||||||
|
Related projects
|
||||||
|
================
|
||||||
|
|
||||||
|
* `jq <https://stedolan.github.io/jq/>`_ — a command-line JSON processor that
|
||||||
|
works great in conjunction with HTTPie
|
||||||
|
* `http-prompt <https://github.com/eliangcs/http-prompt>`_ — an interactive
|
||||||
|
shell for HTTPie featuring autocomplete and command syntax highlighting
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
55
extras/get-homebrew-formula-vars.py
Executable file
55
extras/get-homebrew-formula-vars.py
Executable file
@@ -0,0 +1,55 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
"""
|
||||||
|
Generate URLs and file hashes to be included in the Homebrew formula
|
||||||
|
after a new release of HTTPie is published on PyPi.
|
||||||
|
|
||||||
|
https://github.com/Homebrew/homebrew-core/blob/master/Formula/httpie.rb
|
||||||
|
|
||||||
|
"""
|
||||||
|
import hashlib
|
||||||
|
import requests
|
||||||
|
|
||||||
|
|
||||||
|
PACKAGES = [
|
||||||
|
'httpie',
|
||||||
|
'requests',
|
||||||
|
'pygments',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_info(package_name):
|
||||||
|
api_url = 'https://pypi.python.org/pypi/{}/json'.format(package_name)
|
||||||
|
resp = requests.get(api_url).json()
|
||||||
|
hasher = hashlib.sha256()
|
||||||
|
for release in resp['urls']:
|
||||||
|
download_url = release['url']
|
||||||
|
if download_url.endswith('.tar.gz'):
|
||||||
|
hasher.update(requests.get(download_url).content)
|
||||||
|
return {
|
||||||
|
'name': package_name,
|
||||||
|
'url': download_url,
|
||||||
|
'sha256': hasher.hexdigest(),
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
raise RuntimeError(
|
||||||
|
'{}: download not found: {}'.format(package_name, resp))
|
||||||
|
|
||||||
|
|
||||||
|
packages = {
|
||||||
|
package_name: get_info(package_name) for package_name in PACKAGES
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
httpie_info = packages.pop('httpie')
|
||||||
|
print("""
|
||||||
|
url "{url}"
|
||||||
|
sha256 "{sha256}"
|
||||||
|
""".format(**httpie_info))
|
||||||
|
|
||||||
|
|
||||||
|
for package_info in packages.values():
|
||||||
|
print("""
|
||||||
|
resource "{name}" do
|
||||||
|
url "{url}"
|
||||||
|
sha256 "{sha256}"
|
||||||
|
end""".format(**package_info))
|
60
extras/httpie-completion.fish
Normal file
60
extras/httpie-completion.fish
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
function __fish_httpie_auth_types
|
||||||
|
echo "basic"\t"Basic HTTP auth"
|
||||||
|
echo "digest"\t"Digest HTTP auth"
|
||||||
|
end
|
||||||
|
|
||||||
|
function __fish_httpie_styles
|
||||||
|
echo "autumn"
|
||||||
|
echo "borland"
|
||||||
|
echo "bw"
|
||||||
|
echo "colorful"
|
||||||
|
echo "default"
|
||||||
|
echo "emacs"
|
||||||
|
echo "friendly"
|
||||||
|
echo "fruity"
|
||||||
|
echo "igor"
|
||||||
|
echo "manni"
|
||||||
|
echo "monokai"
|
||||||
|
echo "murphy"
|
||||||
|
echo "native"
|
||||||
|
echo "paraiso-dark"
|
||||||
|
echo "paraiso-light"
|
||||||
|
echo "pastie"
|
||||||
|
echo "perldoc"
|
||||||
|
echo "rrt"
|
||||||
|
echo "solarized"
|
||||||
|
echo "tango"
|
||||||
|
echo "trac"
|
||||||
|
echo "vim"
|
||||||
|
echo "vs"
|
||||||
|
echo "xcode"
|
||||||
|
end
|
||||||
|
|
||||||
|
complete -x -c http -s s -l style -d 'Output coloring style (default is "monokai")' -A -a "autumn borland bw colorful default emacs friendly fruity igor manni monokai murphy native paraiso-dark paraiso-light pastie perldoc rrt solarized tango trac vim vs xcode"
|
||||||
|
complete -c http -s f -l form -d 'Data items from the command line are serialized as form fields'
|
||||||
|
complete -c http -s j -l json -d '(default) Data items from the command line are serialized as a JSON object'
|
||||||
|
complete -x -c http -l pretty -d 'Controls output processing' -a "all colors format none" -A
|
||||||
|
complete -x -c http -s s -l style -d 'Output coloring style (default is "monokai")' -A -a "autumn borland bw colorful default emacs friendly fruity igor manni monokai murphy native paraiso-dark paraiso-light pastie perldoc rrt solarized tango trac vim vs xcode"
|
||||||
|
complete -x -c http -s p -l print -d 'String specifying what the output should contain'
|
||||||
|
complete -c http -s v -l verbose -d 'Print the whole request as well as the response'
|
||||||
|
complete -c http -s h -l headers -d 'Print only the response headers'
|
||||||
|
complete -c http -s b -l body -d 'Print only the response body'
|
||||||
|
complete -c http -s S -l stream -d 'Always stream the output by line'
|
||||||
|
complete -c http -s o -l output -d 'Save output to FILE'
|
||||||
|
complete -c http -s d -l download -d 'Do not print the response body to stdout'
|
||||||
|
complete -c http -s c -l continue -d 'Resume an interrupted download'
|
||||||
|
complete -x -c http -l session -d 'Create, or reuse and update a session'
|
||||||
|
complete -x -c http -s a -l auth -d 'If only the username is provided (-a username), HTTPie will prompt for the password'
|
||||||
|
complete -x -c http -l auth-type -d 'The authentication mechanism to be used' -a '(__fish_httpie_auth_types)' -A
|
||||||
|
complete -x -c http -l proxy -d 'String mapping protocol to the URL of the proxy'
|
||||||
|
complete -c http -l follow -d 'Allow full redirects'
|
||||||
|
complete -x -c http -l verify -d 'SSL cert verification'
|
||||||
|
complete -c http -l cert -d 'SSL cert'
|
||||||
|
complete -c http -l cert-key -d 'Private SSL cert key'
|
||||||
|
complete -x -c http -l timeout -d 'Connection timeout in seconds'
|
||||||
|
complete -c http -l check-status -d 'Error with non-200 HTTP status code'
|
||||||
|
complete -c http -l ignore-stdin -d 'Do not attempt to read stdin'
|
||||||
|
complete -c http -l help -d 'Show help'
|
||||||
|
complete -c http -l version -d 'Show version'
|
||||||
|
complete -c http -l traceback -d 'Prints exception traceback should one occur'
|
||||||
|
complete -c http -l debug -d 'Show debugging information'
|
@@ -3,7 +3,7 @@ HTTPie - a CLI, cURL-like tool for humans.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
__author__ = 'Jakub Roztocil'
|
__author__ = 'Jakub Roztocil'
|
||||||
__version__ = '0.9.4'
|
__version__ = '0.9.6'
|
||||||
__licence__ = 'BSD'
|
__licence__ = 'BSD'
|
||||||
|
|
||||||
|
|
||||||
|
@@ -89,6 +89,7 @@ positional.add_argument(
|
|||||||
metavar='URL',
|
metavar='URL',
|
||||||
help="""
|
help="""
|
||||||
The scheme defaults to 'http://' if the URL does not include one.
|
The scheme defaults to 'http://' if the URL does not include one.
|
||||||
|
(You can override this with: --default-scheme=https)
|
||||||
|
|
||||||
You can also use a shorthand for localhost
|
You can also use a shorthand for localhost
|
||||||
|
|
||||||
@@ -212,7 +213,7 @@ output_processing.add_argument(
|
|||||||
""".format(
|
""".format(
|
||||||
default=DEFAULT_STYLE,
|
default=DEFAULT_STYLE,
|
||||||
available='\n'.join(
|
available='\n'.join(
|
||||||
'{0}{1}'.format(8*' ', line.strip())
|
'{0}{1}'.format(8 * ' ', line.strip())
|
||||||
for line in wrap(', '.join(sorted(AVAILABLE_STYLES)), 60)
|
for line in wrap(', '.join(sorted(AVAILABLE_STYLES)), 60)
|
||||||
).rstrip(),
|
).rstrip(),
|
||||||
)
|
)
|
||||||
@@ -576,7 +577,7 @@ ssl.add_argument(
|
|||||||
troubleshooting = parser.add_argument_group(title='Troubleshooting')
|
troubleshooting = parser.add_argument_group(title='Troubleshooting')
|
||||||
|
|
||||||
troubleshooting.add_argument(
|
troubleshooting.add_argument(
|
||||||
'--ignore-stdin',
|
'--ignore-stdin', '-I',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
default=False,
|
default=False,
|
||||||
help="""
|
help="""
|
||||||
@@ -611,6 +612,14 @@ troubleshooting.add_argument(
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
|
troubleshooting.add_argument(
|
||||||
|
'--default-scheme',
|
||||||
|
default="http",
|
||||||
|
help="""
|
||||||
|
The default scheme to use if not specified in the URL.
|
||||||
|
|
||||||
|
"""
|
||||||
|
)
|
||||||
troubleshooting.add_argument(
|
troubleshooting.add_argument(
|
||||||
'--debug',
|
'--debug',
|
||||||
action='store_true',
|
action='store_true',
|
||||||
|
@@ -1,6 +1,5 @@
|
|||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
from pprint import pformat
|
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from requests.adapters import HTTPAdapter
|
from requests.adapters import HTTPAdapter
|
||||||
@@ -24,8 +23,9 @@ except AttributeError:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
FORM = 'application/x-www-form-urlencoded; charset=utf-8'
|
FORM_CONTENT_TYPE = 'application/x-www-form-urlencoded; charset=utf-8'
|
||||||
JSON = 'application/json'
|
JSON_CONTENT_TYPE = 'application/json'
|
||||||
|
JSON_ACCEPT = '{0}, */*'.format(JSON_CONTENT_TYPE)
|
||||||
DEFAULT_UA = 'HTTPie/%s' % __version__
|
DEFAULT_UA = 'HTTPie/%s' % __version__
|
||||||
|
|
||||||
|
|
||||||
@@ -85,13 +85,23 @@ def dump_request(kwargs):
|
|||||||
% repr_dict_nice(kwargs))
|
% repr_dict_nice(kwargs))
|
||||||
|
|
||||||
|
|
||||||
def encode_headers(headers):
|
def finalize_headers(headers):
|
||||||
# This allows for unicode headers which is non-standard but practical.
|
final_headers = {}
|
||||||
# See: https://github.com/jkbrzt/httpie/issues/212
|
for name, value in headers.items():
|
||||||
return dict(
|
if value is not None:
|
||||||
(name, value.encode('utf8') if isinstance(value, str) else value)
|
|
||||||
for name, value in headers.items()
|
# >leading or trailing LWS MAY be removed without
|
||||||
)
|
# >changing the semantics of the field value"
|
||||||
|
# -https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html
|
||||||
|
# Also, requests raises `InvalidHeader` for leading spaces.
|
||||||
|
value = value.strip()
|
||||||
|
|
||||||
|
if isinstance(value, str):
|
||||||
|
# See: https://github.com/jkbrzt/httpie/issues/212
|
||||||
|
value = value.encode('utf8')
|
||||||
|
|
||||||
|
final_headers[name] = value
|
||||||
|
return final_headers
|
||||||
|
|
||||||
|
|
||||||
def get_default_headers(args):
|
def get_default_headers(args):
|
||||||
@@ -100,16 +110,15 @@ def get_default_headers(args):
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto_json = args.data and not args.form
|
auto_json = args.data and not args.form
|
||||||
# FIXME: Accept is set to JSON with `http url @./file.txt`.
|
|
||||||
if args.json or auto_json:
|
if args.json or auto_json:
|
||||||
default_headers['Accept'] = 'application/json'
|
default_headers['Accept'] = JSON_ACCEPT
|
||||||
if args.json or (auto_json and args.data):
|
if args.json or (auto_json and args.data):
|
||||||
default_headers['Content-Type'] = JSON
|
default_headers['Content-Type'] = JSON_CONTENT_TYPE
|
||||||
|
|
||||||
elif args.form and not args.files:
|
elif args.form and not args.files:
|
||||||
# If sending files, `requests` will set
|
# If sending files, `requests` will set
|
||||||
# the `Content-Type` for us.
|
# the `Content-Type` for us.
|
||||||
default_headers['Content-Type'] = FORM
|
default_headers['Content-Type'] = FORM_CONTENT_TYPE
|
||||||
return default_headers
|
return default_headers
|
||||||
|
|
||||||
|
|
||||||
@@ -134,7 +143,7 @@ def get_requests_kwargs(args, base_headers=None):
|
|||||||
if base_headers:
|
if base_headers:
|
||||||
headers.update(base_headers)
|
headers.update(base_headers)
|
||||||
headers.update(args.headers)
|
headers.update(args.headers)
|
||||||
headers = encode_headers(headers)
|
headers = finalize_headers(headers)
|
||||||
|
|
||||||
credentials = None
|
credentials = None
|
||||||
if args.auth:
|
if args.auth:
|
||||||
|
@@ -14,10 +14,14 @@ is_windows = 'win32' in str(sys.platform).lower()
|
|||||||
|
|
||||||
|
|
||||||
if is_py2:
|
if is_py2:
|
||||||
|
# noinspection PyShadowingBuiltins
|
||||||
bytes = str
|
bytes = str
|
||||||
|
# noinspection PyUnresolvedReferences,PyShadowingBuiltins
|
||||||
str = unicode
|
str = unicode
|
||||||
elif is_py3:
|
elif is_py3:
|
||||||
|
# noinspection PyShadowingBuiltins
|
||||||
str = str
|
str = str
|
||||||
|
# noinspection PyShadowingBuiltins
|
||||||
bytes = bytes
|
bytes = bytes
|
||||||
|
|
||||||
|
|
||||||
@@ -32,7 +36,7 @@ try: # pragma: no cover
|
|||||||
# noinspection PyCompatibility
|
# noinspection PyCompatibility
|
||||||
from urllib.request import urlopen
|
from urllib.request import urlopen
|
||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
# noinspection PyCompatibility
|
# noinspection PyCompatibility,PyUnresolvedReferences
|
||||||
from urllib2 import urlopen
|
from urllib2 import urlopen
|
||||||
|
|
||||||
try: # pragma: no cover
|
try: # pragma: no cover
|
||||||
@@ -40,10 +44,10 @@ try: # pragma: no cover
|
|||||||
except ImportError: # pragma: no cover
|
except ImportError: # pragma: no cover
|
||||||
# Python 2.6 OrderedDict class, needed for headers, parameters, etc .###
|
# Python 2.6 OrderedDict class, needed for headers, parameters, etc .###
|
||||||
# <https://pypi.python.org/pypi/ordereddict/1.1>
|
# <https://pypi.python.org/pypi/ordereddict/1.1>
|
||||||
# noinspection PyCompatibility
|
# noinspection PyCompatibility,PyUnresolvedReferences
|
||||||
from UserDict import DictMixin
|
from UserDict import DictMixin
|
||||||
|
|
||||||
# noinspection PyShadowingBuiltins
|
# noinspection PyShadowingBuiltins,PyCompatibility
|
||||||
class OrderedDict(dict, DictMixin):
|
class OrderedDict(dict, DictMixin):
|
||||||
# Copyright (c) 2009 Raymond Hettinger
|
# Copyright (c) 2009 Raymond Hettinger
|
||||||
#
|
#
|
||||||
@@ -115,6 +119,7 @@ except ImportError: # pragma: no cover
|
|||||||
if not self:
|
if not self:
|
||||||
raise KeyError('dictionary is empty')
|
raise KeyError('dictionary is empty')
|
||||||
if last:
|
if last:
|
||||||
|
# noinspection PyUnresolvedReferences
|
||||||
key = reversed(self).next()
|
key = reversed(self).next()
|
||||||
else:
|
else:
|
||||||
key = iter(self).next()
|
key = iter(self).next()
|
||||||
|
@@ -243,7 +243,7 @@ def main(args=sys.argv[1:], env=Environment(), custom_log_error=None):
|
|||||||
except requests.TooManyRedirects:
|
except requests.TooManyRedirects:
|
||||||
exit_status = ExitStatus.ERROR_TOO_MANY_REDIRECTS
|
exit_status = ExitStatus.ERROR_TOO_MANY_REDIRECTS
|
||||||
log_error('Too many redirects (--max-redirects=%s).',
|
log_error('Too many redirects (--max-redirects=%s).',
|
||||||
parsed_args.max_redirects)
|
parsed_args.max_redirects)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# TODO: Further distinction between expected and unexpected errors.
|
# TODO: Further distinction between expected and unexpected errors.
|
||||||
msg = str(e)
|
msg = str(e)
|
||||||
|
@@ -78,15 +78,15 @@ def parse_content_range(content_range, resumed_from):
|
|||||||
# last-byte-pos value, is invalid. The recipient of an invalid
|
# last-byte-pos value, is invalid. The recipient of an invalid
|
||||||
# byte-content-range- spec MUST ignore it and any content
|
# byte-content-range- spec MUST ignore it and any content
|
||||||
# transferred along with it."
|
# transferred along with it."
|
||||||
if (first_byte_pos >= last_byte_pos
|
if (first_byte_pos >= last_byte_pos or
|
||||||
or (instance_length is not None
|
(instance_length is not None and
|
||||||
and instance_length <= last_byte_pos)):
|
instance_length <= last_byte_pos)):
|
||||||
raise ContentRangeError(
|
raise ContentRangeError(
|
||||||
'Invalid Content-Range returned: %r' % content_range)
|
'Invalid Content-Range returned: %r' % content_range)
|
||||||
|
|
||||||
if (first_byte_pos != resumed_from
|
if (first_byte_pos != resumed_from or
|
||||||
or (instance_length is not None
|
(instance_length is not None and
|
||||||
and last_byte_pos + 1 != instance_length)):
|
last_byte_pos + 1 != instance_length)):
|
||||||
# Not what we asked for.
|
# Not what we asked for.
|
||||||
raise ContentRangeError(
|
raise ContentRangeError(
|
||||||
'Unexpected Content-Range returned (%r)'
|
'Unexpected Content-Range returned (%r)'
|
||||||
@@ -308,9 +308,9 @@ class Downloader(object):
|
|||||||
@property
|
@property
|
||||||
def interrupted(self):
|
def interrupted(self):
|
||||||
return (
|
return (
|
||||||
self.finished
|
self.finished and
|
||||||
and self.status.total_size
|
self.status.total_size and
|
||||||
and self.status.total_size != self.status.downloaded
|
self.status.total_size != self.status.downloaded
|
||||||
)
|
)
|
||||||
|
|
||||||
def chunk_downloaded(self, chunk):
|
def chunk_downloaded(self, chunk):
|
||||||
@@ -399,8 +399,8 @@ class ProgressReporterThread(threading.Thread):
|
|||||||
if now - self._prev_time >= self._update_interval:
|
if now - self._prev_time >= self._update_interval:
|
||||||
downloaded = self.status.downloaded
|
downloaded = self.status.downloaded
|
||||||
try:
|
try:
|
||||||
speed = ((downloaded - self._prev_bytes)
|
speed = ((downloaded - self._prev_bytes) /
|
||||||
/ (now - self._prev_time))
|
(now - self._prev_time))
|
||||||
except ZeroDivisionError:
|
except ZeroDivisionError:
|
||||||
speed = 0
|
speed = 0
|
||||||
|
|
||||||
@@ -434,11 +434,11 @@ class ProgressReporterThread(threading.Thread):
|
|||||||
self._prev_bytes = downloaded
|
self._prev_bytes = downloaded
|
||||||
|
|
||||||
self.output.write(
|
self.output.write(
|
||||||
CLEAR_LINE
|
CLEAR_LINE +
|
||||||
+ ' '
|
' ' +
|
||||||
+ SPINNER[self._spinner_pos]
|
SPINNER[self._spinner_pos] +
|
||||||
+ ' '
|
' ' +
|
||||||
+ self._status_line
|
self._status_line
|
||||||
)
|
)
|
||||||
self.output.flush()
|
self.output.flush()
|
||||||
|
|
||||||
@@ -447,8 +447,8 @@ class ProgressReporterThread(threading.Thread):
|
|||||||
else 0)
|
else 0)
|
||||||
|
|
||||||
def sum_up(self):
|
def sum_up(self):
|
||||||
actually_downloaded = (self.status.downloaded
|
actually_downloaded = (
|
||||||
- self.status.resumed_from)
|
self.status.downloaded - self.status.resumed_from)
|
||||||
time_taken = self.status.time_finished - self.status.time_started
|
time_taken = self.status.time_finished - self.status.time_started
|
||||||
|
|
||||||
self.output.write(CLEAR_LINE)
|
self.output.write(CLEAR_LINE)
|
||||||
@@ -463,8 +463,8 @@ class ProgressReporterThread(threading.Thread):
|
|||||||
|
|
||||||
self.output.write(SUMMARY.format(
|
self.output.write(SUMMARY.format(
|
||||||
downloaded=humanize_bytes(actually_downloaded),
|
downloaded=humanize_bytes(actually_downloaded),
|
||||||
total=(self.status.total_size
|
total=(self.status.total_size and
|
||||||
and humanize_bytes(self.status.total_size)),
|
humanize_bytes(self.status.total_size)),
|
||||||
speed=humanize_bytes(speed),
|
speed=humanize_bytes(speed),
|
||||||
time=time_taken,
|
time=time_taken,
|
||||||
))
|
))
|
||||||
|
@@ -28,12 +28,11 @@ URL_SCHEME_RE = re.compile(r'^[a-z][a-z0-9.+-]*://', re.IGNORECASE)
|
|||||||
|
|
||||||
HTTP_POST = 'POST'
|
HTTP_POST = 'POST'
|
||||||
HTTP_GET = 'GET'
|
HTTP_GET = 'GET'
|
||||||
HTTP = 'http://'
|
|
||||||
HTTPS = 'https://'
|
|
||||||
|
|
||||||
|
|
||||||
# Various separators used in args
|
# Various separators used in args
|
||||||
SEP_HEADERS = ':'
|
SEP_HEADERS = ':'
|
||||||
|
SEP_HEADERS_EMPTY = ';'
|
||||||
SEP_CREDENTIALS = ':'
|
SEP_CREDENTIALS = ':'
|
||||||
SEP_PROXY = ':'
|
SEP_PROXY = ':'
|
||||||
SEP_DATA = '='
|
SEP_DATA = '='
|
||||||
@@ -67,6 +66,7 @@ SEP_GROUP_RAW_JSON_ITEMS = frozenset([
|
|||||||
# Separators allowed in ITEM arguments
|
# Separators allowed in ITEM arguments
|
||||||
SEP_GROUP_ALL_ITEMS = frozenset([
|
SEP_GROUP_ALL_ITEMS = frozenset([
|
||||||
SEP_HEADERS,
|
SEP_HEADERS,
|
||||||
|
SEP_HEADERS_EMPTY,
|
||||||
SEP_QUERY,
|
SEP_QUERY,
|
||||||
SEP_DATA,
|
SEP_DATA,
|
||||||
SEP_DATA_RAW_JSON,
|
SEP_DATA_RAW_JSON,
|
||||||
@@ -151,7 +151,7 @@ class HTTPieArgumentParser(ArgumentParser):
|
|||||||
if not self.args.ignore_stdin and not env.stdin_isatty:
|
if not self.args.ignore_stdin and not env.stdin_isatty:
|
||||||
self._body_from_file(self.env.stdin)
|
self._body_from_file(self.env.stdin)
|
||||||
if not URL_SCHEME_RE.match(self.args.url):
|
if not URL_SCHEME_RE.match(self.args.url):
|
||||||
scheme = HTTP
|
scheme = self.args.default_scheme + "://"
|
||||||
|
|
||||||
# See if we're using curl style shorthand for localhost (:3000/foo)
|
# See if we're using curl style shorthand for localhost (:3000/foo)
|
||||||
shorthand = re.match(r'^:(?!:)(\d*)(/?.*)$', self.args.url)
|
shorthand = re.match(r'^:(?!:)(\d*)(/?.*)$', self.args.url)
|
||||||
@@ -309,9 +309,10 @@ class HTTPieArgumentParser(ArgumentParser):
|
|||||||
self.args.url = self.args.method
|
self.args.url = self.args.method
|
||||||
# Infer the method
|
# Infer the method
|
||||||
has_data = (
|
has_data = (
|
||||||
(not self.args.ignore_stdin and not self.env.stdin_isatty)
|
(not self.args.ignore_stdin and
|
||||||
or any(item.sep in SEP_GROUP_DATA_ITEMS
|
not self.env.stdin_isatty) or
|
||||||
for item in self.args.items)
|
any(item.sep in SEP_GROUP_DATA_ITEMS
|
||||||
|
for item in self.args.items)
|
||||||
)
|
)
|
||||||
self.args.method = HTTP_POST if has_data else HTTP_GET
|
self.args.method = HTTP_POST if has_data else HTTP_GET
|
||||||
|
|
||||||
@@ -439,8 +440,8 @@ class SessionNameValidator(object):
|
|||||||
|
|
||||||
def __call__(self, value):
|
def __call__(self, value):
|
||||||
# Session name can be a path or just a name.
|
# Session name can be a path or just a name.
|
||||||
if (os.path.sep not in value
|
if (os.path.sep not in value and
|
||||||
and not VALID_SESSION_NAME_PATTERN.search(value)):
|
not VALID_SESSION_NAME_PATTERN.search(value)):
|
||||||
raise ArgumentError(None, self.error_message)
|
raise ArgumentError(None, self.error_message)
|
||||||
return value
|
return value
|
||||||
|
|
||||||
@@ -655,11 +656,20 @@ def parse_items(items,
|
|||||||
data = []
|
data = []
|
||||||
files = []
|
files = []
|
||||||
params = []
|
params = []
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
value = item.value
|
value = item.value
|
||||||
|
|
||||||
if item.sep == SEP_HEADERS:
|
if item.sep == SEP_HEADERS:
|
||||||
|
if value == '':
|
||||||
|
# No value => unset the header
|
||||||
|
value = None
|
||||||
|
target = headers
|
||||||
|
elif item.sep == SEP_HEADERS_EMPTY:
|
||||||
|
if item.value:
|
||||||
|
raise ParseError(
|
||||||
|
'Invalid item "%s" '
|
||||||
|
'(to specify an empty header use `Header;`)'
|
||||||
|
% item.orig
|
||||||
|
)
|
||||||
target = headers
|
target = headers
|
||||||
elif item.sep == SEP_QUERY:
|
elif item.sep == SEP_QUERY:
|
||||||
target = params
|
target = params
|
||||||
|
@@ -5,6 +5,7 @@ import requests.auth
|
|||||||
from httpie.plugins.base import AuthPlugin
|
from httpie.plugins.base import AuthPlugin
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyAbstractClass
|
||||||
class BuiltinAuthPlugin(AuthPlugin):
|
class BuiltinAuthPlugin(AuthPlugin):
|
||||||
|
|
||||||
package_name = '(builtin)'
|
package_name = '(builtin)'
|
||||||
|
5
setup.py
5
setup.py
@@ -35,10 +35,11 @@ tests_require = [
|
|||||||
|
|
||||||
|
|
||||||
install_requires = [
|
install_requires = [
|
||||||
'requests>=2.3.0',
|
'requests>=2.11.0',
|
||||||
'Pygments>=1.5'
|
'Pygments>=2.1.3'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
# Conditional dependencies:
|
# Conditional dependencies:
|
||||||
|
|
||||||
# sdist
|
# sdist
|
||||||
|
@@ -68,10 +68,11 @@ class TestItemParsing:
|
|||||||
def test_valid_items(self):
|
def test_valid_items(self):
|
||||||
items = input.parse_items([
|
items = input.parse_items([
|
||||||
self.key_value('string=value'),
|
self.key_value('string=value'),
|
||||||
self.key_value('header:value'),
|
self.key_value('Header:value'),
|
||||||
|
self.key_value('Unset-Header:'),
|
||||||
|
self.key_value('Empty-Header;'),
|
||||||
self.key_value('list:=["a", 1, {}, false]'),
|
self.key_value('list:=["a", 1, {}, false]'),
|
||||||
self.key_value('obj:={"a": "b"}'),
|
self.key_value('obj:={"a": "b"}'),
|
||||||
self.key_value('eh:'),
|
|
||||||
self.key_value('ed='),
|
self.key_value('ed='),
|
||||||
self.key_value('bool:=true'),
|
self.key_value('bool:=true'),
|
||||||
self.key_value('file@' + FILE_PATH_ARG),
|
self.key_value('file@' + FILE_PATH_ARG),
|
||||||
@@ -83,7 +84,11 @@ class TestItemParsing:
|
|||||||
# Parsed headers
|
# Parsed headers
|
||||||
# `requests.structures.CaseInsensitiveDict` => `dict`
|
# `requests.structures.CaseInsensitiveDict` => `dict`
|
||||||
headers = dict(items.headers._store.values())
|
headers = dict(items.headers._store.values())
|
||||||
assert headers == {'header': 'value', 'eh': ''}
|
assert headers == {
|
||||||
|
'Header': 'value',
|
||||||
|
'Unset-Header': None,
|
||||||
|
'Empty-Header': ''
|
||||||
|
}
|
||||||
|
|
||||||
# Parsed data
|
# Parsed data
|
||||||
raw_json_embed = items.data.pop('raw-json-embed')
|
raw_json_embed = items.data.pop('raw-json-embed')
|
||||||
@@ -103,8 +108,8 @@ class TestItemParsing:
|
|||||||
|
|
||||||
# Parsed file fields
|
# Parsed file fields
|
||||||
assert 'file' in items.files
|
assert 'file' in items.files
|
||||||
assert (items.files['file'][1].read().strip().decode('utf8')
|
assert (items.files['file'][1].read().strip().
|
||||||
== FILE_CONTENT)
|
decode('utf8') == FILE_CONTENT)
|
||||||
|
|
||||||
def test_multiple_file_fields_with_same_field_name(self):
|
def test_multiple_file_fields_with_same_field_name(self):
|
||||||
items = input.parse_items([
|
items = input.parse_items([
|
||||||
@@ -325,8 +330,18 @@ class TestIgnoreStdin:
|
|||||||
|
|
||||||
class TestSchemes:
|
class TestSchemes:
|
||||||
|
|
||||||
def test_custom_scheme(self):
|
def test_invalid_custom_scheme(self):
|
||||||
# InvalidSchema is expected because HTTPie
|
# InvalidSchema is expected because HTTPie
|
||||||
# shouldn't touch a formally valid scheme.
|
# shouldn't touch a formally valid scheme.
|
||||||
with pytest.raises(InvalidSchema):
|
with pytest.raises(InvalidSchema):
|
||||||
http('foo+bar-BAZ.123://bah')
|
http('foo+bar-BAZ.123://bah')
|
||||||
|
|
||||||
|
def test_invalid_scheme_via_via_default_scheme(self):
|
||||||
|
# InvalidSchema is expected because HTTPie
|
||||||
|
# shouldn't touch a formally valid scheme.
|
||||||
|
with pytest.raises(InvalidSchema):
|
||||||
|
http('bah', '--default=scheme=foo+bar-BAZ.123')
|
||||||
|
|
||||||
|
def test_default_scheme(self, httpbin_secure):
|
||||||
|
url = '{0}:{1}'.format(httpbin_secure.host, httpbin_secure.port)
|
||||||
|
assert HTTP_OK in http(url, '--default-scheme=https')
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
Tests for the provided defaults regarding HTTP method, and --json vs. --form.
|
Tests for the provided defaults regarding HTTP method, and --json vs. --form.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
from httpie.client import JSON_ACCEPT
|
||||||
from utils import TestEnvironment, http, HTTP_OK
|
from utils import TestEnvironment, http, HTTP_OK
|
||||||
from fixtures import FILE_PATH
|
from fixtures import FILE_PATH
|
||||||
|
|
||||||
@@ -58,20 +59,20 @@ class TestAutoContentTypeAndAcceptHeaders:
|
|||||||
def test_POST_with_data_auto_JSON_headers(self, httpbin):
|
def test_POST_with_data_auto_JSON_headers(self, httpbin):
|
||||||
r = http('POST', httpbin.url + '/post', 'a=b')
|
r = http('POST', httpbin.url + '/post', 'a=b')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert '"Accept": "application/json"' in r
|
assert r.json['headers']['Accept'] == JSON_ACCEPT
|
||||||
assert '"Content-Type": "application/json' in r
|
assert r.json['headers']['Content-Type'] == 'application/json'
|
||||||
|
|
||||||
def test_GET_with_data_auto_JSON_headers(self, httpbin):
|
def test_GET_with_data_auto_JSON_headers(self, httpbin):
|
||||||
# JSON headers should automatically be set also for GET with data.
|
# JSON headers should automatically be set also for GET with data.
|
||||||
r = http('POST', httpbin.url + '/post', 'a=b')
|
r = http('POST', httpbin.url + '/post', 'a=b')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert '"Accept": "application/json"' in r, r
|
assert r.json['headers']['Accept'] == JSON_ACCEPT
|
||||||
assert '"Content-Type": "application/json' in r
|
assert r.json['headers']['Content-Type'] == 'application/json'
|
||||||
|
|
||||||
def test_POST_explicit_JSON_auto_JSON_accept(self, httpbin):
|
def test_POST_explicit_JSON_auto_JSON_accept(self, httpbin):
|
||||||
r = http('--json', 'POST', httpbin.url + '/post')
|
r = http('--json', 'POST', httpbin.url + '/post')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json['headers']['Accept'] == 'application/json'
|
assert r.json['headers']['Accept'] == JSON_ACCEPT
|
||||||
# Make sure Content-Type gets set even with no data.
|
# Make sure Content-Type gets set even with no data.
|
||||||
# https://github.com/jkbrzt/httpie/issues/137
|
# https://github.com/jkbrzt/httpie/issues/137
|
||||||
assert 'application/json' in r.json['headers']['Content-Type']
|
assert 'application/json' in r.json['headers']['Content-Type']
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
"""High-level tests."""
|
"""High-level tests."""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from httpie.input import ParseError
|
||||||
from utils import TestEnvironment, http, HTTP_OK
|
from utils import TestEnvironment, http, HTTP_OK
|
||||||
from fixtures import FILE_PATH, FILE_CONTENT
|
from fixtures import FILE_PATH, FILE_CONTENT
|
||||||
|
|
||||||
@@ -75,6 +77,27 @@ def test_headers(httpbin_both):
|
|||||||
assert '"Foo": "bar"' in r
|
assert '"Foo": "bar"' in r
|
||||||
|
|
||||||
|
|
||||||
|
def test_headers_unset(httpbin_both):
|
||||||
|
r = http('GET', httpbin_both + '/headers')
|
||||||
|
assert 'Accept' in r.json['headers'] # default Accept present
|
||||||
|
|
||||||
|
r = http('GET', httpbin_both + '/headers', 'Accept:')
|
||||||
|
assert 'Accept' not in r.json['headers'] # default Accept unset
|
||||||
|
|
||||||
|
|
||||||
|
def test_headers_empty_value(httpbin_both):
|
||||||
|
r = http('GET', httpbin_both + '/headers')
|
||||||
|
assert r.json['headers']['Accept'] # default Accept has value
|
||||||
|
|
||||||
|
r = http('GET', httpbin_both + '/headers', 'Accept;')
|
||||||
|
assert r.json['headers']['Accept'] == '' # Accept has no value
|
||||||
|
|
||||||
|
|
||||||
|
def test_headers_empty_value_with_value_gives_error(httpbin):
|
||||||
|
with pytest.raises(ParseError):
|
||||||
|
http('GET', httpbin + '/headers', 'Accept;SYNTAX_ERROR')
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
@pytest.mark.skipif(
|
||||||
is_py26,
|
is_py26,
|
||||||
reason='the `object_pairs_hook` arg for `json.loads()` is Py>2.6 only'
|
reason='the `object_pairs_hook` arg for `json.loads()` is Py>2.6 only'
|
||||||
|
10
tox.ini
10
tox.ini
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
[tox]
|
[tox]
|
||||||
envlist = py26, py27, py35, pypy
|
envlist = py26, py27, py35, pypy, codestyle
|
||||||
|
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
@@ -20,3 +20,11 @@ commands =
|
|||||||
--verbose \
|
--verbose \
|
||||||
--doctest-modules \
|
--doctest-modules \
|
||||||
{posargs:./httpie ./tests}
|
{posargs:./httpie ./tests}
|
||||||
|
|
||||||
|
[testenv:codestyle]
|
||||||
|
deps = pycodestyle
|
||||||
|
commands =
|
||||||
|
pycodestyle \
|
||||||
|
--ignore=E241,E501
|
||||||
|
# 241 - multiple spaces after ‘,’
|
||||||
|
# 501 - line too long
|
||||||
|
Reference in New Issue
Block a user