Compare commits

...

36 Commits
0.9.4 ... 0.9.6

Author SHA1 Message Date
Jakub Roztocil
8e96238323 v0.9.6 2016-08-13 23:01:05 +02:00
Jakub Roztocil
8a9206eceb Fixed Makefile 2016-08-13 22:57:44 +02:00
Jakub Roztocil
8ac3c5961c Upgrade Pygments version 2016-08-13 22:57:33 +02:00
Jakub Roztocil
487c7a9221 v0.9.5 2016-08-13 22:51:42 +02:00
Jakub Roztocil
6d65668355 Strip request header values 2016-08-13 22:40:01 +02:00
Jakub Roztočil
3e5115e4a2 Merge pull request #501 from ii-v/master
Fixed spelling mistake in the AUTHORS.rst file
2016-08-11 08:37:41 +02:00
ii-v
2b8b572f22 Merge pull request #1 from ii-v/ii-v-patch-1
Fixed spelling mistake `GitHib` to `GitHub`
2016-08-11 01:44:04 +02:00
ii-v
af737fd338 Fixed spelling mistage GitHib to GitHub 2016-08-11 01:43:15 +02:00
Jakub Roztočil
ee375b6942 Merge pull request #493 from medecau/codestyle_environment
Codestyle environment
2016-07-29 23:17:00 +02:00
Pedro Rodrigues
becb63de9a useful info 2016-07-26 21:59:34 +01:00
Pedro Rodrigues
86c8abc485 force os to be linux (+1 squashed commit)
Squashed commits:
[444c56d] no vars for you (+1 squashed commit)
Squashed commits:
[c7d1bf9] added pycodestyle environment to travis config
2016-07-26 21:43:13 +01:00
Pedro Rodrigues
8f6bee9196 codestyle fixes 2016-07-19 17:23:40 +01:00
Pedro Rodrigues
9c2c058ae5 separate environment to test codestyle as proposed by @sigmavirus24 2016-07-19 17:23:18 +01:00
Jakub Roztocil
6238b59e72 Fix formatting 2016-07-08 15:05:43 +02:00
Jakub Roztocil
702c21aa91 Added related projects 2016-07-08 15:03:48 +02:00
Jakub Roztocil
aab5cd9da0 PEP8. clean-up 2016-07-04 20:30:55 +02:00
Jakub Roztocil
8c0f0b578c Clean-up 2016-07-02 18:44:02 +02:00
Jakub Roztocil
bb4881a873 Fixed README 2016-07-02 18:30:04 +02:00
Jakub Roztocil
3a1726b4ed Fixed README 2016-07-02 15:04:19 +02:00
Jakub Roztocil
e1fa57d228 Added -I as a shortcut for --ignore-stdin 2016-07-02 15:01:46 +02:00
Jakub Roztocil
bfc64bce21 Upgrade requests to 2.10.0 to enable optional SOCKS support
Closes #86
2016-07-02 14:58:34 +02:00
Jakub Roztocil
595dc51b2d Fish shell completion 2016-07-02 14:33:04 +02:00
Jakub Roztočil
83fa772247 Merge pull request #459 from dickeyxxx/fish-completion
added completions for fish shell
2016-07-02 14:31:06 +02:00
Jakub Roztocil
49a0fb6e0f More liberal default JSON Accept header
Closes #470
2016-07-02 14:18:36 +02:00
Jakub Roztocil
41e822ca2f Clean-up 2016-07-02 12:51:35 +02:00
Jakub Roztocil
1124d68946 Added --default-scheme <URL_SCHEME>
Closes #289
2016-07-02 12:47:02 +02:00
Jakub Roztočil
c3735d0422 Merge pull request #401 from lgarron/default-scheme
Add a --default-scheme argument.
2016-07-02 12:32:07 +02:00
Jakub Roztocil
364b91cbc4 Skip pypy3 tests on TravisCI 2016-07-02 12:03:52 +02:00
Jakub Roztocil
c8e06b55e1 Fix tests 2016-07-02 12:03:19 +02:00
Jakub Roztocil
5acbc904b7 Added the ability to unset headers
Closes #476
2016-07-02 11:50:30 +02:00
Jakub Roztocil
0c7c248dce Fix CHANGELOG 2016-07-02 11:17:38 +02:00
Jakub Roztocil
caf60cbc65 Typos 2016-07-02 11:11:06 +02:00
Jakub Roztocil
2b0e642842 Document preference for Python 3
Also mention that the Homebrew formula depends on Python 3 starting with HTTPie 0.9.4.
2016-07-02 11:07:46 +02:00
Jakub Roztocil
e25948f6a0 1.0.0-dev 2016-07-01 19:17:31 +02:00
Jeff Dickey
ec245a1e80 added completions for fish shell 2016-04-06 11:28:03 -07:00
Lucas Garron
6259b5dd3b Add a --default-scheme argument. 2015-10-28 15:06:04 -07:00
21 changed files with 412 additions and 109 deletions

View File

@@ -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
if [[ $CODESTYLE ]]; then
# 241 - multiple spaces after ,
# 501 - line too long
pycodestyle --ignore=E241,E501
else
make make
fi
else else
PATH="/usr/local/bin:$PATH" tox -e "$TOXENV" PATH="/usr/local/bin:$PATH" tox -e "$TOXENV"
fi fi

View File

@@ -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>`_

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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))

View 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'

View File

@@ -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'

View File

@@ -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',

View File

@@ -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 = {}
for name, value in headers.items():
if value is not None:
# >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 # See: https://github.com/jkbrzt/httpie/issues/212
return dict( value = value.encode('utf8')
(name, value.encode('utf8') if isinstance(value, str) else value)
for name, value in headers.items() 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:

View File

@@ -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()

View File

@@ -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,
)) ))

View File

@@ -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,8 +309,9 @@ 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
any(item.sep in SEP_GROUP_DATA_ITEMS
for item in self.args.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

View File

@@ -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)'

View File

@@ -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

View File

@@ -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')

View File

@@ -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']

View File

@@ -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
View File

@@ -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