Compare commits

..

1 Commits
2.4.0 ... 2.3.0

Author SHA1 Message Date
Jakub Roztocil
dc3687f7ac v2.3.0 2020-10-25 20:39:01 +01:00
37 changed files with 368 additions and 920 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# 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.9
python-version: 3.8
- run: python -m pip install --upgrade pip setuptools wheel
- run: make install
- run: make pycodestyle
@@ -23,7 +23,10 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
python-version: [3.6, 3.7, 3.8, 3.9]
python-version: [3.6, 3.7, 3.8]
exclude:
- os: windows-latest
python-version: 3.8
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/httpie/httpie/graphs/contributors>`_
`Complete list of contributors on GitHub <https://github.com/jakubroztocil/httpie/graphs/contributors>`_
* `Cláudia T. Delgado <https://github.com/claudiatd>`_ (logo)
* `Hank Gay <https://github.com/gthank>`_
@@ -38,6 +38,5 @@ 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,24 +6,13 @@ This document records all notable changes to `HTTPie <https://httpie.org>`_.
This project adheres to `Semantic Versioning <https://semver.org/>`_.
`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 pie.dev/post @file``).
* Added ``--chunked`` to enable chunked transfer encoding (`#753`_).
* Added support for body-from-file upload streaming (``http httpbin.org/post @file``).
* Added ``--chunked`` to allow chunked transfer encoding.
* 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`_).
@@ -430,65 +419,57 @@ This project adheres to `Semantic Versioning <https://semver.org/>`_.
* Initial public release
.. _`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
.. _`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: https://github.com/jakubroztocil/httpie/compare/2.2.0...2.3.0
.. _#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
.. _#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

View File

@@ -53,7 +53,7 @@ Development Environment
Getting the code
****************
Go to https://github.com/httpie/httpie and fork the project repository.
Go to https://github.com/jakubroztocil/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/httpie/httpie/actions>`_
`GitHub Actions <https://github.com/jakubroztocil/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/httpie/httpie/issues?state=open
.. _AUTHORS: https://github.com/httpie/httpie/blob/master/AUTHORS.rst
.. _Makefile: https://github.com/httpie/httpie/blob/master/Makefile
.. _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
.. _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/httpie/httpie/tree/master/tests
.. _test suite: https://github.com/jakubroztocil/httpie/tree/master/tests

View File

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

View File

@@ -2,8 +2,6 @@
# See ./CONTRIBUTING.rst
###############################################################################
.PHONY: build
ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
VERSION=$(shell grep __version__ httpie/__init__.py)
REQUIREMENTS=requirements-dev.txt
@@ -113,9 +111,6 @@ 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
@@ -136,11 +131,6 @@ codecov-upload:
###############################################################################
build:
rm -rf build/
$(VENV_PYTHON) setup.py sdist bdist_wheel
publish: test-all publish-no-test
@@ -148,9 +138,8 @@ 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
make build
make twine-check
$(VENV_BIN)/twine upload --repository=httpie dist/*
$(VENV_PYTHON) setup.py sdist bdist_wheel
$(VENV_BIN)/twine upload dist/*
@echo

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,15 @@ 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/httpie/httpie/issues
https://github.com/jakubroztocil/httpie/issues
'''),
)

View File

@@ -1,4 +1,5 @@
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/httpie/httpie/issues/802>
# <https://github.com/jakubroztocil/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/httpie/httpie/issues/212>
# See <https://github.com/jakubroztocil/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/httpie/httpie/issues/895>
<https://github.com/jakubroztocil/httpie/issues/895>
<https://ec.haxx.se/http/http-basics#path-as-is>

View File

@@ -9,17 +9,26 @@ 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, MESSAGE_SEPARATOR_BYTES
from httpie.output.writer import (
write_message,
write_stream,
)
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.
@@ -125,22 +134,40 @@ 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
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.pre_request(args.headers)
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 separate():
getattr(env.stdout, 'buffer', env.stdout).write(MESSAGE_SEPARATOR_BYTES)
def request_body_read_callback(chunk: bytes):
should_pipe_to_stdout = bool(
should_pipe_to_stdout = (
# Request body output desired
OUT_REQ_BODY in args.output_options
# & not `.read()` already pre-request (e.g., for compression)
@@ -153,54 +180,66 @@ def program(args: argparse.Namespace, env: Environment) -> ExitStatus:
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)
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.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
# Process messages as theyre generated
messages = collect_messages(
args=args,
config_dir=env.config.directory,
request_body_read_callback=request_body_read_callback
)
for message in messages:
maybe_separate()
is_request = isinstance(message, requests.PreparedRequest)
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
with_headers, with_body = get_output_options(
args=args, message=message)
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:
do_write_body = not is_streamed_upload
force_separator = is_streamed_upload and env.stdout_isatty
with_body = not is_streamed_upload
needs_separator = is_streamed_upload
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 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
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()
# 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
@@ -214,7 +253,9 @@ def program(args: argparse.Namespace, env: Environment) -> ExitStatus:
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()
@@ -242,6 +283,6 @@ def decode_raw_args(
"""
return [
arg.decode(stdin_encoding)
if type(arg) is bytes else arg
if type(arg) == 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/httpie/httpie/issues/423>
# <https://github.com/jakubroztocil/httpie/issues/423>
try:
total_size = int(final_response.headers['Content-Length'])
except (KeyError, ValueError, TypeError):

View File

@@ -12,10 +12,6 @@ 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,
@@ -115,7 +111,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 MESSAGE_SEPARATOR_BYTES
yield b'\n\n'
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/httpie/httpie/issues/212
See https://github.com/jakubroztocil/httpie/issues/212
"""
# noinspection PyTypeChecker

View File

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

View File

@@ -109,8 +109,6 @@ def get_expired_cookies(
for attrs in attr_sets
]
_max_age_to_expires(cookies=cookies, now=now)
return [
{
'name': cookie['name'],
@@ -119,18 +117,3 @@ 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 --doctest-modules
addopts = --tb=native
[pycodestyle]

View File

@@ -15,7 +15,6 @@ 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 = [
@@ -74,7 +73,6 @@ 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/httpie/httpie/archive/{httpie.__version__}.tar.gz',
author=httpie.__author__,
@@ -112,5 +110,6 @@ setup(
'Twitter': 'https://twitter.com/httpie',
'Documentation': 'https://httpie.org/docs',
'Online Demo': 'https://httpie.org/run',
'Donate': 'https://httpie.org/donate',
},
)

View File

@@ -5,4 +5,4 @@ HTTPie Test Suite
Please see `CONTRIBUTING`_.
.. _CONTRIBUTING: https://github.com/httpie/httpie/blob/master/CONTRIBUTING.rst
.. _CONTRIBUTING: https://github.com/jakubroztocil/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/httpie/httpie/issues/242
https://github.com/jakubroztocil/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/httpie/httpie/issues/644>
<https://github.com/jakubroztocil/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/httpie/httpie/issues/62
# https://github.com/jakubroztocil/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/httpie/httpie/issues/137
# https://github.com/jakubroztocil/httpie/issues/137
assert 'application/json' in r.json['headers']['Content-Type']
def test_GET_explicit_JSON_explicit_headers(self, httpbin):

View File

@@ -5,6 +5,7 @@ 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/httpie/httpie/issues' in r
assert 'https://github.com/jakubroztocil/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/httpie/httpie/issues/840>
<https://github.com/jakubroztocil/httpie/issues/840>
"""
with open(FILE_PATH) as f:

View File

@@ -54,21 +54,6 @@ 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):
@@ -142,7 +127,7 @@ class TestVerboseFlag:
assert r.count('__test__') == 2
def test_verbose_form(self, httpbin):
# https://github.com/httpie/httpie/issues/53
# https://github.com/jakubroztocil/httpie/issues/53
r = http('--verbose', '--form', 'POST', httpbin.url + '/post',
'A=B', 'C=D')
assert HTTP_OK in r

View File

@@ -1,17 +1,16 @@
"""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/httpie/httpie/issues/235
https://github.com/jakubroztocil/httpie/issues/235
"""
host = 'pie.dev'
host = 'httpbin.org'
url = httpbin.url + '/get'
r = http('--print=hH', url, 'host:{0}'.format(host))
assert HTTP_OK in r
@@ -22,28 +21,7 @@ def test_Host_header_overwrite(httpbin):
@pytest.mark.skipif(is_windows, reason='Unix-only')
def test_output_devnull(httpbin):
"""
https://github.com/httpie/httpie/issues/252
https://github.com/jakubroztocil/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,7 +16,6 @@ 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:
@@ -162,12 +161,6 @@ 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'
@@ -200,7 +193,7 @@ class TestSession(SessionTestBase):
def test_session_default_header_value_overwritten(self, httpbin):
self.start_session(httpbin)
# https://github.com/httpie/httpie/issues/180
# https://github.com/jakubroztocil/httpie/issues/180
r1 = http('--session=test',
httpbin.url + '/headers', 'User-Agent:custom',
env=self.env())
@@ -212,7 +205,7 @@ class TestSession(SessionTestBase):
assert r2.json['headers']['User-Agent'] == 'custom'
def test_download_in_session(self, httpbin):
# https://github.com/httpie/httpie/issues/412
# https://github.com/jakubroztocil/httpie/issues/412
self.start_session(httpbin)
cwd = os.getcwd()
os.chdir(gettempdir())
@@ -345,15 +338,6 @@ 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/httpie/httpie/issues/729>
# See <https://github.com/jakubroztocil/httpie/issues/729>
# noinspection PyUnresolvedReferences
import OpenSSL.SSL
ssl_errors = (

View File

@@ -1,141 +0,0 @@
"""
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,23 +52,6 @@ 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,14 +1,12 @@
# 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, List
from typing import Optional, Union
from httpie.status import ExitStatus
from httpie.config import Config
@@ -19,10 +17,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://pie.dev'
HTTPBIN_WITH_CHUNKED_SUPPORT = 'http://httpbin.org'
TESTS_ROOT = Path(__file__).parent.parent
TESTS_ROOT = Path(__file__).parent
CRLF = '\r\n'
COLOR = '\x1b['
HTTP_OK = '200 OK'
@@ -51,7 +49,7 @@ class StdinBytesIO(BytesIO):
class MockEnvironment(Environment):
"""Environment subclass with reasonable defaults for testing."""
colors = 0 # For easier debugging
colors = 0
stdin_isatty = True,
stdout_isatty = True
is_windows = False
@@ -115,15 +113,6 @@ 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):
@@ -209,7 +198,7 @@ def http(
Example:
$ http --auth=user:password GET pie.dev/basic-auth/user/password
$ http --auth=user:password GET httpbin.org/basic-auth/user/password
>>> httpbin = getfixture('httpbin')
>>> r = http('-a', 'user:pw', httpbin.url + '/basic-auth/user/pw')
@@ -295,13 +284,10 @@ 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

@@ -1,32 +0,0 @@
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

@@ -1,107 +0,0 @@
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

@@ -1,190 +0,0 @@
"""
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]
)