mirror of
https://github.com/httpie/cli.git
synced 2025-08-17 23:59:41 +02:00
Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
dc3687f7ac |
12
.github/FUNDING.yml
vendored
Normal file
12
.github/FUNDING.yml
vendored
Normal 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:
|
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
@@ -8,7 +8,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- uses: actions/setup-python@v1
|
- uses: actions/setup-python@v1
|
||||||
with:
|
with:
|
||||||
python-version: 3.9
|
python-version: 3.8
|
||||||
- run: python -m pip install --upgrade pip setuptools wheel
|
- run: python -m pip install --upgrade pip setuptools wheel
|
||||||
- run: make install
|
- run: make install
|
||||||
- run: make pycodestyle
|
- run: make pycodestyle
|
||||||
@@ -23,7 +23,10 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
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:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- uses: actions/setup-python@v1
|
- uses: actions/setup-python@v1
|
||||||
|
@@ -8,7 +8,7 @@ HTTPie authors
|
|||||||
Patches and ideas
|
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)
|
* `Cláudia T. Delgado <https://github.com/claudiatd>`_ (logo)
|
||||||
* `Hank Gay <https://github.com/gthank>`_
|
* `Hank Gay <https://github.com/gthank>`_
|
||||||
@@ -38,6 +38,5 @@ Patches and ideas
|
|||||||
* `Edward Yang <https://github.com/honorabrutroll>`_
|
* `Edward Yang <https://github.com/honorabrutroll>`_
|
||||||
* `Aleksandr Vinokurov <https://github.com/aleksandr-vin>`_
|
* `Aleksandr Vinokurov <https://github.com/aleksandr-vin>`_
|
||||||
* `Jeff Byrnes <https://github.com/jeffbyrnes>`_
|
* `Jeff Byrnes <https://github.com/jeffbyrnes>`_
|
||||||
* `Denis Belavin <https://github.com/LuckyDenis>`_
|
|
||||||
|
|
||||||
|
|
||||||
|
129
CHANGELOG.rst
129
CHANGELOG.rst
@@ -6,24 +6,13 @@ This document records all notable changes to `HTTPie <https://httpie.org>`_.
|
|||||||
This project adheres to `Semantic Versioning <https://semver.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)
|
`2.3.0`_ (2020-10-25)
|
||||||
-------------------------
|
-------------------------
|
||||||
|
|
||||||
* Added support for streamed uploads (`#201`_).
|
|
||||||
* Added support for multipart upload streaming (`#684`_).
|
* Added support for multipart upload streaming (`#684`_).
|
||||||
* Added support for body-from-file upload streaming (``http pie.dev/post @file``).
|
* Added support for body-from-file upload streaming (``http httpbin.org/post @file``).
|
||||||
* Added ``--chunked`` to enable chunked transfer encoding (`#753`_).
|
* Added ``--chunked`` to allow chunked transfer encoding.
|
||||||
* Added ``--multipart`` to allow ``multipart/form-data`` encoding for non-file ``--form`` requests as well.
|
* 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 ``--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 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`_).
|
* 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
|
* Initial public release
|
||||||
|
|
||||||
|
|
||||||
.. _`0.1.0`: https://github.com/httpie/httpie/commit/b966efa
|
.. _`0.1.0`: https://github.com/jakubroztocil/httpie/commit/b966efa
|
||||||
.. _0.1.4: https://github.com/httpie/httpie/compare/b966efa...0.1.4
|
.. _0.1.4: https://github.com/jakubroztocil/httpie/compare/b966efa...0.1.4
|
||||||
.. _0.1.5: https://github.com/httpie/httpie/compare/0.1.4...0.1.5
|
.. _0.1.5: https://github.com/jakubroztocil/httpie/compare/0.1.4...0.1.5
|
||||||
.. _0.1.6: https://github.com/httpie/httpie/compare/0.1.5...0.1.6
|
.. _0.1.6: https://github.com/jakubroztocil/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.0: https://github.com/jakubroztocil/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.1: https://github.com/jakubroztocil/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.2: https://github.com/jakubroztocil/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.5: https://github.com/jakubroztocil/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.6: https://github.com/jakubroztocil/httpie/compare/0.2.5...0.2.6
|
||||||
.. _0.2.7: https://github.com/httpie/httpie/compare/0.2.5...0.2.7
|
.. _0.2.7: https://github.com/jakubroztocil/httpie/compare/0.2.5...0.2.7
|
||||||
.. _0.3.0: https://github.com/httpie/httpie/compare/0.2.7...0.3.0
|
.. _0.3.0: https://github.com/jakubroztocil/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.0: https://github.com/jakubroztocil/httpie/compare/0.3.0...0.4.0
|
||||||
.. _0.4.1: https://github.com/httpie/httpie/compare/0.4.0...0.4.1
|
.. _0.4.1: https://github.com/jakubroztocil/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.0: https://github.com/jakubroztocil/httpie/compare/0.4.1...0.5.0
|
||||||
.. _0.5.1: https://github.com/httpie/httpie/compare/0.5.0...0.5.1
|
.. _0.5.1: https://github.com/jakubroztocil/httpie/compare/0.5.0...0.5.1
|
||||||
.. _0.6.0: https://github.com/httpie/httpie/compare/0.5.1...0.6.0
|
.. _0.6.0: https://github.com/jakubroztocil/httpie/compare/0.5.1...0.6.0
|
||||||
.. _0.7.1: https://github.com/httpie/httpie/compare/0.6.0...0.7.1
|
.. _0.7.1: https://github.com/jakubroztocil/httpie/compare/0.6.0...0.7.1
|
||||||
.. _0.8.0: https://github.com/httpie/httpie/compare/0.7.1...0.8.0
|
.. _0.8.0: https://github.com/jakubroztocil/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.0: https://github.com/jakubroztocil/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.1: https://github.com/jakubroztocil/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.2: https://github.com/jakubroztocil/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.3: https://github.com/jakubroztocil/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.4: https://github.com/jakubroztocil/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.6: https://github.com/jakubroztocil/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.8: https://github.com/jakubroztocil/httpie/compare/0.9.6...0.9.8
|
||||||
.. _0.9.9: https://github.com/httpie/httpie/compare/0.9.8...0.9.9
|
.. _0.9.9: https://github.com/jakubroztocil/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.0: https://github.com/jakubroztocil/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.1: https://github.com/jakubroztocil/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.2: https://github.com/jakubroztocil/httpie/compare/1.0.1...1.0.2
|
||||||
.. _1.0.3: https://github.com/httpie/httpie/compare/1.0.2...1.0.3
|
.. _1.0.3: https://github.com/jakubroztocil/httpie/compare/1.0.2...1.0.3
|
||||||
.. _2.0.0: https://github.com/httpie/httpie/compare/1.0.3...2.0.0
|
.. _2.0.0: https://github.com/jakubroztocil/httpie/compare/1.0.3...2.0.0
|
||||||
.. _2.1.0: https://github.com/httpie/httpie/compare/2.0.0...2.1.0
|
.. _2.1.0: https://github.com/jakubroztocil/httpie/compare/2.0.0...2.1.0
|
||||||
.. _2.2.0: https://github.com/httpie/httpie/compare/2.1.0...2.2.0
|
.. _2.2.0: https://github.com/jakubroztocil/httpie/compare/2.1.0...2.2.0
|
||||||
.. _2.3.0: https://github.com/httpie/httpie/compare/2.2.0...2.3.0
|
.. _2.3.0: https://github.com/jakubroztocil/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/httpie/httpie/issues/128
|
|
||||||
.. _#201: https://github.com/httpie/httpie/issues/201
|
.. _#128: https://github.com/jakubroztocil/httpie/issues/128
|
||||||
.. _#488: https://github.com/httpie/httpie/issues/488
|
.. _#488: https://github.com/jakubroztocil/httpie/issues/488
|
||||||
.. _#668: https://github.com/httpie/httpie/issues/668
|
.. _#668: https://github.com/jakubroztocil/httpie/issues/668
|
||||||
.. _#684: https://github.com/httpie/httpie/issues/684
|
.. _#684: https://github.com/jakubroztocil/httpie/issues/684
|
||||||
.. _#718: https://github.com/httpie/httpie/issues/718
|
.. _#718: https://github.com/jakubroztocil/httpie/issues/718
|
||||||
.. _#719: https://github.com/httpie/httpie/issues/719
|
.. _#719: https://github.com/jakubroztocil/httpie/issues/719
|
||||||
.. _#753: https://github.com/httpie/httpie/issues/753
|
.. _#840: https://github.com/jakubroztocil/httpie/issues/840
|
||||||
.. _#840: https://github.com/httpie/httpie/issues/840
|
.. _#853: https://github.com/jakubroztocil/httpie/issues/853
|
||||||
.. _#853: https://github.com/httpie/httpie/issues/853
|
.. _#852: https://github.com/jakubroztocil/httpie/issues/852
|
||||||
.. _#852: https://github.com/httpie/httpie/issues/852
|
.. _#870: https://github.com/jakubroztocil/httpie/issues/870
|
||||||
.. _#870: https://github.com/httpie/httpie/issues/870
|
.. _#895: https://github.com/jakubroztocil/httpie/issues/895
|
||||||
.. _#895: https://github.com/httpie/httpie/issues/895
|
.. _#920: https://github.com/jakubroztocil/httpie/issues/920
|
||||||
.. _#903: https://github.com/httpie/httpie/issues/903
|
.. _#904: https://github.com/jakubroztocil/httpie/issues/904
|
||||||
.. _#920: https://github.com/httpie/httpie/issues/920
|
.. _#925: https://github.com/jakubroztocil/httpie/issues/925
|
||||||
.. _#904: https://github.com/httpie/httpie/issues/904
|
.. _#932: https://github.com/jakubroztocil/httpie/issues/932
|
||||||
.. _#925: https://github.com/httpie/httpie/issues/925
|
.. _#934: https://github.com/jakubroztocil/httpie/issues/934
|
||||||
.. _#932: https://github.com/httpie/httpie/issues/932
|
.. _#943: https://github.com/jakubroztocil/httpie/issues/943
|
||||||
.. _#934: https://github.com/httpie/httpie/issues/934
|
.. _#963: https://github.com/jakubroztocil/httpie/issues/963
|
||||||
.. _#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
|
|
||||||
|
@@ -53,7 +53,7 @@ Development Environment
|
|||||||
Getting the code
|
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
|
.. code-block:: bash
|
||||||
@@ -132,7 +132,7 @@ Testing & CI
|
|||||||
Please add tests for any new features and bug fixes.
|
Please add tests for any new features and bug fixes.
|
||||||
|
|
||||||
When you open a pull request,
|
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 HTTPie’s `test suite`_ against your code
|
will automatically run HTTPie’s `test suite`_ against your code
|
||||||
so please make sure all checks pass.
|
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`_!
|
Finally, feel free to add yourself to `AUTHORS`_!
|
||||||
|
|
||||||
|
|
||||||
.. _existing issues: https://github.com/httpie/httpie/issues?state=open
|
.. _existing issues: https://github.com/jakubroztocil/httpie/issues?state=open
|
||||||
.. _AUTHORS: https://github.com/httpie/httpie/blob/master/AUTHORS.rst
|
.. _AUTHORS: https://github.com/jakubroztocil/httpie/blob/master/AUTHORS.rst
|
||||||
.. _Makefile: https://github.com/httpie/httpie/blob/master/Makefile
|
.. _Makefile: https://github.com/jakubroztocil/httpie/blob/master/Makefile
|
||||||
.. _venv: https://docs.python.org/3/library/venv.html
|
.. _venv: https://docs.python.org/3/library/venv.html
|
||||||
.. _pytest: https://pytest.org/
|
.. _pytest: https://pytest.org/
|
||||||
.. _Style Guide for Python Code: https://python.org/dev/peps/pep-0008/
|
.. _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
|
||||||
|
@@ -3,5 +3,5 @@ include README.rst
|
|||||||
include CHANGELOG.rst
|
include CHANGELOG.rst
|
||||||
include AUTHORS.rst
|
include AUTHORS.rst
|
||||||
|
|
||||||
# <https://github.com/httpie/httpie/issues/182>
|
# <https://github.com/jakubroztocil/httpie/issues/182>
|
||||||
recursive-include tests/ *
|
recursive-include tests/ *
|
||||||
|
15
Makefile
15
Makefile
@@ -2,8 +2,6 @@
|
|||||||
# See ./CONTRIBUTING.rst
|
# See ./CONTRIBUTING.rst
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
.PHONY: build
|
|
||||||
|
|
||||||
ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
|
||||||
VERSION=$(shell grep __version__ httpie/__init__.py)
|
VERSION=$(shell grep __version__ httpie/__init__.py)
|
||||||
REQUIREMENTS=requirements-dev.txt
|
REQUIREMENTS=requirements-dev.txt
|
||||||
@@ -113,9 +111,6 @@ test-bdist-wheel: clean venv
|
|||||||
@echo
|
@echo
|
||||||
|
|
||||||
|
|
||||||
twine-check:
|
|
||||||
twine check dist/*
|
|
||||||
|
|
||||||
pycodestyle:
|
pycodestyle:
|
||||||
@echo $(H1)Running pycodestyle$(H1END)
|
@echo $(H1)Running pycodestyle$(H1END)
|
||||||
@[ -f $(VENV_BIN)/pycodestyle ] || $(VENV_PIP) install pycodestyle
|
@[ -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
|
publish: test-all publish-no-test
|
||||||
|
|
||||||
|
|
||||||
@@ -148,9 +138,8 @@ publish-no-test:
|
|||||||
@echo $(H1)Testing wheel build an installation$(H1END)
|
@echo $(H1)Testing wheel build an installation$(H1END)
|
||||||
@echo "$(VERSION)"
|
@echo "$(VERSION)"
|
||||||
@echo "$(VERSION)" | grep -q "dev" && echo '!!!Not publishing dev version!!!' && exit 1 || echo ok
|
@echo "$(VERSION)" | grep -q "dev" && echo '!!!Not publishing dev version!!!' && exit 1 || echo ok
|
||||||
make build
|
$(VENV_PYTHON) setup.py sdist bdist_wheel
|
||||||
make twine-check
|
$(VENV_BIN)/twine upload dist/*
|
||||||
$(VENV_BIN)/twine upload --repository=httpie dist/*
|
|
||||||
@echo
|
@echo
|
||||||
|
|
||||||
|
|
||||||
|
271
README.rst
271
README.rst
@@ -30,11 +30,9 @@ They use simple and natural syntax and provide formatted and colorized output.
|
|||||||
About this document
|
About this document
|
||||||
===================
|
===================
|
||||||
|
|
||||||
This documentation is best viewed at `httpie.org/docs <https://httpie.org/docs>`_.
|
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
|
||||||
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.
|
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.
|
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
|
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>`_.
|
`README.rst <https://github.com/httpie/httpie/blob/master/README.rst>`_.
|
||||||
@@ -119,11 +117,6 @@ system package manager, for example:
|
|||||||
# Arch Linux
|
# Arch Linux
|
||||||
$ pacman -S httpie
|
$ pacman -S httpie
|
||||||
|
|
||||||
.. code-block:: bash
|
|
||||||
|
|
||||||
# Solus
|
|
||||||
$ eopkg install httpie
|
|
||||||
|
|
||||||
|
|
||||||
Windows, etc.
|
Windows, etc.
|
||||||
-------------
|
-------------
|
||||||
@@ -135,9 +128,9 @@ and always provides the latest version) is to use `pip`_:
|
|||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
# Make sure we have an up-to-date version of pip and setuptools:
|
# 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
|
(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
|
.. code-block:: bash
|
||||||
|
|
||||||
$ brew uninstall --force httpie
|
$ brew install httpie --HEAD
|
||||||
$ brew install --HEAD httpie
|
|
||||||
|
|
||||||
|
|
||||||
Otherwise with ``pip``:
|
Otherwise with ``pip``:
|
||||||
@@ -197,7 +189,7 @@ Hello World:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ https httpie.io/hello
|
$ http https://httpie.org/hello
|
||||||
|
|
||||||
|
|
||||||
Synopsis:
|
Synopsis:
|
||||||
@@ -217,28 +209,28 @@ Custom `HTTP method`_, `HTTP headers`_ and `JSON`_ data:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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`_:
|
Submitting `forms`_:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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`_:
|
See the request that is being sent using one of the `output options`_:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http -v pie.dev/get
|
$ http -v httpbin.org/get
|
||||||
|
|
||||||
|
|
||||||
Build and print a request without sending it using `offline mode`_:
|
Build and print a request without sending it using `offline mode`_:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
Use `GitHub API`_ to post a comment on an
|
||||||
@@ -254,21 +246,21 @@ Upload a file using `redirected input`_:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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`_:
|
Download a file and save it via `redirected output`_:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http pie.dev/image/png > image.png
|
$ http httpbin.org/image/png > image.png
|
||||||
|
|
||||||
|
|
||||||
Download a file ``wget`` style:
|
Download a file ``wget`` style:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
Use named `sessions`_ to make certain aspects of the communication persistent
|
||||||
between requests to the same host:
|
between requests to the same host:
|
||||||
@@ -276,12 +268,12 @@ between requests to the same host:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
.. 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:
|
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
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http DELETE pie.dev/delete
|
$ http DELETE httpbin.org/delete
|
||||||
|
|
||||||
|
|
||||||
Which looks similar to the actual ``Request-Line`` that is sent:
|
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
|
``:``, ``=``, ``:=``, ``==``, ``@``, ``=@``, and ``:=@``. The ones with an
|
||||||
``@`` expect a file path as value.
|
``@`` expect a file path as value.
|
||||||
|
|
||||||
+------------------------------+---------------------------------------------------+
|
+-----------------------+-----------------------------------------------------+
|
||||||
| Item Type | Description |
|
| Item Type | Description |
|
||||||
+==============================+===================================================+
|
+=======================+=====================================================+
|
||||||
| HTTP Headers | Arbitrary HTTP header, e.g. ``X-API-Token:123``. |
|
| HTTP Headers | Arbitrary HTTP header, e.g. ``X-API-Token:123``. |
|
||||||
| ``Name:Value`` | |
|
| ``Name:Value`` | |
|
||||||
+------------------------------+---------------------------------------------------+
|
+-----------------------+-----------------------------------------------------+
|
||||||
| URL parameters | Appends the given name/value pair as a query |
|
| URL parameters | Appends the given name/value pair as a query |
|
||||||
| ``name==value`` | string parameter to the URL. |
|
| ``name==value`` | string parameter to the URL. |
|
||||||
| | The ``==`` separator is used. |
|
| | The ``==`` separator is used. |
|
||||||
+------------------------------+---------------------------------------------------+
|
+-----------------------+-----------------------------------------------------+
|
||||||
| Data Fields | Request data fields to be serialized as a JSON |
|
| Data Fields | Request data fields to be serialized as a JSON |
|
||||||
| ``field=value``, | object (default), to be form-encoded |
|
| ``field=value``, | object (default), to be form-encoded |
|
||||||
| ``field=@file.txt`` | (with ``--form, -f``), or to be serialized as |
|
| ``field=@file.txt`` | (with ``--form, -f``), or to be serialized as |
|
||||||
| | ``multipart/form-data`` (with ``--multipart``). |
|
| | ``multipart/form-data`` (with ``--multipart``). |
|
||||||
+------------------------------+---------------------------------------------------+
|
+-----------------------+-----------------------------------------------------+
|
||||||
| Raw JSON fields | Useful when sending JSON and one or |
|
| Raw JSON fields | Useful when sending JSON and one or |
|
||||||
| ``field:=json``, | more fields need to be a ``Boolean``, ``Number``, |
|
| ``field:=json``, | more fields need to be a ``Boolean``, ``Number``, |
|
||||||
| ``field:=@file.json`` | nested ``Object``, or an ``Array``, e.g., |
|
| ``field:=@file.json`` | nested ``Object``, or an ``Array``, e.g., |
|
||||||
| | ``meals:='["ham","spam"]'`` or ``pies:=[1,2,3]`` |
|
| | ``meals:='["ham","spam"]'`` or ``pies:=[1,2,3]`` |
|
||||||
| | (note the quotes). |
|
| | (note the quotes). |
|
||||||
+------------------------------+---------------------------------------------------+
|
+-----------------------+-----------------------------------------------------+
|
||||||
| Fields upload fields | Only available with ``--form, -f`` and |
|
| Fields upload fields | Only available with ``--form, -f`` and |
|
||||||
| ``field@/dir/file`` | ``--multipart``. |
|
| ``field@/dir/file`` | ``--multipart``. |
|
||||||
| ``field@file;type=mime`` | For example ``screenshot@~/Pictures/img.png``, or |
|
| ``field@file;type`` | For example ``screenshot@~/Pictures/img.png``, or |
|
||||||
| | ``'cv@cv.txt;type=text/markdown'``. |
|
| | ``'cv@cv.txt;text/markdown'``. |
|
||||||
| | With ``--form``, the presence of a file field |
|
| | With ``--form``, the presence of a file field |
|
||||||
| | results in a ``--multipart`` request. |
|
| | results in a ``--multipart`` request. |
|
||||||
+------------------------------+---------------------------------------------------+
|
+-----------------------+-----------------------------------------------------+
|
||||||
|
|
||||||
|
|
||||||
Note that data fields aren’t the only way to specify request data:
|
Note that data fields aren’t the only way to specify request data:
|
||||||
@@ -518,7 +510,7 @@ token ``--`` to prevent confusion with ``--arguments``:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
.. code-block:: http
|
||||||
|
|
||||||
@@ -543,15 +535,14 @@ Simple example:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
.. code-block:: http
|
||||||
|
|
||||||
PUT / HTTP/1.1
|
PUT / HTTP/1.1
|
||||||
Accept: application/json, */*;q=0.5
|
Accept: application/json, */*;q=0.5
|
||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Host: pie.dev
|
Host: httpbin.org
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "John",
|
"name": "John",
|
||||||
@@ -594,7 +585,7 @@ fields using ``=@`` and ``:=@``:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http PUT pie.dev/put \
|
$ http PUT httpbin.org/put \
|
||||||
name=John \ # String (default)
|
name=John \ # String (default)
|
||||||
age:=29 \ # Raw JSON — Number
|
age:=29 \ # Raw JSON — Number
|
||||||
married:=false \ # Raw JSON — Boolean
|
married:=false \ # Raw JSON — Boolean
|
||||||
@@ -609,7 +600,7 @@ fields using ``=@`` and ``:=@``:
|
|||||||
PUT /person/1 HTTP/1.1
|
PUT /person/1 HTTP/1.1
|
||||||
Accept: application/json, */*;q=0.5
|
Accept: application/json, */*;q=0.5
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Host: pie.dev
|
Host: httpbin.org
|
||||||
|
|
||||||
{
|
{
|
||||||
"age": 29,
|
"age": 29,
|
||||||
@@ -639,11 +630,11 @@ In such cases, it’s better to pass the full raw JSON data via
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ echo '{"hello": "world"}' | http POST pie.dev/post
|
$ echo '{"hello": "world"}' | http POST httpbin.org/post
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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.
|
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`_.
|
Here, again, the solution is to use `redirected input`_.
|
||||||
@@ -664,7 +655,7 @@ Regular forms
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http --form POST pie.dev/post name='John Smith'
|
$ http --form POST httpbin.org/post name='John Smith'
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: http
|
.. code-block:: http
|
||||||
@@ -683,7 +674,7 @@ If one or more file fields is present, the serialization and content type is
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
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
|
.. 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
|
To perform a ``multipart/form-data`` request even without any files, use
|
||||||
``--multipart`` instead of ``--form``:
|
``--multipart`` instead of ``--form``:
|
||||||
@@ -778,7 +769,7 @@ To set custom headers you can use the ``Header:Value`` notation:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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/
|
X-Foo:Bar Referer:https://httpie.org/
|
||||||
|
|
||||||
|
|
||||||
@@ -788,7 +779,7 @@ To set custom headers you can use the ``Header:Value`` notation:
|
|||||||
Accept: */*
|
Accept: */*
|
||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
Cookie: valued-visitor=yes;foo=bar
|
Cookie: valued-visitor=yes;foo=bar
|
||||||
Host: pie.dev
|
Host: httpbin.org
|
||||||
Referer: https://httpie.org/
|
Referer: https://httpie.org/
|
||||||
User-Agent: Bacon/1.0
|
User-Agent: Bacon/1.0
|
||||||
X-Foo: Bar
|
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
|
.. 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;``:
|
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
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http pie.dev/headers 'Header;'
|
$ http httpbin.org/headers 'Header;'
|
||||||
|
|
||||||
|
|
||||||
Limiting response headers
|
Limiting response headers
|
||||||
@@ -842,7 +833,7 @@ HTTPie reads before giving up (the default ``0``, i.e., there’s no limit).
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
.. code-block:: bash
|
||||||
|
|
||||||
# 1. save a raw request to a file:
|
# 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
|
.. code-block:: bash
|
||||||
|
|
||||||
# 2. send it over the wire with, for example, the fantastic netcat tool:
|
# 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”.
|
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
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http pie.dev/cookies Cookie:sessionid=foo
|
$ http httpbin.org/cookies Cookie:sessionid=foo
|
||||||
|
|
||||||
.. code-block:: http
|
.. code-block:: http
|
||||||
|
|
||||||
@@ -911,7 +902,7 @@ Send a single cookie:
|
|||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
Connection: keep-alive
|
Connection: keep-alive
|
||||||
Cookie: sessionid=foo
|
Cookie: sessionid=foo
|
||||||
Host: pie.dev
|
Host: httpbin.org
|
||||||
User-Agent: HTTPie/0.9.9
|
User-Agent: HTTPie/0.9.9
|
||||||
|
|
||||||
|
|
||||||
@@ -920,7 +911,7 @@ Send multiple cookies
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
.. code-block:: http
|
||||||
|
|
||||||
@@ -929,7 +920,7 @@ Send multiple cookies
|
|||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
Connection: keep-alive
|
Connection: keep-alive
|
||||||
Cookie: sessionid=foo;another-cookie=bar
|
Cookie: sessionid=foo;another-cookie=bar
|
||||||
Host: pie.dev
|
Host: httpbin.org
|
||||||
User-Agent: HTTPie/0.9.9
|
User-Agent: HTTPie/0.9.9
|
||||||
|
|
||||||
|
|
||||||
@@ -966,7 +957,7 @@ Basic auth
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
Digest auth
|
||||||
@@ -975,7 +966,7 @@ Digest auth
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
Password prompt
|
||||||
@@ -983,7 +974,7 @@ Password prompt
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http -a username pie.dev/basic-auth/username/password
|
$ http -a username httpbin.org/basic-auth/username/password
|
||||||
|
|
||||||
|
|
||||||
Empty password
|
Empty password
|
||||||
@@ -991,7 +982,7 @@ Empty password
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http -a username: pie.dev/headers
|
$ http -a username: httpbin.org/headers
|
||||||
|
|
||||||
|
|
||||||
``.netrc``
|
``.netrc``
|
||||||
@@ -1005,13 +996,13 @@ For example:
|
|||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ cat ~/.netrc
|
$ cat ~/.netrc
|
||||||
machine pie.dev
|
machine httpbin.org
|
||||||
login httpie
|
login httpie
|
||||||
password test
|
password test
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http pie.dev/basic-auth/httpie/test
|
$ http httpbin.org/basic-auth/httpie/test
|
||||||
HTTP/1.1 200 OK
|
HTTP/1.1 200 OK
|
||||||
[...]
|
[...]
|
||||||
|
|
||||||
@@ -1019,7 +1010,7 @@ This can be disabled with the ``--ignore-netrc`` option:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
HTTP/1.1 401 UNAUTHORIZED
|
||||||
[...]
|
[...]
|
||||||
|
|
||||||
@@ -1053,7 +1044,7 @@ response is shown:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http pie.dev/redirect/3
|
$ http httpbin.org/redirect/3
|
||||||
|
|
||||||
|
|
||||||
Follow ``Location``
|
Follow ``Location``
|
||||||
@@ -1065,7 +1056,7 @@ and show the final response instead, use the ``--follow, -F`` option:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http --follow pie.dev/redirect/3
|
$ http --follow httpbin.org/redirect/3
|
||||||
|
|
||||||
|
|
||||||
Showing intermediary redirect responses
|
Showing intermediary redirect responses
|
||||||
@@ -1077,7 +1068,7 @@ then use the ``--all`` option as well:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http --follow --all --max-redirects=2 pie.dev/redirect/3
|
$ http --follow --all --max-redirects=2 httpbin.org/redirect/3
|
||||||
|
|
||||||
|
|
||||||
Proxies
|
Proxies
|
||||||
@@ -1150,7 +1141,7 @@ To skip the host’s SSL certificate verification, you can pass ``--verify=no``
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http --verify=no https://pie.dev/get
|
$ http --verify=no https://httpbin.org/get
|
||||||
|
|
||||||
|
|
||||||
Custom CA bundle
|
Custom CA bundle
|
||||||
@@ -1209,7 +1200,7 @@ It should be a string in the
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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,
|
Note: these cipher strings do not change the negotiated version of SSL or TLS,
|
||||||
they only affect the list of available cipher suites.
|
they only affect the list of available cipher suites.
|
||||||
@@ -1256,7 +1247,7 @@ Print request and response headers:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http --print=Hh PUT pie.dev/put hello=world
|
$ http --print=Hh PUT httpbin.org/put hello=world
|
||||||
|
|
||||||
Verbose output
|
Verbose output
|
||||||
--------------
|
--------------
|
||||||
@@ -1266,12 +1257,12 @@ documentation examples:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http --verbose PUT pie.dev/put hello=world
|
$ http --verbose PUT httpbin.org/put hello=world
|
||||||
PUT /put HTTP/1.1
|
PUT /put HTTP/1.1
|
||||||
Accept: application/json, */*;q=0.5
|
Accept: application/json, */*;q=0.5
|
||||||
Accept-Encoding: gzip, deflate
|
Accept-Encoding: gzip, deflate
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Host: pie.dev
|
Host: httpbin.org
|
||||||
User-Agent: HTTPie/0.2.7dev
|
User-Agent: HTTPie/0.2.7dev
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -1294,13 +1285,13 @@ Quiet output
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
``--quiet`` redirects all output that would otherwise go to ``stdout``
|
``--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 doesn’t affect output to a file via ``--output`` or ``--download``.
|
This doesn’t affect output to a file via ``--output`` or ``--download``.
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
# There will be no output:
|
# 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
|
Viewing intermediary requests/responses
|
||||||
@@ -1315,7 +1306,7 @@ authentication is used (``--auth=digest``), etc.
|
|||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
# Include all responses that lead to the final one:
|
# 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
|
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
|
.. code-block:: bash
|
||||||
|
|
||||||
# Print the intermediary requests/responses differently than the final one:
|
# 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
|
Conditional body download
|
||||||
@@ -1343,7 +1334,7 @@ status code after an update:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
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
|
.. 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:
|
Or the output of another program:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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:
|
You can use ``echo`` for simple data:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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*:
|
You can also use a Bash *here string*:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http pie.dev/post <<<'{"name": "John"}'
|
$ http httpbin.org/post <<<'{"name": "John"}'
|
||||||
|
|
||||||
|
|
||||||
You can even pipe web services together using HTTPie:
|
You can even pipe web services together using HTTPie:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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:
|
You can use ``cat`` to enter multiline data on the terminal:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ cat | http POST pie.dev/post
|
$ cat | http POST httpbin.org/post
|
||||||
<paste>
|
<paste>
|
||||||
^D
|
^D
|
||||||
|
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
- buy milk
|
||||||
- call parents
|
- call parents
|
||||||
^D
|
^D
|
||||||
@@ -1422,7 +1413,7 @@ On OS X, you can send the contents of the clipboard with ``pbpaste``:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
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
|
.. 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.
|
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
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http --chunked PUT pie.dev/put hello=world
|
$ http --chunked PUT httpbin.org/put hello=world
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http --chunked pie.dev/post @files/data.xml
|
$ http --chunked httpbin.org/post @files/data.xml
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
.. 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
|
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.
|
`config`_ file. See ``http --help`` for all the available formatting options.
|
||||||
@@ -1551,7 +1542,7 @@ that the response body is binary,
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http pie.dev/bytes/2000
|
$ http httpbin.org/bytes/2000
|
||||||
|
|
||||||
|
|
||||||
You will nearly instantly see something like this:
|
You will nearly instantly see something like this:
|
||||||
@@ -1584,7 +1575,7 @@ Download a file:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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:
|
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
|
.. 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
|
The ``-R`` flag tells ``less`` to interpret color escape sequences included
|
||||||
@@ -1728,15 +1719,17 @@ Prettified streamed response:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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``:
|
Streamed output by small chunks à la ``tail -f``:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
# Send each new line (JSON object) to another URL as soon as it arrives from a streaming API:
|
# Send each new tweet (JSON object) mentioning "Apple" to another
|
||||||
$ http --stream pie.dev/stream/3 | while read line; do echo "$line" | http pie.dev/post ; done
|
# 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
|
Sessions
|
||||||
========
|
========
|
||||||
@@ -1756,7 +1749,7 @@ to the same host.
|
|||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
# Create a new session:
|
# 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
|
.. code-block:: bash
|
||||||
@@ -1767,7 +1760,7 @@ to the same host.
|
|||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
# Re-use the existing session — the API-Token header will be set:
|
# 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,
|
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 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
|
.. 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
|
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
|
to use the session again, any previously specified authentication or HTTP headers
|
||||||
@@ -1794,13 +1787,13 @@ will automatically be set:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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:
|
To create or reuse a different session, simple specify a different name:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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 sessions’s data is stored in JSON files inside the ``sessions``
|
Named sessions’s data is stored in JSON files inside the ``sessions``
|
||||||
subdirectory of the `config`_ directory, typically:
|
subdirectory of the `config`_ directory, typically:
|
||||||
@@ -1813,7 +1806,7 @@ you should be able list the generated sessions files using:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ ls -l ~/.config/httpie/sessions/pie.dev
|
$ ls -l ~/.config/httpie/sessions/httpbin.org
|
||||||
|
|
||||||
|
|
||||||
Anonymous sessions
|
Anonymous sessions
|
||||||
@@ -1855,12 +1848,12 @@ exchange after it has been created, specify the session name via
|
|||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
# If the session file doesn’t exist, then it is created:
|
# If the session file doesn’t 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
|
.. code-block:: bash
|
||||||
|
|
||||||
# But it is not updated:
|
# 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
|
Cookie Storage Behaviour
|
||||||
------------------------
|
------------------------
|
||||||
@@ -1873,13 +1866,13 @@ To set a cookie within a Session there are three options:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. 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`_
|
2. Set the cookie name and value through the command line as seen in `cookies`_
|
||||||
|
|
||||||
.. code-block:: bash
|
.. 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
|
3. Manually set cookie parameters in the json file of the session
|
||||||
|
|
||||||
@@ -1944,7 +1937,7 @@ environment variable:
|
|||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ export HTTPIE_CONFIG_DIR=/tmp/httpie
|
$ export HTTPIE_CONFIG_DIR=/tmp/httpie
|
||||||
$ http pie.dev/get
|
$ http httpbin.org/get
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -2005,7 +1998,7 @@ respectively.
|
|||||||
|
|
||||||
#!/bin/bash
|
#!/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!'
|
echo 'OK!'
|
||||||
else
|
else
|
||||||
case $? in
|
case $? in
|
||||||
@@ -2054,7 +2047,7 @@ HTTP request:
|
|||||||
.. code-block:: http
|
.. code-block:: http
|
||||||
|
|
||||||
POST /post HTTP/1.1
|
POST /post HTTP/1.1
|
||||||
Host: pie.dev
|
Host: httpbin.org
|
||||||
X-API-Key: 123
|
X-API-Key: 123
|
||||||
User-Agent: Bacon/1.0
|
User-Agent: Bacon/1.0
|
||||||
Content-Type: application/x-www-form-urlencoded
|
Content-Type: application/x-www-form-urlencoded
|
||||||
@@ -2066,7 +2059,7 @@ with the HTTPie command that sends it:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ http -f POST pie.dev/post \
|
$ http -f POST httpbin.org/post \
|
||||||
X-API-Key:123 \
|
X-API-Key:123 \
|
||||||
User-Agent:Bacon/1.0 \
|
User-Agent:Bacon/1.0 \
|
||||||
name=value \
|
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>`_
|
* `GitHub issues <https://github.com/jkbr/httpie/issues>`_
|
||||||
for bug reports and feature requests.
|
for bug reports and feature requests.
|
||||||
* `Discord server <https://httpie.io/chat>`_
|
* `Our Gitter chat room <https://gitter.im/jkbrzt/httpie>`_
|
||||||
to ask questions, discuss features, and for general API development discussion.
|
to ask questions, discuss features, and for general discussion.
|
||||||
* `StackOverflow <https://stackoverflow.com>`_
|
* `StackOverflow <https://stackoverflow.com>`_
|
||||||
to ask questions (please make sure to use the
|
to ask questions (please make sure to use the
|
||||||
`httpie <https://stackoverflow.com/questions/tagged/httpie>`_ tag).
|
`httpie <https://stackoverflow.com/questions/tagged/httpie>`_ tag).
|
||||||
@@ -2202,9 +2195,9 @@ have contributed.
|
|||||||
:target: https://github.com/httpie/httpie/actions
|
:target: https://github.com/httpie/httpie/actions
|
||||||
:alt: Build status of the master branch on Mac/Linux/Windows
|
: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
|
.. |gitter| image:: https://img.shields.io/gitter/room/jkbrzt/httpie.svg?style=flat-square
|
||||||
:target: https://httpie.io/chat
|
:target: https://gitter.im/jkbrzt/httpie
|
||||||
:alt: Chat on Discord
|
:alt: Chat on Gitter
|
||||||
|
|
||||||
.. |downloads| image:: https://pepy.tech/badge/httpie
|
.. |downloads| image:: https://pepy.tech/badge/httpie
|
||||||
:target: https://pepy.tech/project/httpie
|
:target: https://pepy.tech/project/httpie
|
||||||
|
@@ -9,20 +9,22 @@ class Httpie < Formula
|
|||||||
|
|
||||||
desc "User-friendly cURL replacement (command-line HTTP client)"
|
desc "User-friendly cURL replacement (command-line HTTP client)"
|
||||||
homepage "https://httpie.org/"
|
homepage "https://httpie.org/"
|
||||||
url "https://files.pythonhosted.org/packages/b4/d4/712645808103f2d15c281b9eacd184c88754ef7e9a322d9a30ba343fd341/httpie-2.3.0.tar.gz"
|
url "https://files.pythonhosted.org/packages/37/6c/0d050f49e3b2bac589367d0c3aee9c078e23c6914b0210ffc0117218bdaf/httpie-2.2.0.tar.gz"
|
||||||
sha256 "d540571991d07329d217c31bf1ff95fd217957da2aa2def09bcfa0c0fca0cf96"
|
sha256 "31ac28088ee6a0b6f3ba7a53379000c4d1910c1708c9ff768f84b111c14405a0"
|
||||||
license "BSD-3-Clause"
|
head "https://github.com/jakubroztocil/httpie.git"
|
||||||
head "https://github.com/httpie/httpie.git"
|
|
||||||
|
|
||||||
livecheck do
|
bottle do
|
||||||
url :stable
|
cellar :any_skip_relocation
|
||||||
|
sha256 "25f0e58f81a2cdd9cba772f07d67591533b4b31a2b970a356701aa046d4d9638" => :catalina
|
||||||
|
sha256 "be158ebb4cfd327ebea02f7b8b8d63d093e474cd303eafff4a2b56b0611983a2" => :mojave
|
||||||
|
sha256 "f331edb94183bfc5fa9de4b4abf148cc91a3a8b3c0e24cc1f5e6b0a4172dd34d" => :high_sierra
|
||||||
end
|
end
|
||||||
|
|
||||||
depends_on "python@3.9"
|
depends_on "python@3.8"
|
||||||
|
|
||||||
resource "Pygments" do
|
resource "Pygments" do
|
||||||
url "https://files.pythonhosted.org/packages/5d/0e/ff13c055b014d634ed17e9e9345a312c28ec6a06448ba6d6ccfa77c3b5e8/Pygments-2.7.2.tar.gz"
|
url "https://files.pythonhosted.org/packages/6e/4d/4d2fe93a35dfba417311a4ff627489a947b01dc0cc377a3673c00cf7e4b2/Pygments-2.6.1.tar.gz"
|
||||||
sha256 "381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0"
|
sha256 "647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"
|
||||||
end
|
end
|
||||||
|
|
||||||
resource "requests" do
|
resource "requests" do
|
||||||
@@ -30,24 +32,19 @@ class Httpie < Formula
|
|||||||
sha256 "b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"
|
sha256 "b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"
|
||||||
end
|
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
|
resource "certifi" do
|
||||||
url "https://files.pythonhosted.org/packages/40/a7/ded59fa294b85ca206082306bba75469a38ea1c7d44ea7e1d64f5443d67a/certifi-2020.6.20.tar.gz"
|
url "https://files.pythonhosted.org/packages/b4/19/53433f37a31543364c8676f30b291d128cdf4cd5b31b755b7890f8e89ac8/certifi-2020.4.5.2.tar.gz"
|
||||||
sha256 "5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"
|
sha256 "5ad7e9a056d25ffa5082862e36f119f7f7cec6457fa07ee2f8c339814b80c9b1"
|
||||||
end
|
end
|
||||||
|
|
||||||
resource "urllib3" do
|
resource "urllib3" do
|
||||||
url "https://files.pythonhosted.org/packages/76/d9/bbbafc76b18da706451fa91bc2ebe21c0daf8868ef3c30b869ac7cb7f01d/urllib3-1.25.11.tar.gz"
|
url "https://files.pythonhosted.org/packages/05/8c/40cd6949373e23081b3ea20d5594ae523e681b6f472e600fbc95ed046a36/urllib3-1.25.9.tar.gz"
|
||||||
sha256 "8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2"
|
sha256 "3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527"
|
||||||
end
|
end
|
||||||
|
|
||||||
resource "idna" do
|
resource "idna" do
|
||||||
url "https://files.pythonhosted.org/packages/ea/b7/e0e3c1c467636186c39925827be42f16fee389dc404ac29e930e9136be70/idna-2.10.tar.gz"
|
url "https://files.pythonhosted.org/packages/cb/19/57503b5de719ee45e83472f339f617b0c01ad75cba44aba1e4c97c2b0abd/idna-2.9.tar.gz"
|
||||||
sha256 "b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"
|
sha256 "7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb"
|
||||||
end
|
end
|
||||||
|
|
||||||
resource "chardet" do
|
resource "chardet" do
|
||||||
|
@@ -3,6 +3,6 @@ HTTPie: command-line HTTP client for the API era.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = '2.4.0'
|
__version__ = '2.3.0'
|
||||||
__author__ = 'Jakub Roztocil'
|
__author__ = 'Jakub Roztocil'
|
||||||
__licence__ = 'BSD'
|
__licence__ = 'BSD'
|
||||||
|
@@ -5,6 +5,15 @@ import enum
|
|||||||
import re
|
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)
|
URL_SCHEME_RE = re.compile(r'^[a-z][a-z0-9.+-]*://', re.IGNORECASE)
|
||||||
|
|
||||||
HTTP_POST = 'POST'
|
HTTP_POST = 'POST'
|
||||||
|
@@ -37,7 +37,7 @@ parser = HTTPieArgumentParser(
|
|||||||
|
|
||||||
Suggestions and bug reports are greatly appreciated:
|
Suggestions and bug reports are greatly appreciated:
|
||||||
|
|
||||||
https://github.com/httpie/httpie/issues
|
https://github.com/jakubroztocil/httpie/issues
|
||||||
|
|
||||||
'''),
|
'''),
|
||||||
)
|
)
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
from io import BytesIO
|
||||||
from typing import Callable, Dict, IO, List, Optional, Tuple, Union
|
from typing import Callable, Dict, IO, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from httpie.cli.argtypes import KeyValueArg
|
from httpie.cli.argtypes import KeyValueArg
|
||||||
|
@@ -134,7 +134,7 @@ def collect_messages(
|
|||||||
# noinspection PyProtectedMember
|
# noinspection PyProtectedMember
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def max_headers(limit):
|
def max_headers(limit):
|
||||||
# <https://github.com/httpie/httpie/issues/802>
|
# <https://github.com/jakubroztocil/httpie/issues/802>
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
orig = http.client._MAXHEADERS
|
orig = http.client._MAXHEADERS
|
||||||
http.client._MAXHEADERS = limit or float('Inf')
|
http.client._MAXHEADERS = limit or float('Inf')
|
||||||
@@ -188,7 +188,7 @@ def finalize_headers(headers: RequestHeadersDict) -> RequestHeadersDict:
|
|||||||
# Also, requests raises `InvalidHeader` for leading spaces.
|
# Also, requests raises `InvalidHeader` for leading spaces.
|
||||||
value = value.strip()
|
value = value.strip()
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
# See <https://github.com/httpie/httpie/issues/212>
|
# See <https://github.com/jakubroztocil/httpie/issues/212>
|
||||||
value = value.encode('utf8')
|
value = value.encode('utf8')
|
||||||
final_headers[name] = value
|
final_headers[name] = value
|
||||||
return final_headers
|
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
|
untouched because other (welcome) processing on the URL might have
|
||||||
taken place.
|
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>
|
<https://ec.haxx.se/http/http-basics#path-as-is>
|
||||||
|
147
httpie/core.py
147
httpie/core.py
@@ -9,17 +9,26 @@ from pygments import __version__ as pygments_version
|
|||||||
from requests import __version__ as requests_version
|
from requests import __version__ as requests_version
|
||||||
|
|
||||||
from httpie import __version__ as httpie_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.client import collect_messages
|
||||||
from httpie.context import Environment
|
from httpie.context import Environment
|
||||||
from httpie.downloads import Downloader
|
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.plugins.registry import plugin_manager
|
||||||
from httpie.status import ExitStatus, http_status_to_exit_status
|
from httpie.status import ExitStatus, http_status_to_exit_status
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyDefaultArgument
|
# 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.
|
The main function.
|
||||||
|
|
||||||
@@ -125,82 +134,112 @@ def get_output_options(
|
|||||||
}[type(message)]
|
}[type(message)]
|
||||||
|
|
||||||
|
|
||||||
def program(args: argparse.Namespace, env: Environment) -> ExitStatus:
|
def program(
|
||||||
|
args: argparse.Namespace,
|
||||||
|
env: Environment,
|
||||||
|
) -> ExitStatus:
|
||||||
"""
|
"""
|
||||||
The main program without error handling.
|
The main program without error handling.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# TODO: Refactor and drastically simplify, especially so that the separator logic is elsewhere.
|
|
||||||
exit_status = ExitStatus.SUCCESS
|
exit_status = ExitStatus.SUCCESS
|
||||||
downloader = None
|
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:
|
try:
|
||||||
if args.download:
|
if args.download:
|
||||||
args.follow = True # --download implies --follow.
|
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)
|
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 they’re generated
|
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
|
||||||
|
)
|
||||||
for message in messages:
|
for message in messages:
|
||||||
|
maybe_separate()
|
||||||
is_request = isinstance(message, requests.PreparedRequest)
|
is_request = isinstance(message, requests.PreparedRequest)
|
||||||
with_headers, with_body = get_output_options(args=args, message=message)
|
with_headers, with_body = get_output_options(
|
||||||
do_write_body = with_body
|
args=args, message=message)
|
||||||
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 is_request:
|
||||||
if not initial_request:
|
if not initial_request:
|
||||||
initial_request = message
|
initial_request = message
|
||||||
is_streamed_upload = not isinstance(message.body, (str, bytes))
|
is_streamed_upload = not isinstance(
|
||||||
|
message.body, (str, bytes))
|
||||||
if with_body:
|
if with_body:
|
||||||
do_write_body = not is_streamed_upload
|
with_body = not is_streamed_upload
|
||||||
force_separator = is_streamed_upload and env.stdout_isatty
|
needs_separator = is_streamed_upload
|
||||||
else:
|
else:
|
||||||
final_response = message
|
final_response = message
|
||||||
if args.check_status or downloader:
|
if args.check_status or downloader:
|
||||||
exit_status = http_status_to_exit_status(http_status=message.status_code, follow=args.follow)
|
exit_status = http_status_to_exit_status(
|
||||||
if exit_status != ExitStatus.SUCCESS and (not env.stdout_isatty or args.quiet):
|
http_status=message.status_code,
|
||||||
env.log_error(f'HTTP {message.raw.status} {message.raw.reason}', level='warning')
|
follow=args.follow
|
||||||
write_message(requests_message=message, env=env, args=args, with_headers=with_headers,
|
)
|
||||||
with_body=do_write_body)
|
if (not env.stdout_isatty
|
||||||
prev_with_body = with_body
|
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:
|
if downloader and exit_status == ExitStatus.SUCCESS:
|
||||||
# Last response body download.
|
# Last response body download.
|
||||||
download_stream, download_to = downloader.start(
|
download_stream, download_to = downloader.start(
|
||||||
initial_url=initial_request.url,
|
initial_url=initial_request.url,
|
||||||
final_response=final_response,
|
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()
|
downloader.finish()
|
||||||
if downloader.interrupted:
|
if downloader.interrupted:
|
||||||
exit_status = ExitStatus.ERROR
|
exit_status = ExitStatus.ERROR
|
||||||
@@ -214,7 +253,9 @@ def program(args: argparse.Namespace, env: Environment) -> ExitStatus:
|
|||||||
finally:
|
finally:
|
||||||
if downloader and not downloader.finished:
|
if downloader and not downloader.finished:
|
||||||
downloader.failed()
|
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()
|
args.output_file.close()
|
||||||
|
|
||||||
|
|
||||||
@@ -242,6 +283,6 @@ def decode_raw_args(
|
|||||||
"""
|
"""
|
||||||
return [
|
return [
|
||||||
arg.decode(stdin_encoding)
|
arg.decode(stdin_encoding)
|
||||||
if type(arg) is bytes else arg
|
if type(arg) == bytes else arg
|
||||||
for arg in args
|
for arg in args
|
||||||
]
|
]
|
||||||
|
@@ -247,7 +247,7 @@ class Downloader:
|
|||||||
assert not self.status.time_started
|
assert not self.status.time_started
|
||||||
|
|
||||||
# FIXME: some servers still might sent Content-Encoding: gzip
|
# FIXME: some servers still might sent Content-Encoding: gzip
|
||||||
# <https://github.com/httpie/httpie/issues/423>
|
# <https://github.com/jakubroztocil/httpie/issues/423>
|
||||||
try:
|
try:
|
||||||
total_size = int(final_response.headers['Content-Length'])
|
total_size = int(final_response.headers['Content-Length'])
|
||||||
except (KeyError, ValueError, TypeError):
|
except (KeyError, ValueError, TypeError):
|
||||||
|
@@ -12,10 +12,6 @@ from httpie.output.streams import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
MESSAGE_SEPARATOR = '\n\n'
|
|
||||||
MESSAGE_SEPARATOR_BYTES = MESSAGE_SEPARATOR.encode()
|
|
||||||
|
|
||||||
|
|
||||||
def write_message(
|
def write_message(
|
||||||
requests_message: Union[requests.PreparedRequest, requests.Response],
|
requests_message: Union[requests.PreparedRequest, requests.Response],
|
||||||
env: Environment,
|
env: Environment,
|
||||||
@@ -115,7 +111,7 @@ def build_output_stream_for_message(
|
|||||||
and not getattr(requests_message, 'is_body_upload_chunk', False)):
|
and not getattr(requests_message, 'is_body_upload_chunk', False)):
|
||||||
# Ensure a blank line after the response body.
|
# Ensure a blank line after the response body.
|
||||||
# For terminal output only.
|
# For terminal output only.
|
||||||
yield MESSAGE_SEPARATOR_BYTES
|
yield b'\n\n'
|
||||||
|
|
||||||
|
|
||||||
def get_stream_type_and_kwargs(
|
def get_stream_type_and_kwargs(
|
||||||
|
@@ -19,7 +19,7 @@ class HTTPBasicAuth(requests.auth.HTTPBasicAuth):
|
|||||||
"""
|
"""
|
||||||
Override username/password serialization to allow unicode.
|
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
|
# noinspection PyTypeChecker
|
||||||
|
@@ -77,9 +77,7 @@ class Session(BaseConfigDict):
|
|||||||
if value is None:
|
if value is None:
|
||||||
continue # Ignore explicitly unset headers
|
continue # Ignore explicitly unset headers
|
||||||
|
|
||||||
if type(value) is not str:
|
value = value.decode('utf8')
|
||||||
value = value.decode('utf8')
|
|
||||||
|
|
||||||
if name.lower() == 'user-agent' and value.startswith('HTTPie/'):
|
if name.lower() == 'user-agent' and value.startswith('HTTPie/'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
@@ -109,8 +109,6 @@ def get_expired_cookies(
|
|||||||
for attrs in attr_sets
|
for attrs in attr_sets
|
||||||
]
|
]
|
||||||
|
|
||||||
_max_age_to_expires(cookies=cookies, now=now)
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
'name': cookie['name'],
|
'name': cookie['name'],
|
||||||
@@ -119,18 +117,3 @@ def get_expired_cookies(
|
|||||||
for cookie in cookies
|
for cookie in cookies
|
||||||
if is_expired(expires=cookie.get('expires'))
|
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)
|
|
||||||
|
@@ -4,7 +4,7 @@
|
|||||||
[tool:pytest]
|
[tool:pytest]
|
||||||
# <https://docs.pytest.org/en/latest/customize.html>
|
# <https://docs.pytest.org/en/latest/customize.html>
|
||||||
norecursedirs = tests/fixtures
|
norecursedirs = tests/fixtures
|
||||||
addopts = --tb=native --doctest-modules
|
addopts = --tb=native
|
||||||
|
|
||||||
|
|
||||||
[pycodestyle]
|
[pycodestyle]
|
||||||
|
3
setup.py
3
setup.py
@@ -15,7 +15,6 @@ class PyTest(TestCommand):
|
|||||||
and runs the tests with no fancy stuff like parallel execution.
|
and runs the tests with no fancy stuff like parallel execution.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def finalize_options(self):
|
def finalize_options(self):
|
||||||
TestCommand.finalize_options(self)
|
TestCommand.finalize_options(self)
|
||||||
self.test_args = [
|
self.test_args = [
|
||||||
@@ -74,7 +73,6 @@ setup(
|
|||||||
version=httpie.__version__,
|
version=httpie.__version__,
|
||||||
description=httpie.__doc__.strip(),
|
description=httpie.__doc__.strip(),
|
||||||
long_description=long_description(),
|
long_description=long_description(),
|
||||||
long_description_content_type='text/x-rst',
|
|
||||||
url='https://httpie.org/',
|
url='https://httpie.org/',
|
||||||
download_url=f'https://github.com/httpie/httpie/archive/{httpie.__version__}.tar.gz',
|
download_url=f'https://github.com/httpie/httpie/archive/{httpie.__version__}.tar.gz',
|
||||||
author=httpie.__author__,
|
author=httpie.__author__,
|
||||||
@@ -112,5 +110,6 @@ setup(
|
|||||||
'Twitter': 'https://twitter.com/httpie',
|
'Twitter': 'https://twitter.com/httpie',
|
||||||
'Documentation': 'https://httpie.org/docs',
|
'Documentation': 'https://httpie.org/docs',
|
||||||
'Online Demo': 'https://httpie.org/run',
|
'Online Demo': 'https://httpie.org/run',
|
||||||
|
'Donate': 'https://httpie.org/donate',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@@ -5,4 +5,4 @@ HTTPie Test Suite
|
|||||||
Please see `CONTRIBUTING`_.
|
Please see `CONTRIBUTING`_.
|
||||||
|
|
||||||
|
|
||||||
.. _CONTRIBUTING: https://github.com/httpie/httpie/blob/master/CONTRIBUTING.rst
|
.. _CONTRIBUTING: https://github.com/jakubroztocil/httpie/blob/master/CONTRIBUTING.rst
|
||||||
|
@@ -58,7 +58,7 @@ def test_credentials_in_url_auth_flag_has_priority(httpbin_both):
|
|||||||
])
|
])
|
||||||
def test_only_username_in_url(url):
|
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())
|
args = httpie.cli.definition.parser.parse_args(args=[url], env=MockEnvironment())
|
||||||
|
@@ -11,7 +11,7 @@ from fixtures import FILE_PATH
|
|||||||
|
|
||||||
def test_default_headers_case_insensitive(httpbin):
|
def test_default_headers_case_insensitive(httpbin):
|
||||||
"""
|
"""
|
||||||
<https://github.com/httpie/httpie/issues/644>
|
<https://github.com/jakubroztocil/httpie/issues/644>
|
||||||
"""
|
"""
|
||||||
r = http(
|
r = http(
|
||||||
'--debug',
|
'--debug',
|
||||||
@@ -63,7 +63,7 @@ class TestAutoContentTypeAndAcceptHeaders:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def test_GET_no_data_no_auto_headers(self, httpbin):
|
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')
|
r = http('GET', httpbin.url + '/headers')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json['headers']['Accept'] == '*/*'
|
assert r.json['headers']['Accept'] == '*/*'
|
||||||
@@ -94,7 +94,7 @@ class TestAutoContentTypeAndAcceptHeaders:
|
|||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json['headers']['Accept'] == JSON_ACCEPT
|
assert r.json['headers']['Accept'] == JSON_ACCEPT
|
||||||
# Make sure Content-Type gets set even with no data.
|
# Make sure Content-Type gets set even with no data.
|
||||||
# https://github.com/httpie/httpie/issues/137
|
# https://github.com/jakubroztocil/httpie/issues/137
|
||||||
assert 'application/json' in r.json['headers']['Content-Type']
|
assert 'application/json' in r.json['headers']['Content-Type']
|
||||||
|
|
||||||
def test_GET_explicit_JSON_explicit_headers(self, httpbin):
|
def test_GET_explicit_JSON_explicit_headers(self, httpbin):
|
||||||
|
@@ -5,6 +5,7 @@ from urllib.request import urlopen
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import mock
|
import mock
|
||||||
|
import requests
|
||||||
from requests.structures import CaseInsensitiveDict
|
from requests.structures import CaseInsensitiveDict
|
||||||
|
|
||||||
from httpie.downloads import (
|
from httpie.downloads import (
|
||||||
|
@@ -39,7 +39,7 @@ def test_debug():
|
|||||||
def test_help():
|
def test_help():
|
||||||
r = http('--help', tolerate_error_exit_status=True)
|
r = http('--help', tolerate_error_exit_status=True)
|
||||||
assert r.exit_status == ExitStatus.SUCCESS
|
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():
|
def test_version():
|
||||||
@@ -121,7 +121,7 @@ def test_POST_file(httpbin_both):
|
|||||||
|
|
||||||
def test_form_POST_file_redirected_stdin(httpbin):
|
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:
|
with open(FILE_PATH) as f:
|
||||||
@@ -182,4 +182,4 @@ def test_json_input_preserve_order(httpbin_both):
|
|||||||
'order:={"map":{"1":"first","2":"second"}}')
|
'order:={"map":{"1":"first","2":"second"}}')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json['data'] == \
|
assert r.json['data'] == \
|
||||||
'{"order": {"map": {"1": "first", "2": "second"}}}'
|
'{"order": {"map": {"1": "first", "2": "second"}}}'
|
||||||
|
@@ -54,21 +54,6 @@ class TestQuietFlag:
|
|||||||
assert r == ''
|
assert r == ''
|
||||||
assert r.stderr == ''
|
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',
|
@mock.patch('httpie.cli.argtypes.AuthCredentials._getpass',
|
||||||
new=lambda self, prompt: 'password')
|
new=lambda self, prompt: 'password')
|
||||||
def test_quiet_with_password_prompt(self, httpbin):
|
def test_quiet_with_password_prompt(self, httpbin):
|
||||||
@@ -142,7 +127,7 @@ class TestVerboseFlag:
|
|||||||
assert r.count('__test__') == 2
|
assert r.count('__test__') == 2
|
||||||
|
|
||||||
def test_verbose_form(self, httpbin):
|
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',
|
r = http('--verbose', '--form', 'POST', httpbin.url + '/post',
|
||||||
'A=B', 'C=D')
|
'A=B', 'C=D')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
|
@@ -1,17 +1,16 @@
|
|||||||
"""Miscellaneous regression tests"""
|
"""Miscellaneous regression tests"""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from utils import http, HTTP_OK
|
||||||
from httpie.compat import is_windows
|
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):
|
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'
|
url = httpbin.url + '/get'
|
||||||
r = http('--print=hH', url, 'host:{0}'.format(host))
|
r = http('--print=hH', url, 'host:{0}'.format(host))
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
@@ -22,28 +21,7 @@ def test_Host_header_overwrite(httpbin):
|
|||||||
@pytest.mark.skipif(is_windows, reason='Unix-only')
|
@pytest.mark.skipif(is_windows, reason='Unix-only')
|
||||||
def test_output_devnull(httpbin):
|
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')
|
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,
|
|
||||||
])
|
|
||||||
|
@@ -16,7 +16,6 @@ from httpie.sessions import Session
|
|||||||
from httpie.utils import get_expired_cookies
|
from httpie.utils import get_expired_cookies
|
||||||
from tests.test_auth_plugins import basic_auth
|
from tests.test_auth_plugins import basic_auth
|
||||||
from utils import HTTP_OK, MockEnvironment, http, mk_config_dir
|
from utils import HTTP_OK, MockEnvironment, http, mk_config_dir
|
||||||
from fixtures import FILE_PATH_ARG
|
|
||||||
|
|
||||||
|
|
||||||
class SessionTestBase:
|
class SessionTestBase:
|
||||||
@@ -162,12 +161,6 @@ class TestSession(SessionTestBase):
|
|||||||
assert 'Content-Type' not in r2.json['headers']
|
assert 'Content-Type' not in r2.json['headers']
|
||||||
assert 'If-Unmodified-Since' 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):
|
def test_session_by_path(self, httpbin):
|
||||||
self.start_session(httpbin)
|
self.start_session(httpbin)
|
||||||
session_path = self.config_dir / 'session-by-path.json'
|
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):
|
def test_session_default_header_value_overwritten(self, httpbin):
|
||||||
self.start_session(httpbin)
|
self.start_session(httpbin)
|
||||||
# https://github.com/httpie/httpie/issues/180
|
# https://github.com/jakubroztocil/httpie/issues/180
|
||||||
r1 = http('--session=test',
|
r1 = http('--session=test',
|
||||||
httpbin.url + '/headers', 'User-Agent:custom',
|
httpbin.url + '/headers', 'User-Agent:custom',
|
||||||
env=self.env())
|
env=self.env())
|
||||||
@@ -212,7 +205,7 @@ class TestSession(SessionTestBase):
|
|||||||
assert r2.json['headers']['User-Agent'] == 'custom'
|
assert r2.json['headers']['User-Agent'] == 'custom'
|
||||||
|
|
||||||
def test_download_in_session(self, httpbin):
|
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)
|
self.start_session(httpbin)
|
||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
os.chdir(gettempdir())
|
os.chdir(gettempdir())
|
||||||
@@ -345,15 +338,6 @@ class TestExpiredCookies(CookieTestBase):
|
|||||||
assert 'cookie1' in updated_session['cookies']
|
assert 'cookie1' in updated_session['cookies']
|
||||||
assert 'cookie2' not 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(
|
@pytest.mark.parametrize(
|
||||||
argnames=['headers', 'now', 'expected_expired'],
|
argnames=['headers', 'now', 'expected_expired'],
|
||||||
argvalues=[
|
argvalues=[
|
||||||
|
@@ -11,7 +11,7 @@ from utils import HTTP_OK, TESTS_ROOT, http
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
# Handle OpenSSL errors, if installed.
|
# Handle OpenSSL errors, if installed.
|
||||||
# See <https://github.com/httpie/httpie/issues/729>
|
# See <https://github.com/jakubroztocil/httpie/issues/729>
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
import OpenSSL.SSL
|
import OpenSSL.SSL
|
||||||
ssl_errors = (
|
ssl_errors = (
|
||||||
|
@@ -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)
|
|
@@ -52,23 +52,6 @@ def test_chunked_stdin():
|
|||||||
assert r.count(FILE_CONTENT) == 2
|
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:
|
class TestMultipartFormDataFileUpload:
|
||||||
|
|
||||||
def test_non_existent_file_raises_parse_error(self, httpbin):
|
def test_non_existent_file_raises_parse_error(self, httpbin):
|
||||||
|
@@ -1,14 +1,12 @@
|
|||||||
# coding=utf-8
|
# coding=utf-8
|
||||||
"""Utilities for HTTPie test suite."""
|
"""Utilities for HTTPie test suite."""
|
||||||
import re
|
|
||||||
import shlex
|
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
import tempfile
|
import tempfile
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Union, List
|
from typing import Optional, Union
|
||||||
|
|
||||||
from httpie.status import ExitStatus
|
from httpie.status import ExitStatus
|
||||||
from httpie.config import Config
|
from httpie.config import Config
|
||||||
@@ -19,10 +17,10 @@ from httpie.core import main
|
|||||||
# pytest-httpbin currently does not support chunked requests:
|
# pytest-httpbin currently does not support chunked requests:
|
||||||
# <https://github.com/kevin1024/pytest-httpbin/issues/33>
|
# <https://github.com/kevin1024/pytest-httpbin/issues/33>
|
||||||
# <https://github.com/kevin1024/pytest-httpbin/issues/28>
|
# <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'
|
CRLF = '\r\n'
|
||||||
COLOR = '\x1b['
|
COLOR = '\x1b['
|
||||||
HTTP_OK = '200 OK'
|
HTTP_OK = '200 OK'
|
||||||
@@ -51,7 +49,7 @@ class StdinBytesIO(BytesIO):
|
|||||||
|
|
||||||
class MockEnvironment(Environment):
|
class MockEnvironment(Environment):
|
||||||
"""Environment subclass with reasonable defaults for testing."""
|
"""Environment subclass with reasonable defaults for testing."""
|
||||||
colors = 0 # For easier debugging
|
colors = 0
|
||||||
stdin_isatty = True,
|
stdin_isatty = True,
|
||||||
stdout_isatty = True
|
stdout_isatty = True
|
||||||
is_windows = False
|
is_windows = False
|
||||||
@@ -115,15 +113,6 @@ class BaseCLIResponse:
|
|||||||
devnull: str = None
|
devnull: str = None
|
||||||
json: dict = None
|
json: dict = None
|
||||||
exit_status: ExitStatus = 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):
|
class BytesCLIResponse(bytes, BaseCLIResponse):
|
||||||
@@ -209,7 +198,7 @@ def http(
|
|||||||
|
|
||||||
Example:
|
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')
|
>>> httpbin = getfixture('httpbin')
|
||||||
>>> r = http('-a', 'user:pw', httpbin.url + '/basic-auth/user/pw')
|
>>> r = http('-a', 'user:pw', httpbin.url + '/basic-auth/user/pw')
|
||||||
@@ -295,13 +284,10 @@ def http(
|
|||||||
r.devnull = devnull_output
|
r.devnull = devnull_output
|
||||||
r.stderr = stderr.read()
|
r.stderr = stderr.read()
|
||||||
r.exit_status = exit_status
|
r.exit_status = exit_status
|
||||||
r.args = args
|
|
||||||
r.complete_args = ' '.join(complete_args)
|
|
||||||
|
|
||||||
if r.exit_status != ExitStatus.SUCCESS:
|
if r.exit_status != ExitStatus.SUCCESS:
|
||||||
sys.stderr.write(r.stderr)
|
sys.stderr.write(r.stderr)
|
||||||
|
|
||||||
# print(f'\n\n$ {r.command}\n')
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
finally:
|
finally:
|
@@ -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)
|
|
@@ -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
|
|
@@ -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]
|
|
||||||
)
|
|
Reference in New Issue
Block a user