Compare commits

...

34 Commits
2.3.0 ... 2.4.0

Author SHA1 Message Date
bb36897054 2.4.0 2021-02-06 11:17:24 +01:00
173e622567 Update upload command 2021-02-06 11:17:14 +01:00
3426030370 Fix README formatting 2021-02-06 11:13:04 +01:00
d014498713 Changelog entry for cookie expiration based on Set-Cookie: max-age=<n>
#1029
2021-02-06 11:02:26 +01:00
5414d1853e Refactoring
#1029
2021-02-06 10:58:36 +01:00
1ac8f69651 Add more output matching tests 2021-02-06 10:52:30 +01:00
3c07a25326 Add support for max-age=0 cookie expiry (#1029)
Close #998
2021-02-06 10:50:34 +01:00
cf78a12e46 Show --check-status warning with --quiet as well. (#1026)
Fixes #1028
2021-01-31 00:58:56 +01:00
0f1e098cc4 Fix incorrect separators and introduce assert_output_matches() (close #1027) 2021-01-30 22:14:57 +01:00
0401d7b31c fixed typo (#1024) 2021-01-21 22:35:41 +01:00
795627f965 Update chat icon 2021-01-13 22:24:20 +01:00
21cc008cb2 Add Linux Solus install to README (#1018) 2021-01-13 21:52:00 +01:00
77b8c37cb0 Update changelog
#1020
2021-01-13 21:49:53 +01:00
db685d58b5 Decode headers using utf-8 only if they are not str (#1020) 2021-01-13 21:45:56 +01:00
44ae67685d Add HTTPie Discord link to README (#1016)
* add discord link

* update link
2021-01-12 15:27:45 +01:00
6922a0c912 Switch from httpbin.org to pie.dev 2020-12-24 21:34:30 +01:00
2afdc958c6 Update URLs 2020-12-23 22:07:27 +01:00
57b1baf1d1 Add a test to reproduce #1006 2020-12-22 22:56:45 +01:00
1828da6a50 Update --stream example comment 2020-12-21 12:17:04 +01:00
0629f2ff42 Fix --stream example II 2020-12-21 12:14:41 +01:00
d71b7eee81 Fix --stream example
Close #1002
2020-12-21 12:12:11 +01:00
9883a46575 Cleanup (#993) 2020-12-21 12:03:25 +01:00
2409077a6d Clarify 2020-12-21 11:51:19 +01:00
02971b938d Fix documentation on file upload (#1000)
As documented later on in "File upload forms", the correct syntax to set
the mimetype of the upload is `field@file;type=filetype`
2020-12-21 11:47:47 +01:00
f7e77efe4b Test on Python 3.9 (#986) 2020-12-21 11:42:21 +01:00
5d8bd0da7c python -m pip (#1005) 2020-12-21 11:38:00 +01:00
3c6e7c73fe Update setup.py 2020-12-19 14:07:31 +01:00
d64c0ee415 Remove funding request 2020-12-18 17:54:13 +01:00
311a5ede70 Simplify Hello World 2020-10-29 10:07:45 +01:00
f64c90010f Simplify Hello World 2020-10-29 10:06:26 +01:00
8456ddb27c Update brew instructions for dev 2020-10-25 21:48:09 +01:00
cf254680b7 Update homebrew 2020-10-25 21:43:20 +01:00
42c4a7596b 2.4.0-dev 2020-10-25 21:36:24 +01:00
1573058811 v2.3.0 2020-10-25 21:12:38 +01:00
37 changed files with 929 additions and 375 deletions

12
.github/FUNDING.yml vendored
View File

@ -1,12 +0,0 @@
# These are supported funding model platforms
github: jakubroztocil # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom:

View File

@ -8,7 +8,7 @@ jobs:
- uses: actions/checkout@v1
- uses: actions/setup-python@v1
with:
python-version: 3.8
python-version: 3.9
- run: python -m pip install --upgrade pip setuptools wheel
- run: make install
- run: make pycodestyle
@ -23,10 +23,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
python-version: [3.6, 3.7, 3.8]
exclude:
- os: windows-latest
python-version: 3.8
python-version: [3.6, 3.7, 3.8, 3.9]
steps:
- uses: actions/checkout@v1
- uses: actions/setup-python@v1

View File

@ -8,7 +8,7 @@ HTTPie authors
Patches and ideas
-----------------
`Complete list of contributors on GitHub <https://github.com/jakubroztocil/httpie/graphs/contributors>`_
`Complete list of contributors on GitHub <https://github.com/httpie/httpie/graphs/contributors>`_
* `Cláudia T. Delgado <https://github.com/claudiatd>`_ (logo)
* `Hank Gay <https://github.com/gthank>`_
@ -38,5 +38,6 @@ Patches and ideas
* `Edward Yang <https://github.com/honorabrutroll>`_
* `Aleksandr Vinokurov <https://github.com/aleksandr-vin>`_
* `Jeff Byrnes <https://github.com/jeffbyrnes>`_
* `Denis Belavin <https://github.com/LuckyDenis>`_

View File

@ -6,13 +6,24 @@ This document records all notable changes to `HTTPie <https://httpie.org>`_.
This project adheres to `Semantic Versioning <https://semver.org/>`_.
`2.3.0-dev`_ (unreleased)
`2.4.0`_ (2021-02-06)
---------------------
* Added support for ``--session`` cookie expiration based on ``Set-Cookie: max-age=<n>``. (`#1029`_)
* Show a ``--check-status`` warning with ``--quiet`` as well, not only when the output si redirected. (`#1026`_)
* Fixed upload with ``--session`` (`#1020`_).
* Fixed a missing blank line between request and response (`#1006`_).
`2.3.0`_ (2020-10-25)
-------------------------
* Added support for streamed uploads (`#201`_).
* Added support for multipart upload streaming (`#684`_).
* Added support for body-from-file upload streaming (``http httpbin.org/post @file``).
* Added ``--chunked`` to allow chunked transfer encoding.
* Added support for body-from-file upload streaming (``http pie.dev/post @file``).
* Added ``--chunked`` to enable chunked transfer encoding (`#753`_).
* Added ``--multipart`` to allow ``multipart/form-data`` encoding for non-file ``--form`` requests as well.
* Added support for preserving field order in multipart requests (`#903`_).
* Added ``--boundary`` to allow a custom boundary string for ``multipart/form-data`` requests.
* Added support for combining cookies specified on the CLI and in a session file (`#932`_).
* Added out of the box SOCKS support with no extra installation (`#904`_).
@ -419,57 +430,65 @@ This project adheres to `Semantic Versioning <https://semver.org/>`_.
* Initial public release
.. _`0.1.0`: https://github.com/jakubroztocil/httpie/commit/b966efa
.. _0.1.4: https://github.com/jakubroztocil/httpie/compare/b966efa...0.1.4
.. _0.1.5: https://github.com/jakubroztocil/httpie/compare/0.1.4...0.1.5
.. _0.1.6: https://github.com/jakubroztocil/httpie/compare/0.1.5...0.1.6
.. _0.2.0: https://github.com/jakubroztocil/httpie/compare/0.1.6...0.2.0
.. _0.2.1: https://github.com/jakubroztocil/httpie/compare/0.2.0...0.2.1
.. _0.2.2: https://github.com/jakubroztocil/httpie/compare/0.2.1...0.2.2
.. _0.2.5: https://github.com/jakubroztocil/httpie/compare/0.2.2...0.2.5
.. _0.2.6: https://github.com/jakubroztocil/httpie/compare/0.2.5...0.2.6
.. _0.2.7: https://github.com/jakubroztocil/httpie/compare/0.2.5...0.2.7
.. _0.3.0: https://github.com/jakubroztocil/httpie/compare/0.2.7...0.3.0
.. _0.4.0: https://github.com/jakubroztocil/httpie/compare/0.3.0...0.4.0
.. _0.4.1: https://github.com/jakubroztocil/httpie/compare/0.4.0...0.4.1
.. _0.5.0: https://github.com/jakubroztocil/httpie/compare/0.4.1...0.5.0
.. _0.5.1: https://github.com/jakubroztocil/httpie/compare/0.5.0...0.5.1
.. _0.6.0: https://github.com/jakubroztocil/httpie/compare/0.5.1...0.6.0
.. _0.7.1: https://github.com/jakubroztocil/httpie/compare/0.6.0...0.7.1
.. _0.8.0: https://github.com/jakubroztocil/httpie/compare/0.7.1...0.8.0
.. _0.9.0: https://github.com/jakubroztocil/httpie/compare/0.8.0...0.9.0
.. _0.9.1: https://github.com/jakubroztocil/httpie/compare/0.9.0...0.9.1
.. _0.9.2: https://github.com/jakubroztocil/httpie/compare/0.9.1...0.9.2
.. _0.9.3: https://github.com/jakubroztocil/httpie/compare/0.9.2...0.9.3
.. _0.9.4: https://github.com/jakubroztocil/httpie/compare/0.9.3...0.9.4
.. _0.9.6: https://github.com/jakubroztocil/httpie/compare/0.9.4...0.9.6
.. _0.9.8: https://github.com/jakubroztocil/httpie/compare/0.9.6...0.9.8
.. _0.9.9: https://github.com/jakubroztocil/httpie/compare/0.9.8...0.9.9
.. _1.0.0: https://github.com/jakubroztocil/httpie/compare/0.9.9...1.0.0
.. _1.0.1: https://github.com/jakubroztocil/httpie/compare/1.0.0...1.0.1
.. _1.0.2: https://github.com/jakubroztocil/httpie/compare/1.0.1...1.0.2
.. _1.0.3: https://github.com/jakubroztocil/httpie/compare/1.0.2...1.0.3
.. _2.0.0: https://github.com/jakubroztocil/httpie/compare/1.0.3...2.0.0
.. _2.1.0: https://github.com/jakubroztocil/httpie/compare/2.0.0...2.1.0
.. _2.2.0: https://github.com/jakubroztocil/httpie/compare/2.1.0...2.2.0
.. _2.3.0-dev: https://github.com/jakubroztocil/httpie/compare/2.2.0...master
.. _`0.1.0`: https://github.com/httpie/httpie/commit/b966efa
.. _0.1.4: https://github.com/httpie/httpie/compare/b966efa...0.1.4
.. _0.1.5: https://github.com/httpie/httpie/compare/0.1.4...0.1.5
.. _0.1.6: https://github.com/httpie/httpie/compare/0.1.5...0.1.6
.. _0.2.0: https://github.com/httpie/httpie/compare/0.1.6...0.2.0
.. _0.2.1: https://github.com/httpie/httpie/compare/0.2.0...0.2.1
.. _0.2.2: https://github.com/httpie/httpie/compare/0.2.1...0.2.2
.. _0.2.5: https://github.com/httpie/httpie/compare/0.2.2...0.2.5
.. _0.2.6: https://github.com/httpie/httpie/compare/0.2.5...0.2.6
.. _0.2.7: https://github.com/httpie/httpie/compare/0.2.5...0.2.7
.. _0.3.0: https://github.com/httpie/httpie/compare/0.2.7...0.3.0
.. _0.4.0: https://github.com/httpie/httpie/compare/0.3.0...0.4.0
.. _0.4.1: https://github.com/httpie/httpie/compare/0.4.0...0.4.1
.. _0.5.0: https://github.com/httpie/httpie/compare/0.4.1...0.5.0
.. _0.5.1: https://github.com/httpie/httpie/compare/0.5.0...0.5.1
.. _0.6.0: https://github.com/httpie/httpie/compare/0.5.1...0.6.0
.. _0.7.1: https://github.com/httpie/httpie/compare/0.6.0...0.7.1
.. _0.8.0: https://github.com/httpie/httpie/compare/0.7.1...0.8.0
.. _0.9.0: https://github.com/httpie/httpie/compare/0.8.0...0.9.0
.. _0.9.1: https://github.com/httpie/httpie/compare/0.9.0...0.9.1
.. _0.9.2: https://github.com/httpie/httpie/compare/0.9.1...0.9.2
.. _0.9.3: https://github.com/httpie/httpie/compare/0.9.2...0.9.3
.. _0.9.4: https://github.com/httpie/httpie/compare/0.9.3...0.9.4
.. _0.9.6: https://github.com/httpie/httpie/compare/0.9.4...0.9.6
.. _0.9.8: https://github.com/httpie/httpie/compare/0.9.6...0.9.8
.. _0.9.9: https://github.com/httpie/httpie/compare/0.9.8...0.9.9
.. _1.0.0: https://github.com/httpie/httpie/compare/0.9.9...1.0.0
.. _1.0.1: https://github.com/httpie/httpie/compare/1.0.0...1.0.1
.. _1.0.2: https://github.com/httpie/httpie/compare/1.0.1...1.0.2
.. _1.0.3: https://github.com/httpie/httpie/compare/1.0.2...1.0.3
.. _2.0.0: https://github.com/httpie/httpie/compare/1.0.3...2.0.0
.. _2.1.0: https://github.com/httpie/httpie/compare/2.0.0...2.1.0
.. _2.2.0: https://github.com/httpie/httpie/compare/2.1.0...2.2.0
.. _2.3.0: https://github.com/httpie/httpie/compare/2.2.0...2.3.0
.. _2.4.0: https://github.com/httpie/httpie/compare/2.3.0...2.4.0
.. _2.5.0-dev: https://github.com/httpie/httpie/compare/2.4.0...master
.. _#128: https://github.com/jakubroztocil/httpie/issues/128
.. _#488: https://github.com/jakubroztocil/httpie/issues/488
.. _#668: https://github.com/jakubroztocil/httpie/issues/668
.. _#684: https://github.com/jakubroztocil/httpie/issues/684
.. _#718: https://github.com/jakubroztocil/httpie/issues/718
.. _#719: https://github.com/jakubroztocil/httpie/issues/719
.. _#840: https://github.com/jakubroztocil/httpie/issues/840
.. _#853: https://github.com/jakubroztocil/httpie/issues/853
.. _#852: https://github.com/jakubroztocil/httpie/issues/852
.. _#870: https://github.com/jakubroztocil/httpie/issues/870
.. _#895: https://github.com/jakubroztocil/httpie/issues/895
.. _#920: https://github.com/jakubroztocil/httpie/issues/920
.. _#904: https://github.com/jakubroztocil/httpie/issues/904
.. _#925: https://github.com/jakubroztocil/httpie/issues/925
.. _#932: https://github.com/jakubroztocil/httpie/issues/932
.. _#934: https://github.com/jakubroztocil/httpie/issues/934
.. _#943: https://github.com/jakubroztocil/httpie/issues/943
.. _#963: https://github.com/jakubroztocil/httpie/issues/963
.. _#128: https://github.com/httpie/httpie/issues/128
.. _#201: https://github.com/httpie/httpie/issues/201
.. _#488: https://github.com/httpie/httpie/issues/488
.. _#668: https://github.com/httpie/httpie/issues/668
.. _#684: https://github.com/httpie/httpie/issues/684
.. _#718: https://github.com/httpie/httpie/issues/718
.. _#719: https://github.com/httpie/httpie/issues/719
.. _#753: https://github.com/httpie/httpie/issues/753
.. _#840: https://github.com/httpie/httpie/issues/840
.. _#853: https://github.com/httpie/httpie/issues/853
.. _#852: https://github.com/httpie/httpie/issues/852
.. _#870: https://github.com/httpie/httpie/issues/870
.. _#895: https://github.com/httpie/httpie/issues/895
.. _#903: https://github.com/httpie/httpie/issues/903
.. _#920: https://github.com/httpie/httpie/issues/920
.. _#904: https://github.com/httpie/httpie/issues/904
.. _#925: https://github.com/httpie/httpie/issues/925
.. _#932: https://github.com/httpie/httpie/issues/932
.. _#934: https://github.com/httpie/httpie/issues/934
.. _#943: https://github.com/httpie/httpie/issues/943
.. _#963: https://github.com/httpie/httpie/issues/963
.. _#1006: https://github.com/httpie/httpie/issues/1006
.. _#1020: https://github.com/httpie/httpie/issues/1020
.. _#1026: https://github.com/httpie/httpie/issues/1026
.. _#1029: https://github.com/httpie/httpie/issues/1029

View File

@ -53,7 +53,7 @@ Development Environment
Getting the code
****************
Go to https://github.com/jakubroztocil/httpie and fork the project repository.
Go to https://github.com/httpie/httpie and fork the project repository.
.. code-block:: bash
@ -132,7 +132,7 @@ Testing & CI
Please add tests for any new features and bug fixes.
When you open a pull request,
`GitHub Actions <https://github.com/jakubroztocil/httpie/actions>`_
`GitHub Actions <https://github.com/httpie/httpie/actions>`_
will automatically run HTTPies `test suite`_ against your code
so please make sure all checks pass.
@ -230,10 +230,10 @@ Use ``pytest`` to run tests locally with an active virtual environment:
Finally, feel free to add yourself to `AUTHORS`_!
.. _existing issues: https://github.com/jakubroztocil/httpie/issues?state=open
.. _AUTHORS: https://github.com/jakubroztocil/httpie/blob/master/AUTHORS.rst
.. _Makefile: https://github.com/jakubroztocil/httpie/blob/master/Makefile
.. _existing issues: https://github.com/httpie/httpie/issues?state=open
.. _AUTHORS: https://github.com/httpie/httpie/blob/master/AUTHORS.rst
.. _Makefile: https://github.com/httpie/httpie/blob/master/Makefile
.. _venv: https://docs.python.org/3/library/venv.html
.. _pytest: https://pytest.org/
.. _Style Guide for Python Code: https://python.org/dev/peps/pep-0008/
.. _test suite: https://github.com/jakubroztocil/httpie/tree/master/tests
.. _test suite: https://github.com/httpie/httpie/tree/master/tests

View File

@ -3,5 +3,5 @@ include README.rst
include CHANGELOG.rst
include AUTHORS.rst
# <https://github.com/jakubroztocil/httpie/issues/182>
# <https://github.com/httpie/httpie/issues/182>
recursive-include tests/ *

View File

@ -2,6 +2,8 @@
# See ./CONTRIBUTING.rst
###############################################################################
.PHONY: build
ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
VERSION=$(shell grep __version__ httpie/__init__.py)
REQUIREMENTS=requirements-dev.txt
@ -111,6 +113,9 @@ test-bdist-wheel: clean venv
@echo
twine-check:
twine check dist/*
pycodestyle:
@echo $(H1)Running pycodestyle$(H1END)
@[ -f $(VENV_BIN)/pycodestyle ] || $(VENV_PIP) install pycodestyle
@ -131,6 +136,11 @@ codecov-upload:
###############################################################################
build:
rm -rf build/
$(VENV_PYTHON) setup.py sdist bdist_wheel
publish: test-all publish-no-test
@ -138,8 +148,9 @@ publish-no-test:
@echo $(H1)Testing wheel build an installation$(H1END)
@echo "$(VERSION)"
@echo "$(VERSION)" | grep -q "dev" && echo '!!!Not publishing dev version!!!' && exit 1 || echo ok
$(VENV_PYTHON) setup.py sdist bdist_wheel
$(VENV_BIN)/twine upload dist/*
make build
make twine-check
$(VENV_BIN)/twine upload --repository=httpie dist/*
@echo

View File

@ -30,9 +30,11 @@ They use simple and natural syntax and provide formatted and colorized output.
About this document
===================
This documentation is best viewed at `httpie.org/docs <https://httpie.org/docs>`_,
where you can select your corresponding HTTPie version as well as run examples directly from the
This documentation is best viewed at `httpie.org/docs <https://httpie.org/docs>`_.
You can select your corresponding HTTPie version as well as run examples directly from the
browser using a `termible.io <https://termible.io?utm_source=httpie-readme>`_ embedded terminal.
If you are reading this on GitHub, then this text covers the current *development* version.
You are invited to submit fixes and improvements to the the docs by editing
`README.rst <https://github.com/httpie/httpie/blob/master/README.rst>`_.
@ -117,6 +119,11 @@ system package manager, for example:
# Arch Linux
$ pacman -S httpie
.. code-block:: bash
# Solus
$ eopkg install httpie
Windows, etc.
-------------
@ -128,9 +135,9 @@ and always provides the latest version) is to use `pip`_:
.. code-block:: bash
# Make sure we have an up-to-date version of pip and setuptools:
$ pip install --upgrade pip setuptools
$ python -m pip install --upgrade pip setuptools
$ pip install --upgrade httpie
$ python -m pip install --upgrade httpie
(If ``pip`` installation fails for some reason, you can try
@ -160,7 +167,8 @@ On macOS you can install it with Homebrew:
.. code-block:: bash
$ brew install httpie --HEAD
$ brew uninstall --force httpie
$ brew install --HEAD httpie
Otherwise with ``pip``:
@ -189,7 +197,7 @@ Hello World:
.. code-block:: bash
$ http https://httpie.org/hello
$ https httpie.io/hello
Synopsis:
@ -209,28 +217,28 @@ Custom `HTTP method`_, `HTTP headers`_ and `JSON`_ data:
.. code-block:: bash
$ http PUT httpbin.org/put X-API-Token:123 name=John
$ http PUT pie.dev/put X-API-Token:123 name=John
Submitting `forms`_:
.. code-block:: bash
$ http -f POST httpbin.org/post hello=World
$ http -f POST pie.dev/post hello=World
See the request that is being sent using one of the `output options`_:
.. code-block:: bash
$ http -v httpbin.org/get
$ http -v pie.dev/get
Build and print a request without sending it using `offline mode`_:
.. code-block:: bash
$ http --offline httpbin.org/post hello=offline
$ http --offline pie.dev/post hello=offline
Use `GitHub API`_ to post a comment on an
@ -246,21 +254,21 @@ Upload a file using `redirected input`_:
.. code-block:: bash
$ http httpbin.org/post < files/data.json
$ http pie.dev/post < files/data.json
Download a file and save it via `redirected output`_:
.. code-block:: bash
$ http httpbin.org/image/png > image.png
$ http pie.dev/image/png > image.png
Download a file ``wget`` style:
.. code-block:: bash
$ http --download httpbin.org/image/png
$ http --download pie.dev/image/png
Use named `sessions`_ to make certain aspects of the communication persistent
between requests to the same host:
@ -268,12 +276,12 @@ between requests to the same host:
.. code-block:: bash
$ http --session=logged-in -a username:password httpbin.org/get API-Key:123
$ http --session=logged-in -a username:password pie.dev/get API-Key:123
.. code-block:: bash
$ http --session=logged-in httpbin.org/headers
$ http --session=logged-in pie.dev/headers
Set a custom ``Host`` header to work around missing DNS records:
@ -292,7 +300,7 @@ The name of the HTTP method comes right before the URL argument:
.. code-block:: bash
$ http DELETE httpbin.org/delete
$ http DELETE pie.dev/delete
Which looks similar to the actual ``Request-Line`` that is sent:
@ -461,34 +469,34 @@ their type is distinguished only by the separator used:
``:``, ``=``, ``:=``, ``==``, ``@``, ``=@``, and ``:=@``. The ones with an
``@`` expect a file path as value.
+-----------------------+-----------------------------------------------------+
| Item Type | Description |
+=======================+=====================================================+
| HTTP Headers | Arbitrary HTTP header, e.g. ``X-API-Token:123``. |
| ``Name:Value`` | |
+-----------------------+-----------------------------------------------------+
| URL parameters | Appends the given name/value pair as a query |
| ``name==value`` | string parameter to the URL. |
| | The ``==`` separator is used. |
+-----------------------+-----------------------------------------------------+
| Data Fields | Request data fields to be serialized as a JSON |
| ``field=value``, | object (default), to be form-encoded |
| ``field=@file.txt`` | (with ``--form, -f``), or to be serialized as |
| | ``multipart/form-data`` (with ``--multipart``). |
+-----------------------+-----------------------------------------------------+
| Raw JSON fields | Useful when sending JSON and one or |
| ``field:=json``, | more fields need to be a ``Boolean``, ``Number``, |
| ``field:=@file.json`` | nested ``Object``, or an ``Array``, e.g., |
| | ``meals:='["ham","spam"]'`` or ``pies:=[1,2,3]`` |
| | (note the quotes). |
+-----------------------+-----------------------------------------------------+
| Fields upload fields | Only available with ``--form, -f`` and |
| ``field@/dir/file`` | ``--multipart``. |
| ``field@file;type`` | For example ``screenshot@~/Pictures/img.png``, or |
| | ``'cv@cv.txt;text/markdown'``. |
| | With ``--form``, the presence of a file field |
| | results in a ``--multipart`` request. |
+-----------------------+-----------------------------------------------------+
+------------------------------+---------------------------------------------------+
| Item Type | Description |
+==============================+===================================================+
| HTTP Headers | Arbitrary HTTP header, e.g. ``X-API-Token:123``. |
| ``Name:Value`` | |
+------------------------------+---------------------------------------------------+
| URL parameters | Appends the given name/value pair as a query |
| ``name==value`` | string parameter to the URL. |
| | The ``==`` separator is used. |
+------------------------------+---------------------------------------------------+
| Data Fields | Request data fields to be serialized as a JSON |
| ``field=value``, | object (default), to be form-encoded |
| ``field=@file.txt`` | (with ``--form, -f``), or to be serialized as |
| | ``multipart/form-data`` (with ``--multipart``). |
+------------------------------+---------------------------------------------------+
| Raw JSON fields | Useful when sending JSON and one or |
| ``field:=json``, | more fields need to be a ``Boolean``, ``Number``, |
| ``field:=@file.json`` | nested ``Object``, or an ``Array``, e.g., |
| | ``meals:='["ham","spam"]'`` or ``pies:=[1,2,3]`` |
| | (note the quotes). |
+------------------------------+---------------------------------------------------+
| Fields upload fields | Only available with ``--form, -f`` and |
| ``field@/dir/file`` | ``--multipart``. |
| ``field@file;type=mime`` | For example ``screenshot@~/Pictures/img.png``, or |
| | ``'cv@cv.txt;type=text/markdown'``. |
| | With ``--form``, the presence of a file field |
| | results in a ``--multipart`` request. |
+------------------------------+---------------------------------------------------+
Note that data fields arent the only way to specify request data:
@ -510,7 +518,7 @@ token ``--`` to prevent confusion with ``--arguments``:
.. code-block:: bash
$ http httpbin.org/post -- -name-starting-with-dash=foo -Unusual-Header:bar
$ http pie.dev/post -- -name-starting-with-dash=foo -Unusual-Header:bar
.. code-block:: http
@ -535,14 +543,15 @@ Simple example:
.. code-block:: bash
$ http PUT httpbin.org/put name=John email=john@example.org
$ http PUT pie.dev/put name=John email=john@example.org
.. code-block:: http
PUT / HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Host: httpbin.org
Host: pie.dev
{
"name": "John",
@ -585,7 +594,7 @@ fields using ``=@`` and ``:=@``:
.. code-block:: bash
$ http PUT httpbin.org/put \
$ http PUT pie.dev/put \
name=John \ # String (default)
age:=29 \ # Raw JSON — Number
married:=false \ # Raw JSON — Boolean
@ -600,7 +609,7 @@ fields using ``=@`` and ``:=@``:
PUT /person/1 HTTP/1.1
Accept: application/json, */*;q=0.5
Content-Type: application/json
Host: httpbin.org
Host: pie.dev
{
"age": 29,
@ -630,11 +639,11 @@ In such cases, its better to pass the full raw JSON data via
.. code-block:: bash
$ echo '{"hello": "world"}' | http POST httpbin.org/post
$ echo '{"hello": "world"}' | http POST pie.dev/post
.. code-block:: bash
$ http POST httpbin.org/post < files/data.json
$ http POST pie.dev/post < files/data.json
Furthermore, this syntax only allows you to send an object as the JSON document, but not an array, etc.
Here, again, the solution is to use `redirected input`_.
@ -655,7 +664,7 @@ Regular forms
.. code-block:: bash
$ http --form POST httpbin.org/post name='John Smith'
$ http --form POST pie.dev/post name='John Smith'
.. code-block:: http
@ -674,7 +683,7 @@ If one or more file fields is present, the serialization and content type is
.. code-block:: bash
$ http -f POST httpbin.org/post name='John Smith' cv@~/files/data.xml
$ http -f POST pie.dev/post name='John Smith' cv@~/files/data.xml
The request above is the same as if the following HTML form were
@ -695,7 +704,7 @@ override the inferred content type:
.. code-block:: bash
$ http -f POST httpbin.org/post name='John Smith' cv@'~/files/data.bin;type=application/pdf'
$ http -f POST pie.dev/post name='John Smith' cv@'~/files/data.bin;type=application/pdf'
To perform a ``multipart/form-data`` request even without any files, use
``--multipart`` instead of ``--form``:
@ -769,7 +778,7 @@ To set custom headers you can use the ``Header:Value`` notation:
.. code-block:: bash
$ http httpbin.org/headers User-Agent:Bacon/1.0 'Cookie:valued-visitor=yes;foo=bar' \
$ http pie.dev/headers User-Agent:Bacon/1.0 'Cookie:valued-visitor=yes;foo=bar' \
X-Foo:Bar Referer:https://httpie.org/
@ -779,7 +788,7 @@ To set custom headers you can use the ``Header:Value`` notation:
Accept: */*
Accept-Encoding: gzip, deflate
Cookie: valued-visitor=yes;foo=bar
Host: httpbin.org
Host: pie.dev
Referer: https://httpie.org/
User-Agent: Bacon/1.0
X-Foo: Bar
@ -800,7 +809,7 @@ There are a couple of default headers that HTTPie sets:
Any of these can be overwritten and some of them unset (see bellow).
Any of these can be overwritten and some of them unset (see below).
@ -813,7 +822,7 @@ To unset a previously specified header
.. code-block:: bash
$ http httpbin.org/headers Accept: User-Agent:
$ http pie.dev/headers Accept: User-Agent:
To send a header with an empty value, use ``Header;``:
@ -821,7 +830,7 @@ To send a header with an empty value, use ``Header;``:
.. code-block:: bash
$ http httpbin.org/headers 'Header;'
$ http pie.dev/headers 'Header;'
Limiting response headers
@ -833,7 +842,7 @@ HTTPie reads before giving up (the default ``0``, i.e., theres no limit).
.. code-block:: bash
$ http --max-headers=100 httpbin.org/get
$ http --max-headers=100 pie.dev/get
@ -864,13 +873,13 @@ Generating raw requests that can be sent with any other client:
.. code-block:: bash
# 1. save a raw request to a file:
$ http --offline POST httpbin.org/post hello=world > request.http
$ http --offline POST pie.dev/post hello=world > request.http
.. code-block:: bash
# 2. send it over the wire with, for example, the fantastic netcat tool:
$ nc httpbin.org 80 < request.http
$ nc pie.dev 80 < request.http
You can also use the ``--offline`` mode for debugging and exploring HTTP and HTTPie, and for “dry runs”.
@ -893,7 +902,7 @@ Send a single cookie:
.. code-block:: bash
$ http httpbin.org/cookies Cookie:sessionid=foo
$ http pie.dev/cookies Cookie:sessionid=foo
.. code-block:: http
@ -902,7 +911,7 @@ Send a single cookie:
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: sessionid=foo
Host: httpbin.org
Host: pie.dev
User-Agent: HTTPie/0.9.9
@ -911,7 +920,7 @@ Send multiple cookies
.. code-block:: bash
$ http httpbin.org/cookies 'Cookie:sessionid=foo;another-cookie=bar'
$ http pie.dev/cookies 'Cookie:sessionid=foo;another-cookie=bar'
.. code-block:: http
@ -920,7 +929,7 @@ Send multiple cookies
Accept-Encoding: gzip, deflate
Connection: keep-alive
Cookie: sessionid=foo;another-cookie=bar
Host: httpbin.org
Host: pie.dev
User-Agent: HTTPie/0.9.9
@ -957,7 +966,7 @@ Basic auth
.. code-block:: bash
$ http -a username:password httpbin.org/basic-auth/username/password
$ http -a username:password pie.dev/basic-auth/username/password
Digest auth
@ -966,7 +975,7 @@ Digest auth
.. code-block:: bash
$ http -A digest -a username:password httpbin.org/digest-auth/httpie/username/password
$ http -A digest -a username:password pie.dev/digest-auth/httpie/username/password
Password prompt
@ -974,7 +983,7 @@ Password prompt
.. code-block:: bash
$ http -a username httpbin.org/basic-auth/username/password
$ http -a username pie.dev/basic-auth/username/password
Empty password
@ -982,7 +991,7 @@ Empty password
.. code-block:: bash
$ http -a username: httpbin.org/headers
$ http -a username: pie.dev/headers
``.netrc``
@ -996,13 +1005,13 @@ For example:
.. code-block:: bash
$ cat ~/.netrc
machine httpbin.org
machine pie.dev
login httpie
password test
.. code-block:: bash
$ http httpbin.org/basic-auth/httpie/test
$ http pie.dev/basic-auth/httpie/test
HTTP/1.1 200 OK
[...]
@ -1010,7 +1019,7 @@ This can be disabled with the ``--ignore-netrc`` option:
.. code-block:: bash
$ http --ignore-netrc httpbin.org/basic-auth/httpie/test
$ http --ignore-netrc pie.dev/basic-auth/httpie/test
HTTP/1.1 401 UNAUTHORIZED
[...]
@ -1044,7 +1053,7 @@ response is shown:
.. code-block:: bash
$ http httpbin.org/redirect/3
$ http pie.dev/redirect/3
Follow ``Location``
@ -1056,7 +1065,7 @@ and show the final response instead, use the ``--follow, -F`` option:
.. code-block:: bash
$ http --follow httpbin.org/redirect/3
$ http --follow pie.dev/redirect/3
Showing intermediary redirect responses
@ -1068,7 +1077,7 @@ then use the ``--all`` option as well:
.. code-block:: bash
$ http --follow --all httpbin.org/redirect/3
$ http --follow --all pie.dev/redirect/3
@ -1081,7 +1090,7 @@ To change the default limit of maximum ``30`` redirects, use the
.. code-block:: bash
$ http --follow --all --max-redirects=2 httpbin.org/redirect/3
$ http --follow --all --max-redirects=2 pie.dev/redirect/3
Proxies
@ -1141,7 +1150,7 @@ To skip the hosts SSL certificate verification, you can pass ``--verify=no``
.. code-block:: bash
$ http --verify=no https://httpbin.org/get
$ http --verify=no https://pie.dev/get
Custom CA bundle
@ -1200,7 +1209,7 @@ It should be a string in the
.. code-block:: bash
$ http --ciphers=ECDHE-RSA-AES128-GCM-SHA256 https://httpbin.org/get
$ http --ciphers=ECDHE-RSA-AES128-GCM-SHA256 https://pie.dev/get
Note: these cipher strings do not change the negotiated version of SSL or TLS,
they only affect the list of available cipher suites.
@ -1247,7 +1256,7 @@ Print request and response headers:
.. code-block:: bash
$ http --print=Hh PUT httpbin.org/put hello=world
$ http --print=Hh PUT pie.dev/put hello=world
Verbose output
--------------
@ -1257,12 +1266,12 @@ documentation examples:
.. code-block:: bash
$ http --verbose PUT httpbin.org/put hello=world
$ http --verbose PUT pie.dev/put hello=world
PUT /put HTTP/1.1
Accept: application/json, */*;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
Host: httpbin.org
Host: pie.dev
User-Agent: HTTPie/0.2.7dev
{
@ -1285,13 +1294,13 @@ Quiet output
------------
``--quiet`` redirects all output that would otherwise go to ``stdout``
and ``stderr`` (except for error messages) to ``/dev/null``.
and ``stderr`` to ``/dev/null`` (except for errors and warnings).
This doesnt affect output to a file via ``--output`` or ``--download``.
.. code-block:: bash
# There will be no output:
$ http --quiet httpbin.org/post enjoy='the silence'
$ http --quiet pie.dev/post enjoy='the silence'
Viewing intermediary requests/responses
@ -1306,7 +1315,7 @@ authentication is used (``--auth=digest``), etc.
.. code-block:: bash
# Include all responses that lead to the final one:
$ http --all --follow httpbin.org/redirect/3
$ http --all --follow pie.dev/redirect/3
The intermediary requests/response are by default formatted according to
@ -1318,7 +1327,7 @@ arguments as ``--print, -p`` but applies to the intermediary requests only.
.. code-block:: bash
# Print the intermediary requests/responses differently than the final one:
$ http -A digest -a foo:bar --all -p Hh -P H httpbin.org/digest-auth/auth/foo/bar
$ http -A digest -a foo:bar --all -p Hh -P H pie.dev/digest-auth/auth/foo/bar
Conditional body download
@ -1334,7 +1343,7 @@ status code after an update:
.. code-block:: bash
$ http --headers PATCH httpbin.org/patch name='New Name'
$ http --headers PATCH pie.dev/patch name='New Name'
Since we are only printing the HTTP headers here, the connection to the server
@ -1361,49 +1370,49 @@ Redirect from a file:
.. code-block:: bash
$ http PUT httpbin.org/put X-API-Token:123 < files/data.json
$ http PUT pie.dev/put X-API-Token:123 < files/data.json
Or the output of another program:
.. code-block:: bash
$ grep '401 Unauthorized' /var/log/httpd/error_log | http POST httpbin.org/post
$ grep '401 Unauthorized' /var/log/httpd/error_log | http POST pie.dev/post
You can use ``echo`` for simple data:
.. code-block:: bash
$ echo '{"name": "John"}' | http PATCH httpbin.org/patch X-API-Token:123
$ echo '{"name": "John"}' | http PATCH pie.dev/patch X-API-Token:123
You can also use a Bash *here string*:
.. code-block:: bash
$ http httpbin.org/post <<<'{"name": "John"}'
$ http pie.dev/post <<<'{"name": "John"}'
You can even pipe web services together using HTTPie:
.. code-block:: bash
$ http GET https://api.github.com/repos/httpie/httpie | http POST httpbin.org/post
$ http GET https://api.github.com/repos/httpie/httpie | http POST pie.dev/post
You can use ``cat`` to enter multiline data on the terminal:
.. code-block:: bash
$ cat | http POST httpbin.org/post
$ cat | http POST pie.dev/post
<paste>
^D
.. code-block:: bash
$ cat | http POST httpbin.org/post Content-Type:text/plain
$ cat | http POST pie.dev/post Content-Type:text/plain
- buy milk
- call parents
^D
@ -1413,7 +1422,7 @@ On OS X, you can send the contents of the clipboard with ``pbpaste``:
.. code-block:: bash
$ pbpaste | http PUT httpbin.org/put
$ pbpaste | http PUT pie.dev/put
Passing data through ``stdin`` cannot be combined with data fields specified
@ -1442,7 +1451,7 @@ verbatim contents of that XML file with ``Content-Type: application/xml``:
.. code-block:: bash
$ http PUT httpbin.org/put @files/data.xml
$ http PUT pie.dev/put @files/data.xml
File uploads are always streamed to avoid memory issues with large files.
@ -1456,19 +1465,19 @@ You can use the ``--chunked`` flag to instruct HTTPie to use
.. code-block:: bash
$ http --chunked PUT httpbin.org/put hello=world
$ http --chunked PUT pie.dev/put hello=world
.. code-block:: bash
$ http --chunked --multipart PUT httpbin.org/put hello=world foo@files/data.xml
$ http --chunked --multipart PUT pie.dev/put hello=world foo@files/data.xml
.. code-block:: bash
$ http --chunked httpbin.org/post @files/data.xml
$ http --chunked pie.dev/post @files/data.xml
.. code-block:: bash
$ cat files/data.xml | http --chunked httpbin.org/post
$ cat files/data.xml | http --chunked pie.dev/post
@ -1522,7 +1531,7 @@ sorting, and specify a custom JSON indent size:
.. code-block:: bash
$ http --format-options headers.sort:false,json.sort_keys:false,json.indent:2 httpbin.org/get
$ http --format-options headers.sort:false,json.sort_keys:false,json.indent:2 pie.dev/get
This is something you will typically store as one of the default options in your
`config`_ file. See ``http --help`` for all the available formatting options.
@ -1542,7 +1551,7 @@ that the response body is binary,
.. code-block:: bash
$ http httpbin.org/bytes/2000
$ http pie.dev/bytes/2000
You will nearly instantly see something like this:
@ -1575,7 +1584,7 @@ Download a file:
.. code-block:: bash
$ http httpbin.org/image/png > image.png
$ http pie.dev/image/png > image.png
Download an image of Octocat, resize it using ImageMagick, upload it elsewhere:
@ -1590,7 +1599,7 @@ Force colorizing and formatting, and show both the request and the response in
.. code-block:: bash
$ http --pretty=all --verbose httpbin.org/get | less -R
$ http --pretty=all --verbose pie.dev/get | less -R
The ``-R`` flag tells ``less`` to interpret color escape sequences included
@ -1719,17 +1728,15 @@ Prettified streamed response:
.. code-block:: bash
$ http --stream -f -a YOUR-TWITTER-NAME https://stream.twitter.com/1/statuses/filter.json track='Justin Bieber'
$ http --stream pie.dev/stream/3
Streamed output by small chunks à la ``tail -f``:
.. code-block:: bash
# Send each new tweet (JSON object) mentioning "Apple" to another
# server as soon as it arrives from the Twitter streaming API:
$ http --stream -f -a YOUR-TWITTER-NAME https://stream.twitter.com/1/statuses/filter.json track=Apple \
| while read tweet; do echo "$tweet" | http POST example.org/tweets ; done
# Send each new line (JSON object) to another URL as soon as it arrives from a streaming API:
$ http --stream pie.dev/stream/3 | while read line; do echo "$line" | http pie.dev/post ; done
Sessions
========
@ -1749,7 +1756,7 @@ to the same host.
.. code-block:: bash
# Create a new session:
$ http --session=./session.json httpbin.org/headers API-Token:123
$ http --session=./session.json pie.dev/headers API-Token:123
.. code-block:: bash
@ -1760,7 +1767,7 @@ to the same host.
.. code-block:: bash
# Re-use the existing session — the API-Token header will be set:
$ http --session=./session.json httpbin.org/headers
$ http --session=./session.json pie.dev/headers
All session data, including credentials, cookie data,
@ -1775,11 +1782,11 @@ Named sessions
You can create one or more named session per host. For example, this is how
you can create a new session named ``user1`` for ``httpbin.org``:
you can create a new session named ``user1`` for ``pie.dev``:
.. code-block:: bash
$ http --session=user1 -a user1:password httpbin.org/get X-Foo:Bar
$ http --session=user1 -a user1:password pie.dev/get X-Foo:Bar
From now on, you can refer to the session by its name (``user1``). When you choose
to use the session again, any previously specified authentication or HTTP headers
@ -1787,13 +1794,13 @@ will automatically be set:
.. code-block:: bash
$ http --session=user1 httpbin.org/get
$ http --session=user1 pie.dev/get
To create or reuse a different session, simple specify a different name:
.. code-block:: bash
$ http --session=user2 -a user2:password httpbin.org/get X-Bar:Foo
$ http --session=user2 -a user2:password pie.dev/get X-Bar:Foo
Named sessionss data is stored in JSON files inside the ``sessions``
subdirectory of the `config`_ directory, typically:
@ -1806,7 +1813,7 @@ you should be able list the generated sessions files using:
.. code-block:: bash
$ ls -l ~/.config/httpie/sessions/httpbin.org
$ ls -l ~/.config/httpie/sessions/pie.dev
Anonymous sessions
@ -1848,12 +1855,12 @@ exchange after it has been created, specify the session name via
.. code-block:: bash
# If the session file doesnt exist, then it is created:
$ http --session-read-only=./ro-session.json httpbin.org/headers Custom-Header:orig-value
$ http --session-read-only=./ro-session.json pie.dev/headers Custom-Header:orig-value
.. code-block:: bash
# But it is not updated:
$ http --session-read-only=./ro-session.json httpbin.org/headers Custom-Header:new-value
$ http --session-read-only=./ro-session.json pie.dev/headers Custom-Header:new-value
Cookie Storage Behaviour
------------------------
@ -1866,13 +1873,13 @@ To set a cookie within a Session there are three options:
.. code-block:: bash
$ http --session=./session.json httpbin.org/cookie/set?foo=bar
$ http --session=./session.json pie.dev/cookie/set?foo=bar
2. Set the cookie name and value through the command line as seen in `cookies`_
.. code-block:: bash
$ http --session=./session.json httpbin.org/headers Cookie:foo=bar
$ http --session=./session.json pie.dev/headers Cookie:foo=bar
3. Manually set cookie parameters in the json file of the session
@ -1937,7 +1944,7 @@ environment variable:
.. code-block:: bash
$ export HTTPIE_CONFIG_DIR=/tmp/httpie
$ http httpbin.org/get
$ http pie.dev/get
@ -1998,7 +2005,7 @@ respectively.
#!/bin/bash
if http --check-status --ignore-stdin --timeout=2.5 HEAD httpbin.org/get &> /dev/null; then
if http --check-status --ignore-stdin --timeout=2.5 HEAD pie.dev/get &> /dev/null; then
echo 'OK!'
else
case $? in
@ -2047,7 +2054,7 @@ HTTP request:
.. code-block:: http
POST /post HTTP/1.1
Host: httpbin.org
Host: pie.dev
X-API-Key: 123
User-Agent: Bacon/1.0
Content-Type: application/x-www-form-urlencoded
@ -2059,7 +2066,7 @@ with the HTTPie command that sends it:
.. code-block:: bash
$ http -f POST httpbin.org/post \
$ http -f POST pie.dev/post \
X-API-Key:123 \
User-Agent:Bacon/1.0 \
name=value \
@ -2082,15 +2089,15 @@ HTTPie reaches its final version ``1.0``. All changes are recorded in the
User support
------------
Community and Support
---------------------
Please use the following support channels:
HTTPie has the following community channels:
* `GitHub issues <https://github.com/jkbr/httpie/issues>`_
for bug reports and feature requests.
* `Our Gitter chat room <https://gitter.im/jkbrzt/httpie>`_
to ask questions, discuss features, and for general discussion.
* `Discord server <https://httpie.io/chat>`_
to ask questions, discuss features, and for general API development discussion.
* `StackOverflow <https://stackoverflow.com>`_
to ask questions (please make sure to use the
`httpie <https://stackoverflow.com/questions/tagged/httpie>`_ tag).
@ -2195,9 +2202,9 @@ have contributed.
:target: https://github.com/httpie/httpie/actions
:alt: Build status of the master branch on Mac/Linux/Windows
.. |gitter| image:: https://img.shields.io/gitter/room/jkbrzt/httpie.svg?style=flat-square
:target: https://gitter.im/jkbrzt/httpie
:alt: Chat on Gitter
.. |gitter| image:: https://img.shields.io/badge/chat-on%20Discord-brightgreen?style=flat-square
:target: https://httpie.io/chat
:alt: Chat on Discord
.. |downloads| image:: https://pepy.tech/badge/httpie
:target: https://pepy.tech/project/httpie

View File

@ -9,22 +9,20 @@ class Httpie < Formula
desc "User-friendly cURL replacement (command-line HTTP client)"
homepage "https://httpie.org/"
url "https://files.pythonhosted.org/packages/37/6c/0d050f49e3b2bac589367d0c3aee9c078e23c6914b0210ffc0117218bdaf/httpie-2.2.0.tar.gz"
sha256 "31ac28088ee6a0b6f3ba7a53379000c4d1910c1708c9ff768f84b111c14405a0"
head "https://github.com/jakubroztocil/httpie.git"
url "https://files.pythonhosted.org/packages/b4/d4/712645808103f2d15c281b9eacd184c88754ef7e9a322d9a30ba343fd341/httpie-2.3.0.tar.gz"
sha256 "d540571991d07329d217c31bf1ff95fd217957da2aa2def09bcfa0c0fca0cf96"
license "BSD-3-Clause"
head "https://github.com/httpie/httpie.git"
bottle do
cellar :any_skip_relocation
sha256 "25f0e58f81a2cdd9cba772f07d67591533b4b31a2b970a356701aa046d4d9638" => :catalina
sha256 "be158ebb4cfd327ebea02f7b8b8d63d093e474cd303eafff4a2b56b0611983a2" => :mojave
sha256 "f331edb94183bfc5fa9de4b4abf148cc91a3a8b3c0e24cc1f5e6b0a4172dd34d" => :high_sierra
livecheck do
url :stable
end
depends_on "python@3.8"
depends_on "python@3.9"
resource "Pygments" do
url "https://files.pythonhosted.org/packages/6e/4d/4d2fe93a35dfba417311a4ff627489a947b01dc0cc377a3673c00cf7e4b2/Pygments-2.6.1.tar.gz"
sha256 "647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"
url "https://files.pythonhosted.org/packages/5d/0e/ff13c055b014d634ed17e9e9345a312c28ec6a06448ba6d6ccfa77c3b5e8/Pygments-2.7.2.tar.gz"
sha256 "381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0"
end
resource "requests" do
@ -32,19 +30,24 @@ class Httpie < Formula
sha256 "b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"
end
resource "requests-toolbelt" do
url "https://files.pythonhosted.org/packages/28/30/7bf7e5071081f761766d46820e52f4b16c8a08fef02d2eb4682ca7534310/requests-toolbelt-0.9.1.tar.gz"
sha256 "968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"
end
resource "certifi" do
url "https://files.pythonhosted.org/packages/b4/19/53433f37a31543364c8676f30b291d128cdf4cd5b31b755b7890f8e89ac8/certifi-2020.4.5.2.tar.gz"
sha256 "5ad7e9a056d25ffa5082862e36f119f7f7cec6457fa07ee2f8c339814b80c9b1"
url "https://files.pythonhosted.org/packages/40/a7/ded59fa294b85ca206082306bba75469a38ea1c7d44ea7e1d64f5443d67a/certifi-2020.6.20.tar.gz"
sha256 "5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"
end
resource "urllib3" do
url "https://files.pythonhosted.org/packages/05/8c/40cd6949373e23081b3ea20d5594ae523e681b6f472e600fbc95ed046a36/urllib3-1.25.9.tar.gz"
sha256 "3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527"
url "https://files.pythonhosted.org/packages/76/d9/bbbafc76b18da706451fa91bc2ebe21c0daf8868ef3c30b869ac7cb7f01d/urllib3-1.25.11.tar.gz"
sha256 "8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2"
end
resource "idna" do
url "https://files.pythonhosted.org/packages/cb/19/57503b5de719ee45e83472f339f617b0c01ad75cba44aba1e4c97c2b0abd/idna-2.9.tar.gz"
sha256 "7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"
url "https://files.pythonhosted.org/packages/ea/b7/e0e3c1c467636186c39925827be42f16fee389dc404ac29e930e9136be70/idna-2.10.tar.gz"
sha256 "b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"
end
resource "chardet" do

View File

@ -3,6 +3,6 @@ HTTPie: command-line HTTP client for the API era.
"""
__version__ = '2.3.0-dev'
__version__ = '2.4.0'
__author__ = 'Jakub Roztocil'
__licence__ = 'BSD'

View File

@ -5,15 +5,6 @@ import enum
import re
# TODO: Use MultiDict for headers once added to `requests`.
# <https://github.com/jakubroztocil/httpie/issues/130>
# ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
# <https://tools.ietf.org/html/rfc3986#section-3.1>
from enum import Enum
URL_SCHEME_RE = re.compile(r'^[a-z][a-z0-9.+-]*://', re.IGNORECASE)
HTTP_POST = 'POST'

View File

@ -37,7 +37,7 @@ parser = HTTPieArgumentParser(
Suggestions and bug reports are greatly appreciated:
https://github.com/jakubroztocil/httpie/issues
https://github.com/httpie/httpie/issues
'''),
)

View File

@ -1,5 +1,4 @@
import os
from io import BytesIO
from typing import Callable, Dict, IO, List, Optional, Tuple, Union
from httpie.cli.argtypes import KeyValueArg

View File

@ -134,7 +134,7 @@ def collect_messages(
# noinspection PyProtectedMember
@contextmanager
def max_headers(limit):
# <https://github.com/jakubroztocil/httpie/issues/802>
# <https://github.com/httpie/httpie/issues/802>
# noinspection PyUnresolvedReferences
orig = http.client._MAXHEADERS
http.client._MAXHEADERS = limit or float('Inf')
@ -188,7 +188,7 @@ def finalize_headers(headers: RequestHeadersDict) -> RequestHeadersDict:
# Also, requests raises `InvalidHeader` for leading spaces.
value = value.strip()
if isinstance(value, str):
# See <https://github.com/jakubroztocil/httpie/issues/212>
# See <https://github.com/httpie/httpie/issues/212>
value = value.encode('utf8')
final_headers[name] = value
return final_headers
@ -304,7 +304,7 @@ def ensure_path_as_is(orig_url: str, prepped_url: str) -> str:
untouched because other (welcome) processing on the URL might have
taken place.
<https://github.com/jakubroztocil/httpie/issues/895>
<https://github.com/httpie/httpie/issues/895>
<https://ec.haxx.se/http/http-basics#path-as-is>

View File

@ -9,26 +9,17 @@ from pygments import __version__ as pygments_version
from requests import __version__ as requests_version
from httpie import __version__ as httpie_version
from httpie.cli.constants import (
OUT_REQ_BODY, OUT_REQ_HEAD, OUT_RESP_BODY,
OUT_RESP_HEAD,
)
from httpie.cli.constants import OUT_REQ_BODY, OUT_REQ_HEAD, OUT_RESP_BODY, OUT_RESP_HEAD
from httpie.client import collect_messages
from httpie.context import Environment
from httpie.downloads import Downloader
from httpie.output.writer import (
write_message,
write_stream,
)
from httpie.output.writer import write_message, write_stream, MESSAGE_SEPARATOR_BYTES
from httpie.plugins.registry import plugin_manager
from httpie.status import ExitStatus, http_status_to_exit_status
# noinspection PyDefaultArgument
def main(
args: List[Union[str, bytes]] = sys.argv,
env=Environment(),
) -> ExitStatus:
def main(args: List[Union[str, bytes]] = sys.argv, env=Environment()) -> ExitStatus:
"""
The main function.
@ -134,112 +125,82 @@ def get_output_options(
}[type(message)]
def program(
args: argparse.Namespace,
env: Environment,
) -> ExitStatus:
def program(args: argparse.Namespace, env: Environment) -> ExitStatus:
"""
The main program without error handling.
"""
# TODO: Refactor and drastically simplify, especially so that the separator logic is elsewhere.
exit_status = ExitStatus.SUCCESS
downloader = None
initial_request: Optional[requests.PreparedRequest] = None
final_response: Optional[requests.Response] = None
def separate():
getattr(env.stdout, 'buffer', env.stdout).write(MESSAGE_SEPARATOR_BYTES)
def request_body_read_callback(chunk: bytes):
should_pipe_to_stdout = bool(
# Request body output desired
OUT_REQ_BODY in args.output_options
# & not `.read()` already pre-request (e.g., for compression)
and initial_request
# & non-EOF chunk
and chunk
)
if should_pipe_to_stdout:
msg = requests.PreparedRequest()
msg.is_body_upload_chunk = True
msg.body = chunk
msg.headers = initial_request.headers
write_message(requests_message=msg, env=env, args=args, with_body=True, with_headers=False)
try:
if args.download:
args.follow = True # --download implies --follow.
downloader = Downloader(
output_file=args.output_file,
progress_file=env.stderr,
resume=args.download_resume
)
downloader = Downloader(output_file=args.output_file, progress_file=env.stderr, resume=args.download_resume)
downloader.pre_request(args.headers)
messages = collect_messages(args=args, config_dir=env.config.directory,
request_body_read_callback=request_body_read_callback)
force_separator = False
prev_with_body = False
needs_separator = False
def maybe_separate():
nonlocal needs_separator
if env.stdout.isatty() and needs_separator:
needs_separator = False
getattr(env.stdout, 'buffer', env.stdout).write(b'\n\n')
initial_request: Optional[requests.PreparedRequest] = None
final_response: Optional[requests.Response] = None
def request_body_read_callback(chunk: bytes):
should_pipe_to_stdout = (
# Request body output desired
OUT_REQ_BODY in args.output_options
# & not `.read()` already pre-request (e.g., for compression)
and initial_request
# & non-EOF chunk
and chunk
)
if should_pipe_to_stdout:
msg = requests.PreparedRequest()
msg.is_body_upload_chunk = True
msg.body = chunk
msg.headers = initial_request.headers
write_message(
requests_message=msg,
env=env,
args=args,
with_body=True,
with_headers=False
)
messages = collect_messages(
args=args,
config_dir=env.config.directory,
request_body_read_callback=request_body_read_callback
)
# Process messages as theyre generated
for message in messages:
maybe_separate()
is_request = isinstance(message, requests.PreparedRequest)
with_headers, with_body = get_output_options(
args=args, message=message)
with_headers, with_body = get_output_options(args=args, message=message)
do_write_body = with_body
if prev_with_body and (with_headers or with_body) and (force_separator or not env.stdout_isatty):
# Separate after a previous message with body, if needed. See test_tokens.py.
separate()
force_separator = False
if is_request:
if not initial_request:
initial_request = message
is_streamed_upload = not isinstance(
message.body, (str, bytes))
is_streamed_upload = not isinstance(message.body, (str, bytes))
if with_body:
with_body = not is_streamed_upload
needs_separator = is_streamed_upload
do_write_body = not is_streamed_upload
force_separator = is_streamed_upload and env.stdout_isatty
else:
final_response = message
if args.check_status or downloader:
exit_status = http_status_to_exit_status(
http_status=message.status_code,
follow=args.follow
)
if (not env.stdout_isatty
and exit_status != ExitStatus.SUCCESS):
env.log_error(
f'HTTP {message.raw.status} {message.raw.reason}',
level='warning'
)
write_message(
requests_message=message,
env=env,
args=args,
with_headers=with_headers,
with_body=with_body,
)
maybe_separate()
exit_status = http_status_to_exit_status(http_status=message.status_code, follow=args.follow)
if exit_status != ExitStatus.SUCCESS and (not env.stdout_isatty or args.quiet):
env.log_error(f'HTTP {message.raw.status} {message.raw.reason}', level='warning')
write_message(requests_message=message, env=env, args=args, with_headers=with_headers,
with_body=do_write_body)
prev_with_body = with_body
# Cleanup
if force_separator:
separate()
if downloader and exit_status == ExitStatus.SUCCESS:
# Last response body download.
download_stream, download_to = downloader.start(
initial_url=initial_request.url,
final_response=final_response,
)
write_stream(
stream=download_stream,
outfile=download_to,
flush=False,
)
write_stream(stream=download_stream, outfile=download_to, flush=False)
downloader.finish()
if downloader.interrupted:
exit_status = ExitStatus.ERROR
@ -253,9 +214,7 @@ def program(
finally:
if downloader and not downloader.finished:
downloader.failed()
if (not isinstance(args, list) and args.output_file
and args.output_file_specified):
if not isinstance(args, list) and args.output_file and args.output_file_specified:
args.output_file.close()
@ -283,6 +242,6 @@ def decode_raw_args(
"""
return [
arg.decode(stdin_encoding)
if type(arg) == bytes else arg
if type(arg) is bytes else arg
for arg in args
]

View File

@ -247,7 +247,7 @@ class Downloader:
assert not self.status.time_started
# FIXME: some servers still might sent Content-Encoding: gzip
# <https://github.com/jakubroztocil/httpie/issues/423>
# <https://github.com/httpie/httpie/issues/423>
try:
total_size = int(final_response.headers['Content-Length'])
except (KeyError, ValueError, TypeError):

View File

@ -12,6 +12,10 @@ from httpie.output.streams import (
)
MESSAGE_SEPARATOR = '\n\n'
MESSAGE_SEPARATOR_BYTES = MESSAGE_SEPARATOR.encode()
def write_message(
requests_message: Union[requests.PreparedRequest, requests.Response],
env: Environment,
@ -111,7 +115,7 @@ def build_output_stream_for_message(
and not getattr(requests_message, 'is_body_upload_chunk', False)):
# Ensure a blank line after the response body.
# For terminal output only.
yield b'\n\n'
yield MESSAGE_SEPARATOR_BYTES
def get_stream_type_and_kwargs(

View File

@ -19,7 +19,7 @@ class HTTPBasicAuth(requests.auth.HTTPBasicAuth):
"""
Override username/password serialization to allow unicode.
See https://github.com/jakubroztocil/httpie/issues/212
See https://github.com/httpie/httpie/issues/212
"""
# noinspection PyTypeChecker

View File

@ -77,7 +77,9 @@ class Session(BaseConfigDict):
if value is None:
continue # Ignore explicitly unset headers
value = value.decode('utf8')
if type(value) is not str:
value = value.decode('utf8')
if name.lower() == 'user-agent' and value.startswith('HTTPie/'):
continue

View File

@ -109,6 +109,8 @@ def get_expired_cookies(
for attrs in attr_sets
]
_max_age_to_expires(cookies=cookies, now=now)
return [
{
'name': cookie['name'],
@ -117,3 +119,18 @@ def get_expired_cookies(
for cookie in cookies
if is_expired(expires=cookie.get('expires'))
]
def _max_age_to_expires(cookies, now):
"""
Translate `max-age` into `expires` for Requests to take it into account.
HACK/FIXME: <https://github.com/psf/requests/issues/5743>
"""
for cookie in cookies:
if 'expires' in cookie:
continue
max_age = cookie.get('max-age')
if max_age and max_age.isdigit():
cookie['expires'] = now + float(max_age)

View File

@ -4,7 +4,7 @@
[tool:pytest]
# <https://docs.pytest.org/en/latest/customize.html>
norecursedirs = tests/fixtures
addopts = --tb=native
addopts = --tb=native --doctest-modules
[pycodestyle]

View File

@ -15,11 +15,14 @@ class PyTest(TestCommand):
and runs the tests with no fancy stuff like parallel execution.
"""
def finalize_options(self):
TestCommand.finalize_options(self)
self.test_args = [
'--doctest-modules', '--verbose',
'./httpie', './tests'
'--doctest-modules',
'--verbose',
'./httpie',
'./tests',
]
self.test_suite = True
@ -71,8 +74,9 @@ setup(
version=httpie.__version__,
description=httpie.__doc__.strip(),
long_description=long_description(),
long_description_content_type='text/x-rst',
url='https://httpie.org/',
download_url=f'https://github.com/jakubroztocil/httpie/archive/{httpie.__version__}.tar.gz',
download_url=f'https://github.com/httpie/httpie/archive/{httpie.__version__}.tar.gz',
author=httpie.__author__,
author_email='jakub@roztocil.co',
license=httpie.__licence__,
@ -104,10 +108,9 @@ setup(
'Topic :: Utilities'
],
project_urls={
'Documentation': 'https://httpie.org/docs',
'Source': 'https://github.com/jakubroztocil/httpie',
'Online Demo': 'https://httpie.org/run',
'Donate': 'https://httpie.org/donate',
'GitHub': 'https://github.com/httpie/httpie',
'Twitter': 'https://twitter.com/httpie',
'Documentation': 'https://httpie.org/docs',
'Online Demo': 'https://httpie.org/run',
},
)

View File

@ -5,4 +5,4 @@ HTTPie Test Suite
Please see `CONTRIBUTING`_.
.. _CONTRIBUTING: https://github.com/jakubroztocil/httpie/blob/master/CONTRIBUTING.rst
.. _CONTRIBUTING: https://github.com/httpie/httpie/blob/master/CONTRIBUTING.rst

View File

@ -58,7 +58,7 @@ def test_credentials_in_url_auth_flag_has_priority(httpbin_both):
])
def test_only_username_in_url(url):
"""
https://github.com/jakubroztocil/httpie/issues/242
https://github.com/httpie/httpie/issues/242
"""
args = httpie.cli.definition.parser.parse_args(args=[url], env=MockEnvironment())

View File

@ -11,7 +11,7 @@ from fixtures import FILE_PATH
def test_default_headers_case_insensitive(httpbin):
"""
<https://github.com/jakubroztocil/httpie/issues/644>
<https://github.com/httpie/httpie/issues/644>
"""
r = http(
'--debug',
@ -63,7 +63,7 @@ class TestAutoContentTypeAndAcceptHeaders:
"""
def test_GET_no_data_no_auto_headers(self, httpbin):
# https://github.com/jakubroztocil/httpie/issues/62
# https://github.com/httpie/httpie/issues/62
r = http('GET', httpbin.url + '/headers')
assert HTTP_OK in r
assert r.json['headers']['Accept'] == '*/*'
@ -94,7 +94,7 @@ class TestAutoContentTypeAndAcceptHeaders:
assert HTTP_OK in r
assert r.json['headers']['Accept'] == JSON_ACCEPT
# Make sure Content-Type gets set even with no data.
# https://github.com/jakubroztocil/httpie/issues/137
# https://github.com/httpie/httpie/issues/137
assert 'application/json' in r.json['headers']['Content-Type']
def test_GET_explicit_JSON_explicit_headers(self, httpbin):

View File

@ -5,7 +5,6 @@ from urllib.request import urlopen
import pytest
import mock
import requests
from requests.structures import CaseInsensitiveDict
from httpie.downloads import (

View File

@ -39,7 +39,7 @@ def test_debug():
def test_help():
r = http('--help', tolerate_error_exit_status=True)
assert r.exit_status == ExitStatus.SUCCESS
assert 'https://github.com/jakubroztocil/httpie/issues' in r
assert 'https://github.com/httpie/httpie/issues' in r
def test_version():
@ -121,7 +121,7 @@ def test_POST_file(httpbin_both):
def test_form_POST_file_redirected_stdin(httpbin):
"""
<https://github.com/jakubroztocil/httpie/issues/840>
<https://github.com/httpie/httpie/issues/840>
"""
with open(FILE_PATH) as f:
@ -182,4 +182,4 @@ def test_json_input_preserve_order(httpbin_both):
'order:={"map":{"1":"first","2":"second"}}')
assert HTTP_OK in r
assert r.json['data'] == \
'{"order": {"map": {"1": "first", "2": "second"}}}'
'{"order": {"map": {"1": "first", "2": "second"}}}'

View File

@ -54,6 +54,21 @@ class TestQuietFlag:
assert r == ''
assert r.stderr == ''
def test_quiet_with_check_status_non_zero(self, httpbin):
r = http(
'--quiet', '--check-status', httpbin + '/status/500',
tolerate_error_exit_status=True,
)
assert 'http: warning: HTTP 500' in r.stderr
def test_quiet_with_check_status_non_zero_pipe(self, httpbin):
r = http(
'--quiet', '--check-status', httpbin + '/status/500',
tolerate_error_exit_status=True,
env=MockEnvironment(stdout_isatty=False)
)
assert 'http: warning: HTTP 500' in r.stderr
@mock.patch('httpie.cli.argtypes.AuthCredentials._getpass',
new=lambda self, prompt: 'password')
def test_quiet_with_password_prompt(self, httpbin):
@ -127,7 +142,7 @@ class TestVerboseFlag:
assert r.count('__test__') == 2
def test_verbose_form(self, httpbin):
# https://github.com/jakubroztocil/httpie/issues/53
# https://github.com/httpie/httpie/issues/53
r = http('--verbose', '--form', 'POST', httpbin.url + '/post',
'A=B', 'C=D')
assert HTTP_OK in r

View File

@ -1,16 +1,17 @@
"""Miscellaneous regression tests"""
import pytest
from utils import http, HTTP_OK
from httpie.compat import is_windows
from tests.utils.matching import assert_output_matches, Expect
from utils import HTTP_OK, MockEnvironment, http
def test_Host_header_overwrite(httpbin):
"""
https://github.com/jakubroztocil/httpie/issues/235
https://github.com/httpie/httpie/issues/235
"""
host = 'httpbin.org'
host = 'pie.dev'
url = httpbin.url + '/get'
r = http('--print=hH', url, 'host:{0}'.format(host))
assert HTTP_OK in r
@ -21,7 +22,28 @@ def test_Host_header_overwrite(httpbin):
@pytest.mark.skipif(is_windows, reason='Unix-only')
def test_output_devnull(httpbin):
"""
https://github.com/jakubroztocil/httpie/issues/252
https://github.com/httpie/httpie/issues/252
"""
http('--output=/dev/null', httpbin + '/get')
def test_verbose_redirected_stdout_separator(httpbin):
"""
<https://github.com/httpie/httpie/issues/1006>
"""
r = http(
'-v',
httpbin.url + '/post',
'a=b',
env=MockEnvironment(stdout_isatty=False),
)
assert '}HTTP/' not in r
assert_output_matches(r, [
Expect.REQUEST_HEADERS,
Expect.BODY,
Expect.SEPARATOR,
Expect.RESPONSE_HEADERS,
Expect.BODY,
])

View File

@ -16,6 +16,7 @@ from httpie.sessions import Session
from httpie.utils import get_expired_cookies
from tests.test_auth_plugins import basic_auth
from utils import HTTP_OK, MockEnvironment, http, mk_config_dir
from fixtures import FILE_PATH_ARG
class SessionTestBase:
@ -161,6 +162,12 @@ class TestSession(SessionTestBase):
assert 'Content-Type' not in r2.json['headers']
assert 'If-Unmodified-Since' not in r2.json['headers']
def test_session_with_upload(self, httpbin):
self.start_session(httpbin)
r = http('--session=test', '--form', '--verbose', 'POST', httpbin.url + '/post',
f'test-file@{FILE_PATH_ARG}', 'foo=bar', env=self.env())
assert HTTP_OK in r
def test_session_by_path(self, httpbin):
self.start_session(httpbin)
session_path = self.config_dir / 'session-by-path.json'
@ -193,7 +200,7 @@ class TestSession(SessionTestBase):
def test_session_default_header_value_overwritten(self, httpbin):
self.start_session(httpbin)
# https://github.com/jakubroztocil/httpie/issues/180
# https://github.com/httpie/httpie/issues/180
r1 = http('--session=test',
httpbin.url + '/headers', 'User-Agent:custom',
env=self.env())
@ -205,7 +212,7 @@ class TestSession(SessionTestBase):
assert r2.json['headers']['User-Agent'] == 'custom'
def test_download_in_session(self, httpbin):
# https://github.com/jakubroztocil/httpie/issues/412
# https://github.com/httpie/httpie/issues/412
self.start_session(httpbin)
cwd = os.getcwd()
os.chdir(gettempdir())
@ -338,6 +345,15 @@ class TestExpiredCookies(CookieTestBase):
assert 'cookie1' in updated_session['cookies']
assert 'cookie2' not in updated_session['cookies']
def test_get_expired_cookies_using_max_age(self):
headers = [
('Set-Cookie', 'one=two; Max-Age=0; path=/; domain=.tumblr.com; HttpOnly')
]
expected_expired = [
{'name': 'one', 'path': '/'}
]
assert get_expired_cookies(headers, now=None) == expected_expired
@pytest.mark.parametrize(
argnames=['headers', 'now', 'expected_expired'],
argvalues=[

View File

@ -11,7 +11,7 @@ from utils import HTTP_OK, TESTS_ROOT, http
try:
# Handle OpenSSL errors, if installed.
# See <https://github.com/jakubroztocil/httpie/issues/729>
# See <https://github.com/httpie/httpie/issues/729>
# noinspection PyUnresolvedReferences
import OpenSSL.SSL
ssl_errors = (

141
tests/test_tokens.py Normal file
View File

@ -0,0 +1,141 @@
"""
The ideas behind these test and the named templates is to ensure consistent output
across all supported different scenarios:
TODO: cover more scenarios
* terminal vs. redirect stdout
* different combinations of `--print=HBhb` (request/response headers/body)
* multipart requests
* streamed uploads
"""
from tests.utils.matching import assert_output_matches, Expect
from utils import http, HTTP_OK, MockEnvironment, HTTPBIN_WITH_CHUNKED_SUPPORT
RAW_REQUEST = [
Expect.REQUEST_HEADERS,
Expect.BODY,
]
RAW_RESPONSE = [
Expect.RESPONSE_HEADERS,
Expect.BODY,
]
RAW_EXCHANGE = [
*RAW_REQUEST,
Expect.SEPARATOR, # Good choice?
*RAW_RESPONSE,
]
RAW_BODY = [
Expect.BODY,
]
TERMINAL_REQUEST = [
*RAW_REQUEST,
Expect.SEPARATOR,
]
TERMINAL_RESPONSE = [
*RAW_RESPONSE,
Expect.SEPARATOR,
]
TERMINAL_EXCHANGE = [
*TERMINAL_REQUEST,
*TERMINAL_RESPONSE,
]
TERMINAL_BODY = [
RAW_BODY,
Expect.SEPARATOR
]
def test_headers():
r = http('--print=H', '--offline', 'pie.dev')
assert_output_matches(r, [Expect.REQUEST_HEADERS])
def test_redirected_headers():
r = http('--print=H', '--offline', 'pie.dev', env=MockEnvironment(stdout_isatty=False))
assert_output_matches(r, [Expect.REQUEST_HEADERS])
def test_terminal_headers_and_body():
r = http('--print=HB', '--offline', 'pie.dev', 'AAA=BBB')
assert_output_matches(r, TERMINAL_REQUEST)
def test_terminal_request_headers_response_body(httpbin):
r = http('--print=Hb', httpbin + '/get')
assert_output_matches(r, TERMINAL_REQUEST)
def test_raw_request_headers_response_body(httpbin):
r = http('--print=Hb', httpbin + '/get', env=MockEnvironment(stdout_isatty=False))
assert_output_matches(r, RAW_REQUEST)
def test_terminal_request_headers_response_headers(httpbin):
r = http('--print=Hh', httpbin + '/get')
assert_output_matches(r, [Expect.REQUEST_HEADERS, Expect.RESPONSE_HEADERS])
def test_raw_request_headers_response_headers(httpbin):
r = http('--print=Hh', httpbin + '/get')
assert_output_matches(r, [Expect.REQUEST_HEADERS, Expect.RESPONSE_HEADERS])
def test_terminal_request_body_response_body(httpbin):
r = http('--print=Hh', httpbin + '/get')
assert_output_matches(r, [Expect.REQUEST_HEADERS, Expect.RESPONSE_HEADERS])
def test_raw_headers_and_body():
r = http(
'--print=HB', '--offline', 'pie.dev', 'AAA=BBB',
env=MockEnvironment(stdout_isatty=False),
)
assert_output_matches(r, RAW_REQUEST)
def test_raw_body():
r = http(
'--print=B', '--offline', 'pie.dev', 'AAA=BBB',
env=MockEnvironment(stdout_isatty=False),
)
assert_output_matches(r, RAW_BODY)
def test_raw_exchange(httpbin):
r = http('--verbose', httpbin + '/post', 'a=b', env=MockEnvironment(stdout_isatty=False))
assert HTTP_OK in r
assert_output_matches(r, RAW_EXCHANGE)
def test_terminal_exchange(httpbin):
r = http('--verbose', httpbin + '/post', 'a=b')
assert HTTP_OK in r
assert_output_matches(r, TERMINAL_EXCHANGE)
def test_headers_multipart_body_separator():
r = http('--print=HB', '--multipart', '--offline', 'pie.dev', 'AAA=BBB')
assert_output_matches(r, TERMINAL_REQUEST)
def test_redirected_headers_multipart_no_separator():
r = http(
'--print=HB', '--multipart', '--offline', 'pie.dev', 'AAA=BBB',
env=MockEnvironment(stdout_isatty=False),
)
assert_output_matches(r, RAW_REQUEST)
def test_verbose_chunked():
r = http('--verbose', '--chunked', HTTPBIN_WITH_CHUNKED_SUPPORT + '/post', 'hello=world')
assert HTTP_OK in r
assert 'Transfer-Encoding: chunked' in r
assert_output_matches(r, TERMINAL_EXCHANGE)
def test_request_headers_response_body(httpbin):
r = http('--print=Hb', httpbin + '/get')
assert_output_matches(r, TERMINAL_REQUEST)

View File

@ -52,6 +52,23 @@ def test_chunked_stdin():
assert r.count(FILE_CONTENT) == 2
def test_chunked_stdin_multiple_chunks():
stdin_bytes = FILE_PATH.read_bytes() + b'\n' + FILE_PATH.read_bytes()
r = http(
'--verbose',
'--chunked',
HTTPBIN_WITH_CHUNKED_SUPPORT + '/post',
env=MockEnvironment(
stdin=StdinBytesIO(stdin_bytes),
stdin_isatty=False,
stdout_isatty=True,
)
)
assert HTTP_OK in r
assert 'Transfer-Encoding: chunked' in r
assert r.count(FILE_CONTENT) == 4
class TestMultipartFormDataFileUpload:
def test_non_existent_file_raises_parse_error(self, httpbin):

View File

@ -1,12 +1,14 @@
# coding=utf-8
"""Utilities for HTTPie test suite."""
import re
import shlex
import sys
import time
import json
import tempfile
from io import BytesIO
from pathlib import Path
from typing import Optional, Union
from typing import Optional, Union, List
from httpie.status import ExitStatus
from httpie.config import Config
@ -17,10 +19,10 @@ from httpie.core import main
# pytest-httpbin currently does not support chunked requests:
# <https://github.com/kevin1024/pytest-httpbin/issues/33>
# <https://github.com/kevin1024/pytest-httpbin/issues/28>
HTTPBIN_WITH_CHUNKED_SUPPORT = 'http://httpbin.org'
HTTPBIN_WITH_CHUNKED_SUPPORT = 'http://pie.dev'
TESTS_ROOT = Path(__file__).parent
TESTS_ROOT = Path(__file__).parent.parent
CRLF = '\r\n'
COLOR = '\x1b['
HTTP_OK = '200 OK'
@ -49,7 +51,7 @@ class StdinBytesIO(BytesIO):
class MockEnvironment(Environment):
"""Environment subclass with reasonable defaults for testing."""
colors = 0
colors = 0 # For easier debugging
stdin_isatty = True,
stdout_isatty = True
is_windows = False
@ -113,6 +115,15 @@ class BaseCLIResponse:
devnull: str = None
json: dict = None
exit_status: ExitStatus = None
command: str = None
args: List[str] = []
complete_args: List[str] = []
@property
def command(self):
cmd = ' '.join(shlex.quote(arg) for arg in ['http', *self.args])
# pytest-httpbin to real httpbin.
return re.sub(r'127\.0\.0\.1:\d+', 'httpbin.org', cmd)
class BytesCLIResponse(bytes, BaseCLIResponse):
@ -198,7 +209,7 @@ def http(
Example:
$ http --auth=user:password GET httpbin.org/basic-auth/user/password
$ http --auth=user:password GET pie.dev/basic-auth/user/password
>>> httpbin = getfixture('httpbin')
>>> r = http('-a', 'user:pw', httpbin.url + '/basic-auth/user/pw')
@ -284,10 +295,13 @@ def http(
r.devnull = devnull_output
r.stderr = stderr.read()
r.exit_status = exit_status
r.args = args
r.complete_args = ' '.join(complete_args)
if r.exit_status != ExitStatus.SUCCESS:
sys.stderr.write(r.stderr)
# print(f'\n\n$ {r.command}\n')
return r
finally:

View File

@ -0,0 +1,32 @@
from typing import Iterable
import pytest
from tests.utils.matching.parsing import OutputMatchingError, expect_tokens, Expect
__all__ = [
'assert_output_matches',
'assert_output_does_not_match',
'Expect',
]
def assert_output_matches(output: str, tokens: Iterable[Expect]):
r"""
Check the command `output` for an exact full sequence of `tokens`.
>>> out = 'GET / HTTP/1.1\r\nAAA:BBB\r\n\r\nCCC\n\n'
>>> assert_output_matches(out, [Expect.REQUEST_HEADERS, Expect.BODY, Expect.SEPARATOR])
"""
# TODO: auto-remove ansi colors to allow for testing of colorized output as well.
expect_tokens(tokens=tokens, s=output)
def assert_output_does_not_match(output: str, tokens: Iterable[Expect]):
r"""
>>> assert_output_does_not_match('\r\n', [Expect.BODY])
"""
with pytest.raises(OutputMatchingError):
assert_output_matches(output=output, tokens=tokens)

View File

@ -0,0 +1,107 @@
import re
from typing import Iterable
from enum import Enum, auto
from httpie.output.writer import MESSAGE_SEPARATOR
from tests.utils import CRLF
class Expect(Enum):
"""
Predefined token types we can expect in the output.
"""
REQUEST_HEADERS = auto()
RESPONSE_HEADERS = auto()
BODY = auto()
SEPARATOR = auto()
SEPARATOR_RE = re.compile(f'^{MESSAGE_SEPARATOR}')
def make_headers_re(message_type: Expect):
assert message_type in {Expect.REQUEST_HEADERS, Expect.RESPONSE_HEADERS}
# language=RegExp
crlf = r'[\r][\n]'
non_crlf = rf'[^{CRLF}]'
# language=RegExp
http_version = r'HTTP/\d+\.\d+'
if message_type is Expect.REQUEST_HEADERS:
# POST /post HTTP/1.1
start_line_re = fr'{non_crlf}*{http_version}{crlf}'
else:
# HTTP/1.1 200 OK
start_line_re = fr'{http_version}{non_crlf}*{crlf}'
return re.compile(
fr'''
^
{start_line_re}
({non_crlf}+:{non_crlf}+{crlf})+
{crlf}
''',
flags=re.VERBOSE
)
BODY_ENDINGS = [
MESSAGE_SEPARATOR,
CRLF, # Not really but useful for testing (just remember not to include it in a body).
]
TOKEN_REGEX_MAP = {
Expect.REQUEST_HEADERS: make_headers_re(Expect.REQUEST_HEADERS),
Expect.RESPONSE_HEADERS: make_headers_re(Expect.RESPONSE_HEADERS),
Expect.SEPARATOR: SEPARATOR_RE,
}
class OutputMatchingError(ValueError):
pass
def expect_tokens(tokens: Iterable[Expect], s: str):
for token in tokens:
s = expect_token(token, s)
if s:
raise OutputMatchingError(f'Unmatched remaining output for {tokens} in {s!r}')
def expect_token(token: Expect, s: str) -> str:
if token is Expect.BODY:
s = expect_body(s)
else:
s = expect_regex(token, s)
return s
def expect_regex(token: Expect, s: str) -> str:
match = TOKEN_REGEX_MAP[token].match(s)
if not match:
raise OutputMatchingError(f'No match for {token} in {s!r}')
return s[match.end():]
def expect_body(s: str) -> str:
"""
We require some text, and continue to read until we find an ending or until the end of the string.
"""
if 'content-disposition:' in s.lower():
# Multipart body heuristic.
final_boundary_re = re.compile('\r\n--[^-]+?--\r\n')
match = final_boundary_re.search(s)
if match:
return s[match.end():]
endings = [s.index(sep) for sep in BODY_ENDINGS if sep in s]
if not endings:
s = '' # Only body
else:
end = min(endings)
if end == 0:
raise OutputMatchingError(f'Empty body: {s!r}')
s = s[end:]
return s

View File

@ -0,0 +1,190 @@
"""
Here we test our output parsing and matching implementation, not HTTPie itself.
"""
from httpie.output.writer import MESSAGE_SEPARATOR
from tests.utils import CRLF
from tests.utils.matching import assert_output_does_not_match, assert_output_matches, Expect
def test_assert_output_matches_headers_incomplete():
assert_output_does_not_match(f'HTTP/1.1{CRLF}', [Expect.RESPONSE_HEADERS])
def test_assert_output_matches_headers_unterminated():
assert_output_does_not_match(
(
f'HTTP/1.1{CRLF}'
f'AAA:BBB'
f'{CRLF}'
),
[Expect.RESPONSE_HEADERS],
)
def test_assert_output_matches_response_headers():
assert_output_matches(
(
f'HTTP/1.1 200 OK{CRLF}'
f'AAA:BBB{CRLF}'
f'{CRLF}'
),
[Expect.RESPONSE_HEADERS],
)
def test_assert_output_matches_request_headers():
assert_output_matches(
(
f'GET / HTTP/1.1{CRLF}'
f'AAA:BBB{CRLF}'
f'{CRLF}'
),
[Expect.REQUEST_HEADERS],
)
def test_assert_output_matches_headers_and_separator():
assert_output_matches(
(
f'HTTP/1.1{CRLF}'
f'AAA:BBB{CRLF}'
f'{CRLF}'
f'{MESSAGE_SEPARATOR}'
),
[Expect.RESPONSE_HEADERS, Expect.SEPARATOR],
)
def test_assert_output_matches_body_unmatched_crlf():
assert_output_does_not_match(f'AAA{CRLF}', [Expect.BODY])
def test_assert_output_matches_body_unmatched_separator():
assert_output_does_not_match(f'AAA{MESSAGE_SEPARATOR}', [Expect.BODY])
def test_assert_output_matches_body_and_separator():
assert_output_matches(f'AAA{MESSAGE_SEPARATOR}', [Expect.BODY, Expect.SEPARATOR])
def test_assert_output_matches_body_r():
assert_output_matches(f'AAA\r', [Expect.BODY])
def test_assert_output_matches_body_n():
assert_output_matches(f'AAA\n', [Expect.BODY])
def test_assert_output_matches_body_r_body():
assert_output_matches(f'AAA\rBBB', [Expect.BODY])
def test_assert_output_matches_body_n_body():
assert_output_matches(f'AAA\nBBB', [Expect.BODY])
def test_assert_output_matches_headers_and_body():
assert_output_matches(
(
f'HTTP/1.1{CRLF}'
f'AAA:BBB{CRLF}'
f'{CRLF}'
f'CCC'
),
[Expect.RESPONSE_HEADERS, Expect.BODY]
)
def test_assert_output_matches_headers_with_body_and_separator():
assert_output_matches(
(
f'HTTP/1.1 {CRLF}'
f'AAA:BBB{CRLF}{CRLF}'
f'CCC{MESSAGE_SEPARATOR}'
),
[Expect.RESPONSE_HEADERS, Expect.BODY, Expect.SEPARATOR]
)
def test_assert_output_matches_multiple_messages():
assert_output_matches(
(
f'POST / HTTP/1.1{CRLF}'
f'AAA:BBB{CRLF}'
f'{CRLF}'
f'CCC'
f'{MESSAGE_SEPARATOR}'
f'HTTP/1.1 200 OK{CRLF}'
f'EEE:FFF{CRLF}'
f'{CRLF}'
f'GGG'
f'{MESSAGE_SEPARATOR}'
), [
Expect.REQUEST_HEADERS,
Expect.BODY,
Expect.SEPARATOR,
Expect.RESPONSE_HEADERS,
Expect.BODY,
Expect.SEPARATOR,
]
)
def test_assert_output_matches_multipart_body():
output = (
'POST / HTTP/1.1\r\n'
'User-Agent: HTTPie/2.4.0-dev\r\n'
'Accept-Encoding: gzip, deflate\r\n'
'Accept: */*\r\n'
'Connection: keep-alive\r\n'
'Content-Type: multipart/form-data; boundary=1e22169de43e4a2e8d9e41c0a1c93cc5\r\n'
'Content-Length: 212\r\n'
'Host: pie.dev\r\n'
'\r\n'
'--1e22169de43e4a2e8d9e41c0a1c93cc5\r\n'
'Content-Disposition: form-data; name="AAA"\r\n'
'\r\n'
'BBB\r\n'
'--1e22169de43e4a2e8d9e41c0a1c93cc5\r\n'
'Content-Disposition: form-data; name="CCC"\r\n'
'\r\n'
'DDD\r\n'
'--1e22169de43e4a2e8d9e41c0a1c93cc5--\r\n'
)
assert_output_matches(output, [Expect.REQUEST_HEADERS, Expect.BODY])
def test_assert_output_matches_multipart_body_with_separator():
output = (
'POST / HTTP/1.1\r\n'
'User-Agent: HTTPie/2.4.0-dev\r\n'
'Accept-Encoding: gzip, deflate\r\n'
'Accept: */*\r\n'
'Connection: keep-alive\r\n'
'Content-Type: multipart/form-data; boundary=1e22169de43e4a2e8d9e41c0a1c93cc5\r\n'
'Content-Length: 212\r\n'
'Host: pie.dev\r\n'
'\r\n'
'--1e22169de43e4a2e8d9e41c0a1c93cc5\r\n'
'Content-Disposition: form-data; name="AAA"\r\n'
'\r\n'
'BBB\r\n'
'--1e22169de43e4a2e8d9e41c0a1c93cc5\r\n'
'Content-Disposition: form-data; name="CCC"\r\n'
'\r\n'
'DDD\r\n'
'--1e22169de43e4a2e8d9e41c0a1c93cc5--\r\n'
f'{MESSAGE_SEPARATOR}'
)
assert_output_matches(output, [Expect.REQUEST_HEADERS, Expect.BODY, Expect.SEPARATOR])
def test_assert_output_matches_multiple_separators():
assert_output_matches(
MESSAGE_SEPARATOR + MESSAGE_SEPARATOR + 'AAA' + MESSAGE_SEPARATOR + MESSAGE_SEPARATOR,
[Expect.SEPARATOR, Expect.SEPARATOR, Expect.BODY, Expect.SEPARATOR, Expect.SEPARATOR]
)