mirror of
https://github.com/httpie/cli.git
synced 2025-08-14 09:49:12 +02:00
Compare commits
102 Commits
auto-updat
...
3.2.4
Author | SHA1 | Date | |
---|---|---|---|
2105caa49b | |||
8560d1196d | |||
2ef4a57d8c | |||
ff742581f4 | |||
fd30c4ef62 | |||
cee82c825e | |||
9eb8699873 | |||
3037327410 | |||
50e1564600 | |||
f4cf43ecdd | |||
7f03c52d22 | |||
5c068f8102 | |||
10b7d317d0 | |||
3de7c82077 | |||
db16bbee96 | |||
3524ccf0ba | |||
8ac44b57ce | |||
2db28ef692 | |||
7a234d60da | |||
a842a932cc | |||
b934eec7fc | |||
9e8e3691c8 | |||
e52a60e67c | |||
8aa654d1ef | |||
011402152c | |||
30a6f73ec8 | |||
ec4fb84254 | |||
c8c135ffff | |||
2da955fb06 | |||
c2677eeccf | |||
5325a9bc07 | |||
2e3272b5ba | |||
5dc30bc438 | |||
442aa673ac | |||
3e290e5dba | |||
2a9cd226aa | |||
3b58a4a4a2 | |||
7512ca7e47 | |||
cc697db730 | |||
cbe53ed79a | |||
3664644722 | |||
29de4ce115 | |||
879fedc10a | |||
18bb49b268 | |||
fcd3f7ece6 | |||
8e56e9fc64 | |||
44d3cff03f | |||
d021b94b5d | |||
4e29a6d561 | |||
1ae4152e1e | |||
47e9b99ba1 | |||
265841f866 | |||
b16392fbb9 | |||
e73c3e6c24 | |||
f0563deb7f | |||
4894b4c0fc | |||
3a123c4125 | |||
621042a048 | |||
0689b55e1d | |||
a7321d8ac4 | |||
d9a73cd8eb | |||
930cd9081a | |||
3549ee8342 | |||
810bb1c77b | |||
767f3c3a19 | |||
1236793272 | |||
c3a2f87dd2 | |||
1121d695a8 | |||
5794a070e1 | |||
4736a16698 | |||
3ad408add7 | |||
91cdb22a4b | |||
c995fd9b24 | |||
418b12bbd6 | |||
ecff53f2d5 | |||
41da87f7c8 | |||
4f172a61b4 | |||
542a2d35de | |||
d9e1dc08c9 | |||
3b734fb0bc | |||
8abe47969e | |||
8173cb0337 | |||
7fd34fc8ce | |||
80ae644464 | |||
69fe5dbfd1 | |||
f09e7564e7 | |||
dc5274e491 | |||
ad2b86ccf4 | |||
11b2af0f59 | |||
b54239b525 | |||
b0b0f3dc53 | |||
9f7612cdeb | |||
5e76ebc5e1 | |||
343a521673 | |||
2142ae60c3 | |||
0b6a9b23c2 | |||
9e1c0b98c7 | |||
003f2095d4 | |||
f9b5c2f696 | |||
76495cbdec | |||
c4d7d05f3b | |||
7a4fb5d966 |
2
.github/workflows/benchmark.yml
vendored
2
.github/workflows/benchmark.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v3
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: "3.9"
|
python-version: "3.9"
|
||||||
|
|
||||||
|
2
.github/workflows/code-style.yml
vendored
2
.github/workflows/code-style.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v3
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.9
|
python-version: 3.9
|
||||||
- run: make venv
|
- run: make venv
|
||||||
|
@ -1,28 +1,22 @@
|
|||||||
name: Update Autogenerated Files
|
name: Update Generated Content
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
regen-autogenerated-files:
|
update-content:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
- uses: actions/setup-python@v3
|
|
||||||
with:
|
with:
|
||||||
python-version: 3.9
|
python-version: 3.9
|
||||||
|
- run: make content
|
||||||
- run: make regen-all
|
|
||||||
|
|
||||||
- name: Create Pull Request
|
- name: Create Pull Request
|
||||||
id: cpr
|
id: cpr
|
||||||
uses: peter-evans/create-pull-request@v4
|
uses: peter-evans/create-pull-request@v4
|
||||||
with:
|
with:
|
||||||
commit-message: "[automated] Update auto-generated files"
|
commit-message: "[automated] Update generated content"
|
||||||
title: "[automated] Update auto-generated files"
|
title: "[automated] Update generated content"
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
2
.github/workflows/coverage.yml
vendored
2
.github/workflows/coverage.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-python@v3
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: "3.10"
|
python-version: "3.10"
|
||||||
- run: make install
|
- run: make install
|
||||||
|
2
.github/workflows/docs-deploy.yml
vendored
2
.github/workflows/docs-deploy.yml
vendored
@ -17,6 +17,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Install HTTPie
|
- name: Install HTTPie
|
||||||
run: sudo snap install --edge httpie
|
run: sudo pip install httpie
|
||||||
- name: Trigger new documentation build
|
- name: Trigger new documentation build
|
||||||
run: http --ignore-stdin POST ${{ secrets.DOCS_UPDATE_VERCEL_HOOK }}
|
run: http --ignore-stdin POST ${{ secrets.DOCS_UPDATE_VERCEL_HOOK }}
|
||||||
|
2
.github/workflows/release-brew.yml
vendored
2
.github/workflows/release-brew.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
ref: ${{ github.event.inputs.branch }}
|
ref: ${{ github.event.inputs.branch }}
|
||||||
|
|
||||||
- uses: mislav/bump-homebrew-formula-action@v1
|
- uses: mislav/bump-homebrew-formula-action@v2
|
||||||
with:
|
with:
|
||||||
formula-name: httpie
|
formula-name: httpie
|
||||||
tag-name: ${{ github.events.inputs.branch }}
|
tag-name: ${{ github.events.inputs.branch }}
|
||||||
|
17
.github/workflows/release-choco.yml
vendored
17
.github/workflows/release-choco.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
name: Release the Chocolatey
|
name: Release the Chocolatey
|
||||||
runs-on: windows-2019
|
runs-on: windows-2019
|
||||||
env:
|
env:
|
||||||
package-dir: ./httpie/docs/packaging/windows-chocolatey
|
package-dir: docs\packaging\windows-chocolatey
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@ -30,9 +30,21 @@ jobs:
|
|||||||
run: choco info httpie -s .
|
run: choco info httpie -s .
|
||||||
working-directory: ${{ env.package-dir }}
|
working-directory: ${{ env.package-dir }}
|
||||||
|
|
||||||
- name: Check the Installation
|
- name: Local installation
|
||||||
run: |
|
run: |
|
||||||
choco install httpie -y -dv -s "'.;https://community.chocolatey.org/api/v2/'"
|
choco install httpie -y -dv -s "'.;https://community.chocolatey.org/api/v2/'"
|
||||||
|
working-directory: ${{ env.package-dir }}
|
||||||
|
|
||||||
|
- name: Test the locally installed binaries
|
||||||
|
run: |
|
||||||
|
# Source: https://stackoverflow.com/a/46760714/15330941
|
||||||
|
|
||||||
|
# Make `refreshenv` available right away, by defining the $env:ChocolateyInstall
|
||||||
|
# variable and importing the Chocolatey profile module.
|
||||||
|
$env:ChocolateyInstall = Convert-Path "$((Get-Command choco).Path)\..\.."
|
||||||
|
Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
|
||||||
|
refreshenv
|
||||||
|
|
||||||
http --version
|
http --version
|
||||||
https --version
|
https --version
|
||||||
httpie --version
|
httpie --version
|
||||||
@ -46,3 +58,4 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
choco apikey --key $CHOCO_API_KEY --source https://push.chocolatey.org/
|
choco apikey --key $CHOCO_API_KEY --source https://push.chocolatey.org/
|
||||||
choco push httpie*.nupkg --source https://push.chocolatey.org/
|
choco push httpie*.nupkg --source https://push.chocolatey.org/
|
||||||
|
working-directory: ${{ env.package-dir }}
|
||||||
|
23
.github/workflows/release-linux-standalone.yml
vendored
23
.github/workflows/release-linux-standalone.yml
vendored
@ -7,6 +7,9 @@ on:
|
|||||||
description: "The branch, tag or SHA to release from"
|
description: "The branch, tag or SHA to release from"
|
||||||
required: true
|
required: true
|
||||||
default: "master"
|
default: "master"
|
||||||
|
tag_name:
|
||||||
|
description: "Which release to upload the artifacts to (e.g., 3.0)"
|
||||||
|
required: true
|
||||||
|
|
||||||
release:
|
release:
|
||||||
types: [released, prereleased]
|
types: [released, prereleased]
|
||||||
@ -21,7 +24,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
ref: ${{ github.event.inputs.branch }}
|
ref: ${{ github.event.inputs.branch }}
|
||||||
|
|
||||||
- uses: actions/setup-python@v3
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.9
|
python-version: 3.9
|
||||||
|
|
||||||
@ -45,24 +48,30 @@ jobs:
|
|||||||
name: httpie.rpm
|
name: httpie.rpm
|
||||||
path: extras/packaging/linux/artifacts/dist/*.rpm
|
path: extras/packaging/linux/artifacts/dist/*.rpm
|
||||||
|
|
||||||
|
- name: Determine the release upload upload_url
|
||||||
|
id: release_id
|
||||||
|
run: |
|
||||||
|
pip install httpie
|
||||||
|
export API_URL="api.github.com/repos/httpie/cli/releases/tags/${{ github.event.inputs.tag_name }}"
|
||||||
|
export UPLOAD_URL=`https --ignore-stdin GET $API_URL | jq -r ".upload_url"`
|
||||||
|
echo "::set-output name=UPLOAD_URL::$UPLOAD_URL"
|
||||||
|
|
||||||
- name: Publish Debian Package
|
- name: Publish Debian Package
|
||||||
if: github.event_name == 'release'
|
|
||||||
uses: actions/upload-release-asset@v1.0.2
|
uses: actions/upload-release-asset@v1.0.2
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ github.event.release.upload_url }}
|
upload_url: ${{ steps.release_id.outputs.UPLOAD_URL }}
|
||||||
asset_path: extras/packaging/linux/artifacts/dist/httpie-${{ github.event.release.tag_name }}.deb
|
asset_path: extras/packaging/linux/artifacts/dist/httpie_${{ github.event.inputs.tag_name }}_amd64.deb
|
||||||
asset_name: httpie-${{ github.event.release.tag_name }}.deb
|
asset_name: httpie-${{ github.event.inputs.tag_name }}.deb
|
||||||
asset_content_type: binary/octet-stream
|
asset_content_type: binary/octet-stream
|
||||||
|
|
||||||
- name: Publish Single Executable
|
- name: Publish Single Executable
|
||||||
if: github.event_name == 'release'
|
|
||||||
uses: actions/upload-release-asset@v1.0.2
|
uses: actions/upload-release-asset@v1.0.2
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
upload_url: ${{ github.event.release.upload_url }}
|
upload_url: ${{ steps.release_id.outputs.UPLOAD_URL }}
|
||||||
asset_path: extras/packaging/linux/artifacts/dist/http
|
asset_path: extras/packaging/linux/artifacts/dist/http
|
||||||
asset_name: http
|
asset_name: http
|
||||||
asset_content_type: binary/octet-stream
|
asset_content_type: binary/octet-stream
|
||||||
|
7
.github/workflows/release-pypi.yml
vendored
7
.github/workflows/release-pypi.yml
vendored
@ -17,15 +17,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
ref: ${{ github.event.inputs.branch }}
|
ref: ${{ github.event.inputs.branch }}
|
||||||
|
|
||||||
- uses: actions/setup-python@v3
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: 3.9
|
python-version: 3.9
|
||||||
|
|
||||||
- name: Install pypa/build
|
|
||||||
run: python -m pip install build
|
|
||||||
|
|
||||||
- name: Build a binary wheel and a source tarball
|
- name: Build a binary wheel and a source tarball
|
||||||
run: python -m build --sdist --wheel --outdir dist/
|
run: make install && make build
|
||||||
|
|
||||||
- name: Release on PyPI
|
- name: Release on PyPI
|
||||||
uses: pypa/gh-action-pypi-publish@master
|
uses: pypa/gh-action-pypi-publish@master
|
||||||
|
3
.github/workflows/release-snap.yml
vendored
3
.github/workflows/release-snap.yml
vendored
@ -34,7 +34,8 @@ jobs:
|
|||||||
id: build
|
id: build
|
||||||
|
|
||||||
- uses: snapcore/action-publish@v1
|
- uses: snapcore/action-publish@v1
|
||||||
|
env:
|
||||||
|
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }}
|
||||||
with:
|
with:
|
||||||
store_login: ${{ secrets.SNAP_STORE_LOGIN }}
|
|
||||||
snap: ${{ steps.build.outputs.snap }}
|
snap: ${{ steps.build.outputs.snap }}
|
||||||
release: ${{ matrix.level }}
|
release: ${{ matrix.level }}
|
||||||
|
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v5
|
- uses: actions/stale@v8
|
||||||
with:
|
with:
|
||||||
close-pr-message: 'Thanks for the pull request, but since it was stale for more than a 30 days we are closing it. If you want to work back on it, feel free to re-open it or create a new one.'
|
close-pr-message: 'Thanks for the pull request, but since it was stale for more than a 30 days we are closing it. If you want to work back on it, feel free to re-open it or create a new one.'
|
||||||
stale-pr-label: 'stale'
|
stale-pr-label: 'stale'
|
||||||
|
14
.github/workflows/tests.yml
vendored
14
.github/workflows/tests.yml
vendored
@ -24,13 +24,19 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
os: [ubuntu-latest, macos-13, windows-latest]
|
||||||
python-version: [3.7, 3.8, 3.9, "3.10"]
|
python-version:
|
||||||
|
- '3.12'
|
||||||
|
- '3.11'
|
||||||
|
- '3.10'
|
||||||
|
- '3.9'
|
||||||
|
- '3.8'
|
||||||
|
- '3.7'
|
||||||
pyopenssl: [0, 1]
|
pyopenssl: [0, 1]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-python@v3
|
- uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
- name: Windows setup
|
- name: Windows setup
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
## Patches, features, ideas
|
## Patches, features, ideas
|
||||||
|
|
||||||
[Complete list of contributors on GitHub](https://github.com/httpie/httpie/graphs/contributors)
|
[Complete list of contributors on GitHub](https://github.com/httpie/cli/graphs/contributors)
|
||||||
|
|
||||||
- [Cláudia T. Delgado](https://github.com/claudiatd)
|
- [Cláudia T. Delgado](https://github.com/claudiatd)
|
||||||
- [Hank Gay](https://github.com/gthank)
|
- [Hank Gay](https://github.com/gthank)
|
||||||
|
251
CHANGELOG.md
251
CHANGELOG.md
@ -3,133 +3,156 @@
|
|||||||
This document records all notable changes to [HTTPie](https://httpie.io).
|
This document records all notable changes to [HTTPie](https://httpie.io).
|
||||||
This project adheres to [Semantic Versioning](https://semver.org/).
|
This project adheres to [Semantic Versioning](https://semver.org/).
|
||||||
|
|
||||||
## [3.1.1.dev0](https://github.com/httpie/httpie/compare/3.1.0...HEAD) (Unreleased)
|
## [3.2.4](https://github.com/httpie/cli/compare/3.2.3...3.2.4) (2024-11-01)
|
||||||
|
|
||||||
- Added support for session persistence of repeated headers with the same name. ([#1335](https://github.com/httpie/httpie/pull/1335))
|
- Fix default certs loading and unpin `requests`. ([#1596](https://github.com/httpie/cli/issues/1596))
|
||||||
- Changed `httpie plugins` to the new `httpie cli` namespace as `httpie cli plugins` (`httpie plugins` continues to work as a hidden alias). ([#1320](https://github.com/httpie/httpie/issues/1320))
|
|
||||||
- Fixed redundant creation of `Content-Length` header on `OPTIONS` requests. ([#1310](https://github.com/httpie/httpie/issues/1310))
|
|
||||||
- Fixed blocking of warning thread on some use cases. ([#1349](https://github.com/httpie/httpie/issues/1349))
|
|
||||||
- Added support for sending `Secure` cookies to the `localhost` (and `.local` suffixed domains). ([#1308](https://github.com/httpie/httpie/issues/1308))
|
|
||||||
|
|
||||||
|
## [3.2.3](https://github.com/httpie/cli/compare/3.2.2...3.2.3) (2024-07-10)
|
||||||
|
|
||||||
## [3.1.0](https://github.com/httpie/httpie/compare/3.0.2...3.1.0) (2022-03-08)
|
- Fix SSL connections by pinning the `requests` version to `2.31.0`. (#1583, #1581)
|
||||||
|
- Make it possible to [unset](https://httpie.io/docs/cli/default-request-headers) the `User-Agent` and `Accept-Encoding` request headers. ([#1502](https://github.com/httpie/cli/issues/1502))
|
||||||
|
|
||||||
- **SECURITY** Fixed the [vulnerability](https://github.com/httpie/httpie/security/advisories/GHSA-9w4w-cpc8-h2fq) that caused exposure of cookies on redirects to third party hosts. ([#1312](https://github.com/httpie/httpie/pull/1312))
|
## [3.2.2](https://github.com/httpie/cli/compare/3.2.1...3.2.2) (2023-05-19)
|
||||||
- Fixed escaping of integer indexes with multiple backslashes in the nested JSON builder. ([#1285](https://github.com/httpie/httpie/issues/1285))
|
|
||||||
- Fixed displaying of status code without a status message on non-`auto` themes. ([#1300](https://github.com/httpie/httpie/issues/1300))
|
|
||||||
- Fixed redundant issuance of stdin detection warnings on some rare cases due to underlying implementation. ([#1303](https://github.com/httpie/httpie/pull/1303))
|
|
||||||
- Fixed double `--quiet` so that it will now suppress all python level warnings. ([#1271](https://github.com/httpie/httpie/issues/1271))
|
|
||||||
- Added support for specifying certificate private key passphrases through `--cert-key-pass` and prompts. ([#946](https://github.com/httpie/httpie/issues/946))
|
|
||||||
- Added `httpie cli export-args` command for exposing the parser specification for the `http`/`https` commands. ([#1293](https://github.com/httpie/httpie/pull/1293))
|
|
||||||
- Improved regulation of top-level arrays. ([#1292](https://github.com/httpie/httpie/commit/225dccb2186f14f871695b6c4e0bfbcdb2e3aa28))
|
|
||||||
- Improved UI layout for standalone invocations. ([#1296](https://github.com/httpie/httpie/pull/1296))
|
|
||||||
|
|
||||||
## [3.0.2](https://github.com/httpie/httpie/compare/3.0.1...3.0.2) (2022-01-24)
|
- Fixed compatibility with urllib3 2.0.0. ([#1499](https://github.com/httpie/cli/issues/1499))
|
||||||
|
|
||||||
|
## [3.2.1](https://github.com/httpie/cli/compare/3.1.0...3.2.1) (2022-05-06)
|
||||||
|
|
||||||
|
- Improved support for determining auto-streaming when the `Content-Type` header includes encoding information. ([#1383](https://github.com/httpie/cli/pull/1383))
|
||||||
|
- Fixed the display of the crash happening in the secondary process for update checks. ([#1388](https://github.com/httpie/cli/issues/1388))
|
||||||
|
|
||||||
|
## [3.2.0](https://github.com/httpie/cli/compare/3.1.0...3.2.0) (2022-05-05)
|
||||||
|
|
||||||
|
- Added a warning for notifying the user about the new updates. ([#1336](https://github.com/httpie/cli/pull/1336))
|
||||||
|
- Added support for single binary executables. ([#1330](https://github.com/httpie/cli/pull/1330))
|
||||||
|
- Added support for man pages (and auto generation of them from the parser declaration). ([#1317](https://github.com/httpie/cli/pull/1317))
|
||||||
|
- Added `http --manual` for man pages & regular manual with pager. ([#1343](https://github.com/httpie/cli/pull/1343))
|
||||||
|
- Added support for session persistence of repeated headers with the same name. ([#1335](https://github.com/httpie/cli/pull/1335))
|
||||||
|
- Added support for sending `Secure` cookies to the `localhost` (and `.local` suffixed domains). ([#1308](https://github.com/httpie/cli/issues/1308))
|
||||||
|
- Improved UI for the progress bars. ([#1324](https://github.com/httpie/cli/pull/1324))
|
||||||
|
- Fixed redundant creation of `Content-Length` header on `OPTIONS` requests. ([#1310](https://github.com/httpie/cli/issues/1310))
|
||||||
|
- Fixed blocking of warning thread on some use cases. ([#1349](https://github.com/httpie/cli/issues/1349))
|
||||||
|
- Changed `httpie plugins` to the new `httpie cli` namespace as `httpie cli plugins` (`httpie plugins` continues to work as a hidden alias). ([#1320](https://github.com/httpie/cli/issues/1320))
|
||||||
|
- Soft deprecated the `--history-print`. ([#1380](https://github.com/httpie/cli/pull/1380))
|
||||||
|
|
||||||
|
## [3.1.0](https://github.com/httpie/cli/compare/3.0.2...3.1.0) (2022-03-08)
|
||||||
|
|
||||||
|
- **SECURITY** Fixed the [vulnerability](https://github.com/httpie/cli/security/advisories/GHSA-9w4w-cpc8-h2fq) that caused exposure of cookies on redirects to third party hosts. ([#1312](https://github.com/httpie/cli/pull/1312))
|
||||||
|
- Fixed escaping of integer indexes with multiple backslashes in the nested JSON builder. ([#1285](https://github.com/httpie/cli/issues/1285))
|
||||||
|
- Fixed displaying of status code without a status message on non-`auto` themes. ([#1300](https://github.com/httpie/cli/issues/1300))
|
||||||
|
- Fixed redundant issuance of stdin detection warnings on some rare cases due to underlying implementation. ([#1303](https://github.com/httpie/cli/pull/1303))
|
||||||
|
- Fixed double `--quiet` so that it will now suppress all python level warnings. ([#1271](https://github.com/httpie/cli/issues/1271))
|
||||||
|
- Added support for specifying certificate private key passphrases through `--cert-key-pass` and prompts. ([#946](https://github.com/httpie/cli/issues/946))
|
||||||
|
- Added `httpie cli export-args` command for exposing the parser specification for the `http`/`https` commands. ([#1293](https://github.com/httpie/cli/pull/1293))
|
||||||
|
- Improved regulation of top-level arrays. ([#1292](https://github.com/httpie/cli/commit/225dccb2186f14f871695b6c4e0bfbcdb2e3aa28))
|
||||||
|
- Improved UI layout for standalone invocations. ([#1296](https://github.com/httpie/cli/pull/1296))
|
||||||
|
|
||||||
|
## [3.0.2](https://github.com/httpie/cli/compare/3.0.1...3.0.2) (2022-01-24)
|
||||||
|
|
||||||
[What’s new in HTTPie for Terminal 3.0 →](https://httpie.io/blog/httpie-3.0.0)
|
[What’s new in HTTPie for Terminal 3.0 →](https://httpie.io/blog/httpie-3.0.0)
|
||||||
|
|
||||||
- Fixed usage of `httpie` when there is a presence of a config with `default_options`. ([#1280](https://github.com/httpie/httpie/pull/1280))
|
- Fixed usage of `httpie` when there is a presence of a config with `default_options`. ([#1280](https://github.com/httpie/cli/pull/1280))
|
||||||
|
|
||||||
## [3.0.1](https://github.com/httpie/httpie/compare/3.0.0...3.0.1) (2022-01-23)
|
## [3.0.1](https://github.com/httpie/cli/compare/3.0.0...3.0.1) (2022-01-23)
|
||||||
|
|
||||||
[What’s new in HTTPie for Terminal 3.0 →](https://httpie.io/blog/httpie-3.0.0)
|
[What’s new in HTTPie for Terminal 3.0 →](https://httpie.io/blog/httpie-3.0.0)
|
||||||
|
|
||||||
- Changed the value shown as time elapsed from time-to-read-headers to total exchange time. ([#1277](https://github.com/httpie/httpie/issues/1277))
|
- Changed the value shown as time elapsed from time-to-read-headers to total exchange time. ([#1277](https://github.com/httpie/cli/issues/1277))
|
||||||
|
|
||||||
## [3.0.0](https://github.com/httpie/httpie/compare/2.6.0...3.0.0) (2022-01-21)
|
## [3.0.0](https://github.com/httpie/cli/compare/2.6.0...3.0.0) (2022-01-21)
|
||||||
|
|
||||||
[What’s new in HTTPie for Terminal 3.0 →](https://httpie.io/blog/httpie-3.0.0)
|
[What’s new in HTTPie for Terminal 3.0 →](https://httpie.io/blog/httpie-3.0.0)
|
||||||
|
|
||||||
- Dropped support for Python 3.6. ([#1177](https://github.com/httpie/httpie/issues/1177))
|
- Dropped support for Python 3.6. ([#1177](https://github.com/httpie/cli/issues/1177))
|
||||||
- Improved startup time by 40%. ([#1211](https://github.com/httpie/httpie/pull/1211))
|
- Improved startup time by 40%. ([#1211](https://github.com/httpie/cli/pull/1211))
|
||||||
- Added support for nested JSON syntax. ([#1169](https://github.com/httpie/httpie/issues/1169))
|
- Added support for nested JSON syntax. ([#1169](https://github.com/httpie/cli/issues/1169))
|
||||||
- Added `httpie plugins` interface for plugin management. ([#566](https://github.com/httpie/httpie/issues/566))
|
- Added `httpie plugins` interface for plugin management. ([#566](https://github.com/httpie/cli/issues/566))
|
||||||
- Added support for Bearer authentication via `--auth-type=bearer` ([#1215](https://github.com/httpie/httpie/issues/1215)).
|
- Added support for Bearer authentication via `--auth-type=bearer` ([#1215](https://github.com/httpie/cli/issues/1215)).
|
||||||
- Added support for quick conversions of pasted URLs into HTTPie calls by adding a space after the protocol name (`$ https ://pie.dev` → `https://pie.dev`). ([#1195](https://github.com/httpie/httpie/issues/1195))
|
- Added support for quick conversions of pasted URLs into HTTPie calls by adding a space after the protocol name (`$ https ://pie.dev` → `https://pie.dev`). ([#1195](https://github.com/httpie/cli/issues/1195))
|
||||||
- Added support for _sending_ multiple HTTP header lines with the same name. ([#130](https://github.com/httpie/httpie/issues/130))
|
- Added support for _sending_ multiple HTTP header lines with the same name. ([#130](https://github.com/httpie/cli/issues/130))
|
||||||
- Added support for _receiving_ multiple HTTP headers lines with the same name. ([#1207](https://github.com/httpie/httpie/issues/1207))
|
- Added support for _receiving_ multiple HTTP headers lines with the same name. ([#1207](https://github.com/httpie/cli/issues/1207))
|
||||||
- Added support for basic JSON types on `--form`/`--multipart` when using JSON only operators (`:=`/`:=@`). ([#1212](https://github.com/httpie/httpie/issues/1212))
|
- Added support for basic JSON types on `--form`/`--multipart` when using JSON only operators (`:=`/`:=@`). ([#1212](https://github.com/httpie/cli/issues/1212))
|
||||||
- Added support for automatically enabling `--stream` when `Content-Type` is `text/event-stream`. ([#376](https://github.com/httpie/httpie/issues/376))
|
- Added support for automatically enabling `--stream` when `Content-Type` is `text/event-stream`. ([#376](https://github.com/httpie/cli/issues/376))
|
||||||
- Added support for displaying the total elapsed time through `--meta`/`-vv` or `--print=m`. ([#243](https://github.com/httpie/httpie/issues/243))
|
- Added support for displaying the total elapsed time through `--meta`/`-vv` or `--print=m`. ([#243](https://github.com/httpie/cli/issues/243))
|
||||||
- Added new `pie-dark`/`pie-light` (and `pie`) styles that match with [HTTPie for Web and Desktop](https://httpie.io/product). ([#1237](https://github.com/httpie/httpie/issues/1237))
|
- Added new `pie-dark`/`pie-light` (and `pie`) styles that match with [HTTPie for Web and Desktop](https://httpie.io/product). ([#1237](https://github.com/httpie/cli/issues/1237))
|
||||||
- Added support for better error handling on DNS failures. ([#1248](https://github.com/httpie/httpie/issues/1248))
|
- Added support for better error handling on DNS failures. ([#1248](https://github.com/httpie/cli/issues/1248))
|
||||||
- Added support for storing prompted passwords in the local sessions. ([#1098](https://github.com/httpie/httpie/issues/1098))
|
- Added support for storing prompted passwords in the local sessions. ([#1098](https://github.com/httpie/cli/issues/1098))
|
||||||
- Added warnings about the `--ignore-stdin`, when there is no incoming data from stdin. ([#1255](https://github.com/httpie/httpie/issues/1255))
|
- Added warnings about the `--ignore-stdin`, when there is no incoming data from stdin. ([#1255](https://github.com/httpie/cli/issues/1255))
|
||||||
- Fixed crashing due to broken plugins. ([#1204](https://github.com/httpie/httpie/issues/1204))
|
- Fixed crashing due to broken plugins. ([#1204](https://github.com/httpie/cli/issues/1204))
|
||||||
- Fixed auto addition of XML declaration to every formatted XML response. ([#1156](https://github.com/httpie/httpie/issues/1156))
|
- Fixed auto addition of XML declaration to every formatted XML response. ([#1156](https://github.com/httpie/cli/issues/1156))
|
||||||
- Fixed highlighting when `Content-Type` specifies `charset`. ([#1242](https://github.com/httpie/httpie/issues/1242))
|
- Fixed highlighting when `Content-Type` specifies `charset`. ([#1242](https://github.com/httpie/cli/issues/1242))
|
||||||
- Fixed an unexpected crash when `--raw` is used with `--chunked`. ([#1253](https://github.com/httpie/httpie/issues/1253))
|
- Fixed an unexpected crash when `--raw` is used with `--chunked`. ([#1253](https://github.com/httpie/cli/issues/1253))
|
||||||
- Changed the default Windows theme from `fruity` to `auto`. ([#1266](https://github.com/httpie/httpie/issues/1266))
|
- Changed the default Windows theme from `fruity` to `auto`. ([#1266](https://github.com/httpie/cli/issues/1266))
|
||||||
|
|
||||||
## [2.6.0](https://github.com/httpie/httpie/compare/2.5.0...2.6.0) (2021-10-14)
|
## [2.6.0](https://github.com/httpie/cli/compare/2.5.0...2.6.0) (2021-10-14)
|
||||||
|
|
||||||
[What’s new in HTTPie for Terminal 2.6.0 →](https://httpie.io/blog/httpie-2.6.0)
|
[What’s new in HTTPie for Terminal 2.6.0 →](https://httpie.io/blog/httpie-2.6.0)
|
||||||
|
|
||||||
- Added support for formatting & coloring of JSON bodies preceded by non-JSON data (e.g., an XXSI prefix). ([#1130](https://github.com/httpie/httpie/issues/1130))
|
- Added support for formatting & coloring of JSON bodies preceded by non-JSON data (e.g., an XXSI prefix). ([#1130](https://github.com/httpie/cli/issues/1130))
|
||||||
- Added charset auto-detection when `Content-Type` doesn’t include it. ([#1110](https://github.com/httpie/httpie/issues/1110), [#1168](https://github.com/httpie/httpie/issues/1168))
|
- Added charset auto-detection when `Content-Type` doesn’t include it. ([#1110](https://github.com/httpie/cli/issues/1110), [#1168](https://github.com/httpie/cli/issues/1168))
|
||||||
- Added `--response-charset` to allow overriding the response encoding for terminal display purposes. ([#1168](https://github.com/httpie/httpie/issues/1168))
|
- Added `--response-charset` to allow overriding the response encoding for terminal display purposes. ([#1168](https://github.com/httpie/cli/issues/1168))
|
||||||
- Added `--response-mime` to allow overriding the response mime type for coloring and formatting for the terminal. ([#1168](https://github.com/httpie/httpie/issues/1168))
|
- Added `--response-mime` to allow overriding the response mime type for coloring and formatting for the terminal. ([#1168](https://github.com/httpie/cli/issues/1168))
|
||||||
- Added the ability to silence warnings through using `-q` or `--quiet` twice (e.g. `-qq`) ([#1175](https://github.com/httpie/httpie/issues/1175))
|
- Added the ability to silence warnings through using `-q` or `--quiet` twice (e.g. `-qq`) ([#1175](https://github.com/httpie/cli/issues/1175))
|
||||||
- Added installed plugin list to `--debug` output. ([#1165](https://github.com/httpie/httpie/issues/1165))
|
- Added installed plugin list to `--debug` output. ([#1165](https://github.com/httpie/cli/issues/1165))
|
||||||
- Fixed duplicate keys preservation in JSON data. ([#1163](https://github.com/httpie/httpie/issues/1163))
|
- Fixed duplicate keys preservation in JSON data. ([#1163](https://github.com/httpie/cli/issues/1163))
|
||||||
|
|
||||||
## [2.5.0](https://github.com/httpie/httpie/compare/2.4.0...2.5.0) (2021-09-06)
|
## [2.5.0](https://github.com/httpie/cli/compare/2.4.0...2.5.0) (2021-09-06)
|
||||||
|
|
||||||
[What’s new in HTTPie for Terminal 2.5.0 →](https://httpie.io/blog/httpie-2.5.0)
|
[What’s new in HTTPie for Terminal 2.5.0 →](https://httpie.io/blog/httpie-2.5.0)
|
||||||
|
|
||||||
- Added `--raw` to allow specifying the raw request body without extra processing as
|
- Added `--raw` to allow specifying the raw request body without extra processing as
|
||||||
an alternative to `stdin`. ([#534](https://github.com/httpie/httpie/issues/534))
|
an alternative to `stdin`. ([#534](https://github.com/httpie/cli/issues/534))
|
||||||
- Added support for XML formatting. ([#1129](https://github.com/httpie/httpie/issues/1129))
|
- Added support for XML formatting. ([#1129](https://github.com/httpie/cli/issues/1129))
|
||||||
- Added internal support for file-like object responses to improve adapter plugin support. ([#1094](https://github.com/httpie/httpie/issues/1094))
|
- Added internal support for file-like object responses to improve adapter plugin support. ([#1094](https://github.com/httpie/cli/issues/1094))
|
||||||
- Fixed `--continue --download` with a single byte to be downloaded left. ([#1032](https://github.com/httpie/httpie/issues/1032))
|
- Fixed `--continue --download` with a single byte to be downloaded left. ([#1032](https://github.com/httpie/cli/issues/1032))
|
||||||
- Fixed `--verbose` HTTP 307 redirects with streamed request body. ([#1088](https://github.com/httpie/httpie/issues/1088))
|
- Fixed `--verbose` HTTP 307 redirects with streamed request body. ([#1088](https://github.com/httpie/cli/issues/1088))
|
||||||
- Fixed handling of session files with `Cookie:` followed by other headers. ([#1126](https://github.com/httpie/httpie/issues/1126))
|
- Fixed handling of session files with `Cookie:` followed by other headers. ([#1126](https://github.com/httpie/cli/issues/1126))
|
||||||
|
|
||||||
## [2.4.0](https://github.com/httpie/httpie/compare/2.3.0...2.4.0) (2021-02-06)
|
## [2.4.0](https://github.com/httpie/cli/compare/2.3.0...2.4.0) (2021-02-06)
|
||||||
|
|
||||||
- Added support for `--session` cookie expiration based on `Set-Cookie: max-age=<n>`. ([#1029](https://github.com/httpie/httpie/issues/1029))
|
- Added support for `--session` cookie expiration based on `Set-Cookie: max-age=<n>`. ([#1029](https://github.com/httpie/cli/issues/1029))
|
||||||
- Show a `--check-status` warning with `--quiet` as well, not only when the output is redirected. ([#1026](https://github.com/httpie/httpie/issues/1026))
|
- Show a `--check-status` warning with `--quiet` as well, not only when the output is redirected. ([#1026](https://github.com/httpie/cli/issues/1026))
|
||||||
- Fixed upload with `--session` ([#1020](https://github.com/httpie/httpie/issues/1020)).
|
- Fixed upload with `--session` ([#1020](https://github.com/httpie/cli/issues/1020)).
|
||||||
- Fixed a missing blank line between request and response ([#1006](https://github.com/httpie/httpie/issues/1006)).
|
- Fixed a missing blank line between request and response ([#1006](https://github.com/httpie/cli/issues/1006)).
|
||||||
|
|
||||||
## [2.3.0](https://github.com/httpie/httpie/compare/2.2.0...2.3.0) (2020-10-25)
|
## [2.3.0](https://github.com/httpie/cli/compare/2.2.0...2.3.0) (2020-10-25)
|
||||||
|
|
||||||
- Added support for streamed uploads ([#201](https://github.com/httpie/httpie/issues/201)).
|
- Added support for streamed uploads ([#201](https://github.com/httpie/cli/issues/201)).
|
||||||
- Added support for multipart upload streaming ([#684](https://github.com/httpie/httpie/issues/684)).
|
- Added support for multipart upload streaming ([#684](https://github.com/httpie/cli/issues/684)).
|
||||||
- Added support for body-from-file upload streaming (`http pie.dev/post @file`).
|
- Added support for body-from-file upload streaming (`http pie.dev/post @file`).
|
||||||
- Added `--chunked` to enable chunked transfer encoding ([#753](https://github.com/httpie/httpie/issues/753)).
|
- Added `--chunked` to enable chunked transfer encoding ([#753](https://github.com/httpie/cli/issues/753)).
|
||||||
- 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](https://github.com/httpie/httpie/issues/903)).
|
- Added support for preserving field order in multipart requests ([#903](https://github.com/httpie/cli/issues/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](https://github.com/httpie/httpie/issues/932)).
|
- Added support for combining cookies specified on the CLI and in a session file ([#932](https://github.com/httpie/cli/issues/932)).
|
||||||
- Added out of the box SOCKS support with no extra installation ([#904](https://github.com/httpie/httpie/issues/904)).
|
- Added out of the box SOCKS support with no extra installation ([#904](https://github.com/httpie/cli/issues/904)).
|
||||||
- Added `--quiet, -q` flag to enforce silent behaviour.
|
- Added `--quiet, -q` flag to enforce silent behaviour.
|
||||||
- Fixed the handling of invalid `expires` dates in `Set-Cookie` headers ([#963](https://github.com/httpie/httpie/issues/963)).
|
- Fixed the handling of invalid `expires` dates in `Set-Cookie` headers ([#963](https://github.com/httpie/cli/issues/963)).
|
||||||
- Removed Tox testing entirely ([#943](https://github.com/httpie/httpie/issues/943)).
|
- Removed Tox testing entirely ([#943](https://github.com/httpie/cli/issues/943)).
|
||||||
|
|
||||||
## [2.2.0](https://github.com/httpie/httpie/compare/2.1.0...2.2.0) (2020-06-18)
|
## [2.2.0](https://github.com/httpie/cli/compare/2.1.0...2.2.0) (2020-06-18)
|
||||||
|
|
||||||
- Added support for custom content types for uploaded files ([#668](https://github.com/httpie/httpie/issues/668)).
|
- Added support for custom content types for uploaded files ([#668](https://github.com/httpie/cli/issues/668)).
|
||||||
- Added support for `$XDG_CONFIG_HOME` ([#920](https://github.com/httpie/httpie/issues/920)).
|
- Added support for `$XDG_CONFIG_HOME` ([#920](https://github.com/httpie/cli/issues/920)).
|
||||||
- Added support for `Set-Cookie`-triggered cookie expiration ([#853](https://github.com/httpie/httpie/issues/853)).
|
- Added support for `Set-Cookie`-triggered cookie expiration ([#853](https://github.com/httpie/cli/issues/853)).
|
||||||
- Added `--format-options` to allow disabling sorting, etc. ([#128](https://github.com/httpie/httpie/issues/128))
|
- Added `--format-options` to allow disabling sorting, etc. ([#128](https://github.com/httpie/cli/issues/128))
|
||||||
- Added `--sorted` and `--unsorted` shortcuts for (un)setting all sorting-related `--format-options`. ([#128](https://github.com/httpie/httpie/issues/128))
|
- Added `--sorted` and `--unsorted` shortcuts for (un)setting all sorting-related `--format-options`. ([#128](https://github.com/httpie/cli/issues/128))
|
||||||
- Added `--ciphers` to allow configuring OpenSSL ciphers ([#870](https://github.com/httpie/httpie/issues/870)).
|
- Added `--ciphers` to allow configuring OpenSSL ciphers ([#870](https://github.com/httpie/cli/issues/870)).
|
||||||
- Added `netrc` support for auth plugins. Enabled for `--auth-type=basic`
|
- Added `netrc` support for auth plugins. Enabled for `--auth-type=basic`
|
||||||
and `digest`, 3rd parties may opt in ([#718](https://github.com/httpie/httpie/issues/718), [#719](https://github.com/httpie/httpie/issues/719), [#852](https://github.com/httpie/httpie/issues/852), [#934](https://github.com/httpie/httpie/issues/934)).
|
and `digest`, 3rd parties may opt in ([#718](https://github.com/httpie/cli/issues/718), [#719](https://github.com/httpie/cli/issues/719), [#852](https://github.com/httpie/cli/issues/852), [#934](https://github.com/httpie/cli/issues/934)).
|
||||||
- Fixed built-in plugins-related circular imports ([#925](https://github.com/httpie/httpie/issues/925)).
|
- Fixed built-in plugins-related circular imports ([#925](https://github.com/httpie/cli/issues/925)).
|
||||||
|
|
||||||
## [2.1.0](https://github.com/httpie/httpie/compare/2.0.0...2.1.0) (2020-04-18)
|
## [2.1.0](https://github.com/httpie/cli/compare/2.0.0...2.1.0) (2020-04-18)
|
||||||
|
|
||||||
- Added `--path-as-is` to bypass dot segment (`/../` or `/./`)
|
- Added `--path-as-is` to bypass dot segment (`/../` or `/./`)
|
||||||
URL squashing ([#895](https://github.com/httpie/httpie/issues/895)).
|
URL squashing ([#895](https://github.com/httpie/cli/issues/895)).
|
||||||
- Changed the default `Accept` header value for JSON requests from
|
- Changed the default `Accept` header value for JSON requests from
|
||||||
`application/json, */*` to `application/json, */*;q=0.5`
|
`application/json, */*` to `application/json, */*;q=0.5`
|
||||||
to clearly indicate preference ([#488](https://github.com/httpie/httpie/issues/488)).
|
to clearly indicate preference ([#488](https://github.com/httpie/cli/issues/488)).
|
||||||
- Fixed `--form` file upload mixed with redirected `stdin` error handling
|
- Fixed `--form` file upload mixed with redirected `stdin` error handling
|
||||||
([#840](https://github.com/httpie/httpie/issues/840)).
|
([#840](https://github.com/httpie/cli/issues/840)).
|
||||||
|
|
||||||
## [2.0.0](https://github.com/httpie/httpie/compare/1.0.3...2.0.0) (2020-01-12)
|
## [2.0.0](https://github.com/httpie/cli/compare/1.0.3...2.0.0) (2020-01-12)
|
||||||
|
|
||||||
- Removed Python 2.7 support ([EOL Jan 2020](https://www.python.org/doc/sunset-python-2/).
|
- Removed Python 2.7 support ([EOL Jan 2020](https://www.python.org/doc/sunset-python-2/).
|
||||||
- Added `--offline` to allow building an HTTP request and printing it but not
|
- Added `--offline` to allow building an HTTP request and printing it but not
|
||||||
@ -152,7 +175,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Fixed an error when `stdin` was a closed fd.
|
- Fixed an error when `stdin` was a closed fd.
|
||||||
- Improved `--debug` output formatting.
|
- Improved `--debug` output formatting.
|
||||||
|
|
||||||
## [1.0.3](https://github.com/httpie/httpie/compare/1.0.2...1.0.3) (2019-08-26)
|
## [1.0.3](https://github.com/httpie/cli/compare/1.0.2...1.0.3) (2019-08-26)
|
||||||
|
|
||||||
- Fixed CVE-2019-10751 — the way the output filename is generated for
|
- Fixed CVE-2019-10751 — the way the output filename is generated for
|
||||||
`--download` requests without `--output` resulting in a redirect has
|
`--download` requests without `--output` resulting in a redirect has
|
||||||
@ -178,15 +201,15 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
|
|
||||||
Reported by Raul Onitza and Giulio Comi.
|
Reported by Raul Onitza and Giulio Comi.
|
||||||
|
|
||||||
## [1.0.2](https://github.com/httpie/httpie/compare/1.0.1...1.0.2) (2018-11-14)
|
## [1.0.2](https://github.com/httpie/cli/compare/1.0.1...1.0.2) (2018-11-14)
|
||||||
|
|
||||||
- Fixed tests for installation with pyOpenSSL.
|
- Fixed tests for installation with pyOpenSSL.
|
||||||
|
|
||||||
## [1.0.1](https://github.com/httpie/httpie/compare/1.0.0...1.0.1) (2018-11-14)
|
## [1.0.1](https://github.com/httpie/cli/compare/1.0.0...1.0.1) (2018-11-14)
|
||||||
|
|
||||||
- Removed external URL calls from tests.
|
- Removed external URL calls from tests.
|
||||||
|
|
||||||
## [1.0.0](https://github.com/httpie/httpie/compare/0.9.9...1.0.0) (2018-11-02)
|
## [1.0.0](https://github.com/httpie/cli/compare/0.9.9...1.0.0) (2018-11-02)
|
||||||
|
|
||||||
- Added `--style=auto` which follows the terminal ANSI color styles.
|
- Added `--style=auto` which follows the terminal ANSI color styles.
|
||||||
- Added support for selecting TLS 1.3 via `--ssl=tls1.3`
|
- Added support for selecting TLS 1.3 via `--ssl=tls1.3`
|
||||||
@ -197,11 +220,11 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Fixed default headers being incorrectly case-sensitive.
|
- Fixed default headers being incorrectly case-sensitive.
|
||||||
- Removed Python 2.6 support.
|
- Removed Python 2.6 support.
|
||||||
|
|
||||||
## [0.9.9](https://github.com/httpie/httpie/compare/0.9.8...0.9.9) (2016-12-08)
|
## [0.9.9](https://github.com/httpie/cli/compare/0.9.8...0.9.9) (2016-12-08)
|
||||||
|
|
||||||
- Fixed README.
|
- Fixed README.
|
||||||
|
|
||||||
## [0.9.8](https://github.com/httpie/httpie/compare/0.9.6...0.9.8) (2016-12-08)
|
## [0.9.8](https://github.com/httpie/cli/compare/0.9.6...0.9.8) (2016-12-08)
|
||||||
|
|
||||||
- Extended auth plugin API.
|
- Extended auth plugin API.
|
||||||
- Added exit status code `7` for plugin errors.
|
- Added exit status code `7` for plugin errors.
|
||||||
@ -210,7 +233,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Improved `CTRL-C` interrupt handling.
|
- Improved `CTRL-C` interrupt handling.
|
||||||
- Added the standard exit status code `130` for keyboard interrupts.
|
- Added the standard exit status code `130` for keyboard interrupts.
|
||||||
|
|
||||||
## [0.9.6](https://github.com/httpie/httpie/compare/0.9.4...0.9.6) (2016-08-13)
|
## [0.9.6](https://github.com/httpie/cli/compare/0.9.4...0.9.6) (2016-08-13)
|
||||||
|
|
||||||
- Added Python 3 as a dependency for Homebrew installations
|
- Added Python 3 as a dependency for Homebrew installations
|
||||||
to ensure some of the newer HTTP features work out of the box
|
to ensure some of the newer HTTP features work out of the box
|
||||||
@ -229,7 +252,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Changed the pre-processing of request HTTP headers so that any leading
|
- Changed the pre-processing of request HTTP headers so that any leading
|
||||||
and trailing whitespace is removed.
|
and trailing whitespace is removed.
|
||||||
|
|
||||||
## [0.9.4](https://github.com/httpie/httpie/compare/0.9.3...0.9.4) (2016-07-01)
|
## [0.9.4](https://github.com/httpie/cli/compare/0.9.3...0.9.4) (2016-07-01)
|
||||||
|
|
||||||
- Added `Content-Type` of files uploaded in `multipart/form-data` requests
|
- Added `Content-Type` of files uploaded in `multipart/form-data` requests
|
||||||
- Added `--ssl=<PROTOCOL>` to specify the desired SSL/TLS protocol version
|
- Added `--ssl=<PROTOCOL>` to specify the desired SSL/TLS protocol version
|
||||||
@ -253,7 +276,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Fixed the handling of `Content-Type` with multiple `+subtype` parts
|
- Fixed the handling of `Content-Type` with multiple `+subtype` parts
|
||||||
- Removed the XML formatter as the implementation suffered from multiple issues
|
- Removed the XML formatter as the implementation suffered from multiple issues
|
||||||
|
|
||||||
## [0.9.3](https://github.com/httpie/httpie/compare/0.9.2...0.9.3) (2016-01-01)
|
## [0.9.3](https://github.com/httpie/cli/compare/0.9.2...0.9.3) (2016-01-01)
|
||||||
|
|
||||||
- Changed the default color `--style` from `solarized` to `monokai`
|
- Changed the default color `--style` from `solarized` to `monokai`
|
||||||
- Added basic Bash autocomplete support (need to be installed manually)
|
- Added basic Bash autocomplete support (need to be installed manually)
|
||||||
@ -263,19 +286,19 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Fixed colors and formatting on Windows
|
- Fixed colors and formatting on Windows
|
||||||
- Fixed `--auth` prompt on Windows
|
- Fixed `--auth` prompt on Windows
|
||||||
|
|
||||||
## [0.9.2](https://github.com/httpie/httpie/compare/0.9.1...0.9.2) (2015-02-24)
|
## [0.9.2](https://github.com/httpie/cli/compare/0.9.1...0.9.2) (2015-02-24)
|
||||||
|
|
||||||
- Fixed compatibility with Requests 2.5.1
|
- Fixed compatibility with Requests 2.5.1
|
||||||
- Changed the default JSON `Content-Type` to `application/json` as UTF-8
|
- Changed the default JSON `Content-Type` to `application/json` as UTF-8
|
||||||
is the default JSON encoding
|
is the default JSON encoding
|
||||||
|
|
||||||
## [0.9.1](https://github.com/httpie/httpie/compare/0.9.0...0.9.1) (2015-02-07)
|
## [0.9.1](https://github.com/httpie/cli/compare/0.9.0...0.9.1) (2015-02-07)
|
||||||
|
|
||||||
- Added support for Requests transport adapter plugins
|
- Added support for Requests transport adapter plugins
|
||||||
(see [httpie-unixsocket](https://github.com/httpie/httpie-unixsocket)
|
(see [httpie-unixsocket](https://github.com/httpie/httpie-unixsocket)
|
||||||
and [httpie-http2](https://github.com/httpie/httpie-http2))
|
and [httpie-http2](https://github.com/httpie/httpie-http2))
|
||||||
|
|
||||||
## [0.9.0](https://github.com/httpie/httpie/compare/0.8.0...0.9.0) (2015-01-31)
|
## [0.9.0](https://github.com/httpie/cli/compare/0.8.0...0.9.0) (2015-01-31)
|
||||||
|
|
||||||
- Added `--cert` and `--cert-key` parameters to specify a client side
|
- Added `--cert` and `--cert-key` parameters to specify a client side
|
||||||
certificate and private key for SSL
|
certificate and private key for SSL
|
||||||
@ -294,7 +317,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Fixed `--output=/dev/null` on Linux
|
- Fixed `--output=/dev/null` on Linux
|
||||||
- Miscellaneous bugfixes
|
- Miscellaneous bugfixes
|
||||||
|
|
||||||
## [0.8.0](https://github.com/httpie/httpie/compare/0.7.1...0.8.0) (2014-01-25)
|
## [0.8.0](https://github.com/httpie/cli/compare/0.7.1...0.8.0) (2014-01-25)
|
||||||
|
|
||||||
- Added `field=@file.txt` and `field:=@file.json` for embedding
|
- Added `field=@file.txt` and `field:=@file.json` for embedding
|
||||||
the contents of text and JSON files into request data
|
the contents of text and JSON files into request data
|
||||||
@ -302,7 +325,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Fixed request `Host` header value output so that it doesn't contain
|
- Fixed request `Host` header value output so that it doesn't contain
|
||||||
credentials, if included in the URL
|
credentials, if included in the URL
|
||||||
|
|
||||||
## [0.7.1](https://github.com/httpie/httpie/compare/0.6.0...0.7.1) (2013-09-24)
|
## [0.7.1](https://github.com/httpie/cli/compare/0.6.0...0.7.1) (2013-09-24)
|
||||||
|
|
||||||
- Added `--ignore-stdin`
|
- Added `--ignore-stdin`
|
||||||
- Added support for auth plugins
|
- Added support for auth plugins
|
||||||
@ -310,27 +333,27 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Improved `Content-Disposition` parsing for `--download` mode
|
- Improved `Content-Disposition` parsing for `--download` mode
|
||||||
- Update to Requests 2.0.0
|
- Update to Requests 2.0.0
|
||||||
|
|
||||||
## [0.6.0](https://github.com/httpie/httpie/compare/0.5.1...0.6.0) (2013-06-03)
|
## [0.6.0](https://github.com/httpie/cli/compare/0.5.1...0.6.0) (2013-06-03)
|
||||||
|
|
||||||
- XML data is now formatted
|
- XML data is now formatted
|
||||||
- `--session` and `--session-read-only` now also accept paths to
|
- `--session` and `--session-read-only` now also accept paths to
|
||||||
session files (eg. `http --session=/tmp/session.json example.org`)
|
session files (eg. `http --session=/tmp/session.json example.org`)
|
||||||
|
|
||||||
## [0.5.1](https://github.com/httpie/httpie/compare/0.5.0...0.5.1) (2013-05-13)
|
## [0.5.1](https://github.com/httpie/cli/compare/0.5.0...0.5.1) (2013-05-13)
|
||||||
|
|
||||||
- `Content-*` and `If-*` request headers are not stored in sessions
|
- `Content-*` and `If-*` request headers are not stored in sessions
|
||||||
anymore as they are request-specific
|
anymore as they are request-specific
|
||||||
|
|
||||||
## [0.5.0](https://github.com/httpie/httpie/compare/0.4.1...0.5.0) (2013-04-27)
|
## [0.5.0](https://github.com/httpie/cli/compare/0.4.1...0.5.0) (2013-04-27)
|
||||||
|
|
||||||
- Added a download mode via `--download`
|
- Added a download mode via `--download`
|
||||||
- Fixes miscellaneous bugs
|
- Fixes miscellaneous bugs
|
||||||
|
|
||||||
## [0.4.1](https://github.com/httpie/httpie/compare/0.4.0...0.4.1) (2013-02-26)
|
## [0.4.1](https://github.com/httpie/cli/compare/0.4.0...0.4.1) (2013-02-26)
|
||||||
|
|
||||||
- Fixed `setup.py`
|
- Fixed `setup.py`
|
||||||
|
|
||||||
## [0.4.0](https://github.com/httpie/httpie/compare/0.3.0...0.4.0) (2013-02-22)
|
## [0.4.0](https://github.com/httpie/cli/compare/0.3.0...0.4.0) (2013-02-22)
|
||||||
|
|
||||||
- Added Python 3.3 compatibility
|
- Added Python 3.3 compatibility
|
||||||
- Added Requests >= v1.0.4 compatibility
|
- Added Requests >= v1.0.4 compatibility
|
||||||
@ -339,7 +362,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Mutually exclusive arguments can be specified multiple times. The
|
- Mutually exclusive arguments can be specified multiple times. The
|
||||||
last value is used
|
last value is used
|
||||||
|
|
||||||
## [0.3.0](https://github.com/httpie/httpie/compare/0.2.7...0.3.0) (2012-09-21)
|
## [0.3.0](https://github.com/httpie/cli/compare/0.2.7...0.3.0) (2012-09-21)
|
||||||
|
|
||||||
- Allow output redirection on Windows
|
- Allow output redirection on Windows
|
||||||
- Added configuration file
|
- Added configuration file
|
||||||
@ -354,7 +377,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
(`--pretty=all`, `--pretty=colors` and `--pretty=format`)
|
(`--pretty=all`, `--pretty=colors` and `--pretty=format`)
|
||||||
`--ugly` has bee removed in favor of `--pretty=none`
|
`--ugly` has bee removed in favor of `--pretty=none`
|
||||||
|
|
||||||
## [0.2.7](https://github.com/httpie/httpie/compare/0.2.5...0.2.7) (2012-08-07)
|
## [0.2.7](https://github.com/httpie/cli/compare/0.2.5...0.2.7) (2012-08-07)
|
||||||
|
|
||||||
- Added compatibility with Requests 0.13.6
|
- Added compatibility with Requests 0.13.6
|
||||||
- Added streamed terminal output. `--stream, -S` can be used to enable
|
- Added streamed terminal output. `--stream, -S` can be used to enable
|
||||||
@ -371,7 +394,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Fixed printing of `multipart/form-data` requests
|
- Fixed printing of `multipart/form-data` requests
|
||||||
- Renamed `--traceback` to `--debug`
|
- Renamed `--traceback` to `--debug`
|
||||||
|
|
||||||
## [0.2.6](https://github.com/httpie/httpie/compare/0.2.5...0.2.6) (2012-07-26)
|
## [0.2.6](https://github.com/httpie/cli/compare/0.2.5...0.2.6) (2012-07-26)
|
||||||
|
|
||||||
- The short option for `--headers` is now `-h` (`-t` has been
|
- The short option for `--headers` is now `-h` (`-t` has been
|
||||||
removed, for usage use `--help`)
|
removed, for usage use `--help`)
|
||||||
@ -386,7 +409,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Added query string parameters (`param==value`)
|
- Added query string parameters (`param==value`)
|
||||||
- Added support for terminal colors under Windows
|
- Added support for terminal colors under Windows
|
||||||
|
|
||||||
## [0.2.5](https://github.com/httpie/httpie/compare/0.2.2...0.2.5) (2012-07-17)
|
## [0.2.5](https://github.com/httpie/cli/compare/0.2.2...0.2.5) (2012-07-17)
|
||||||
|
|
||||||
- Unicode characters in prettified JSON now don't get escaped for
|
- Unicode characters in prettified JSON now don't get escaped for
|
||||||
improved readability
|
improved readability
|
||||||
@ -397,20 +420,20 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
`--verbose`
|
`--verbose`
|
||||||
- Fixed Content-Type for requests with no data
|
- Fixed Content-Type for requests with no data
|
||||||
|
|
||||||
## [0.2.2](https://github.com/httpie/httpie/compare/0.2.1...0.2.2) (2012-06-24)
|
## [0.2.2](https://github.com/httpie/cli/compare/0.2.1...0.2.2) (2012-06-24)
|
||||||
|
|
||||||
- The `METHOD` positional argument can now be omitted (defaults to
|
- The `METHOD` positional argument can now be omitted (defaults to
|
||||||
`GET`, or to `POST` with data)
|
`GET`, or to `POST` with data)
|
||||||
- Fixed --verbose --form
|
- Fixed --verbose --form
|
||||||
- Added support for Tox
|
- Added support for Tox
|
||||||
|
|
||||||
## [0.2.1](https://github.com/httpie/httpie/compare/0.2.0...0.2.1) (2012-06-13)
|
## [0.2.1](https://github.com/httpie/cli/compare/0.2.0...0.2.1) (2012-06-13)
|
||||||
|
|
||||||
- Added compatibility with `requests-0.12.1`
|
- Added compatibility with `requests-0.12.1`
|
||||||
- Dropped custom JSON and HTTP lexers in favor of the ones newly included
|
- Dropped custom JSON and HTTP lexers in favor of the ones newly included
|
||||||
in `pygments-1.5`
|
in `pygments-1.5`
|
||||||
|
|
||||||
## [0.2.0](https://github.com/httpie/httpie/compare/0.1.6...0.2.0) (2012-04-25)
|
## [0.2.0](https://github.com/httpie/cli/compare/0.1.6...0.2.0) (2012-04-25)
|
||||||
|
|
||||||
- Added Python 3 support
|
- Added Python 3 support
|
||||||
- Added the ability to print the HTTP request as well as the response
|
- Added the ability to print the HTTP request as well as the response
|
||||||
@ -422,18 +445,18 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Added support for field name escaping
|
- Added support for field name escaping
|
||||||
- Many bug fixes
|
- Many bug fixes
|
||||||
|
|
||||||
## [0.1.6](https://github.com/httpie/httpie/compare/0.1.5...0.1.6) (2012-03-04)
|
## [0.1.6](https://github.com/httpie/cli/compare/0.1.5...0.1.6) (2012-03-04)
|
||||||
|
|
||||||
- Fixed `setup.py`
|
- Fixed `setup.py`
|
||||||
|
|
||||||
## [0.1.5](https://github.com/httpie/httpie/compare/0.1.4...0.1.5) (2012-03-04)
|
## [0.1.5](https://github.com/httpie/cli/compare/0.1.4...0.1.5) (2012-03-04)
|
||||||
|
|
||||||
- Many improvements and bug fixes
|
- Many improvements and bug fixes
|
||||||
|
|
||||||
## [0.1.4](https://github.com/httpie/httpie/compare/b966efa...0.1.4) (2012-02-28)
|
## [0.1.4](https://github.com/httpie/cli/compare/b966efa...0.1.4) (2012-02-28)
|
||||||
|
|
||||||
- Many improvements and bug fixes
|
- Many improvements and bug fixes
|
||||||
|
|
||||||
## [0.1.0](https://github.com/httpie/httpie/commit/b966efa) (2012-02-25)
|
## [0.1.0](https://github.com/httpie/cli/commit/b966efa) (2012-02-25)
|
||||||
|
|
||||||
- Initial public release
|
- Initial public release
|
||||||
|
@ -19,7 +19,7 @@ $ http --debug <COMPLETE ARGUMENT LIST THAT TRIGGERS THE ERROR>
|
|||||||
|
|
||||||
## 2. Contributing Code and Docs
|
## 2. Contributing Code and Docs
|
||||||
|
|
||||||
Before working on a new feature or a bug, please browse [existing issues](https://github.com/httpie/httpie/issues)
|
Before working on a new feature or a bug, please browse [existing issues](https://github.com/httpie/cli/issues)
|
||||||
to see whether it has previously been discussed.
|
to see whether it has previously been discussed.
|
||||||
|
|
||||||
If your change alters HTTPie’s behaviour or interface, it's a good idea to
|
If your change alters HTTPie’s behaviour or interface, it's a good idea to
|
||||||
@ -38,13 +38,13 @@ for existing-yet-previously-untested behavior will very likely be merged.
|
|||||||
Therefore, docs and tests improvements are a great candidate for your first
|
Therefore, docs and tests improvements are a great candidate for your first
|
||||||
contribution.
|
contribution.
|
||||||
|
|
||||||
Consider also adding a [CHANGELOG](https://github.com/httpie/httpie/blob/master/CHANGELOG.md) entry for your changes.
|
Consider also adding a [CHANGELOG](https://github.com/httpie/cli/blob/master/CHANGELOG.md) entry for your changes.
|
||||||
|
|
||||||
### Development Environment
|
### 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/httpie/cli> and fork the project repository.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone your fork
|
# Clone your fork
|
||||||
@ -59,8 +59,10 @@ $ git checkout -b my_topical_branch
|
|||||||
|
|
||||||
#### Setup
|
#### Setup
|
||||||
|
|
||||||
The [Makefile](https://github.com/httpie/httpie/blob/master/Makefile) contains a bunch of tasks to get you started. Just run
|
The [Makefile](https://github.com/httpie/cli/blob/master/Makefile) contains a bunch of tasks to get you started.
|
||||||
the following command, which:
|
You can run `$ make` to see all the available tasks.
|
||||||
|
|
||||||
|
To get started, run the command below, which:
|
||||||
|
|
||||||
- Creates an isolated Python virtual environment inside `./venv`
|
- Creates an isolated Python virtual environment inside `./venv`
|
||||||
(via the standard library [venv](https://docs.python.org/3/library/venv.html) tool);
|
(via the standard library [venv](https://docs.python.org/3/library/venv.html) tool);
|
||||||
@ -70,7 +72,7 @@ the following command, which:
|
|||||||
- and runs tests (It is the same as running `make install test`).
|
- and runs tests (It is the same as running `make install test`).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ make
|
$ make all
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Python virtual environment
|
#### Python virtual environment
|
||||||
@ -110,7 +112,7 @@ and that `make pycodestyle` passes.
|
|||||||
|
|
||||||
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, [GitHub Actions](https://github.com/httpie/httpie/actions) will automatically run HTTPie’s [test suite](https://github.com/httpie/httpie/tree/master/tests) against your code, so please make sure all checks pass.
|
When you open a Pull Request, [GitHub Actions](https://github.com/httpie/cli/actions) will automatically run HTTPie’s [test suite](https://github.com/httpie/cli/tree/master/tests) against your code, so please make sure all checks pass.
|
||||||
|
|
||||||
#### Running tests locally
|
#### Running tests locally
|
||||||
|
|
||||||
@ -142,7 +144,7 @@ $ python -m pytest tests/test_uploads.py::TestMultipartFormDataFileUpload
|
|||||||
$ python -m pytest tests/test_uploads.py::TestMultipartFormDataFileUpload::test_upload_ok
|
$ python -m pytest tests/test_uploads.py::TestMultipartFormDataFileUpload::test_upload_ok
|
||||||
```
|
```
|
||||||
|
|
||||||
See [Makefile](https://github.com/httpie/httpie/blob/master/Makefile) for additional development utilities.
|
See [Makefile](https://github.com/httpie/cli/blob/master/Makefile) for additional development utilities.
|
||||||
|
|
||||||
#### Running benchmarks
|
#### Running benchmarks
|
||||||
|
|
||||||
@ -152,7 +154,7 @@ with the master branch of your repository (or a fresh checkout of HTTPie master,
|
|||||||
`--fresh`) and report the results back.
|
`--fresh`) and report the results back.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ python extras/benchmarks/run.py
|
$ python extras/profiling/run.py
|
||||||
```
|
```
|
||||||
|
|
||||||
The benchmarks can also be run on the CI. Since it is a long process, it requires manual
|
The benchmarks can also be run on the CI. Since it is a long process, it requires manual
|
||||||
@ -207,4 +209,4 @@ $ python -m pytest
|
|||||||
|
|
||||||
______________________________________________________________________
|
______________________________________________________________________
|
||||||
|
|
||||||
Finally, feel free to add yourself to [AUTHORS](https://github.com/httpie/httpie/blob/master/AUTHORS.md)!
|
Finally, feel free to add yourself to [AUTHORS](https://github.com/httpie/cli/blob/master/AUTHORS.md)!
|
||||||
|
@ -4,5 +4,5 @@ include CHANGELOG.md
|
|||||||
include AUTHORS.md
|
include AUTHORS.md
|
||||||
include docs/README.md
|
include docs/README.md
|
||||||
|
|
||||||
# <https://github.com/httpie/httpie/issues/182>
|
# <https://github.com/httpie/cli/issues/182>
|
||||||
recursive-include tests/ *
|
recursive-include tests/ *
|
||||||
|
48
Makefile
48
Makefile
@ -22,6 +22,26 @@ VENV_PYTHON=$(VENV_BIN)/python
|
|||||||
export PATH := $(VENV_BIN):$(PATH)
|
export PATH := $(VENV_BIN):$(PATH)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
default: list-tasks
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Default task to get a list of tasks when `make' is run without args.
|
||||||
|
# <https://stackoverflow.com/questions/4219255>
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
list-tasks:
|
||||||
|
@echo Available tasks:
|
||||||
|
@echo ----------------
|
||||||
|
@$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' | sort | grep -E -v -e '^[^[:alnum:]]' -e '^$@$$'
|
||||||
|
@echo
|
||||||
|
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Installation
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
all: uninstall-httpie install test
|
all: uninstall-httpie install test
|
||||||
|
|
||||||
|
|
||||||
@ -30,10 +50,10 @@ install: venv install-reqs
|
|||||||
|
|
||||||
install-reqs:
|
install-reqs:
|
||||||
@echo $(H1)Updating package tools$(H1END)
|
@echo $(H1)Updating package tools$(H1END)
|
||||||
$(VENV_PIP) install --upgrade pip wheel
|
$(VENV_PIP) install --upgrade pip wheel build
|
||||||
|
|
||||||
@echo $(H1)Installing dev requirements$(H1END)
|
@echo $(H1)Installing dev requirements$(H1END)
|
||||||
$(VENV_PIP) install --upgrade --editable '.[dev]'
|
$(VENV_PIP) install --upgrade '.[dev]' '.[test]'
|
||||||
|
|
||||||
@echo $(H1)Installing HTTPie$(H1END)
|
@echo $(H1)Installing HTTPie$(H1END)
|
||||||
$(VENV_PIP) install --upgrade --editable .
|
$(VENV_PIP) install --upgrade --editable .
|
||||||
@ -104,7 +124,8 @@ test-dist: test-sdist test-bdist-wheel
|
|||||||
|
|
||||||
test-sdist: clean venv
|
test-sdist: clean venv
|
||||||
@echo $(H1)Testing sdist build an installation$(H1END)
|
@echo $(H1)Testing sdist build an installation$(H1END)
|
||||||
$(VENV_PYTHON) setup.py sdist
|
$(VENV_PIP) install build
|
||||||
|
$(VENV_PYTHON) -m build --sdist
|
||||||
$(VENV_PIP) install --force-reinstall --upgrade dist/*.gz
|
$(VENV_PIP) install --force-reinstall --upgrade dist/*.gz
|
||||||
$(VENV_BIN)/http --version
|
$(VENV_BIN)/http --version
|
||||||
@echo
|
@echo
|
||||||
@ -112,8 +133,8 @@ test-sdist: clean venv
|
|||||||
|
|
||||||
test-bdist-wheel: clean venv
|
test-bdist-wheel: clean venv
|
||||||
@echo $(H1)Testing wheel build an installation$(H1END)
|
@echo $(H1)Testing wheel build an installation$(H1END)
|
||||||
$(VENV_PIP) install wheel
|
$(VENV_PIP) install build
|
||||||
$(VENV_PYTHON) setup.py bdist_wheel
|
$(VENV_PYTHON) -m build --wheel
|
||||||
$(VENV_PIP) install --force-reinstall --upgrade dist/*.whl
|
$(VENV_PIP) install --force-reinstall --upgrade dist/*.whl
|
||||||
$(VENV_BIN)/http --version
|
$(VENV_BIN)/http --version
|
||||||
@echo
|
@echo
|
||||||
@ -153,8 +174,11 @@ doc-check:
|
|||||||
|
|
||||||
|
|
||||||
build:
|
build:
|
||||||
rm -rf build/
|
rm -rf build/ dist/
|
||||||
$(VENV_PYTHON) setup.py sdist bdist_wheel
|
mv httpie/internal/__build_channel__.py httpie/internal/__build_channel__.py.original
|
||||||
|
echo 'BUILD_CHANNEL = "pip"' > httpie/internal/__build_channel__.py
|
||||||
|
$(VENV_PYTHON) -m build --sdist --wheel --outdir dist/
|
||||||
|
mv httpie/internal/__build_channel__.py.original httpie/internal/__build_channel__.py
|
||||||
|
|
||||||
|
|
||||||
publish: test-all publish-no-test
|
publish: test-all publish-no-test
|
||||||
@ -198,7 +222,7 @@ brew-test:
|
|||||||
- brew uninstall httpie
|
- brew uninstall httpie
|
||||||
|
|
||||||
@echo $(H1)Building from source…$(H1END)
|
@echo $(H1)Building from source…$(H1END)
|
||||||
- brew install --build-from-source ./docs/packaging/brew/httpie.rb
|
- brew install --HEAD --build-from-source ./docs/packaging/brew/httpie.rb
|
||||||
|
|
||||||
@echo $(H1)Verifying…$(H1END)
|
@echo $(H1)Verifying…$(H1END)
|
||||||
http --version
|
http --version
|
||||||
@ -208,15 +232,15 @@ brew-test:
|
|||||||
brew audit --strict httpie
|
brew audit --strict httpie
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Regeneration
|
# Generated content
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
regen-all: regen-man-pages regen-install-methods
|
content: man installation-docs
|
||||||
|
|
||||||
regen-man-pages: install
|
man: install
|
||||||
@echo $(H1)Regenerate man pages$(H1END)
|
@echo $(H1)Regenerate man pages$(H1END)
|
||||||
$(VENV_PYTHON) extras/scripts/generate_man_pages.py
|
$(VENV_PYTHON) extras/scripts/generate_man_pages.py
|
||||||
|
|
||||||
regen-install-methods:
|
installation-docs:
|
||||||
@echo $(H1)Updating installation instructions in the docs$(H1END)
|
@echo $(H1)Updating installation instructions in the docs$(H1END)
|
||||||
$(VENV_PYTHON) docs/installation/generate.py
|
$(VENV_PYTHON) docs/installation/generate.py
|
||||||
|
65
README.md
65
README.md
@ -1,10 +1,31 @@
|
|||||||
<br/>
|
<h2 align="center">
|
||||||
<a href="https://httpie.io" target="blank_">
|
<a href="https://httpie.io" target="blank_">
|
||||||
<img height="100" alt="HTTPie" src="https://raw.githubusercontent.com/httpie/httpie/master/docs/httpie-logo.svg" />
|
<img height="100" alt="HTTPie" src="https://raw.githubusercontent.com/httpie/cli/master/docs/httpie-logo.svg" />
|
||||||
</a>
|
</a>
|
||||||
<br/>
|
<br>
|
||||||
|
HTTPie CLI: human-friendly HTTP client for the API era
|
||||||
|
</h2>
|
||||||
|
|
||||||
# HTTPie: human-friendly CLI HTTP client for the API era
|
<div align="center">
|
||||||
|
|
||||||
|
[](https://httpie.io/product)
|
||||||
|
[](https://httpie.io/app)
|
||||||
|
[](https://httpie.io/cli)
|
||||||
|
[](https://twitter.com/httpie)
|
||||||
|
[](https://httpie.io/discord)
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
[](https://httpie.org/docs/cli)
|
||||||
|
[](https://pypi.python.org/pypi/httpie)
|
||||||
|
[](https://github.com/httpie/cli/actions)
|
||||||
|
[](https://codecov.io/gh/httpie/cli)
|
||||||
|
[](https://www.pepy.tech/projects/httpie)
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
HTTPie (pronounced _aitch-tee-tee-pie_) is a command-line HTTP client.
|
HTTPie (pronounced _aitch-tee-tee-pie_) is a command-line HTTP client.
|
||||||
Its goal is to make CLI interaction with web services as human-friendly as possible.
|
Its goal is to make CLI interaction with web services as human-friendly as possible.
|
||||||
@ -12,14 +33,14 @@ HTTPie is designed for testing, debugging, and generally interacting with APIs &
|
|||||||
The `http` & `https` commands allow for creating and sending arbitrary HTTP requests.
|
The `http` & `https` commands allow for creating and sending arbitrary HTTP requests.
|
||||||
They use simple and natural syntax and provide formatted and colorized output.
|
They use simple and natural syntax and provide formatted and colorized output.
|
||||||
|
|
||||||
[](https://httpie.org/docs)
|
<div align="center">
|
||||||
[](https://pypi.python.org/pypi/httpie)
|
|
||||||
[](https://github.com/httpie/httpie/actions)
|
<img src="https://raw.githubusercontent.com/httpie/cli/master/docs/httpie-animation.gif" alt="HTTPie in action" width="100%"/>
|
||||||
[](https://codecov.io/gh/httpie/httpie)
|
|
||||||
[](https://twitter.com/httpie)
|
|
||||||
[](https://httpie.io/discord)
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<img src="https://raw.githubusercontent.com/httpie/httpie/master/docs/httpie-animation.gif" alt="HTTPie in action" width="100%"/>
|
|
||||||
|
|
||||||
|
|
||||||
## We lost 54k GitHub stars
|
## We lost 54k GitHub stars
|
||||||
@ -53,25 +74,25 @@ Please note we recently accidentally made this repo private for a moment, and Gi
|
|||||||
Hello World:
|
Hello World:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ https httpie.io/hello
|
https httpie.io/hello
|
||||||
```
|
```
|
||||||
|
|
||||||
Custom [HTTP method](https://httpie.io/docs#http-method), [HTTP headers](https://httpie.io/docs#http-headers) and [JSON](https://httpie.io/docs#json) data:
|
Custom [HTTP method](https://httpie.io/docs#http-method), [HTTP headers](https://httpie.io/docs#http-headers) and [JSON](https://httpie.io/docs#json) data:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ http PUT pie.dev/put X-API-Token:123 name=John
|
http PUT pie.dev/put X-API-Token:123 name=John
|
||||||
```
|
```
|
||||||
|
|
||||||
Build and print a request without sending it using [offline mode](https://httpie.io/docs#offline-mode):
|
Build and print a request without sending it using [offline mode](https://httpie.io/docs/cli/offline-mode):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ http --offline pie.dev/post hello=offline
|
http --offline pie.dev/post hello=offline
|
||||||
```
|
```
|
||||||
|
|
||||||
Use [GitHub API](https://developer.github.com/v3/issues/comments/#create-a-comment) to post a comment on an [Issue](https://github.com/httpie/httpie/issues/83) with [authentication](https://httpie.io/docs#authentication):
|
Use [GitHub API](https://developer.github.com/v3/issues/comments/#create-a-comment) to post a comment on an [Issue](https://github.com/httpie/cli/issues/83) with [authentication](https://httpie.io/docs#authentication):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ http -a USERNAME POST https://api.github.com/repos/httpie/httpie/issues/83/comments body='HTTPie is awesome! :heart:'
|
http -a USERNAME POST https://api.github.com/repos/httpie/cli/issues/83/comments body='HTTPie is awesome! :heart:'
|
||||||
```
|
```
|
||||||
|
|
||||||
[See more examples →](https://httpie.io/docs#examples)
|
[See more examples →](https://httpie.io/docs#examples)
|
||||||
@ -82,11 +103,11 @@ $ http -a USERNAME POST https://api.github.com/repos/httpie/httpie/issues/83/com
|
|||||||
- Join our [Discord server](https://httpie.io/discord) is to ask questions, discuss features, and for general API chat.
|
- Join our [Discord server](https://httpie.io/discord) is to ask questions, discuss features, and for general API chat.
|
||||||
- Tweet at [@httpie](https://twitter.com/httpie) on Twitter.
|
- Tweet at [@httpie](https://twitter.com/httpie) on Twitter.
|
||||||
- Use [StackOverflow](https://stackoverflow.com/questions/tagged/httpie) to ask questions and include a `httpie` tag.
|
- Use [StackOverflow](https://stackoverflow.com/questions/tagged/httpie) to ask questions and include a `httpie` tag.
|
||||||
- Create [GitHub Issues](https://github.com/httpie/httpie/issues) for bug reports and feature requests.
|
- Create [GitHub Issues](https://github.com/httpie/cli/issues) for bug reports and feature requests.
|
||||||
- Subscribe to the [HTTPie newsletter](https://httpie.io) for occasional updates.
|
- Subscribe to the [HTTPie newsletter](https://httpie.io) for occasional updates.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Have a look through existing [Issues](https://github.com/httpie/httpie/issues) and [Pull Requests](https://github.com/httpie/httpie/pulls) that you could help with. If you'd like to request a feature or report a bug, please [create a GitHub Issue](https://github.com/httpie/httpie/issues) using one of the templates provided.
|
Have a look through existing [Issues](https://github.com/httpie/cli/issues) and [Pull Requests](https://github.com/httpie/cli/pulls) that you could help with. If you'd like to request a feature or report a bug, please [create a GitHub Issue](https://github.com/httpie/cli/issues) using one of the templates provided.
|
||||||
|
|
||||||
[See contribution guide →](https://github.com/httpie/httpie/blob/master/CONTRIBUTING.md)
|
[See contribution guide →](https://github.com/httpie/cli/blob/master/CONTRIBUTING.md)
|
||||||
|
199
docs/README.md
199
docs/README.md
@ -19,7 +19,7 @@ This documentation is best viewed at [httpie.io/docs](https://httpie.org/docs).
|
|||||||
You can select your corresponding HTTPie version as well as run examples directly from the browser using a [termible.io](https://termible.io?utm_source=httpie-readme) embedded terminal.
|
You can select your corresponding HTTPie version as well as run examples directly from the browser using a [termible.io](https://termible.io?utm_source=httpie-readme) embedded terminal.
|
||||||
|
|
||||||
If you are reading this on GitHub, then this text covers the current *development* version.
|
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 docs by editing [this file](https://github.com/httpie/httpie/blob/master/docs/README.md).
|
You are invited to submit fixes and improvements to the docs by editing [this file](https://github.com/httpie/cli/blob/master/docs/README.md).
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -126,6 +126,66 @@ $ choco upgrade httpie
|
|||||||
|
|
||||||
### Linux
|
### Linux
|
||||||
|
|
||||||
|
#### Debian and Ubuntu
|
||||||
|
|
||||||
|
Also works for other Debian-derived distributions like MX Linux, Linux Mint, deepin, Pop!_OS, KDE neon, Zorin OS, elementary OS, Kubuntu, Devuan, Linux Lite, Peppermint OS, Lubuntu, antiX, Xubuntu, etc.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install httpie
|
||||||
|
$ curl -SsL https://packages.httpie.io/deb/KEY.gpg | sudo gpg --dearmor -o /usr/share/keyrings/httpie.gpg
|
||||||
|
$ echo "deb [arch=amd64 signed-by=/usr/share/keyrings/httpie.gpg] https://packages.httpie.io/deb ./" | sudo tee /etc/apt/sources.list.d/httpie.list > /dev/null
|
||||||
|
$ sudo apt update
|
||||||
|
$ sudo apt install httpie
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Upgrade httpie
|
||||||
|
$ sudo apt update && sudo apt upgrade httpie
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fedora
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install httpie
|
||||||
|
$ dnf install httpie
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Upgrade httpie
|
||||||
|
$ dnf upgrade httpie
|
||||||
|
```
|
||||||
|
|
||||||
|
#### CentOS and RHEL
|
||||||
|
|
||||||
|
Also works for other RHEL-derived distributions like ClearOS, Oracle Linux, etc.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install httpie
|
||||||
|
$ yum install epel-release
|
||||||
|
$ yum install httpie
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Upgrade httpie
|
||||||
|
$ yum upgrade httpie
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Single binary executables
|
||||||
|
|
||||||
|
Get the standalone HTTPie Linux executables when you don't want to go through the full installation process.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install httpie
|
||||||
|
$ https --download packages.httpie.io/binaries/linux/http-latest -o http
|
||||||
|
$ ln -ls ./http ./https
|
||||||
|
$ chmod +x ./http ./https
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Upgrade httpie
|
||||||
|
$ https --download packages.httpie.io/binaries/linux/http-latest -o http
|
||||||
|
```
|
||||||
|
|
||||||
#### Snapcraft (Linux)
|
#### Snapcraft (Linux)
|
||||||
|
|
||||||
To install [Snapcraft](https://snapcraft.io/), see [its installation](https://snapcraft.io/docs/installing-snapd).
|
To install [Snapcraft](https://snapcraft.io/), see [its installation](https://snapcraft.io/docs/installing-snapd).
|
||||||
@ -156,49 +216,6 @@ $ brew update
|
|||||||
$ brew upgrade httpie
|
$ brew upgrade httpie
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Debian and Ubuntu
|
|
||||||
|
|
||||||
Also works for other Debian-derived distributions like MX Linux, Linux Mint, deepin, Pop!_OS, KDE neon, Zorin OS, elementary OS, Kubuntu, Devuan, Linux Lite, Peppermint OS, Lubuntu, antiX, Xubuntu, etc.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install httpie
|
|
||||||
$ apt update
|
|
||||||
$ apt install httpie
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Upgrade httpie
|
|
||||||
$ apt update
|
|
||||||
$ apt upgrade httpie
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Fedora
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install httpie
|
|
||||||
$ dnf install httpie
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Upgrade httpie
|
|
||||||
$ dnf upgrade httpie
|
|
||||||
```
|
|
||||||
|
|
||||||
#### CentOS and RHEL
|
|
||||||
|
|
||||||
Also works for other RHEL-derived distributions like ClearOS, Oracle Linux, etc.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Install httpie
|
|
||||||
$ yum install epel-release
|
|
||||||
$ yum install httpie
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Upgrade httpie
|
|
||||||
$ yum upgrade httpie
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Arch Linux
|
#### Arch Linux
|
||||||
|
|
||||||
Also works for other Arch-derived distributions like ArcoLinux, EndeavourOS, Artix Linux, etc.
|
Also works for other Arch-derived distributions like ArcoLinux, EndeavourOS, Artix Linux, etc.
|
||||||
@ -233,36 +250,39 @@ $ pkg upgrade www/py-httpie
|
|||||||
|
|
||||||
### Unstable version
|
### Unstable version
|
||||||
|
|
||||||
You can also install the latest unreleased development version directly from the `master` branch on GitHub.
|
If you want to try out the latest version of HTTPie that hasn't been officially released yet, you can install the development or unstable version directly from the master branch on GitHub. However, keep in mind that the development version is a work in progress and may not be as reliable as the stable version.
|
||||||
It is a work-in-progress of a future stable release so the experience might be not as smooth.
|
|
||||||
|
|
||||||
You can install it on Linux, macOS, Windows, or FreeBSD with `pip`:
|
You can use the following command to install the development version of HTTPie on Linux, macOS, Windows, or FreeBSD operating systems. With this command, the code present in the `master` branch is downloaded and installed using `pip`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ python -m pip install --upgrade https://github.com/httpie/httpie/archive/master.tar.gz
|
$ python -m pip install --upgrade https://github.com/httpie/cli/archive/master.tar.gz
|
||||||
```
|
```
|
||||||
|
|
||||||
Or on macOS, and Linux, with Homebrew:
|
There are other ways to install the development version of HTTPie on macOS and Linux.
|
||||||
|
|
||||||
|
You can install it using Homebrew by running the following commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ brew uninstall --force httpie
|
$ brew uninstall --force httpie
|
||||||
$ brew install --HEAD httpie
|
$ brew install --HEAD httpie
|
||||||
```
|
```
|
||||||
|
|
||||||
And even on macOS, and Linux, with Snapcraft:
|
You can install it using Snapcraft by running the following commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ snap remove httpie
|
$ snap remove httpie
|
||||||
$ snap install httpie --edge
|
$ snap install httpie --edge
|
||||||
```
|
```
|
||||||
|
|
||||||
Verify that now you have the [current development version identifier](https://github.com/httpie/httpie/blob/master/httpie/__init__.py#L6) with the `.dev0` suffix, for example:
|
To verify the installation, you can compare the [version identifier on GitHub](https://github.com/httpie/cli/blob/master/httpie/__init__.py#L6) with the one available on your machine. You can check the version of HTTPie on your machine by using the command `http --version`.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ http --version
|
$ http --version
|
||||||
# 3.X.X.dev0
|
# 3.X.X.dev0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Note that on your machine, the version name will have the `.dev0` suffix.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
Hello World:
|
Hello World:
|
||||||
@ -277,7 +297,7 @@ Synopsis:
|
|||||||
$ http [flags] [METHOD] URL [ITEM [ITEM]]
|
$ http [flags] [METHOD] URL [ITEM [ITEM]]
|
||||||
```
|
```
|
||||||
|
|
||||||
See also `http --help`.
|
See also `http --help` (and for systems where man pages are available, you can use `man http`).
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
@ -305,10 +325,10 @@ Build and print a request without sending it using [offline mode](#offline-mode)
|
|||||||
$ http --offline pie.dev/post hello=offline
|
$ http --offline pie.dev/post hello=offline
|
||||||
```
|
```
|
||||||
|
|
||||||
Use [GitHub API](https://developer.github.com/v3/issues/comments/#create-a-comment) to post a comment on an [issue](https://github.com/httpie/httpie/issues/83) with [authentication](#authentication):
|
Use [GitHub API](https://developer.github.com/v3/issues/comments/#create-a-comment) to post a comment on an [issue](https://github.com/httpie/cli/issues/83) with [authentication](#authentication):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ http -a USERNAME POST https://api.github.com/repos/httpie/httpie/issues/83/comments body='HTTPie is awesome! :heart:'
|
$ http -a USERNAME POST https://api.github.com/repos/httpie/cli/issues/83/comments body='HTTPie is awesome! :heart:'
|
||||||
```
|
```
|
||||||
|
|
||||||
Upload a file using [redirected input](#redirected-input):
|
Upload a file using [redirected input](#redirected-input):
|
||||||
@ -457,7 +477,7 @@ $ http pie.dev/get text==@files/text.txt
|
|||||||
### URL shortcuts for `localhost`
|
### URL shortcuts for `localhost`
|
||||||
|
|
||||||
Additionally, curl-like shorthand for localhost is supported.
|
Additionally, curl-like shorthand for localhost is supported.
|
||||||
This means that, for example, `:3000` would expand to `http://localhost:3000`
|
This means that, for example, `:3000` would expand to `http://localhost:3000`.
|
||||||
If the port is omitted, then port 80 is assumed.
|
If the port is omitted, then port 80 is assumed.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -510,7 +530,7 @@ $ http-unix %2Fvar%2Frun%2Fdocker.sock/info
|
|||||||
|
|
||||||
### `--path-as-is`
|
### `--path-as-is`
|
||||||
|
|
||||||
The standard behavior of HTTP clients is to normalize the path portion of URLs by squashing dot segments as a typically filesystem would:
|
The standard behavior of HTTP clients is to normalize the path portion of URLs by squashing dot segments as a typical filesystem would:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ http -v example.org/./../../etc/password
|
$ http -v example.org/./../../etc/password
|
||||||
@ -554,7 +574,7 @@ $ http PUT pie.dev/put \
|
|||||||
| HTTP Headers `Name:Value` | Arbitrary HTTP header, e.g. `X-API-Token:123` |
|
| HTTP Headers `Name:Value` | Arbitrary HTTP header, e.g. `X-API-Token:123` |
|
||||||
| URL parameters `name==value` | Appends the given name/value pair as a querystring parameter to the URL. The `==` separator is used. |
|
| URL parameters `name==value` | Appends the given name/value pair as a querystring parameter to the URL. The `==` separator is used. |
|
||||||
| Data Fields `field=value` | Request data fields to be serialized as a JSON object (default), to be form-encoded (with `--form, -f`), or to be serialized as `multipart/form-data` (with `--multipart`) |
|
| Data Fields `field=value` | Request data fields to be serialized as a JSON object (default), to be form-encoded (with `--form, -f`), or to be serialized as `multipart/form-data` (with `--multipart`) |
|
||||||
| Raw JSON fields `field:=json` | Useful when sending JSON and one or more fields need to be a `Boolean`, `Number`, nested `Object`, or an `Array`, e.g., `meals:='["ham","spam"]'` or `pies:=[1,2,3]` (note the quotes) |
|
| Raw JSON fields `field:=json` | Useful when sending JSON and one or more fields need to be a `Boolean`, `Number`, nested `Object`, or an `Array`, e.g., `meals:='["ham","spam"]'` or `pies:='[1,2,3]'` (note the quotes) |
|
||||||
| File upload fields `field@/dir/file`, `field@file;type=mime` | Only available with `--form`, `-f` and `--multipart`. For example `screenshot@~/Pictures/img.png`, or `'cv@cv.txt;type=text/markdown'`. With `--form`, the presence of a file field results in a `--multipart` request |
|
| File upload fields `field@/dir/file`, `field@file;type=mime` | Only available with `--form`, `-f` and `--multipart`. For example `screenshot@~/Pictures/img.png`, or `'cv@cv.txt;type=text/markdown'`. With `--form`, the presence of a file field results in a `--multipart` request |
|
||||||
|
|
||||||
Note that the structured data fields aren’t the only way to specify request data:
|
Note that the structured data fields aren’t the only way to specify request data:
|
||||||
@ -563,7 +583,7 @@ Note that the structured data fields aren’t the only way to specify request da
|
|||||||
### File based separators
|
### File based separators
|
||||||
|
|
||||||
Using file contents as values for specific fields is a very common use case, which can be achieved through adding the `@` suffix to
|
Using file contents as values for specific fields is a very common use case, which can be achieved through adding the `@` suffix to
|
||||||
the operators above. For example instead of using a static string as the value for some header, you can use `:@` operator
|
the operators above. For example, instead of using a static string as the value for some header, you can use `:@` operator
|
||||||
to pass the desired value from a file.
|
to pass the desired value from a file.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -729,7 +749,7 @@ $ http --offline --print=B pie.dev/post \
|
|||||||
```
|
```
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"category": "tools",
|
"category": "tools",
|
||||||
"search": {
|
"search": {
|
||||||
"keywords": [
|
"keywords": [
|
||||||
@ -895,6 +915,8 @@ http --offline --print=B pie.dev/post \
|
|||||||
"]": 3
|
"]": 3
|
||||||
},
|
},
|
||||||
"foo[bar]": 1
|
"foo[bar]": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
If you want to send the literal backslash character (`\`), escape it with another backslash:
|
If you want to send the literal backslash character (`\`), escape it with another backslash:
|
||||||
|
|
||||||
@ -1022,7 +1044,7 @@ $ http POST pie.dev/post < files/data.json
|
|||||||
```bash
|
```bash
|
||||||
$ http -f POST pie.dev/post name='John Smith' cv@~/files/data.xml
|
$ http -f POST pie.dev/post name='John Smith' cv@~/files/data.xml
|
||||||
```
|
```
|
||||||
|
|
||||||
The request above is the same as if the following HTML form were submitted:
|
The request above is the same as if the following HTML form were submitted:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
@ -1154,11 +1176,11 @@ User-Agent: HTTPie/<version>
|
|||||||
```bash
|
```bash
|
||||||
$ http pie.dev/headers 'Header;'
|
$ http pie.dev/headers 'Header;'
|
||||||
```
|
```
|
||||||
|
|
||||||
Please note that some internal headers, such as `Content-Length`, can’t be unset if
|
Please note that some internal headers, such as `Content-Length`, can’t be unset if
|
||||||
they are automatically added by the client itself.
|
they are automatically added by the client itself.
|
||||||
|
|
||||||
### Multiple header values with the same name
|
### Multiple header values with the same name
|
||||||
|
|
||||||
If the request is sent with multiple headers that are sharing the same name, then
|
If the request is sent with multiple headers that are sharing the same name, then
|
||||||
the HTTPie will send them individually.
|
the HTTPie will send them individually.
|
||||||
@ -1213,7 +1235,7 @@ by individual commands when sending a request instead of being joined together.
|
|||||||
|
|
||||||
Generating raw requests that can be sent with any other client:
|
Generating raw requests that can be sent with any other client:
|
||||||
|
|
||||||
```bash
|
```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 pie.dev/post hello=world > request.http
|
||||||
```
|
```
|
||||||
@ -1436,7 +1458,8 @@ $ http --proxy=http:http://user:pass@10.10.1.10:3128 example.org
|
|||||||
## HTTPS
|
## HTTPS
|
||||||
|
|
||||||
### Server SSL certificate verification
|
### Server SSL certificate verification
|
||||||
To skip the host’s SSL certificate verification, you can pass `--verify=no` (default is `yes`):
|
|
||||||
|
To skip the host’s SSL certificate verification, you can pass `--verify=no` (default is `yes`):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ http --verify=no https://pie.dev/get
|
$ http --verify=no https://pie.dev/get
|
||||||
@ -1653,6 +1676,10 @@ If you’d like to silence warnings as well, use `-q` or `--quiet` twice:
|
|||||||
Let’s say that there is an API that returns the whole resource when it is updated, but you are only interested in the response headers to see the status code after an update:
|
Let’s say that there is an API that returns the whole resource when it is updated, but you are only interested in the response headers to see the status code after an update:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
$ http --headers PATCH pie.dev/patch name='New Name'
|
||||||
|
```
|
||||||
|
|
||||||
|
Since you are only printing the HTTP headers here, the connection to the server is closed as soon as all the response headers have been received.
|
||||||
Therefore, bandwidth and time isn’t wasted downloading the body which you don’t care about.
|
Therefore, bandwidth and time isn’t wasted downloading the body which you don’t care about.
|
||||||
The response headers are downloaded always, even if they are not part of the output
|
The response headers are downloaded always, even if they are not part of the output
|
||||||
|
|
||||||
@ -1727,7 +1754,7 @@ $ http pie.dev/post <<<'{"name": "John"}'
|
|||||||
$ pbpaste | http PUT pie.dev/put
|
$ pbpaste | http PUT pie.dev/put
|
||||||
```
|
```
|
||||||
|
|
||||||
Passing data through `stdin` **can’t** be combined with data fields specified on the command line:
|
Passing data through `stdin` **can’t** be combined with data fields specified on the command line:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ echo -n 'data' | http POST example.org more=data # This is invalid
|
$ echo -n 'data' | http POST example.org more=data # This is invalid
|
||||||
@ -1975,7 +2002,7 @@ HTTPie features a download mode in which it acts similarly to `wget`.
|
|||||||
### Piping while downloading
|
### Piping while downloading
|
||||||
|
|
||||||
You can also redirect the response body to another program while the response headers and progress are still shown in the terminal:
|
You can also redirect the response body to another program while the response headers and progress are still shown in the terminal:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ http -d https://github.com/httpie/cli/archive/master.tar.gz | tar zxf -
|
$ http -d https://github.com/httpie/cli/archive/master.tar.gz | tar zxf -
|
||||||
```
|
```
|
||||||
@ -2004,7 +2031,7 @@ To prevent data loss by overwriting, HTTPie adds a unique numerical suffix to th
|
|||||||
## Streamed responses
|
## Streamed responses
|
||||||
|
|
||||||
Responses are downloaded and printed in chunks.
|
Responses are downloaded and printed in chunks.
|
||||||
This allows for streaming and large file downloads without using too much memory.
|
This allows for streaming and large file downloads without using too much memory.
|
||||||
However, when [colors and formatting](#colors-and-formatting) are applied, the whole response is buffered and only then processed at once.
|
However, when [colors and formatting](#colors-and-formatting) are applied, the whole response is buffered and only then processed at once.
|
||||||
|
|
||||||
### Disabling buffering
|
### Disabling buffering
|
||||||
@ -2286,7 +2313,7 @@ These flags are available for both `sessions upgrade` and `sessions upgrade-all`
|
|||||||
|
|
||||||
### Configurable options
|
### Configurable options
|
||||||
|
|
||||||
Currently, HTTPie offers a single configurable option:
|
Currently, HTTPie offers a single configurable option:
|
||||||
|
|
||||||
#### `default_options`
|
#### `default_options`
|
||||||
|
|
||||||
@ -2373,16 +2400,13 @@ fi
|
|||||||
You can check whether a new update is available for your system by running `httpie cli check-updates`:
|
You can check whether a new update is available for your system by running `httpie cli check-updates`:
|
||||||
|
|
||||||
```bash-termible
|
```bash-termible
|
||||||
`httpie cli export-args` command can expose the parser specification of `http`/`https` commands
|
$ httpie cli check-updates
|
||||||
(like an API definition) to outside tools so that they can use this to build better interactions
|
|
||||||
over them (e.g., offer auto-complete).
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `httpie cli export-args`
|
#### `httpie cli export-args`
|
||||||
|
|
||||||
`httpie cli export-args` command can expose the parser specification of `http`/`https` commands
|
`httpie cli export-args` command can expose the parser specification of `http`/`https` commands
|
||||||
|
(like an API definition) to outside tools so that they can use this to build better interactions
|
||||||
over them (e.g., offer auto-complete).
|
over them (e.g., offer auto-complete).
|
||||||
|
|
||||||
Available formats to export in include:
|
Available formats to export in include:
|
||||||
@ -2398,6 +2422,14 @@ This command is currently in beta.
|
|||||||
"Program: http, Version: 0.0.1a0"
|
"Program: http, Version: 0.0.1a0"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### `httpie cli plugins`
|
||||||
|
|
||||||
|
`plugins` interface is a very simple plugin manager for installing, listing and uninstalling HTTPie plugins.
|
||||||
|
|
||||||
|
In the past `pip` was used to install/uninstall plugins, but on some environments (e.g., brew installed
|
||||||
|
packages) it wasn’t working properly. The new interface is a very simple overlay on top of `pip` to allow
|
||||||
|
plugin installations on every installation method.
|
||||||
|
|
||||||
By default, the plugins (and their missing dependencies) will be stored under the configuration directory,
|
By default, the plugins (and their missing dependencies) will be stored under the configuration directory,
|
||||||
but this can be modified through `plugins_dir` variable on the config.
|
but this can be modified through `plugins_dir` variable on the config.
|
||||||
|
|
||||||
@ -2514,7 +2546,7 @@ All changes are recorded in the [change log](#change-log).
|
|||||||
#### Alternatives
|
#### Alternatives
|
||||||
|
|
||||||
- [httpcat](https://github.com/httpie/httpcat) — a lower-level sister utility of HTTPie for constructing raw HTTP requests on the command line
|
- [httpcat](https://github.com/httpie/httpcat) — a lower-level sister utility of HTTPie for constructing raw HTTP requests on the command line
|
||||||
- [curl](https://curl.haxx.se) — a "Swiss knife" command line tool and an exceptional library for transferring data with URLs.
|
- [curl](https://curl.haxx.se) — a "Swiss knife" command line tool and an exceptional library for transferring data with URLs.
|
||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
|
|
||||||
@ -2524,7 +2556,7 @@ HTTPie has the following community channels:
|
|||||||
|
|
||||||
See [github.com/httpie/cli/security/policy](https://github.com/httpie/cli/security/policy).
|
See [github.com/httpie/cli/security/policy](https://github.com/httpie/cli/security/policy).
|
||||||
|
|
||||||
### Change log
|
### Change log
|
||||||
|
|
||||||
See [CHANGELOG](https://github.com/httpie/cli/blob/master/CHANGELOG.md).
|
See [CHANGELOG](https://github.com/httpie/cli/blob/master/CHANGELOG.md).
|
||||||
|
|
||||||
@ -2536,7 +2568,7 @@ HTTPie plays exceptionally well with the following tools:
|
|||||||
|
|
||||||
BSD-3-Clause: [LICENSE](https://github.com/httpie/cli/blob/master/LICENSE).
|
BSD-3-Clause: [LICENSE](https://github.com/httpie/cli/blob/master/LICENSE).
|
||||||
|
|
||||||
### Authors
|
### Authors
|
||||||
|
|
||||||
[Jakub Roztocil](https://roztocil.co) ([@jakubroztocil](https://twitter.com/jakubroztocil)) created HTTPie and [these fine people](https://github.com/httpie/cli/blob/master/AUTHORS.md) have contributed.
|
[Jakub Roztocil](https://roztocil.co) ([@jakubroztocil](https://twitter.com/jakubroztocil)) created HTTPie and [these fine people](https://github.com/httpie/cli/blob/master/AUTHORS.md) have contributed.
|
||||||
|
|
||||||
@ -2545,24 +2577,25 @@ Helpers to convert from other client tools:
|
|||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
|
|
||||||
See [CONTRIBUTING](https://github.com/httpie/httpie/blob/master/CONTRIBUTING.md).
|
See [CONTRIBUTING](https://github.com/httpie/cli/blob/master/CONTRIBUTING.md).
|
||||||
|
|
||||||
### Security policy
|
### Security policy
|
||||||
|
|
||||||
See [github.com/httpie/httpie/security/policy](https://github.com/httpie/httpie/security/policy).
|
See [github.com/httpie/cli/security/policy](https://github.com/httpie/cli/security/policy).
|
||||||
|
|
||||||
### Change log
|
### Change log
|
||||||
|
|
||||||
See [CHANGELOG](https://github.com/httpie/httpie/blob/master/CHANGELOG.md).
|
See [CHANGELOG](https://github.com/httpie/cli/blob/master/CHANGELOG.md).
|
||||||
|
|
||||||
### Artwork
|
### Artwork
|
||||||
|
|
||||||
- [README Animation](https://github.com/httpie/httpie/blob/master/docs/httpie-animation.gif) by [Allen Smith](https://github.com/loranallensmith).
|
- [README Animation](https://github.com/httpie/cli/blob/master/docs/httpie-animation.gif) by [Allen Smith](https://github.com/loranallensmith).
|
||||||
|
|
||||||
### Licence
|
### Licence
|
||||||
|
|
||||||
BSD-3-Clause: [LICENSE](https://github.com/httpie/httpie/blob/master/LICENSE).
|
BSD-3-Clause: [LICENSE](https://github.com/httpie/cli/blob/master/LICENSE).
|
||||||
|
|
||||||
### Authors
|
### Authors
|
||||||
|
|
||||||
[Jakub Roztocil](https://roztocil.co) ([@jakubroztocil](https://twitter.com/jakubroztocil)) created HTTPie and [these fine people](https://github.com/httpie/httpie/blob/master/AUTHORS.md) have contributed.
|
[Jakub Roztocil](https://roztocil.co) ([@jakubroztocil](https://twitter.com/jakubroztocil)) created HTTPie and [these fine people](https://github.com/httpie/cli/blob/master/AUTHORS.md) have contributed.
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
Here we maintain a database of contributors, from which we generate credits on release blog posts and social medias.
|
Here we maintain a database of contributors, from which we generate credits on release blog posts and social media.
|
||||||
|
|
||||||
For the HTTPie blog see: <https://httpie.io/blog>.
|
For the HTTPie blog see: <https://httpie.io/blog>.
|
||||||
|
@ -252,6 +252,7 @@ def fetch_missing_users_details(people: People) -> None:
|
|||||||
def save_awesome_people(people: People) -> None:
|
def save_awesome_people(people: People) -> None:
|
||||||
with DB_FILE.open(mode='w', encoding='utf-8') as fh:
|
with DB_FILE.open(mode='w', encoding='utf-8') as fh:
|
||||||
json.dump(people, fh, indent=4, sort_keys=True)
|
json.dump(people, fh, indent=4, sort_keys=True)
|
||||||
|
fh.write("\n")
|
||||||
|
|
||||||
|
|
||||||
def debug(*args: Any) -> None:
|
def debug(*args: Any) -> None:
|
||||||
|
@ -8,19 +8,27 @@ from jinja2 import Template
|
|||||||
from fetch import HERE, load_awesome_people
|
from fetch import HERE, load_awesome_people
|
||||||
|
|
||||||
TPL_FILE = HERE / 'snippet.jinja2'
|
TPL_FILE = HERE / 'snippet.jinja2'
|
||||||
|
|
||||||
HTTPIE_TEAM = {
|
HTTPIE_TEAM = {
|
||||||
'claudiatd',
|
'claudiatd',
|
||||||
'jakubroztocil',
|
'jakubroztocil',
|
||||||
'jkbr',
|
'jkbr',
|
||||||
|
'isidentical'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BOT_ACCOUNTS = {
|
||||||
|
'dependabot-sr'
|
||||||
|
}
|
||||||
|
|
||||||
|
IGNORE_ACCOUNTS = HTTPIE_TEAM | BOT_ACCOUNTS
|
||||||
|
|
||||||
|
|
||||||
def generate_snippets(release: str) -> str:
|
def generate_snippets(release: str) -> str:
|
||||||
people = load_awesome_people()
|
people = load_awesome_people()
|
||||||
contributors = {
|
contributors = {
|
||||||
name: details
|
name: details
|
||||||
for name, details in people.items()
|
for name, details in people.items()
|
||||||
if details['github'] not in HTTPIE_TEAM
|
if details['github'] not in IGNORE_ACCOUNTS
|
||||||
and (release in details['committed'] or release in details['reported'])
|
and (release in details['committed'] or release in details['reported'])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,11 +53,13 @@
|
|||||||
},
|
},
|
||||||
"Batuhan Taskaya": {
|
"Batuhan Taskaya": {
|
||||||
"committed": [
|
"committed": [
|
||||||
"3.0.0"
|
"3.0.0",
|
||||||
|
"3.2.0"
|
||||||
],
|
],
|
||||||
"github": "isidentical",
|
"github": "isidentical",
|
||||||
"reported": [
|
"reported": [
|
||||||
"3.0.0"
|
"3.0.0",
|
||||||
|
"3.2.0"
|
||||||
],
|
],
|
||||||
"twitter": "isidentical"
|
"twitter": "isidentical"
|
||||||
},
|
},
|
||||||
@ -118,6 +120,14 @@
|
|||||||
"reported": [],
|
"reported": [],
|
||||||
"twitter": "elena_lape"
|
"twitter": "elena_lape"
|
||||||
},
|
},
|
||||||
|
"Ethan Mills": {
|
||||||
|
"committed": [
|
||||||
|
"3.2.0"
|
||||||
|
],
|
||||||
|
"github": "ethanmills",
|
||||||
|
"reported": [],
|
||||||
|
"twitter": null
|
||||||
|
},
|
||||||
"Fabio Peruzzo": {
|
"Fabio Peruzzo": {
|
||||||
"committed": [],
|
"committed": [],
|
||||||
"github": "peruzzof",
|
"github": "peruzzof",
|
||||||
@ -189,7 +199,8 @@
|
|||||||
"committed": [
|
"committed": [
|
||||||
"2.5.0",
|
"2.5.0",
|
||||||
"2.6.0",
|
"2.6.0",
|
||||||
"3.0.0"
|
"3.0.0",
|
||||||
|
"3.2.0"
|
||||||
],
|
],
|
||||||
"github": "jakubroztocil",
|
"github": "jakubroztocil",
|
||||||
"reported": [
|
"reported": [
|
||||||
@ -213,7 +224,8 @@
|
|||||||
],
|
],
|
||||||
"github": "blyxxyz",
|
"github": "blyxxyz",
|
||||||
"reported": [
|
"reported": [
|
||||||
"3.0.0"
|
"3.0.0",
|
||||||
|
"3.2.0"
|
||||||
],
|
],
|
||||||
"twitter": null
|
"twitter": null
|
||||||
},
|
},
|
||||||
@ -309,7 +321,8 @@
|
|||||||
"committed": [],
|
"committed": [],
|
||||||
"github": "ducaale",
|
"github": "ducaale",
|
||||||
"reported": [
|
"reported": [
|
||||||
"2.5.0"
|
"2.5.0",
|
||||||
|
"3.2.0"
|
||||||
],
|
],
|
||||||
"twitter": null
|
"twitter": null
|
||||||
},
|
},
|
||||||
@ -321,6 +334,22 @@
|
|||||||
],
|
],
|
||||||
"twitter": "sevenc_nanashi"
|
"twitter": "sevenc_nanashi"
|
||||||
},
|
},
|
||||||
|
"Nicklas Ansman Giertz": {
|
||||||
|
"committed": [],
|
||||||
|
"github": "ansman",
|
||||||
|
"reported": [
|
||||||
|
"3.2.0"
|
||||||
|
],
|
||||||
|
"twitter": null
|
||||||
|
},
|
||||||
|
"Oliver Fish": {
|
||||||
|
"committed": [],
|
||||||
|
"github": "Oliver-Fish",
|
||||||
|
"reported": [
|
||||||
|
"3.2.0"
|
||||||
|
],
|
||||||
|
"twitter": null
|
||||||
|
},
|
||||||
"Omer Akram": {
|
"Omer Akram": {
|
||||||
"committed": [
|
"committed": [
|
||||||
"2.6.0",
|
"2.6.0",
|
||||||
@ -357,6 +386,14 @@
|
|||||||
],
|
],
|
||||||
"twitter": null
|
"twitter": null
|
||||||
},
|
},
|
||||||
|
"Roberto L\u00f3pez L\u00f3pez": {
|
||||||
|
"committed": [],
|
||||||
|
"github": "robertolopezlopez",
|
||||||
|
"reported": [
|
||||||
|
"3.2.0"
|
||||||
|
],
|
||||||
|
"twitter": null
|
||||||
|
},
|
||||||
"Russell Shurts": {
|
"Russell Shurts": {
|
||||||
"committed": [],
|
"committed": [],
|
||||||
"github": "rshurts",
|
"github": "rshurts",
|
||||||
@ -487,6 +524,14 @@
|
|||||||
],
|
],
|
||||||
"twitter": null
|
"twitter": null
|
||||||
},
|
},
|
||||||
|
"dependabot[bot]": {
|
||||||
|
"committed": [
|
||||||
|
"3.2.0"
|
||||||
|
],
|
||||||
|
"github": "dependabot-sr",
|
||||||
|
"reported": [],
|
||||||
|
"twitter": null
|
||||||
|
},
|
||||||
"dkreeft": {
|
"dkreeft": {
|
||||||
"committed": [
|
"committed": [
|
||||||
"2.6.0",
|
"2.6.0",
|
||||||
@ -553,6 +598,14 @@
|
|||||||
],
|
],
|
||||||
"twitter": null
|
"twitter": null
|
||||||
},
|
},
|
||||||
|
"luzpaz": {
|
||||||
|
"committed": [
|
||||||
|
"3.2.0"
|
||||||
|
],
|
||||||
|
"github": "luzpaz",
|
||||||
|
"reported": [],
|
||||||
|
"twitter": null
|
||||||
|
},
|
||||||
"nixbytes": {
|
"nixbytes": {
|
||||||
"committed": [
|
"committed": [
|
||||||
"2.5.0"
|
"2.5.0"
|
||||||
@ -593,6 +646,14 @@
|
|||||||
],
|
],
|
||||||
"twitter": null
|
"twitter": null
|
||||||
},
|
},
|
||||||
|
"zhaohanqing95": {
|
||||||
|
"committed": [],
|
||||||
|
"github": "zhaohanqing95",
|
||||||
|
"reported": [
|
||||||
|
"3.2.0"
|
||||||
|
],
|
||||||
|
"twitter": null
|
||||||
|
},
|
||||||
"zoulja": {
|
"zoulja": {
|
||||||
"committed": [],
|
"committed": [],
|
||||||
"github": "zoulja",
|
"github": "zoulja",
|
||||||
@ -627,4 +688,4 @@
|
|||||||
],
|
],
|
||||||
"twitter": null
|
"twitter": null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
## Community contributions
|
## Community contributions
|
||||||
|
|
||||||
We’d like to thank these amazing people for their contributions to this release: {% for name, details in contributors.items() -%}
|
We’d like to thank these amazing people for their contributions to this release:
|
||||||
[{{ name }}](https://github.com/{{ details.github }}){{ '' if loop.last else ', ' }}
|
{% for name, details in contributors.items() -%}
|
||||||
{%- endfor %}.
|
- [{{ name }}](https://github.com/{{ details.github }}){{ '' if loop.last else '\n' }}
|
||||||
|
{%- endfor %}
|
||||||
|
|
||||||
<!-- Twitter -->
|
<!-- Twitter -->
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ def build_docs_structure(database: Database):
|
|||||||
tree = database[KEY_DOC_STRUCTURE]
|
tree = database[KEY_DOC_STRUCTURE]
|
||||||
structure = []
|
structure = []
|
||||||
for platform, tools_ids in tree.items():
|
for platform, tools_ids in tree.items():
|
||||||
assert platform.isalnum(), f'{platform=} must be alpha-numeric for generated links to work'
|
assert platform.isalnum(), f'{platform=} must be alphanumeric for generated links to work'
|
||||||
platform_tools = [tools[tool_id] for tool_id in tools_ids]
|
platform_tools = [tools[tool_id] for tool_id in tools_ids]
|
||||||
structure.append((platform, platform_tools))
|
structure.append((platform, platform_tools))
|
||||||
return structure
|
return structure
|
||||||
|
@ -17,11 +17,12 @@ docs-structure:
|
|||||||
Windows:
|
Windows:
|
||||||
- chocolatey
|
- chocolatey
|
||||||
Linux:
|
Linux:
|
||||||
- snap-linux
|
|
||||||
- brew-linux
|
|
||||||
- apt
|
- apt
|
||||||
- dnf
|
- dnf
|
||||||
- yum
|
- yum
|
||||||
|
- single-binary
|
||||||
|
- snap-linux
|
||||||
|
- brew-linux
|
||||||
- pacman
|
- pacman
|
||||||
FreeBSD:
|
FreeBSD:
|
||||||
- pkg
|
- pkg
|
||||||
@ -36,11 +37,13 @@ tools:
|
|||||||
package: https://packages.debian.org/sid/web/httpie
|
package: https://packages.debian.org/sid/web/httpie
|
||||||
commands:
|
commands:
|
||||||
install:
|
install:
|
||||||
- apt update
|
- curl -SsL https://packages.httpie.io/deb/KEY.gpg | sudo gpg --dearmor -o /usr/share/keyrings/httpie.gpg
|
||||||
- apt install httpie
|
# - curl -SsL -o /etc/apt/sources.list.d/httpie.list https://packages.httpie.io/deb/httpie.list
|
||||||
|
- echo "deb [arch=amd64 signed-by=/usr/share/keyrings/httpie.gpg] https://packages.httpie.io/deb ./" | sudo tee /etc/apt/sources.list.d/httpie.list > /dev/null
|
||||||
|
- sudo apt update
|
||||||
|
- sudo apt install httpie
|
||||||
upgrade:
|
upgrade:
|
||||||
- apt update
|
- sudo apt update && sudo apt upgrade httpie
|
||||||
- apt upgrade httpie
|
|
||||||
|
|
||||||
brew-mac:
|
brew-mac:
|
||||||
title: Homebrew
|
title: Homebrew
|
||||||
@ -179,3 +182,16 @@ tools:
|
|||||||
- yum install httpie
|
- yum install httpie
|
||||||
upgrade:
|
upgrade:
|
||||||
- yum upgrade httpie
|
- yum upgrade httpie
|
||||||
|
|
||||||
|
single-binary:
|
||||||
|
title: Single binary executables
|
||||||
|
name: Single binary executables
|
||||||
|
note: Get the standalone HTTPie Linux executables when you don't want to go through the full installation process.
|
||||||
|
links:
|
||||||
|
commands:
|
||||||
|
install:
|
||||||
|
- https --download packages.httpie.io/binaries/linux/http-latest -o http
|
||||||
|
- ln -ls ./http ./https
|
||||||
|
- chmod +x ./http ./https
|
||||||
|
upgrade:
|
||||||
|
- https --download packages.httpie.io/binaries/linux/http-latest -o http
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
Welcome on the documentation part of the **HTTPie release process**.
|
Welcome on the documentation part of the **HTTPie release process**.
|
||||||
|
|
||||||
- If you do not know HTTPie, have a look [here](https://httpie.io/cli).
|
- If you do not know HTTPie, have a look [here](https://httpie.io/cli).
|
||||||
- If you are looking for HTTPie installation or upgrade instructions, then you can find all you need for your OS on [that page](https://httpie.io/docs#installation). In the case you do not find your OS, [let us know](https://github.com/httpie/httpie/issues/).
|
- If you are looking for HTTPie installation or upgrade instructions, then you can find all you need for your OS on [that page](https://httpie.io/docs#installation). In the case you do not find your OS, [let us know](https://github.com/httpie/cli/issues/).
|
||||||
- If you are looking for technical information about the HTTPie packaging, then you are at the good place.
|
- If you are looking for technical information about the HTTPie packaging, then you are at the good place.
|
||||||
|
|
||||||
## About
|
## About
|
||||||
@ -25,7 +25,7 @@ The overall release process starts simple:
|
|||||||
|
|
||||||
## Company-specific tasks
|
## Company-specific tasks
|
||||||
|
|
||||||
- Blank the `master_and_released_docs_differ_after` value in [config.json](https://github.com/httpie/httpie/blob/master/docs/config.json).
|
- Blank the `master_and_released_docs_differ_after` value in [config.json](https://github.com/httpie/cli/blob/master/docs/config.json).
|
||||||
- Update the [contributors list](../contributors).
|
- Update the [contributors list](../contributors).
|
||||||
- Update the HTTPie version bundled into [Termible](https://termible.io/) ([example](https://github.com/httpie/termible/pull/1)).
|
- Update the HTTPie version bundled into [Termible](https://termible.io/) ([example](https://github.com/httpie/termible/pull/1)).
|
||||||
|
|
||||||
@ -44,4 +44,4 @@ A more complete state of deployment can be found on [repology](https://repology.
|
|||||||
| [Snapcraft](snapcraft/) | **HTTPie** |
|
| [Snapcraft](snapcraft/) | **HTTPie** |
|
||||||
| [Windows — Chocolatey](windows-chocolatey/) | **HTTPie** |
|
| [Windows — Chocolatey](windows-chocolatey/) | **HTTPie** |
|
||||||
|
|
||||||
:new: You do not find your system or you would like to see HTTPie supported on another OS? Then [let us know](https://github.com/httpie/httpie/issues/).
|
:new: You do not find your system or you would like to see HTTPie supported on another OS? Then [let us know](https://github.com/httpie/cli/issues/).
|
||||||
|
@ -13,7 +13,7 @@ We will discuss setting up the environment, installing development tools, instal
|
|||||||
|
|
||||||
## Overall process
|
## Overall process
|
||||||
|
|
||||||
The brew deployment is completely automated, and only requires a trigger to [`Release on Homebrew`](https://github.com/httpie/httpie/actions/workflows/release-brew.yml) action
|
The brew deployment is completely automated, and only requires a trigger to [`Release on Homebrew`](https://github.com/httpie/cli/actions/workflows/release-brew.yml) action
|
||||||
from the release manager.
|
from the release manager.
|
||||||
|
|
||||||
If it is needed to be done manually, the following command can be used:
|
If it is needed to be done manually, the following command can be used:
|
||||||
@ -22,10 +22,10 @@ If it is needed to be done manually, the following command can be used:
|
|||||||
$ brew bump-formula-pr httpie --version={TARGET_VERSION}
|
$ brew bump-formula-pr httpie --version={TARGET_VERSION}
|
||||||
```
|
```
|
||||||
|
|
||||||
which will bump the formala, and create a PR against the package index.
|
which will bump the formula, and create a PR against the package index.
|
||||||
|
|
||||||
## Hacking
|
## Hacking
|
||||||
|
|
||||||
Make your changes, test the formula through the [`Test Brew Package`](https://github.com/httpie/httpie/actions/workflows/test-package-mac-brew.yml) action
|
Make your changes, test the formula through the [`Test Brew Package`](https://github.com/httpie/cli/actions/workflows/test-package-mac-brew.yml) action
|
||||||
and then finally submit your patch to [`homebrew-core`](https://github.com/Homebrew/homebrew-core`)
|
and then finally submit your patch to [`homebrew-core`](https://github.com/Homebrew/homebrew-core`)
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ class Httpie < Formula
|
|||||||
url "https://files.pythonhosted.org/packages/32/85/bb095699be20cc98731261cb80884e9458178f8fef2a38273530ce77c0a5/httpie-3.1.0.tar.gz"
|
url "https://files.pythonhosted.org/packages/32/85/bb095699be20cc98731261cb80884e9458178f8fef2a38273530ce77c0a5/httpie-3.1.0.tar.gz"
|
||||||
sha256 "2e4a2040b84a912e65c01fb34f7aafe88cad2a3af2da8c685ca65080f376feda"
|
sha256 "2e4a2040b84a912e65c01fb34f7aafe88cad2a3af2da8c685ca65080f376feda"
|
||||||
license "BSD-3-Clause"
|
license "BSD-3-Clause"
|
||||||
head "https://github.com/httpie/httpie.git", branch: "master"
|
head "https://github.com/httpie/cli.git", branch: "master"
|
||||||
|
|
||||||
bottle do
|
bottle do
|
||||||
sha256 cellar: :any_skip_relocation, arm64_monterey: "9bb6e8c1ef5ba8b019ddedd7e908dd2174da695351aa9a238dfb28b0f57ef005"
|
sha256 cellar: :any_skip_relocation, arm64_monterey: "9bb6e8c1ef5ba8b019ddedd7e908dd2174da695351aa9a238dfb28b0f57ef005"
|
||||||
|
@ -7,7 +7,7 @@ pkgname=httpie
|
|||||||
pkgver=2.6.0
|
pkgver=2.6.0
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="human-friendly CLI HTTP client for the API era"
|
pkgdesc="human-friendly CLI HTTP client for the API era"
|
||||||
url="https://github.com/httpie/httpie"
|
url="https://github.com/httpie/cli"
|
||||||
depends=('python-defusedxml'
|
depends=('python-defusedxml'
|
||||||
'python-pygments'
|
'python-pygments'
|
||||||
'python-pysocks'
|
'python-pysocks'
|
||||||
@ -22,7 +22,7 @@ conflicts=(python-httpie)
|
|||||||
replaces=(python-httpie python2-httpie)
|
replaces=(python-httpie python2-httpie)
|
||||||
license=('BSD')
|
license=('BSD')
|
||||||
arch=('any')
|
arch=('any')
|
||||||
source=($pkgname-$pkgver.tar.gz::"https://github.com/httpie/httpie/archive/$pkgver.tar.gz")
|
source=($pkgname-$pkgver.tar.gz::"https://github.com/httpie/cli/archive/$pkgver.tar.gz")
|
||||||
sha256sums=('3bcd9a8cb2b11299da12d3af36c095c6d4b665e41c395898a07f1ae4d99fc14a')
|
sha256sums=('3bcd9a8cb2b11299da12d3af36c095c6d4b665e41c395898a07f1ae4d99fc14a')
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
|
@ -17,7 +17,7 @@ command (due to the underlying `httpie cli plugins` interface) explicitly depend
|
|||||||
|
|
||||||
## Overall process
|
## Overall process
|
||||||
|
|
||||||
The [`Release as Standalone Linux Binary`](https://github.com/httpie/httpie/actions/workflows/release-linux-standalone.yml) will be automatically
|
The [`Release as Standalone Linux Binary`](https://github.com/httpie/cli/actions/workflows/release-linux-standalone.yml) will be automatically
|
||||||
triggered when a new release is created, and it will submit the `.deb` package as a release asset.
|
triggered when a new release is created, and it will submit the `.deb` package as a release asset.
|
||||||
|
|
||||||
For making that asset available for all debian users, the release manager needs to go to the [`httpie/debian.httpie.io`](https://github.com/httpie/debian.httpie.io) repo
|
For making that asset available for all debian users, the release manager needs to go to the [`httpie/debian.httpie.io`](https://github.com/httpie/debian.httpie.io) repo
|
||||||
|
@ -15,7 +15,7 @@ The current maintainer is [Miro Hrončok](https://github.com/hroncok).
|
|||||||
|
|
||||||
## Overall process
|
## Overall process
|
||||||
|
|
||||||
We added the [.packit.yaml](https://github.com/httpie/httpie/blob/master/.packit.yaml) local file.
|
We added the [.packit.yaml](https://github.com/httpie/cli/blob/master/.packit.yaml) local file.
|
||||||
It unlocks real-time Fedora checks on pull requests and new releases.
|
It unlocks real-time Fedora checks on pull requests and new releases.
|
||||||
|
|
||||||
So there is nothing to do on our side: `Packit` will see the new release and open a pull request [there](https://src.fedoraproject.org/rpms/httpie). Then, the Fedora maintainer will review and merge.
|
So there is nothing to do on our side: `Packit` will see the new release and open a pull request [there](https://src.fedoraproject.org/rpms/httpie). Then, the Fedora maintainer will review and merge.
|
||||||
|
@ -5,7 +5,7 @@ Summary: A Curl-like tool for humans
|
|||||||
|
|
||||||
License: BSD
|
License: BSD
|
||||||
URL: https://httpie.org/
|
URL: https://httpie.org/
|
||||||
Source0: https://github.com/httpie/httpie/archive/%{version}/%{name}-%{version}.tar.gz
|
Source0: https://github.com/httpie/cli/archive/%{version}/%{name}-%{version}.tar.gz
|
||||||
|
|
||||||
BuildArch: noarch
|
BuildArch: noarch
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ Open a pull request to update the [downstream file](https://github.com/macports/
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Download the archive
|
# Download the archive
|
||||||
$ wget https://api.github.com/repos/httpie/httpie/tarball/2.5.0
|
$ wget https://api.github.com/repos/httpie/cli/tarball/2.5.0
|
||||||
|
|
||||||
# Size
|
# Size
|
||||||
$ stat --printf="%s\n" 2.5.0
|
$ stat --printf="%s\n" 2.5.0
|
||||||
|
@ -13,7 +13,7 @@ We will discuss setting up the environment, installing development tools, instal
|
|||||||
|
|
||||||
## Overall process
|
## Overall process
|
||||||
|
|
||||||
Trigger the [`Release on Snap`](https://github.com/httpie/httpie/actions/workflows/release-snap.yml) action, which will
|
Trigger the [`Release on Snap`](https://github.com/httpie/cli/actions/workflows/release-snap.yml) action, which will
|
||||||
create a snap package for HTTPie and then push it to Snap Store in the following channels:
|
create a snap package for HTTPie and then push it to Snap Store in the following channels:
|
||||||
|
|
||||||
- Edge
|
- Edge
|
||||||
@ -37,7 +37,7 @@ From inside the container:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone
|
# Clone
|
||||||
git clone --depth=1 https://github.com/httpie/httpie.git
|
git clone --depth=1 https://github.com/httpie/cli.git
|
||||||
cd httpie
|
cd httpie
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
|
@ -14,7 +14,7 @@ We will discuss setting up the environment, installing development tools, instal
|
|||||||
## Overall process
|
## Overall process
|
||||||
|
|
||||||
After having successfully [built and tested](#hacking) the package, either trigger the
|
After having successfully [built and tested](#hacking) the package, either trigger the
|
||||||
[`Release on Chocolatey`](https://github.com/httpie/httpie/actions/workflows/release-choco.yml) action
|
[`Release on Chocolatey`](https://github.com/httpie/cli/actions/workflows/release-choco.yml) action
|
||||||
to push it to the `Chocolatey` store or use the CLI:
|
to push it to the `Chocolatey` store or use the CLI:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -29,7 +29,7 @@ sets of reviews (some of them are done manually).
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone
|
# Clone
|
||||||
git clone --depth=1 https://github.com/httpie/httpie.git
|
git clone --depth=1 https://github.com/httpie/cli.git
|
||||||
cd httpie/docs/packaging/windows-chocolatey
|
cd httpie/docs/packaging/windows-chocolatey
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
|
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
|
||||||
<metadata>
|
<metadata>
|
||||||
<id>httpie</id>
|
<id>httpie</id>
|
||||||
<version>3.1.0</version>
|
<version>3.2.2</version>
|
||||||
<summary>Modern, user-friendly command-line HTTP client for the API era</summary>
|
<summary>Modern, user-friendly command-line HTTP client for the API era</summary>
|
||||||
<description>
|
<description>
|
||||||
HTTPie *aitch-tee-tee-pie* is a user-friendly command-line HTTP client for the API era.
|
HTTPie *aitch-tee-tee-pie* is a user-friendly command-line HTTP client for the API era.
|
||||||
@ -30,16 +30,16 @@ Main features:
|
|||||||
<authors>HTTPie</authors>
|
<authors>HTTPie</authors>
|
||||||
<owners>jakubroztocil</owners>
|
<owners>jakubroztocil</owners>
|
||||||
<copyright>2012-2022 Jakub Roztocil</copyright>
|
<copyright>2012-2022 Jakub Roztocil</copyright>
|
||||||
<licenseUrl>https://raw.githubusercontent.com/httpie/httpie/master/LICENSE</licenseUrl>
|
<licenseUrl>https://raw.githubusercontent.com/httpie/cli/master/LICENSE</licenseUrl>
|
||||||
<iconUrl>https://pie-assets.s3.eu-central-1.amazonaws.com/LogoIcons/GB.png</iconUrl>
|
<iconUrl>https://pie-assets.s3.eu-central-1.amazonaws.com/LogoIcons/GB.png</iconUrl>
|
||||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||||
<releaseNotes>See the [changelog](https://github.com/httpie/httpie/releases/tag/3.1.0).</releaseNotes>
|
<releaseNotes>See the [changelog](https://github.com/httpie/cli/releases/tag/3.2.2).</releaseNotes>
|
||||||
<tags>httpie http https rest api client curl python ssl cli foss oss url</tags>
|
<tags>httpie http https rest api client curl python ssl cli foss oss url</tags>
|
||||||
<projectUrl>https://httpie.io</projectUrl>
|
<projectUrl>https://httpie.io</projectUrl>
|
||||||
<packageSourceUrl>https://github.com/httpie/httpie/tree/master/docs/packaging/windows-chocolatey</packageSourceUrl>
|
<packageSourceUrl>https://github.com/httpie/cli/tree/master/docs/packaging/windows-chocolatey</packageSourceUrl>
|
||||||
<projectSourceUrl>https://github.com/httpie/httpie</projectSourceUrl>
|
<projectSourceUrl>https://github.com/httpie/cli</projectSourceUrl>
|
||||||
<docsUrl>https://httpie.io/docs</docsUrl>
|
<docsUrl>https://httpie.io/docs</docsUrl>
|
||||||
<bugTrackerUrl>https://github.com/httpie/httpie/issues</bugTrackerUrl>
|
<bugTrackerUrl>https://github.com/httpie/cli/issues</bugTrackerUrl>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency id="python3" version="3.7" />
|
<dependency id="python3" version="3.7" />
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
@ -1,52 +1,24 @@
|
|||||||
function __fish_httpie_styles
|
function __fish_httpie_styles
|
||||||
echo "
|
printf '%s\n' abap algol algol_nu arduino auto autumn borland bw colorful default emacs friendly fruity gruvbox-dark gruvbox-light igor inkpot lovelace manni material monokai murphy native paraiso-dark paraiso-light pastie perldoc pie pie-dark pie-light rainbow_dash rrt sas solarized solarized-dark solarized-light stata stata-dark stata-light tango trac vim vs xcode zenburn
|
||||||
abap
|
end
|
||||||
algol
|
|
||||||
algol_nu
|
function __fish_httpie_mime_types
|
||||||
arduino
|
test -r /usr/share/mime/types && cat /usr/share/mime/types
|
||||||
auto
|
end
|
||||||
autumn
|
|
||||||
borland
|
function __fish_httpie_print_args
|
||||||
bw
|
set -l arg (commandline -t)
|
||||||
colorful
|
string match -qe H "$arg" || echo -e $arg"H\trequest headers"
|
||||||
default
|
string match -qe B "$arg" || echo -e $arg"B\trequest body"
|
||||||
emacs
|
string match -qe h "$arg" || echo -e $arg"h\tresponse headers"
|
||||||
friendly
|
string match -qe b "$arg" || echo -e $arg"b\tresponse body"
|
||||||
fruity
|
string match -qe m "$arg" || echo -e $arg"m\tresponse metadata"
|
||||||
gruvbox-dark
|
|
||||||
gruvbox-light
|
|
||||||
igor
|
|
||||||
inkpot
|
|
||||||
lovelace
|
|
||||||
manni
|
|
||||||
material
|
|
||||||
monokai
|
|
||||||
murphy
|
|
||||||
native
|
|
||||||
paraiso-dark
|
|
||||||
paraiso-light
|
|
||||||
pastie
|
|
||||||
perldoc
|
|
||||||
rainbow_dash
|
|
||||||
rrt
|
|
||||||
sas
|
|
||||||
solarized
|
|
||||||
solarized-dark
|
|
||||||
solarized-light
|
|
||||||
stata
|
|
||||||
stata-dark
|
|
||||||
stata-light
|
|
||||||
tango
|
|
||||||
trac
|
|
||||||
vim
|
|
||||||
vs
|
|
||||||
xcode
|
|
||||||
zenburn"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function __fish_httpie_auth_types
|
function __fish_httpie_auth_types
|
||||||
echo -e "basic\tBasic HTTP auth"
|
echo -e "basic\tBasic HTTP auth"
|
||||||
echo -e "digest\tDigest HTTP auth"
|
echo -e "digest\tDigest HTTP auth"
|
||||||
|
echo -e "bearer\tBearer HTTP Auth"
|
||||||
end
|
end
|
||||||
|
|
||||||
function __fish_http_verify_options
|
function __fish_http_verify_options
|
||||||
@ -54,6 +26,7 @@ function __fish_http_verify_options
|
|||||||
echo -e "no\tDisable cert verification"
|
echo -e "no\tDisable cert verification"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# Predefined Content Types
|
# Predefined Content Types
|
||||||
|
|
||||||
complete -c http -s j -l json -d 'Data items are serialized as a JSON object'
|
complete -c http -s j -l json -d 'Data items are serialized as a JSON object'
|
||||||
@ -70,26 +43,28 @@ complete -c http -s x -l compress -d 'Content compressed with Deflate algorithm'
|
|||||||
|
|
||||||
# Output Processing
|
# Output Processing
|
||||||
|
|
||||||
complete -c http -l pretty -xa "all colors format none" -d 'Controls output processing'
|
complete -c http -l pretty -xa "all colors format none" -d 'Controls output processing'
|
||||||
complete -c http -s s -l style -xa "(__fish_httpie_styles)" -d 'Output coloring style'
|
complete -c http -s s -l style -xa "(__fish_httpie_styles)" -d 'Output coloring style'
|
||||||
complete -c http -l unsorted -d 'Disables all sorting while formatting output'
|
complete -c http -l unsorted -d 'Disables all sorting while formatting output'
|
||||||
complete -c http -l sorted -d 'Re-enables all sorting options while formatting output'
|
complete -c http -l sorted -d 'Re-enables all sorting options while formatting output'
|
||||||
complete -c http -l format-options -x -d 'Controls output formatting'
|
complete -c http -l response-charset -x -d 'Override the response encoding'
|
||||||
|
complete -c http -l response-mime -xa "(__fish_httpie_mime_types)" -d 'Override the response mime type for coloring and formatting'
|
||||||
|
complete -c http -l format-options -x -d 'Controls output formatting'
|
||||||
|
|
||||||
|
|
||||||
# Output Options
|
# Output Options
|
||||||
|
|
||||||
complete -c http -s p -l print -x -d 'String specifying what the output should contain'
|
complete -c http -s p -l print -xa "(__fish_httpie_print_args)" -d 'String specifying what the output should contain'
|
||||||
complete -c http -s h -l headers -d 'Print only the response headers'
|
complete -c http -s h -l headers -d 'Print only the response headers'
|
||||||
complete -c http -s b -l body -d 'Print only the response body'
|
complete -c http -s m -l meta -d 'Print only the response metadata'
|
||||||
complete -c http -s v -l verbose -d 'Print the whole request as well as the response'
|
complete -c http -s b -l body -d 'Print only the response body'
|
||||||
complete -c http -l all -d 'Show any intermediary requests/responses'
|
complete -c http -s v -l verbose -d 'Print the whole request as well as the response'
|
||||||
complete -c http -s P -l history-print -x -d 'The same as --print but applies only to intermediary requests/responses'
|
complete -c http -l all -d 'Show any intermediary requests/responses'
|
||||||
complete -c http -s S -l stream -d 'Always stream the response body by line'
|
complete -c http -s S -l stream -d 'Always stream the response body by line'
|
||||||
complete -c http -s o -l output -F -d 'Save output to FILE'
|
complete -c http -s o -l output -F -d 'Save output to FILE'
|
||||||
complete -c http -s d -l download -d 'Download a file'
|
complete -c http -s d -l download -d 'Download a file'
|
||||||
complete -c http -s c -l continue -d 'Resume an interrupted download'
|
complete -c http -s c -l continue -d 'Resume an interrupted download'
|
||||||
complete -c http -s q -l quiet -d 'Do not print to stdout or stderr'
|
complete -c http -s q -l quiet -d 'Do not print to stdout or stderr'
|
||||||
|
|
||||||
|
|
||||||
# Sessions
|
# Sessions
|
||||||
@ -115,23 +90,32 @@ complete -c http -l max-headers -x -d 'Maximum number of response headers
|
|||||||
complete -c http -l timeout -x -d 'Connection timeout in seconds'
|
complete -c http -l timeout -x -d 'Connection timeout in seconds'
|
||||||
complete -c http -l check-status -d 'Error with non-200 HTTP status code'
|
complete -c http -l check-status -d 'Error with non-200 HTTP status code'
|
||||||
complete -c http -l path-as-is -d 'Bypass dot segment URL squashing'
|
complete -c http -l path-as-is -d 'Bypass dot segment URL squashing'
|
||||||
complete -c http -l chunked -d ''
|
complete -c http -l chunked -d 'Enable streaming via chunked transfer encoding'
|
||||||
|
|
||||||
|
|
||||||
# SSL
|
# SSL
|
||||||
|
|
||||||
complete -c http -l verify -xa "(__fish_http_verify_options)" -d 'Enable/disable cert verification'
|
complete -c http -l verify -xa "(__fish_http_verify_options)" -d 'Enable/disable cert verification'
|
||||||
complete -c http -l ssl -x -d 'Desired protocol version to use'
|
complete -c http -l ssl -x -d 'Desired protocol version to use'
|
||||||
complete -c http -l ciphers -x -d 'String in the OpenSSL cipher list format'
|
complete -c http -l ciphers -x -d 'String in the OpenSSL cipher list format'
|
||||||
complete -c http -l cert -F -d 'Client side SSL certificate'
|
complete -c http -l cert -F -d 'Client side SSL certificate'
|
||||||
complete -c http -l cert-key -F -d 'Private key to use with SSL'
|
complete -c http -l cert-key -F -d 'Private key to use with SSL'
|
||||||
|
complete -c http -l cert-key-pass -x -d 'Passphrase for the given private key'
|
||||||
|
|
||||||
|
|
||||||
# Troubleshooting
|
# Troubleshooting
|
||||||
|
|
||||||
complete -c http -s I -l ignore-stdin -d 'Do not attempt to read stdin'
|
complete -c http -s I -l ignore-stdin -d 'Do not attempt to read stdin'
|
||||||
complete -c http -l help -d 'Show help'
|
complete -c http -l help -d 'Show help'
|
||||||
|
complete -c http -l manual -d 'Show the full manual'
|
||||||
complete -c http -l version -d 'Show version'
|
complete -c http -l version -d 'Show version'
|
||||||
complete -c http -l traceback -d 'Prints exception traceback should one occur'
|
complete -c http -l traceback -d 'Prints exception traceback should one occur'
|
||||||
complete -c http -l default-scheme -x -d 'The default scheme to use'
|
complete -c http -l default-scheme -x -d 'The default scheme to use'
|
||||||
complete -c http -l debug -d 'Show debugging output'
|
complete -c http -l debug -d 'Show debugging output'
|
||||||
|
|
||||||
|
|
||||||
|
# Alias for https to http
|
||||||
|
|
||||||
|
function https --wraps http
|
||||||
|
http $argv;
|
||||||
|
end
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
.TH http 1 "2022-03-08" "HTTPie 3.1.1.dev0" "HTTPie Manual"
|
.\" This file is auto-generated from the parser declaration in httpie/cli/definition.py by extras/scripts/generate_man_pages.py.
|
||||||
|
.TH http 1 "2024-07-10" "HTTPie 3.2.4" "HTTPie Manual"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
http
|
http
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@ -6,7 +7,7 @@ http [METHOD] URL [REQUEST_ITEM ...]
|
|||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
HTTPie: modern, user-friendly command-line HTTP client for the API era. <https://httpie.io>
|
HTTPie: modern, user-friendly command-line HTTP client for the API era. <https://httpie.io>
|
||||||
.SH Positional Arguments
|
.SH Positional arguments
|
||||||
|
|
||||||
These arguments come after any flags and in the order they are listed here.
|
These arguments come after any flags and in the order they are listed here.
|
||||||
Only URL is required.
|
Only URL is required.
|
||||||
@ -27,8 +28,8 @@ is some data to be sent, otherwise GET:
|
|||||||
.IP "\fB\,URL\/\fR"
|
.IP "\fB\,URL\/\fR"
|
||||||
|
|
||||||
|
|
||||||
The request URL. Scheme defaults to \'http://\' if the URL
|
The request URL. Scheme defaults to \[aq]http://\[aq] if the URL
|
||||||
does not include one. (You can override this with:\fB\,--default-scheme\/\fR=http/https)
|
does not include one. (You can override this with: \fB\,--default-scheme\/\fR=http/https)
|
||||||
|
|
||||||
You can also use a shorthand for localhost
|
You can also use a shorthand for localhost
|
||||||
|
|
||||||
@ -43,44 +44,44 @@ You can also use a shorthand for localhost
|
|||||||
Optional key-value pairs to be included in the request. The separator used
|
Optional key-value pairs to be included in the request. The separator used
|
||||||
determines the type:
|
determines the type:
|
||||||
|
|
||||||
\':\' HTTP headers:
|
\[aq]:\[aq] HTTP headers:
|
||||||
|
|
||||||
Referer:https://httpie.io Cookie:foo=bar User-Agent:bacon/1.0
|
Referer:https://httpie.io Cookie:foo=bar User-Agent:bacon/1.0
|
||||||
|
|
||||||
\'==\' URL parameters to be appended to the request URI:
|
\[aq]==\[aq] URL parameters to be appended to the request URI:
|
||||||
|
|
||||||
search==httpie
|
search==httpie
|
||||||
|
|
||||||
\'=\' Data fields to be serialized into a JSON object (with\fB\,--json\/\fR,\fB\,-j\/\fR)
|
\[aq]=\[aq] Data fields to be serialized into a JSON object (with \fB\,--json\/\fR, \fB\,-j\/\fR)
|
||||||
or form data (with\fB\,--form\/\fR,\fB\,-f\/\fR):
|
or form data (with \fB\,--form\/\fR, \fB\,-f\/\fR):
|
||||||
|
|
||||||
name=HTTPie language=Python description=\'CLI HTTP client\'
|
name=HTTPie language=Python description=\[aq]CLI HTTP client\[aq]
|
||||||
|
|
||||||
\':=\' Non-string JSON data fields (only with\fB\,--json\/\fR,\fB\,-j\/\fR):
|
\[aq]:=\[aq] Non-string JSON data fields (only with \fB\,--json\/\fR, \fB\,-j\/\fR):
|
||||||
|
|
||||||
awesome:=true amount:=42 colors:=\'["red", "green", "blue"]\'
|
awesome:=true amount:=42 colors:=\[aq][\[dq]red\[dq], \[dq]green\[dq], \[dq]blue\[dq]]\[aq]
|
||||||
|
|
||||||
\'@\' Form file fields (only with\fB\,--form\/\fR or\fB\,--multipart\/\fR):
|
\[aq]@\[aq] Form file fields (only with \fB\,--form\/\fR or \fB\,--multipart\/\fR):
|
||||||
|
|
||||||
cv@\~/Documents/CV.pdf
|
cv@\(ti/Documents/CV.pdf
|
||||||
cv@\'\~/Documents/CV.pdf;type=application/pdf\'
|
cv@\[aq]\(ti/Documents/CV.pdf;type=application/pdf\[aq]
|
||||||
|
|
||||||
\'=@\' A data field like \'=\', but takes a file path and embeds its content:
|
\[aq]=@\[aq] A data field like \[aq]=\[aq], but takes a file path and embeds its content:
|
||||||
|
|
||||||
essay=@Documents/essay.txt
|
essay=@Documents/essay.txt
|
||||||
|
|
||||||
\':=@\' A raw JSON field like \':=\', but takes a file path and embeds its content:
|
\[aq]:=@\[aq] A raw JSON field like \[aq]:=\[aq], but takes a file path and embeds its content:
|
||||||
|
|
||||||
package:=@./package.json
|
package:=@./package.json
|
||||||
|
|
||||||
You can use a backslash to escape a colliding separator in the field name:
|
You can use a backslash to escape a colliding separator in the field name:
|
||||||
|
|
||||||
field-name-with\\:colon=value
|
field-name-with\e:colon=value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
.SH Predefined Content Types
|
.SH Predefined content types
|
||||||
.IP "\fB\,--json\/\fR, \fB\,-j\/\fR"
|
.IP "\fB\,--json\/\fR, \fB\,-j\/\fR"
|
||||||
|
|
||||||
|
|
||||||
@ -104,13 +105,13 @@ multipart/form-data request.
|
|||||||
.IP "\fB\,--multipart\/\fR"
|
.IP "\fB\,--multipart\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Similar to\fB\,--form\/\fR, but always sends a multipart/form-data request (i.e., even without files).
|
Similar to \fB\,--form\/\fR, but always sends a multipart/form-data request (i.e., even without files).
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--boundary\/\fR"
|
.IP "\fB\,--boundary\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Specify a custom boundary string for multipart/form-data requests. Only has effect only together with\fB\,--form\/\fR.
|
Specify a custom boundary string for multipart/form-data requests. Only has effect only together with \fB\,--form\/\fR.
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--raw\/\fR"
|
.IP "\fB\,--raw\/\fR"
|
||||||
@ -119,7 +120,7 @@ Specify a custom boundary string for multipart/form-data requests. Only has effe
|
|||||||
This option allows you to pass raw request data without extra processing
|
This option allows you to pass raw request data without extra processing
|
||||||
(as opposed to the structured request items syntax):
|
(as opposed to the structured request items syntax):
|
||||||
|
|
||||||
$ http\fB\,--raw\/\fR=\'data\' pie.dev/post
|
$ http \fB\,--raw\/\fR=\[aq]data\[aq] pie.dev/post
|
||||||
|
|
||||||
You can achieve the same by piping the data via stdin:
|
You can achieve the same by piping the data via stdin:
|
||||||
|
|
||||||
@ -133,7 +134,7 @@ Or have HTTPie load the raw data from a file:
|
|||||||
|
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
.SH Content Processing Options
|
.SH Content processing options
|
||||||
.IP "\fB\,--compress\/\fR, \fB\,-x\/\fR"
|
.IP "\fB\,--compress\/\fR, \fB\,-x\/\fR"
|
||||||
|
|
||||||
|
|
||||||
@ -146,39 +147,39 @@ negative. Compression can be forced by repeating the argument.
|
|||||||
|
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
.SH Output Processing
|
.SH Output processing
|
||||||
.IP "\fB\,--pretty\/\fR"
|
.IP "\fB\,--pretty\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Controls output processing. The value can be "none" to not prettify
|
Controls output processing. The value can be \[dq]none\[dq] to not prettify
|
||||||
the output (default for redirected output), "all" to apply both colors
|
the output (default for redirected output), \[dq]all\[dq] to apply both colors
|
||||||
and formatting (default for terminal output), "colors", or "format".
|
and formatting (default for terminal output), \[dq]colors\[dq], or \[dq]format\[dq].
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--style\/\fR, \fB\,-s\/\fR \fI\,STYLE\/\fR"
|
.IP "\fB\,--style\/\fR, \fB\,-s\/\fR \fI\,STYLE\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Output coloring style (default is "auto"). It can be one of:
|
Output coloring style (default is \[dq]auto\[dq]). It can be one of:
|
||||||
|
|
||||||
auto, pie, pie-dark, pie-light, solarized
|
auto, pie, pie-dark, pie-light, solarized
|
||||||
|
|
||||||
|
|
||||||
For finding out all available styles in your system, try:
|
For finding out all available styles in your system, try:
|
||||||
|
|
||||||
$ http\fB\,--style\/\fR
|
$ http \fB\,--style\/\fR
|
||||||
|
|
||||||
The "auto" style follows your terminal\'s ANSI color styles.
|
The \[dq]auto\[dq] style follows your terminal\[aq]s ANSI color styles.
|
||||||
For non-auto styles to work properly, please make sure that the
|
For non-auto styles to work properly, please make sure that the
|
||||||
$TERM environment variable is set to "xterm-256color" or similar
|
$TERM environment variable is set to \[dq]xterm-256color\[dq] or similar
|
||||||
(e.g., via `export TERM=xterm-256color\' in your \~/.bashrc).
|
(e.g., via `export TERM=xterm-256color\[aq] in your \(ti/.bashrc).
|
||||||
|
|
||||||
.IP "\fB\,--unsorted\/\fR"
|
.IP "\fB\,--unsorted\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Disables all sorting while formatting output. It is a shortcut for:
|
Disables all sorting while formatting output. It is a shortcut for:
|
||||||
|
|
||||||
\fB\,--format-options\/\fR=headers.sort:false,json.sort_keys:false
|
\fB\,--format-options\/\fR=headers.sort:false,json.sort_keys:false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -187,7 +188,7 @@ Disables all sorting while formatting output. It is a shortcut for:
|
|||||||
|
|
||||||
Re-enables all sorting options while formatting output. It is a shortcut for:
|
Re-enables all sorting options while formatting output. It is a shortcut for:
|
||||||
|
|
||||||
\fB\,--format-options\/\fR=headers.sort:true,json.sort_keys:true
|
\fB\,--format-options\/\fR=headers.sort:true,json.sort_keys:true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -196,8 +197,8 @@ Re-enables all sorting options while formatting output. It is a shortcut for:
|
|||||||
|
|
||||||
Override the response encoding for terminal display purposes, e.g.:
|
Override the response encoding for terminal display purposes, e.g.:
|
||||||
|
|
||||||
\fB\,--response-charset\/\fR=utf8
|
\fB\,--response-charset\/\fR=utf8
|
||||||
\fB\,--response-charset\/\fR=big5
|
\fB\,--response-charset\/\fR=big5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -206,8 +207,8 @@ Override the response encoding for terminal display purposes, e.g.:
|
|||||||
|
|
||||||
Override the response mime type for coloring and formatting for the terminal, e.g.:
|
Override the response mime type for coloring and formatting for the terminal, e.g.:
|
||||||
|
|
||||||
\fB\,--response-mime\/\fR=application/json
|
\fB\,--response-mime\/\fR=application/json
|
||||||
\fB\,--response-mime\/\fR=text/xml
|
\fB\,--response-mime\/\fR=text/xml
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -215,7 +216,7 @@ Override the response mime type for coloring and formatting for the terminal, e.
|
|||||||
|
|
||||||
|
|
||||||
Controls output formatting. Only relevant when formatting is enabled
|
Controls output formatting. Only relevant when formatting is enabled
|
||||||
through (explicit or implied)\fB\,--pretty\/\fR=all or\fB\,--pretty\/\fR=format.
|
through (explicit or implied) \fB\,--pretty\/\fR=all or \fB\,--pretty\/\fR=format.
|
||||||
The following are the default options:
|
The following are the default options:
|
||||||
|
|
||||||
headers.sort:true
|
headers.sort:true
|
||||||
@ -229,26 +230,26 @@ You may use this option multiple times, as well as specify multiple
|
|||||||
comma-separated options at the same time. For example, this modifies the
|
comma-separated options at the same time. For example, this modifies the
|
||||||
settings to disable the sorting of JSON keys, and sets the indent size to 2:
|
settings to disable the sorting of JSON keys, and sets the indent size to 2:
|
||||||
|
|
||||||
\fB\,--format-options\/\fR json.sort_keys:false,json.indent:2
|
\fB\,--format-options\/\fR json.sort_keys:false,json.indent:2
|
||||||
|
|
||||||
This is something you will typically put into your config file.
|
This is something you will typically put into your config file.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
.SH Output Options
|
.SH Output options
|
||||||
.IP "\fB\,--print\/\fR, \fB\,-p\/\fR \fI\,WHAT\/\fR"
|
.IP "\fB\,--print\/\fR, \fB\,-p\/\fR \fI\,WHAT\/\fR"
|
||||||
|
|
||||||
|
|
||||||
String specifying what the output should contain:
|
String specifying what the output should contain:
|
||||||
|
|
||||||
\'H\' request headers
|
\[aq]H\[aq] request headers
|
||||||
\'B\' request body
|
\[aq]B\[aq] request body
|
||||||
\'h\' response headers
|
\[aq]h\[aq] response headers
|
||||||
\'b\' response body
|
\[aq]b\[aq] response body
|
||||||
\'m\' response metadata
|
\[aq]m\[aq] response metadata
|
||||||
|
|
||||||
The default behaviour is \'hb\' (i.e., the response
|
The default behaviour is \[aq]hb\[aq] (i.e., the response
|
||||||
headers and body is printed), if standard output is not redirected.
|
headers and body is printed), if standard output is not redirected.
|
||||||
If the output is piped to another program or to a file, then only the
|
If the output is piped to another program or to a file, then only the
|
||||||
response body is printed by default.
|
response body is printed by default.
|
||||||
@ -258,34 +259,34 @@ response body is printed by default.
|
|||||||
.IP "\fB\,--headers\/\fR, \fB\,-h\/\fR"
|
.IP "\fB\,--headers\/\fR, \fB\,-h\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Print only the response headers. Shortcut for\fB\,--print\/\fR=h.
|
Print only the response headers. Shortcut for \fB\,--print\/\fR=h.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--meta\/\fR, \fB\,-m\/\fR"
|
.IP "\fB\,--meta\/\fR, \fB\,-m\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Print only the response metadata. Shortcut for\fB\,--print\/\fR=m.
|
Print only the response metadata. Shortcut for \fB\,--print\/\fR=m.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--body\/\fR, \fB\,-b\/\fR"
|
.IP "\fB\,--body\/\fR, \fB\,-b\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Print only the response body. Shortcut for\fB\,--print\/\fR=b.
|
Print only the response body. Shortcut for \fB\,--print\/\fR=b.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--verbose\/\fR, \fB\,-v\/\fR"
|
.IP "\fB\,--verbose\/\fR, \fB\,-v\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Verbose output. For the level one (with single \fB\,-v\/\fR`/\fB\,--verbose\/\fR`), print
|
Verbose output. For the level one (with single `\fB\,-v\/\fR`/`\fB\,--verbose\/\fR`), print
|
||||||
the whole request as well as the response. Also print any intermediary
|
the whole request as well as the response. Also print any intermediary
|
||||||
requests/responses (such as redirects). For the second level and higher,
|
requests/responses (such as redirects). For the second level and higher,
|
||||||
print these as well as the response metadata.
|
print these as well as the response metadata.
|
||||||
|
|
||||||
Level one is a shortcut for:\fB\,--all\/\fR\fB\,--print\/\fR=BHbh
|
Level one is a shortcut for: \fB\,--all\/\fR \fB\,--print\/\fR=BHbh
|
||||||
Level two is a shortcut for:\fB\,--all\/\fR\fB\,--print\/\fR=BHbhm
|
Level two is a shortcut for: \fB\,--all\/\fR \fB\,--print\/\fR=BHbhm
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--all\/\fR"
|
.IP "\fB\,--all\/\fR"
|
||||||
@ -293,23 +294,23 @@ Level two is a shortcut for:\fB\,--all\/\fR\fB\,--print\/\fR=BHbhm
|
|||||||
|
|
||||||
By default, only the final request/response is shown. Use this flag to show
|
By default, only the final request/response is shown. Use this flag to show
|
||||||
any intermediary requests/responses as well. Intermediary requests include
|
any intermediary requests/responses as well. Intermediary requests include
|
||||||
followed redirects (with\fB\,--follow\/\fR), the first unauthorized request when
|
followed redirects (with \fB\,--follow\/\fR), the first unauthorized request when
|
||||||
Digest auth is used \fB\,--auth\/\fR=digest), etc.
|
Digest auth is used (\fB\,--auth\/\fR=digest), etc.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--stream\/\fR, \fB\,-S\/\fR"
|
.IP "\fB\,--stream\/\fR, \fB\,-S\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Always stream the response body by line, i.e., behave like `tail\fB\,-f\/\fR\'.
|
Always stream the response body by line, i.e., behave like `tail \fB\,-f\/\fR\[aq].
|
||||||
|
|
||||||
Without\fB\,--stream\/\fR and with\fB\,--pretty\/\fR (either set or implied),
|
Without \fB\,--stream\/\fR and with \fB\,--pretty\/\fR (either set or implied),
|
||||||
HTTPie fetches the whole response before it outputs the processed data.
|
HTTPie fetches the whole response before it outputs the processed data.
|
||||||
|
|
||||||
Set this option when you want to continuously display a prettified
|
Set this option when you want to continuously display a prettified
|
||||||
long-lived response, such as one from the Twitter streaming API.
|
long-lived response, such as one from the Twitter streaming API.
|
||||||
|
|
||||||
It is useful also without\fB\,--pretty\/\fR: It ensures that the output is flushed
|
It is useful also without \fB\,--pretty\/\fR: It ensures that the output is flushed
|
||||||
more often and in smaller chunks.
|
more often and in smaller chunks.
|
||||||
|
|
||||||
|
|
||||||
@ -317,7 +318,7 @@ more often and in smaller chunks.
|
|||||||
.IP "\fB\,--output\/\fR, \fB\,-o\/\fR \fI\,FILE\/\fR"
|
.IP "\fB\,--output\/\fR, \fB\,-o\/\fR \fI\,FILE\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Save output to FILE instead of stdout. If\fB\,--download\/\fR is also set, then only
|
Save output to FILE instead of stdout. If \fB\,--download\/\fR is also set, then only
|
||||||
the response body is saved to FILE. Other parts of the HTTP exchange are
|
the response body is saved to FILE. Other parts of the HTTP exchange are
|
||||||
printed to stderr.
|
printed to stderr.
|
||||||
|
|
||||||
@ -327,7 +328,7 @@ printed to stderr.
|
|||||||
|
|
||||||
|
|
||||||
Do not print the response body to stdout. Rather, download it and store it
|
Do not print the response body to stdout. Rather, download it and store it
|
||||||
in a file. The filename is guessed unless specified with\fB\,--output\/\fR
|
in a file. The filename is guessed unless specified with \fB\,--output\/\fR
|
||||||
[filename]. This action is similar to the default behaviour of wget.
|
[filename]. This action is similar to the default behaviour of wget.
|
||||||
|
|
||||||
|
|
||||||
@ -335,7 +336,7 @@ in a file. The filename is guessed unless specified with\fB\,--output\/\fR
|
|||||||
.IP "\fB\,--continue\/\fR, \fB\,-c\/\fR"
|
.IP "\fB\,--continue\/\fR, \fB\,-c\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Resume an interrupted download. Note that the\fB\,--output\/\fR option needs to be
|
Resume an interrupted download. Note that the \fB\,--output\/\fR option needs to be
|
||||||
specified as well.
|
specified as well.
|
||||||
|
|
||||||
|
|
||||||
@ -345,8 +346,8 @@ specified as well.
|
|||||||
|
|
||||||
Do not print to stdout or stderr, except for errors and warnings when provided once.
|
Do not print to stdout or stderr, except for errors and warnings when provided once.
|
||||||
Provide twice to suppress warnings as well.
|
Provide twice to suppress warnings as well.
|
||||||
stdout is still redirected if\fB\,--output\/\fR is specified.
|
stdout is still redirected if \fB\,--output\/\fR is specified.
|
||||||
Flag doesn\'t affect behaviour of download beyond not printing to terminal.
|
Flag doesn\[aq]t affect behaviour of download beyond not printing to terminal.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -383,24 +384,24 @@ exchange.
|
|||||||
|
|
||||||
For username/password based authentication mechanisms (e.g
|
For username/password based authentication mechanisms (e.g
|
||||||
basic auth or digest auth) if only the username is provided
|
basic auth or digest auth) if only the username is provided
|
||||||
\fB\,-a\/\fR username), HTTPie will prompt for the password.
|
(\fB\,-a\/\fR username), HTTPie will prompt for the password.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--auth-type\/\fR, \fB\,-A\/\fR"
|
.IP "\fB\,--auth-type\/\fR, \fB\,-A\/\fR"
|
||||||
|
|
||||||
|
|
||||||
The authentication mechanism to be used. Defaults to "basic".
|
The authentication mechanism to be used. Defaults to \[dq]basic\[dq].
|
||||||
|
|
||||||
"basic": Basic HTTP auth
|
\[dq]basic\[dq]: Basic HTTP auth
|
||||||
|
|
||||||
"digest": Digest HTTP auth
|
\[dq]digest\[dq]: Digest HTTP auth
|
||||||
|
|
||||||
"bearer": Bearer HTTP Auth
|
\[dq]bearer\[dq]: Bearer HTTP Auth
|
||||||
|
|
||||||
For finding out all available authentication types in your system, try:
|
To see all available auth types on your system, including ones installed via plugins, run:
|
||||||
|
|
||||||
$ http\fB\,--auth-type\/\fR
|
$ http \fB\,--auth-type\/\fR
|
||||||
|
|
||||||
.IP "\fB\,--ignore-netrc\/\fR"
|
.IP "\fB\,--ignore-netrc\/\fR"
|
||||||
|
|
||||||
@ -413,7 +414,7 @@ Ignore credentials from .netrc.
|
|||||||
.IP "\fB\,--offline\/\fR"
|
.IP "\fB\,--offline\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Build the request and print it but don\'t actually send it.
|
Build the request and print it but don\(gat actually send it.
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--proxy\/\fR \fI\,PROTOCOL:PROXY_URL\/\fR"
|
.IP "\fB\,--proxy\/\fR \fI\,PROTOCOL:PROXY_URL\/\fR"
|
||||||
@ -435,7 +436,7 @@ Follow 30x Location redirects.
|
|||||||
.IP "\fB\,--max-redirects\/\fR"
|
.IP "\fB\,--max-redirects\/\fR"
|
||||||
|
|
||||||
|
|
||||||
By default, requests have a limit of 30 redirects (works with\fB\,--follow\/\fR).
|
By default, requests have a limit of 30 redirects (works with \fB\,--follow\/\fR).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -466,7 +467,7 @@ exit with an error if the status indicates one.
|
|||||||
|
|
||||||
When the server replies with a 4xx (Client Error) or 5xx (Server Error)
|
When the server replies with a 4xx (Client Error) or 5xx (Server Error)
|
||||||
status code, HTTPie exits with 4 or 5 respectively. If the response is a
|
status code, HTTPie exits with 4 or 5 respectively. If the response is a
|
||||||
3xx (Redirect) and\fB\,--follow\/\fR hasn\'t been set, then the exit status is 3.
|
3xx (Redirect) and \fB\,--follow\/\fR hasn\[aq]t been set, then the exit status is 3.
|
||||||
Also an error message is written to stderr if stdout is redirected.
|
Also an error message is written to stderr if stdout is redirected.
|
||||||
|
|
||||||
|
|
||||||
@ -488,8 +489,8 @@ Enable streaming via chunked transfer encoding. The Transfer-Encoding header is
|
|||||||
.IP "\fB\,--verify\/\fR"
|
.IP "\fB\,--verify\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Set to "no" (or "false") to skip checking the host\'s SSL certificate.
|
Set to \[dq]no\[dq] (or \[dq]false\[dq]) to skip checking the host\[aq]s SSL certificate.
|
||||||
Defaults to "yes" ("true"). You can also pass the path to a CA_BUNDLE file
|
Defaults to \[dq]yes\[dq] (\[dq]true\[dq]). You can also pass the path to a CA_BUNDLE file
|
||||||
for private certs. (Or you can set the REQUESTS_CA_BUNDLE environment
|
for private certs. (Or you can set the REQUESTS_CA_BUNDLE environment
|
||||||
variable instead.)
|
variable instead.)
|
||||||
|
|
||||||
@ -509,10 +510,12 @@ are shown here).
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
A string in the OpenSSL cipher list format. By default, the following
|
A string in the OpenSSL cipher list format.
|
||||||
is used:
|
|
||||||
|
|
||||||
|
See `http \fB\,--help\/\fR` for the default ciphers list on you system.
|
||||||
|
|
||||||
|
|
||||||
ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:ECDH+AESGCM:DH+AESGCM:ECDH+AES:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!eNULL:!MD5:!DSS
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -521,14 +524,14 @@ ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:ECDH+AESGCM:DH+AESGCM:ECDH+A
|
|||||||
|
|
||||||
You can specify a local cert to use as client side SSL certificate.
|
You can specify a local cert to use as client side SSL certificate.
|
||||||
This file may either contain both private key and certificate or you may
|
This file may either contain both private key and certificate or you may
|
||||||
specify\fB\,--cert-key\/\fR separately.
|
specify \fB\,--cert-key\/\fR separately.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--cert-key\/\fR"
|
.IP "\fB\,--cert-key\/\fR"
|
||||||
|
|
||||||
|
|
||||||
The private key to use with SSL. Only needed if\fB\,--cert\/\fR is given and the
|
The private key to use with SSL. Only needed if \fB\,--cert\/\fR is given and the
|
||||||
certificate file does not contain the private key.
|
certificate file does not contain the private key.
|
||||||
|
|
||||||
|
|
||||||
@ -536,9 +539,9 @@ certificate file does not contain the private key.
|
|||||||
.IP "\fB\,--cert-key-pass\/\fR"
|
.IP "\fB\,--cert-key-pass\/\fR"
|
||||||
|
|
||||||
|
|
||||||
The passphrase to be used to with the given private key. Only needed if\fB\,--cert-key\/\fR
|
The passphrase to be used to with the given private key. Only needed if \fB\,--cert-key\/\fR
|
||||||
is given and the key file requires a passphrase.
|
is given and the key file requires a passphrase.
|
||||||
If not provided, you\'ll be prompted interactively.
|
If not provided, you\(gall be prompted interactively.
|
||||||
|
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
@ -587,4 +590,11 @@ information useful for debugging HTTPie itself and for reporting bugs.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
|
.SH SEE ALSO
|
||||||
|
|
||||||
|
For every \fB\,--OPTION\/\fR there is also a \fB\,--no-OPTION\/\fR that reverts OPTION
|
||||||
|
to its default value.
|
||||||
|
|
||||||
|
Suggestions and bug reports are greatly appreciated:
|
||||||
|
https://github.com/httpie/cli/issues
|
@ -1,9 +1,9 @@
|
|||||||
.TH httpie 1 "2022-03-08" "HTTPie 3.1.1.dev0" "HTTPie Manual"
|
.\" This file is auto-generated from the parser declaration in httpie/manager/cli.py by extras/scripts/generate_man_pages.py.
|
||||||
|
.TH httpie 1 "2024-07-10" "HTTPie 3.2.4" "HTTPie Manual"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
httpie
|
httpie
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
httpie HOSTNAME SESSION_NAME_OR_PATH TARGET TARGET TARGET TARGET TARGET TARGET
|
httpie
|
||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
|
|
||||||
Managing interface for the HTTPie itself. <https://httpie.io/docs#manager>
|
Managing interface for the HTTPie itself. <https://httpie.io/docs#manager>
|
||||||
@ -12,12 +12,21 @@ Be aware that you might be looking for http/https commands for sending
|
|||||||
HTTP requests. This command is only available for managing the HTTTPie
|
HTTP requests. This command is only available for managing the HTTTPie
|
||||||
plugins and the configuration around it.
|
plugins and the configuration around it.
|
||||||
|
|
||||||
|
|
||||||
|
If you are looking for the man pages of http/https commands, try one of the following:
|
||||||
|
$ man http
|
||||||
|
$ man https
|
||||||
|
|
||||||
|
|
||||||
.SH httpie cli export-args
|
.SH httpie cli export-args
|
||||||
Export available options for the CLI
|
Export available options for the CLI
|
||||||
.IP "\fB\,-f\/\fR, \fB\,--format\/\fR"
|
.IP "\fB\,-f\/\fR, \fB\,--format\/\fR"
|
||||||
|
|
||||||
|
Format to export in.
|
||||||
|
|
||||||
|
.PP
|
||||||
|
.SH httpie cli check-updates
|
||||||
|
Check for updates
|
||||||
.PP
|
.PP
|
||||||
.SH httpie cli sessions upgrade
|
.SH httpie cli sessions upgrade
|
||||||
Upgrade the given HTTPie session with the latest layout. A list of changes between different session versions can be found in the official documentation.
|
Upgrade the given HTTPie session with the latest layout. A list of changes between different session versions can be found in the official documentation.
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
.TH https 1 "2022-03-08" "HTTPie 3.1.1.dev0" "HTTPie Manual"
|
.\" This file is auto-generated from the parser declaration in httpie/cli/definition.py by extras/scripts/generate_man_pages.py.
|
||||||
|
.TH https 1 "2024-07-10" "HTTPie 3.2.4" "HTTPie Manual"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
https
|
https
|
||||||
.SH SYNOPSIS
|
.SH SYNOPSIS
|
||||||
@ -6,7 +7,7 @@ https [METHOD] URL [REQUEST_ITEM ...]
|
|||||||
|
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
HTTPie: modern, user-friendly command-line HTTP client for the API era. <https://httpie.io>
|
HTTPie: modern, user-friendly command-line HTTP client for the API era. <https://httpie.io>
|
||||||
.SH Positional Arguments
|
.SH Positional arguments
|
||||||
|
|
||||||
These arguments come after any flags and in the order they are listed here.
|
These arguments come after any flags and in the order they are listed here.
|
||||||
Only URL is required.
|
Only URL is required.
|
||||||
@ -27,8 +28,8 @@ is some data to be sent, otherwise GET:
|
|||||||
.IP "\fB\,URL\/\fR"
|
.IP "\fB\,URL\/\fR"
|
||||||
|
|
||||||
|
|
||||||
The request URL. Scheme defaults to \'http://\' if the URL
|
The request URL. Scheme defaults to \[aq]http://\[aq] if the URL
|
||||||
does not include one. (You can override this with:\fB\,--default-scheme\/\fR=http/https)
|
does not include one. (You can override this with: \fB\,--default-scheme\/\fR=http/https)
|
||||||
|
|
||||||
You can also use a shorthand for localhost
|
You can also use a shorthand for localhost
|
||||||
|
|
||||||
@ -43,44 +44,44 @@ You can also use a shorthand for localhost
|
|||||||
Optional key-value pairs to be included in the request. The separator used
|
Optional key-value pairs to be included in the request. The separator used
|
||||||
determines the type:
|
determines the type:
|
||||||
|
|
||||||
\':\' HTTP headers:
|
\[aq]:\[aq] HTTP headers:
|
||||||
|
|
||||||
Referer:https://httpie.io Cookie:foo=bar User-Agent:bacon/1.0
|
Referer:https://httpie.io Cookie:foo=bar User-Agent:bacon/1.0
|
||||||
|
|
||||||
\'==\' URL parameters to be appended to the request URI:
|
\[aq]==\[aq] URL parameters to be appended to the request URI:
|
||||||
|
|
||||||
search==httpie
|
search==httpie
|
||||||
|
|
||||||
\'=\' Data fields to be serialized into a JSON object (with\fB\,--json\/\fR,\fB\,-j\/\fR)
|
\[aq]=\[aq] Data fields to be serialized into a JSON object (with \fB\,--json\/\fR, \fB\,-j\/\fR)
|
||||||
or form data (with\fB\,--form\/\fR,\fB\,-f\/\fR):
|
or form data (with \fB\,--form\/\fR, \fB\,-f\/\fR):
|
||||||
|
|
||||||
name=HTTPie language=Python description=\'CLI HTTP client\'
|
name=HTTPie language=Python description=\[aq]CLI HTTP client\[aq]
|
||||||
|
|
||||||
\':=\' Non-string JSON data fields (only with\fB\,--json\/\fR,\fB\,-j\/\fR):
|
\[aq]:=\[aq] Non-string JSON data fields (only with \fB\,--json\/\fR, \fB\,-j\/\fR):
|
||||||
|
|
||||||
awesome:=true amount:=42 colors:=\'["red", "green", "blue"]\'
|
awesome:=true amount:=42 colors:=\[aq][\[dq]red\[dq], \[dq]green\[dq], \[dq]blue\[dq]]\[aq]
|
||||||
|
|
||||||
\'@\' Form file fields (only with\fB\,--form\/\fR or\fB\,--multipart\/\fR):
|
\[aq]@\[aq] Form file fields (only with \fB\,--form\/\fR or \fB\,--multipart\/\fR):
|
||||||
|
|
||||||
cv@\~/Documents/CV.pdf
|
cv@\(ti/Documents/CV.pdf
|
||||||
cv@\'\~/Documents/CV.pdf;type=application/pdf\'
|
cv@\[aq]\(ti/Documents/CV.pdf;type=application/pdf\[aq]
|
||||||
|
|
||||||
\'=@\' A data field like \'=\', but takes a file path and embeds its content:
|
\[aq]=@\[aq] A data field like \[aq]=\[aq], but takes a file path and embeds its content:
|
||||||
|
|
||||||
essay=@Documents/essay.txt
|
essay=@Documents/essay.txt
|
||||||
|
|
||||||
\':=@\' A raw JSON field like \':=\', but takes a file path and embeds its content:
|
\[aq]:=@\[aq] A raw JSON field like \[aq]:=\[aq], but takes a file path and embeds its content:
|
||||||
|
|
||||||
package:=@./package.json
|
package:=@./package.json
|
||||||
|
|
||||||
You can use a backslash to escape a colliding separator in the field name:
|
You can use a backslash to escape a colliding separator in the field name:
|
||||||
|
|
||||||
field-name-with\\:colon=value
|
field-name-with\e:colon=value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
.SH Predefined Content Types
|
.SH Predefined content types
|
||||||
.IP "\fB\,--json\/\fR, \fB\,-j\/\fR"
|
.IP "\fB\,--json\/\fR, \fB\,-j\/\fR"
|
||||||
|
|
||||||
|
|
||||||
@ -104,13 +105,13 @@ multipart/form-data request.
|
|||||||
.IP "\fB\,--multipart\/\fR"
|
.IP "\fB\,--multipart\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Similar to\fB\,--form\/\fR, but always sends a multipart/form-data request (i.e., even without files).
|
Similar to \fB\,--form\/\fR, but always sends a multipart/form-data request (i.e., even without files).
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--boundary\/\fR"
|
.IP "\fB\,--boundary\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Specify a custom boundary string for multipart/form-data requests. Only has effect only together with\fB\,--form\/\fR.
|
Specify a custom boundary string for multipart/form-data requests. Only has effect only together with \fB\,--form\/\fR.
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--raw\/\fR"
|
.IP "\fB\,--raw\/\fR"
|
||||||
@ -119,7 +120,7 @@ Specify a custom boundary string for multipart/form-data requests. Only has effe
|
|||||||
This option allows you to pass raw request data without extra processing
|
This option allows you to pass raw request data without extra processing
|
||||||
(as opposed to the structured request items syntax):
|
(as opposed to the structured request items syntax):
|
||||||
|
|
||||||
$ http\fB\,--raw\/\fR=\'data\' pie.dev/post
|
$ http \fB\,--raw\/\fR=\[aq]data\[aq] pie.dev/post
|
||||||
|
|
||||||
You can achieve the same by piping the data via stdin:
|
You can achieve the same by piping the data via stdin:
|
||||||
|
|
||||||
@ -133,7 +134,7 @@ Or have HTTPie load the raw data from a file:
|
|||||||
|
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
.SH Content Processing Options
|
.SH Content processing options
|
||||||
.IP "\fB\,--compress\/\fR, \fB\,-x\/\fR"
|
.IP "\fB\,--compress\/\fR, \fB\,-x\/\fR"
|
||||||
|
|
||||||
|
|
||||||
@ -146,39 +147,39 @@ negative. Compression can be forced by repeating the argument.
|
|||||||
|
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
.SH Output Processing
|
.SH Output processing
|
||||||
.IP "\fB\,--pretty\/\fR"
|
.IP "\fB\,--pretty\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Controls output processing. The value can be "none" to not prettify
|
Controls output processing. The value can be \[dq]none\[dq] to not prettify
|
||||||
the output (default for redirected output), "all" to apply both colors
|
the output (default for redirected output), \[dq]all\[dq] to apply both colors
|
||||||
and formatting (default for terminal output), "colors", or "format".
|
and formatting (default for terminal output), \[dq]colors\[dq], or \[dq]format\[dq].
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--style\/\fR, \fB\,-s\/\fR \fI\,STYLE\/\fR"
|
.IP "\fB\,--style\/\fR, \fB\,-s\/\fR \fI\,STYLE\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Output coloring style (default is "auto"). It can be one of:
|
Output coloring style (default is \[dq]auto\[dq]). It can be one of:
|
||||||
|
|
||||||
auto, pie, pie-dark, pie-light, solarized
|
auto, pie, pie-dark, pie-light, solarized
|
||||||
|
|
||||||
|
|
||||||
For finding out all available styles in your system, try:
|
For finding out all available styles in your system, try:
|
||||||
|
|
||||||
$ http\fB\,--style\/\fR
|
$ http \fB\,--style\/\fR
|
||||||
|
|
||||||
The "auto" style follows your terminal\'s ANSI color styles.
|
The \[dq]auto\[dq] style follows your terminal\[aq]s ANSI color styles.
|
||||||
For non-auto styles to work properly, please make sure that the
|
For non-auto styles to work properly, please make sure that the
|
||||||
$TERM environment variable is set to "xterm-256color" or similar
|
$TERM environment variable is set to \[dq]xterm-256color\[dq] or similar
|
||||||
(e.g., via `export TERM=xterm-256color\' in your \~/.bashrc).
|
(e.g., via `export TERM=xterm-256color\[aq] in your \(ti/.bashrc).
|
||||||
|
|
||||||
.IP "\fB\,--unsorted\/\fR"
|
.IP "\fB\,--unsorted\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Disables all sorting while formatting output. It is a shortcut for:
|
Disables all sorting while formatting output. It is a shortcut for:
|
||||||
|
|
||||||
\fB\,--format-options\/\fR=headers.sort:false,json.sort_keys:false
|
\fB\,--format-options\/\fR=headers.sort:false,json.sort_keys:false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -187,7 +188,7 @@ Disables all sorting while formatting output. It is a shortcut for:
|
|||||||
|
|
||||||
Re-enables all sorting options while formatting output. It is a shortcut for:
|
Re-enables all sorting options while formatting output. It is a shortcut for:
|
||||||
|
|
||||||
\fB\,--format-options\/\fR=headers.sort:true,json.sort_keys:true
|
\fB\,--format-options\/\fR=headers.sort:true,json.sort_keys:true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -196,8 +197,8 @@ Re-enables all sorting options while formatting output. It is a shortcut for:
|
|||||||
|
|
||||||
Override the response encoding for terminal display purposes, e.g.:
|
Override the response encoding for terminal display purposes, e.g.:
|
||||||
|
|
||||||
\fB\,--response-charset\/\fR=utf8
|
\fB\,--response-charset\/\fR=utf8
|
||||||
\fB\,--response-charset\/\fR=big5
|
\fB\,--response-charset\/\fR=big5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -206,8 +207,8 @@ Override the response encoding for terminal display purposes, e.g.:
|
|||||||
|
|
||||||
Override the response mime type for coloring and formatting for the terminal, e.g.:
|
Override the response mime type for coloring and formatting for the terminal, e.g.:
|
||||||
|
|
||||||
\fB\,--response-mime\/\fR=application/json
|
\fB\,--response-mime\/\fR=application/json
|
||||||
\fB\,--response-mime\/\fR=text/xml
|
\fB\,--response-mime\/\fR=text/xml
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -215,7 +216,7 @@ Override the response mime type for coloring and formatting for the terminal, e.
|
|||||||
|
|
||||||
|
|
||||||
Controls output formatting. Only relevant when formatting is enabled
|
Controls output formatting. Only relevant when formatting is enabled
|
||||||
through (explicit or implied)\fB\,--pretty\/\fR=all or\fB\,--pretty\/\fR=format.
|
through (explicit or implied) \fB\,--pretty\/\fR=all or \fB\,--pretty\/\fR=format.
|
||||||
The following are the default options:
|
The following are the default options:
|
||||||
|
|
||||||
headers.sort:true
|
headers.sort:true
|
||||||
@ -229,26 +230,26 @@ You may use this option multiple times, as well as specify multiple
|
|||||||
comma-separated options at the same time. For example, this modifies the
|
comma-separated options at the same time. For example, this modifies the
|
||||||
settings to disable the sorting of JSON keys, and sets the indent size to 2:
|
settings to disable the sorting of JSON keys, and sets the indent size to 2:
|
||||||
|
|
||||||
\fB\,--format-options\/\fR json.sort_keys:false,json.indent:2
|
\fB\,--format-options\/\fR json.sort_keys:false,json.indent:2
|
||||||
|
|
||||||
This is something you will typically put into your config file.
|
This is something you will typically put into your config file.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
.SH Output Options
|
.SH Output options
|
||||||
.IP "\fB\,--print\/\fR, \fB\,-p\/\fR \fI\,WHAT\/\fR"
|
.IP "\fB\,--print\/\fR, \fB\,-p\/\fR \fI\,WHAT\/\fR"
|
||||||
|
|
||||||
|
|
||||||
String specifying what the output should contain:
|
String specifying what the output should contain:
|
||||||
|
|
||||||
\'H\' request headers
|
\[aq]H\[aq] request headers
|
||||||
\'B\' request body
|
\[aq]B\[aq] request body
|
||||||
\'h\' response headers
|
\[aq]h\[aq] response headers
|
||||||
\'b\' response body
|
\[aq]b\[aq] response body
|
||||||
\'m\' response metadata
|
\[aq]m\[aq] response metadata
|
||||||
|
|
||||||
The default behaviour is \'hb\' (i.e., the response
|
The default behaviour is \[aq]hb\[aq] (i.e., the response
|
||||||
headers and body is printed), if standard output is not redirected.
|
headers and body is printed), if standard output is not redirected.
|
||||||
If the output is piped to another program or to a file, then only the
|
If the output is piped to another program or to a file, then only the
|
||||||
response body is printed by default.
|
response body is printed by default.
|
||||||
@ -258,34 +259,34 @@ response body is printed by default.
|
|||||||
.IP "\fB\,--headers\/\fR, \fB\,-h\/\fR"
|
.IP "\fB\,--headers\/\fR, \fB\,-h\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Print only the response headers. Shortcut for\fB\,--print\/\fR=h.
|
Print only the response headers. Shortcut for \fB\,--print\/\fR=h.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--meta\/\fR, \fB\,-m\/\fR"
|
.IP "\fB\,--meta\/\fR, \fB\,-m\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Print only the response metadata. Shortcut for\fB\,--print\/\fR=m.
|
Print only the response metadata. Shortcut for \fB\,--print\/\fR=m.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--body\/\fR, \fB\,-b\/\fR"
|
.IP "\fB\,--body\/\fR, \fB\,-b\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Print only the response body. Shortcut for\fB\,--print\/\fR=b.
|
Print only the response body. Shortcut for \fB\,--print\/\fR=b.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--verbose\/\fR, \fB\,-v\/\fR"
|
.IP "\fB\,--verbose\/\fR, \fB\,-v\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Verbose output. For the level one (with single \fB\,-v\/\fR`/\fB\,--verbose\/\fR`), print
|
Verbose output. For the level one (with single `\fB\,-v\/\fR`/`\fB\,--verbose\/\fR`), print
|
||||||
the whole request as well as the response. Also print any intermediary
|
the whole request as well as the response. Also print any intermediary
|
||||||
requests/responses (such as redirects). For the second level and higher,
|
requests/responses (such as redirects). For the second level and higher,
|
||||||
print these as well as the response metadata.
|
print these as well as the response metadata.
|
||||||
|
|
||||||
Level one is a shortcut for:\fB\,--all\/\fR\fB\,--print\/\fR=BHbh
|
Level one is a shortcut for: \fB\,--all\/\fR \fB\,--print\/\fR=BHbh
|
||||||
Level two is a shortcut for:\fB\,--all\/\fR\fB\,--print\/\fR=BHbhm
|
Level two is a shortcut for: \fB\,--all\/\fR \fB\,--print\/\fR=BHbhm
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--all\/\fR"
|
.IP "\fB\,--all\/\fR"
|
||||||
@ -293,23 +294,23 @@ Level two is a shortcut for:\fB\,--all\/\fR\fB\,--print\/\fR=BHbhm
|
|||||||
|
|
||||||
By default, only the final request/response is shown. Use this flag to show
|
By default, only the final request/response is shown. Use this flag to show
|
||||||
any intermediary requests/responses as well. Intermediary requests include
|
any intermediary requests/responses as well. Intermediary requests include
|
||||||
followed redirects (with\fB\,--follow\/\fR), the first unauthorized request when
|
followed redirects (with \fB\,--follow\/\fR), the first unauthorized request when
|
||||||
Digest auth is used \fB\,--auth\/\fR=digest), etc.
|
Digest auth is used (\fB\,--auth\/\fR=digest), etc.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--stream\/\fR, \fB\,-S\/\fR"
|
.IP "\fB\,--stream\/\fR, \fB\,-S\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Always stream the response body by line, i.e., behave like `tail\fB\,-f\/\fR\'.
|
Always stream the response body by line, i.e., behave like `tail \fB\,-f\/\fR\[aq].
|
||||||
|
|
||||||
Without\fB\,--stream\/\fR and with\fB\,--pretty\/\fR (either set or implied),
|
Without \fB\,--stream\/\fR and with \fB\,--pretty\/\fR (either set or implied),
|
||||||
HTTPie fetches the whole response before it outputs the processed data.
|
HTTPie fetches the whole response before it outputs the processed data.
|
||||||
|
|
||||||
Set this option when you want to continuously display a prettified
|
Set this option when you want to continuously display a prettified
|
||||||
long-lived response, such as one from the Twitter streaming API.
|
long-lived response, such as one from the Twitter streaming API.
|
||||||
|
|
||||||
It is useful also without\fB\,--pretty\/\fR: It ensures that the output is flushed
|
It is useful also without \fB\,--pretty\/\fR: It ensures that the output is flushed
|
||||||
more often and in smaller chunks.
|
more often and in smaller chunks.
|
||||||
|
|
||||||
|
|
||||||
@ -317,7 +318,7 @@ more often and in smaller chunks.
|
|||||||
.IP "\fB\,--output\/\fR, \fB\,-o\/\fR \fI\,FILE\/\fR"
|
.IP "\fB\,--output\/\fR, \fB\,-o\/\fR \fI\,FILE\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Save output to FILE instead of stdout. If\fB\,--download\/\fR is also set, then only
|
Save output to FILE instead of stdout. If \fB\,--download\/\fR is also set, then only
|
||||||
the response body is saved to FILE. Other parts of the HTTP exchange are
|
the response body is saved to FILE. Other parts of the HTTP exchange are
|
||||||
printed to stderr.
|
printed to stderr.
|
||||||
|
|
||||||
@ -327,7 +328,7 @@ printed to stderr.
|
|||||||
|
|
||||||
|
|
||||||
Do not print the response body to stdout. Rather, download it and store it
|
Do not print the response body to stdout. Rather, download it and store it
|
||||||
in a file. The filename is guessed unless specified with\fB\,--output\/\fR
|
in a file. The filename is guessed unless specified with \fB\,--output\/\fR
|
||||||
[filename]. This action is similar to the default behaviour of wget.
|
[filename]. This action is similar to the default behaviour of wget.
|
||||||
|
|
||||||
|
|
||||||
@ -335,7 +336,7 @@ in a file. The filename is guessed unless specified with\fB\,--output\/\fR
|
|||||||
.IP "\fB\,--continue\/\fR, \fB\,-c\/\fR"
|
.IP "\fB\,--continue\/\fR, \fB\,-c\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Resume an interrupted download. Note that the\fB\,--output\/\fR option needs to be
|
Resume an interrupted download. Note that the \fB\,--output\/\fR option needs to be
|
||||||
specified as well.
|
specified as well.
|
||||||
|
|
||||||
|
|
||||||
@ -345,8 +346,8 @@ specified as well.
|
|||||||
|
|
||||||
Do not print to stdout or stderr, except for errors and warnings when provided once.
|
Do not print to stdout or stderr, except for errors and warnings when provided once.
|
||||||
Provide twice to suppress warnings as well.
|
Provide twice to suppress warnings as well.
|
||||||
stdout is still redirected if\fB\,--output\/\fR is specified.
|
stdout is still redirected if \fB\,--output\/\fR is specified.
|
||||||
Flag doesn\'t affect behaviour of download beyond not printing to terminal.
|
Flag doesn\[aq]t affect behaviour of download beyond not printing to terminal.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -383,24 +384,24 @@ exchange.
|
|||||||
|
|
||||||
For username/password based authentication mechanisms (e.g
|
For username/password based authentication mechanisms (e.g
|
||||||
basic auth or digest auth) if only the username is provided
|
basic auth or digest auth) if only the username is provided
|
||||||
\fB\,-a\/\fR username), HTTPie will prompt for the password.
|
(\fB\,-a\/\fR username), HTTPie will prompt for the password.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--auth-type\/\fR, \fB\,-A\/\fR"
|
.IP "\fB\,--auth-type\/\fR, \fB\,-A\/\fR"
|
||||||
|
|
||||||
|
|
||||||
The authentication mechanism to be used. Defaults to "basic".
|
The authentication mechanism to be used. Defaults to \[dq]basic\[dq].
|
||||||
|
|
||||||
"basic": Basic HTTP auth
|
\[dq]basic\[dq]: Basic HTTP auth
|
||||||
|
|
||||||
"digest": Digest HTTP auth
|
\[dq]digest\[dq]: Digest HTTP auth
|
||||||
|
|
||||||
"bearer": Bearer HTTP Auth
|
\[dq]bearer\[dq]: Bearer HTTP Auth
|
||||||
|
|
||||||
For finding out all available authentication types in your system, try:
|
To see all available auth types on your system, including ones installed via plugins, run:
|
||||||
|
|
||||||
$ http\fB\,--auth-type\/\fR
|
$ http \fB\,--auth-type\/\fR
|
||||||
|
|
||||||
.IP "\fB\,--ignore-netrc\/\fR"
|
.IP "\fB\,--ignore-netrc\/\fR"
|
||||||
|
|
||||||
@ -413,7 +414,7 @@ Ignore credentials from .netrc.
|
|||||||
.IP "\fB\,--offline\/\fR"
|
.IP "\fB\,--offline\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Build the request and print it but don\'t actually send it.
|
Build the request and print it but don\(gat actually send it.
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--proxy\/\fR \fI\,PROTOCOL:PROXY_URL\/\fR"
|
.IP "\fB\,--proxy\/\fR \fI\,PROTOCOL:PROXY_URL\/\fR"
|
||||||
@ -435,7 +436,7 @@ Follow 30x Location redirects.
|
|||||||
.IP "\fB\,--max-redirects\/\fR"
|
.IP "\fB\,--max-redirects\/\fR"
|
||||||
|
|
||||||
|
|
||||||
By default, requests have a limit of 30 redirects (works with\fB\,--follow\/\fR).
|
By default, requests have a limit of 30 redirects (works with \fB\,--follow\/\fR).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -466,7 +467,7 @@ exit with an error if the status indicates one.
|
|||||||
|
|
||||||
When the server replies with a 4xx (Client Error) or 5xx (Server Error)
|
When the server replies with a 4xx (Client Error) or 5xx (Server Error)
|
||||||
status code, HTTPie exits with 4 or 5 respectively. If the response is a
|
status code, HTTPie exits with 4 or 5 respectively. If the response is a
|
||||||
3xx (Redirect) and\fB\,--follow\/\fR hasn\'t been set, then the exit status is 3.
|
3xx (Redirect) and \fB\,--follow\/\fR hasn\[aq]t been set, then the exit status is 3.
|
||||||
Also an error message is written to stderr if stdout is redirected.
|
Also an error message is written to stderr if stdout is redirected.
|
||||||
|
|
||||||
|
|
||||||
@ -488,8 +489,8 @@ Enable streaming via chunked transfer encoding. The Transfer-Encoding header is
|
|||||||
.IP "\fB\,--verify\/\fR"
|
.IP "\fB\,--verify\/\fR"
|
||||||
|
|
||||||
|
|
||||||
Set to "no" (or "false") to skip checking the host\'s SSL certificate.
|
Set to \[dq]no\[dq] (or \[dq]false\[dq]) to skip checking the host\[aq]s SSL certificate.
|
||||||
Defaults to "yes" ("true"). You can also pass the path to a CA_BUNDLE file
|
Defaults to \[dq]yes\[dq] (\[dq]true\[dq]). You can also pass the path to a CA_BUNDLE file
|
||||||
for private certs. (Or you can set the REQUESTS_CA_BUNDLE environment
|
for private certs. (Or you can set the REQUESTS_CA_BUNDLE environment
|
||||||
variable instead.)
|
variable instead.)
|
||||||
|
|
||||||
@ -509,10 +510,12 @@ are shown here).
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
A string in the OpenSSL cipher list format. By default, the following
|
A string in the OpenSSL cipher list format.
|
||||||
is used:
|
|
||||||
|
|
||||||
|
See `http \fB\,--help\/\fR` for the default ciphers list on you system.
|
||||||
|
|
||||||
|
|
||||||
ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:ECDH+AESGCM:DH+AESGCM:ECDH+AES:DH+AES:RSA+AESGCM:RSA+AES:!aNULL:!eNULL:!MD5:!DSS
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -521,14 +524,14 @@ ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20:ECDH+AESGCM:DH+AESGCM:ECDH+A
|
|||||||
|
|
||||||
You can specify a local cert to use as client side SSL certificate.
|
You can specify a local cert to use as client side SSL certificate.
|
||||||
This file may either contain both private key and certificate or you may
|
This file may either contain both private key and certificate or you may
|
||||||
specify\fB\,--cert-key\/\fR separately.
|
specify \fB\,--cert-key\/\fR separately.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
.IP "\fB\,--cert-key\/\fR"
|
.IP "\fB\,--cert-key\/\fR"
|
||||||
|
|
||||||
|
|
||||||
The private key to use with SSL. Only needed if\fB\,--cert\/\fR is given and the
|
The private key to use with SSL. Only needed if \fB\,--cert\/\fR is given and the
|
||||||
certificate file does not contain the private key.
|
certificate file does not contain the private key.
|
||||||
|
|
||||||
|
|
||||||
@ -536,9 +539,9 @@ certificate file does not contain the private key.
|
|||||||
.IP "\fB\,--cert-key-pass\/\fR"
|
.IP "\fB\,--cert-key-pass\/\fR"
|
||||||
|
|
||||||
|
|
||||||
The passphrase to be used to with the given private key. Only needed if\fB\,--cert-key\/\fR
|
The passphrase to be used to with the given private key. Only needed if \fB\,--cert-key\/\fR
|
||||||
is given and the key file requires a passphrase.
|
is given and the key file requires a passphrase.
|
||||||
If not provided, you\'ll be prompted interactively.
|
If not provided, you\(gall be prompted interactively.
|
||||||
|
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
@ -587,4 +590,11 @@ information useful for debugging HTTPie itself and for reporting bugs.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
.PP
|
.PP
|
||||||
|
.SH SEE ALSO
|
||||||
|
|
||||||
|
For every \fB\,--OPTION\/\fR there is also a \fB\,--no-OPTION\/\fR that reverts OPTION
|
||||||
|
to its default value.
|
||||||
|
|
||||||
|
Suggestions and bug reports are greatly appreciated:
|
||||||
|
https://github.com/httpie/cli/issues
|
@ -27,6 +27,7 @@ RUN python -m pip install /app
|
|||||||
RUN python -m pip install pyinstaller wheel
|
RUN python -m pip install pyinstaller wheel
|
||||||
RUN python -m pip install --force-reinstall --upgrade pip
|
RUN python -m pip install --force-reinstall --upgrade pip
|
||||||
|
|
||||||
|
RUN echo 'BUILD_CHANNEL="pypi"' > /app/httpie/internal/__build_channel__.py
|
||||||
RUN python build.py
|
RUN python build.py
|
||||||
|
|
||||||
ENTRYPOINT ["mv", "/app/extras/packaging/linux/dist/", "/artifacts"]
|
ENTRYPOINT ["mv", "/app/extras/packaging/linux/dist/", "/artifacts"]
|
||||||
|
@ -7,13 +7,13 @@ This directory contains the build scripts for creating:
|
|||||||
- A self-contained binary executable for the HTTPie itself
|
- A self-contained binary executable for the HTTPie itself
|
||||||
- `httpie.deb` and `httpie.rpm` packages for Debian and Fedora.
|
- `httpie.deb` and `httpie.rpm` packages for Debian and Fedora.
|
||||||
|
|
||||||
The process of constructing them are fully automated, and can be easily done through the [`Release as Standalone Linux Package`](https://github.com/httpie/httpie/actions/workflows/release-linux-standalone.yml)
|
The process of constructing them are fully automated, and can be easily done through the [`Release as Standalone Linux Package`](https://github.com/httpie/cli/actions/workflows/release-linux-standalone.yml)
|
||||||
action. Once it finishes, the release artifacts will be attached in the summary page of the triggered run.
|
action. Once it finishes, the release artifacts will be attached in the summary page of the triggered run.
|
||||||
|
|
||||||
|
|
||||||
## Hacking
|
## Hacking
|
||||||
|
|
||||||
The main entry point for the package builder is the [`build.py`](https://github.com/httpie/httpie/blob/master/extras/packaging/linux/build.py). It
|
The main entry point for the package builder is the [`build.py`](https://github.com/httpie/cli/blob/master/extras/packaging/linux/build.py). It
|
||||||
contains 2 major methods:
|
contains 2 major methods:
|
||||||
|
|
||||||
- `build_binaries`, for the self-contained executables
|
- `build_binaries`, for the self-contained executables
|
||||||
@ -39,7 +39,7 @@ Since the `httpie` depends on having a pip executable, we explicitly depend on t
|
|||||||
|
|
||||||
### Docker Image
|
### Docker Image
|
||||||
|
|
||||||
This directory also contains a [docker image](https://github.com/httpie/httpie/blob/master/extras/packaging/linux/Dockerfile) which helps
|
This directory also contains a [docker image](https://github.com/httpie/cli/blob/master/extras/packaging/linux/Dockerfile) which helps
|
||||||
building our standalone binaries in an isolated environment with the lowest possible library versions. This is important, since even though
|
building our standalone binaries in an isolated environment with the lowest possible library versions. This is important, since even though
|
||||||
the executables are standalone they still depend on some main system C libraries (like `glibc`) so we need to create our executables inside
|
the executables are standalone they still depend on some main system C libraries (like `glibc`) so we need to create our executables inside
|
||||||
an environment with a very old (but not deprecated) glibc version. It makes us soundproof for all active Ubuntu/Debian versions.
|
an environment with a very old (but not deprecated) glibc version. It makes us soundproof for all active Ubuntu/Debian versions.
|
||||||
|
@ -6,6 +6,9 @@ from typing import Iterator, Tuple
|
|||||||
BUILD_DIR = Path(__file__).parent
|
BUILD_DIR = Path(__file__).parent
|
||||||
HTTPIE_DIR = BUILD_DIR.parent.parent.parent
|
HTTPIE_DIR = BUILD_DIR.parent.parent.parent
|
||||||
|
|
||||||
|
EXTRAS_DIR = HTTPIE_DIR / 'extras'
|
||||||
|
MAN_PAGES_DIR = EXTRAS_DIR / 'man'
|
||||||
|
|
||||||
SCRIPT_DIR = BUILD_DIR / Path('scripts')
|
SCRIPT_DIR = BUILD_DIR / Path('scripts')
|
||||||
HOOKS_DIR = SCRIPT_DIR / 'hooks'
|
HOOKS_DIR = SCRIPT_DIR / 'hooks'
|
||||||
|
|
||||||
@ -50,6 +53,11 @@ def build_packages(http_binary: Path, httpie_binary: Path) -> None:
|
|||||||
(http_binary, '/usr/bin/https'),
|
(http_binary, '/usr/bin/https'),
|
||||||
(httpie_binary, '/usr/bin/httpie'),
|
(httpie_binary, '/usr/bin/httpie'),
|
||||||
]
|
]
|
||||||
|
files.extend(
|
||||||
|
(man_page, f'/usr/share/man/man1/{man_page.name}')
|
||||||
|
for man_page in MAN_PAGES_DIR.glob('*.1')
|
||||||
|
)
|
||||||
|
|
||||||
# A list of additional dependencies
|
# A list of additional dependencies
|
||||||
deps = [
|
deps = [
|
||||||
'python3 >= 3.7',
|
'python3 >= 3.7',
|
||||||
@ -92,8 +100,9 @@ def main():
|
|||||||
build_packages(binaries['http_cli'], binaries['httpie_cli'])
|
build_packages(binaries['http_cli'], binaries['httpie_cli'])
|
||||||
|
|
||||||
# Rename http_cli/httpie_cli to http/httpie
|
# Rename http_cli/httpie_cli to http/httpie
|
||||||
binaries['http_cli'].rename('http')
|
binaries['http_cli'].rename(DIST_DIR / 'http')
|
||||||
binaries['httpie_cli'].rename('httpie')
|
binaries['httpie_cli'].rename(DIST_DIR / 'httpie')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -10,7 +10,7 @@ Ensure the following requirements are satisfied:
|
|||||||
- Python 3.7+
|
- Python 3.7+
|
||||||
- `pyperf`
|
- `pyperf`
|
||||||
|
|
||||||
Then, run the `extras/benchmarks/run.py`:
|
Then, run the `extras/profiling/run.py`:
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$ python extras/profiling/run.py
|
$ python extras/profiling/run.py
|
||||||
@ -28,7 +28,7 @@ on both of them. It will compare the results and print it as a markdown table:
|
|||||||
| Geometric mean | (ref) | 1.10x faster |
|
| Geometric mean | (ref) | 1.10x faster |
|
||||||
|
|
||||||
If your `master` branch is not up-to-date, you can get a fresh clone by passing
|
If your `master` branch is not up-to-date, you can get a fresh clone by passing
|
||||||
`--fresh` option. This way, the benchmark runner will clone the `httpie/httpie`
|
`--fresh` option. This way, the benchmark runner will clone the `httpie/cli`
|
||||||
repo from `GitHub` and use it as the baseline.
|
repo from `GitHub` and use it as the baseline.
|
||||||
|
|
||||||
You can customize these branches by passing `--local-repo`/`--target-branch`,
|
You can customize these branches by passing `--local-repo`/`--target-branch`,
|
||||||
|
@ -9,11 +9,11 @@ timings.
|
|||||||
|
|
||||||
The benchmarks are run through 'pyperf', which allows to
|
The benchmarks are run through 'pyperf', which allows to
|
||||||
do get very precise results. For micro-benchmarks like startup,
|
do get very precise results. For micro-benchmarks like startup,
|
||||||
please run `pyperf system tune` to get even more acurrate results.
|
please run `pyperf system tune` to get even more accurate results.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
# Run everything as usual, the default is that we do 3 warmup runs
|
# Run everything as usual, the default is that we do 3 warm-up runs
|
||||||
# and 5 actual runs.
|
# and 5 actual runs.
|
||||||
$ python extras/profiling/benchmarks.py
|
$ python extras/profiling/benchmarks.py
|
||||||
|
|
||||||
@ -188,7 +188,7 @@ DownloadRunner('download', '`http --download :/big_file.txt` (3GB)', '3G')
|
|||||||
def main() -> None:
|
def main() -> None:
|
||||||
# PyPerf will bring it's own argument parser, so configure the script.
|
# PyPerf will bring it's own argument parser, so configure the script.
|
||||||
# The somewhat fast and also precise enough configuration is this. We run
|
# The somewhat fast and also precise enough configuration is this. We run
|
||||||
# benchmarks 3 times to warmup (e.g especially for download benchmark, this
|
# benchmarks 3 times to warm up (e.g especially for download benchmark, this
|
||||||
# is important). And then 5 actual runs where we record.
|
# is important). And then 5 actual runs where we record.
|
||||||
sys.argv.extend(
|
sys.argv.extend(
|
||||||
['--worker', '--loops=1', '--warmup=3', '--values=5', '--processes=2']
|
['--worker', '--loops=1', '--warmup=3', '--values=5', '--processes=2']
|
||||||
|
@ -19,19 +19,19 @@ which would include additional dependencies like pyOpenSSL.
|
|||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
# Run everything as usual, and compare last commit with master
|
# Run everything as usual, and compare last commit with master
|
||||||
$ python extras/benchmarks/run.py
|
$ python extras/profiling/run.py
|
||||||
|
|
||||||
# Include complex environments
|
# Include complex environments
|
||||||
$ python extras/benchmarks/run.py --complex
|
$ python extras/profiling/run.py --complex
|
||||||
|
|
||||||
# Compare against a fresh copy
|
# Compare against a fresh copy
|
||||||
$ python extras/benchmarks/run.py --fresh
|
$ python extras/profiling/run.py --fresh
|
||||||
|
|
||||||
# Compare against a custom branch of a custom repo
|
# Compare against a custom branch of a custom repo
|
||||||
$ python extras/benchmarks/run.py --target-repo my_repo --target-branch my_branch
|
$ python extras/profiling/run.py --target-repo my_repo --target-branch my_branch
|
||||||
|
|
||||||
# Debug changes made on this script (only run benchmarks once)
|
# Debug changes made on this script (only run benchmarks once)
|
||||||
$ python extras/benchmarks/run.py --debug
|
$ python extras/profiling/run.py --debug
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
@ -50,7 +50,7 @@ from typing import (IO, Dict, Generator, Iterable, List, Optional,
|
|||||||
BENCHMARK_SCRIPT = Path(__file__).parent / 'benchmarks.py'
|
BENCHMARK_SCRIPT = Path(__file__).parent / 'benchmarks.py'
|
||||||
CURRENT_REPO = Path(__file__).parent.parent.parent
|
CURRENT_REPO = Path(__file__).parent.parent.parent
|
||||||
|
|
||||||
GITHUB_URL = 'https://github.com/httpie/httpie.git'
|
GITHUB_URL = 'https://github.com/httpie/cli.git'
|
||||||
TARGET_BRANCH = 'master'
|
TARGET_BRANCH = 'master'
|
||||||
|
|
||||||
# Additional dependencies for --complex
|
# Additional dependencies for --complex
|
||||||
|
@ -1,34 +1,43 @@
|
|||||||
|
import os
|
||||||
import re
|
import re
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Optional, Iterator, Iterable
|
from typing import Optional, Iterator, Iterable
|
||||||
|
|
||||||
|
|
||||||
|
# So that httpie.cli.definition can provide man-page-specific output. Must be set before importing httpie.
|
||||||
|
os.environ['HTTPIE_BUILDING_MAN_PAGES'] = '1'
|
||||||
|
|
||||||
import httpie
|
import httpie
|
||||||
from httpie.cli.definition import options as core_options
|
from httpie.cli.definition import options as core_options, IS_MAN_PAGE
|
||||||
from httpie.cli.options import ParserSpec
|
from httpie.cli.options import ParserSpec
|
||||||
from httpie.manager.cli import options as manager_options
|
from httpie.manager.cli import options as manager_options
|
||||||
from httpie.output.ui.rich_help import OptionsHighlighter, to_usage
|
from httpie.output.ui.rich_help import OptionsHighlighter, to_usage
|
||||||
from httpie.output.ui.rich_utils import render_as_string
|
from httpie.output.ui.rich_utils import render_as_string
|
||||||
from httpie.utils import split
|
|
||||||
|
|
||||||
|
|
||||||
# Escape certain characters so they are rendered properly on
|
assert IS_MAN_PAGE, 'CLI definition does not understand we’re building man pages'
|
||||||
# all terminals.
|
|
||||||
|
# Escape certain characters, so they are rendered properly on all terminals.
|
||||||
|
# <https://man7.org/linux/man-pages/man7/groff_char.7.html>
|
||||||
ESCAPE_MAP = {
|
ESCAPE_MAP = {
|
||||||
"'": "\\'",
|
'"': '\[dq]',
|
||||||
'~': '\\~',
|
"'": '\[aq]',
|
||||||
'’': "\\'",
|
'~': '\(ti',
|
||||||
'\\': '\\\\',
|
'’': "\(ga",
|
||||||
|
'\\': '\e',
|
||||||
}
|
}
|
||||||
ESCAPE_MAP = {ord(key): value for key, value in ESCAPE_MAP.items()}
|
ESCAPE_MAP = {ord(key): value for key, value in ESCAPE_MAP.items()}
|
||||||
|
|
||||||
EXTRAS_DIR = Path(__file__).parent.parent
|
EXTRAS_DIR = Path(__file__).parent.parent
|
||||||
MAN_PAGE_PATH = EXTRAS_DIR / 'man'
|
MAN_PAGE_PATH = EXTRAS_DIR / 'man'
|
||||||
|
PROJECT_ROOT = EXTRAS_DIR.parent
|
||||||
|
|
||||||
OPTION_HIGHLIGHT_RE = re.compile(
|
OPTION_HIGHLIGHT_RE = re.compile(
|
||||||
OptionsHighlighter.highlights[0]
|
OptionsHighlighter.highlights[0]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ManPageBuilder:
|
class ManPageBuilder:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.source = []
|
self.source = []
|
||||||
@ -57,6 +66,18 @@ class ManPageBuilder:
|
|||||||
def separate(self) -> None:
|
def separate(self) -> None:
|
||||||
self.source.append('.PP')
|
self.source.append('.PP')
|
||||||
|
|
||||||
|
def format_desc(self, desc: str) -> str:
|
||||||
|
description = _escape_and_dedent(desc)
|
||||||
|
description = OPTION_HIGHLIGHT_RE.sub(
|
||||||
|
# Boldify the option part, but don't remove the prefix (start of the match).
|
||||||
|
lambda match: match[1] + self.boldify(match['option']),
|
||||||
|
description
|
||||||
|
)
|
||||||
|
return description
|
||||||
|
|
||||||
|
def add_comment(self, comment: str) -> None:
|
||||||
|
self.source.append(f'.\\" {comment}')
|
||||||
|
|
||||||
def add_options(self, options: Iterable[str], *, metavar: Optional[str] = None) -> None:
|
def add_options(self, options: Iterable[str], *, metavar: Optional[str] = None) -> None:
|
||||||
text = ", ".join(map(self.boldify, options))
|
text = ", ".join(map(self.boldify, options))
|
||||||
if metavar:
|
if metavar:
|
||||||
@ -92,8 +113,13 @@ def _escape_and_dedent(text: str) -> str:
|
|||||||
return '\n'.join(lines).translate(ESCAPE_MAP)
|
return '\n'.join(lines).translate(ESCAPE_MAP)
|
||||||
|
|
||||||
|
|
||||||
def to_man_page(program_name: str, spec: ParserSpec) -> str:
|
def to_man_page(program_name: str, spec: ParserSpec, *, is_top_level_cmd: bool = False) -> str:
|
||||||
builder = ManPageBuilder()
|
builder = ManPageBuilder()
|
||||||
|
builder.add_comment(
|
||||||
|
f"This file is auto-generated from the parser declaration "
|
||||||
|
+ (f"in {Path(spec.source_file).relative_to(PROJECT_ROOT)} " if spec.source_file else "")
|
||||||
|
+ f"by {Path(__file__).relative_to(PROJECT_ROOT)}."
|
||||||
|
)
|
||||||
|
|
||||||
builder.title_line(
|
builder.title_line(
|
||||||
full_name='HTTPie',
|
full_name='HTTPie',
|
||||||
@ -104,10 +130,19 @@ def to_man_page(program_name: str, spec: ParserSpec) -> str:
|
|||||||
builder.set_name(program_name)
|
builder.set_name(program_name)
|
||||||
|
|
||||||
with builder.section('SYNOPSIS'):
|
with builder.section('SYNOPSIS'):
|
||||||
builder.write(render_as_string(to_usage(spec, program_name=program_name)))
|
# `http` and `https` are commands that can be directly used, so they can have
|
||||||
|
# a valid usage. But `httpie` is a top-level command with multiple sub commands,
|
||||||
|
# so for the synopsis we'll only reference the `httpie` name.
|
||||||
|
if is_top_level_cmd:
|
||||||
|
synopsis = program_name
|
||||||
|
else:
|
||||||
|
synopsis = render_as_string(to_usage(spec, program_name=program_name))
|
||||||
|
builder.write(synopsis)
|
||||||
|
|
||||||
with builder.section('DESCRIPTION'):
|
with builder.section('DESCRIPTION'):
|
||||||
builder.write(spec.description)
|
builder.write(spec.description)
|
||||||
|
if spec.man_page_hint:
|
||||||
|
builder.write(spec.man_page_hint)
|
||||||
|
|
||||||
for index, group in enumerate(spec.groups, 1):
|
for index, group in enumerate(spec.groups, 1):
|
||||||
with builder.section(group.name):
|
with builder.section(group.name):
|
||||||
@ -124,32 +159,29 @@ def to_man_page(program_name: str, spec: ParserSpec) -> str:
|
|||||||
if raw_arg.get('is_positional'):
|
if raw_arg.get('is_positional'):
|
||||||
# In case of positional arguments, metavar is always equal
|
# In case of positional arguments, metavar is always equal
|
||||||
# to the list of options (e.g `METHOD`).
|
# to the list of options (e.g `METHOD`).
|
||||||
metavar = None
|
metavar = None
|
||||||
builder.add_options(raw_arg['options'], metavar=metavar)
|
builder.add_options(raw_arg['options'], metavar=metavar)
|
||||||
|
|
||||||
description = _escape_and_dedent(raw_arg.get('description', ''))
|
desc = builder.format_desc(raw_arg.get('description', ''))
|
||||||
description = OPTION_HIGHLIGHT_RE.sub(
|
builder.write('\n' + desc + '\n')
|
||||||
lambda match: builder.boldify(match['option']),
|
|
||||||
description
|
|
||||||
)
|
|
||||||
builder.write('\n' + description + '\n')
|
|
||||||
|
|
||||||
builder.separate()
|
builder.separate()
|
||||||
|
|
||||||
|
if spec.epilog:
|
||||||
|
with builder.section('SEE ALSO'):
|
||||||
|
builder.write(builder.format_desc(spec.epilog))
|
||||||
|
|
||||||
|
|
||||||
return builder.build()
|
return builder.build()
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
for program_name, spec in [
|
for program_name, spec, config in [
|
||||||
('http', core_options),
|
('http', core_options, {}),
|
||||||
('https', core_options),
|
('https', core_options, {}),
|
||||||
('httpie', manager_options),
|
('httpie', manager_options, {'is_top_level_cmd': True}),
|
||||||
]:
|
]:
|
||||||
with open((MAN_PAGE_PATH / program_name).with_suffix('.1'), 'w') as stream:
|
with open((MAN_PAGE_PATH / program_name).with_suffix('.1'), 'w') as stream:
|
||||||
stream.write(to_man_page(program_name, spec))
|
stream.write(to_man_page(program_name, spec, **config))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -3,7 +3,7 @@ HTTPie: modern, user-friendly command-line HTTP client for the API era.
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
__version__ = '3.1.1.dev0'
|
__version__ = '3.2.4'
|
||||||
__date__ = '2022-03-08'
|
__date__ = '2024-11-01'
|
||||||
__author__ = 'Jakub Roztocil'
|
__author__ = 'Jakub Roztocil'
|
||||||
__licence__ = 'BSD'
|
__licence__ = 'BSD'
|
||||||
|
@ -572,12 +572,6 @@ class HTTPieArgumentParser(BaseHTTPieArgumentParser):
|
|||||||
highlight=False
|
highlight=False
|
||||||
)
|
)
|
||||||
|
|
||||||
def print_help(self):
|
|
||||||
from httpie.output.ui import rich_help
|
|
||||||
|
|
||||||
for renderable in rich_help.to_help_message(self.spec):
|
|
||||||
self.env.rich_console.print(renderable)
|
|
||||||
|
|
||||||
def print_usage(self, file):
|
def print_usage(self, file):
|
||||||
from rich.text import Text
|
from rich.text import Text
|
||||||
from httpie.output.ui import rich_help
|
from httpie.output.ui import rich_help
|
||||||
|
@ -132,10 +132,3 @@ class RequestType(enum.Enum):
|
|||||||
FORM = enum.auto()
|
FORM = enum.auto()
|
||||||
MULTIPART = enum.auto()
|
MULTIPART = enum.auto()
|
||||||
JSON = enum.auto()
|
JSON = enum.auto()
|
||||||
|
|
||||||
|
|
||||||
EMPTY_STRING = ''
|
|
||||||
OPEN_BRACKET = '['
|
|
||||||
CLOSE_BRACKET = ']'
|
|
||||||
BACKSLASH = '\\'
|
|
||||||
HIGHLIGHTER = '^'
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
import textwrap
|
import textwrap
|
||||||
from argparse import FileType
|
from argparse import FileType
|
||||||
|
|
||||||
@ -20,25 +21,27 @@ from httpie.output.formatters.colors import (AUTO_STYLE, DEFAULT_STYLE, BUNDLED_
|
|||||||
get_available_styles)
|
get_available_styles)
|
||||||
from httpie.plugins.builtin import BuiltinAuthPlugin
|
from httpie.plugins.builtin import BuiltinAuthPlugin
|
||||||
from httpie.plugins.registry import plugin_manager
|
from httpie.plugins.registry import plugin_manager
|
||||||
from httpie.ssl_ import AVAILABLE_SSL_VERSION_ARG_MAPPING, DEFAULT_SSL_CIPHERS
|
from httpie.ssl_ import AVAILABLE_SSL_VERSION_ARG_MAPPING, DEFAULT_SSL_CIPHERS_STRING
|
||||||
|
|
||||||
|
|
||||||
|
# Man pages are static (built when making a release).
|
||||||
|
# We use this check to not include generated, system-specific information there (e.g., default --ciphers).
|
||||||
|
IS_MAN_PAGE = bool(os.environ.get('HTTPIE_BUILDING_MAN_PAGES'))
|
||||||
|
|
||||||
|
|
||||||
options = ParserSpec(
|
options = ParserSpec(
|
||||||
'http',
|
'http',
|
||||||
description=f'{__doc__.strip()} <https://httpie.io>',
|
description=f'{__doc__.strip()} <https://httpie.io>',
|
||||||
epilog="""
|
epilog="""
|
||||||
To learn more, you can try:
|
|
||||||
-> running 'http --manual'
|
|
||||||
-> visiting our full documentation at https://httpie.io/docs/cli
|
|
||||||
|
|
||||||
For every --OPTION there is also a --no-OPTION that reverts OPTION
|
For every --OPTION there is also a --no-OPTION that reverts OPTION
|
||||||
to its default value.
|
to its default value.
|
||||||
|
|
||||||
Suggestions and bug reports are greatly appreciated:
|
Suggestions and bug reports are greatly appreciated:
|
||||||
https://github.com/httpie/httpie/issues
|
https://github.com/httpie/cli/issues
|
||||||
""",
|
""",
|
||||||
|
source_file=__file__
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# Positional arguments.
|
# Positional arguments.
|
||||||
#######################################################################
|
#######################################################################
|
||||||
@ -237,6 +240,7 @@ processing_options.add_argument(
|
|||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# Output processing
|
# Output processing
|
||||||
#######################################################################
|
#######################################################################
|
||||||
@ -613,6 +617,7 @@ sessions.add_argument(
|
|||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# Authentication
|
# Authentication
|
||||||
#######################################################################
|
#######################################################################
|
||||||
@ -633,7 +638,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
|
|||||||
if issubclass(auth_plugin, BuiltinAuthPlugin)
|
if issubclass(auth_plugin, BuiltinAuthPlugin)
|
||||||
]
|
]
|
||||||
text += '\n'
|
text += '\n'
|
||||||
text += 'For finding out all available authentication types in your system, try:\n\n'
|
text += 'To see all available auth types on your system, including ones installed via plugins, run:\n\n'
|
||||||
text += ' $ http --auth-type'
|
text += ' $ http --auth-type'
|
||||||
|
|
||||||
auth_types = '\n\n '.join(
|
auth_types = '\n\n '.join(
|
||||||
@ -649,7 +654,7 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False):
|
|||||||
''
|
''
|
||||||
if not plugin.description
|
if not plugin.description
|
||||||
else '\n '
|
else '\n '
|
||||||
+ ('\n '.join(textwrap.wrap(plugin.description)))
|
+ ('\n '.join(textwrap.wrap(plugin.description)))
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
for plugin in auth_plugins
|
for plugin in auth_plugins
|
||||||
@ -829,23 +834,36 @@ ssl.add_argument(
|
|||||||
|
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
CIPHERS_CURRENT_DEFAULTS = (
|
||||||
|
"""
|
||||||
|
See `http --help` for the default ciphers list on you system.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if IS_MAN_PAGE else
|
||||||
|
f"""
|
||||||
|
By default, the following ciphers are used on your system:
|
||||||
|
|
||||||
|
{DEFAULT_SSL_CIPHERS_STRING}
|
||||||
|
|
||||||
|
"""
|
||||||
|
)
|
||||||
ssl.add_argument(
|
ssl.add_argument(
|
||||||
'--ciphers',
|
'--ciphers',
|
||||||
short_help='A string in the OpenSSL cipher list format.',
|
short_help='A string in the OpenSSL cipher list format.',
|
||||||
help=f"""
|
help=f"""
|
||||||
|
|
||||||
A string in the OpenSSL cipher list format. By default, the following
|
A string in the OpenSSL cipher list format.
|
||||||
is used:
|
|
||||||
|
|
||||||
{DEFAULT_SSL_CIPHERS}
|
{CIPHERS_CURRENT_DEFAULTS}
|
||||||
|
|
||||||
""",
|
"""
|
||||||
)
|
)
|
||||||
ssl.add_argument(
|
ssl.add_argument(
|
||||||
'--cert',
|
'--cert',
|
||||||
default=None,
|
default=None,
|
||||||
type=readable_file_arg,
|
type=readable_file_arg,
|
||||||
short_help='Specifys a local cert to use as client side SSL certificate.',
|
short_help='Specifies a local cert to use as the client-side SSL certificate.',
|
||||||
help="""
|
help="""
|
||||||
You can specify a local cert to use as client side SSL certificate.
|
You can specify a local cert to use as client side SSL certificate.
|
||||||
This file may either contain both private key and certificate or you may
|
This file may either contain both private key and certificate or you may
|
||||||
|
@ -92,7 +92,3 @@ class MultipartRequestDataDict(MultiValueOrderedDict):
|
|||||||
|
|
||||||
class RequestFilesDict(RequestDataDict):
|
class RequestFilesDict(RequestDataDict):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class NestedJSONArray(list):
|
|
||||||
"""Denotes a top-level JSON array."""
|
|
||||||
|
@ -1,392 +0,0 @@
|
|||||||
from enum import Enum, auto
|
|
||||||
from typing import (
|
|
||||||
Any,
|
|
||||||
Iterator,
|
|
||||||
NamedTuple,
|
|
||||||
Optional,
|
|
||||||
List,
|
|
||||||
NoReturn,
|
|
||||||
Type,
|
|
||||||
Union,
|
|
||||||
)
|
|
||||||
from httpie.cli.dicts import NestedJSONArray
|
|
||||||
from httpie.cli.constants import EMPTY_STRING, OPEN_BRACKET, CLOSE_BRACKET, BACKSLASH, HIGHLIGHTER
|
|
||||||
|
|
||||||
|
|
||||||
class HTTPieSyntaxError(ValueError):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
source: str,
|
|
||||||
token: Optional['Token'],
|
|
||||||
message: str,
|
|
||||||
message_kind: str = 'Syntax',
|
|
||||||
) -> None:
|
|
||||||
self.source = source
|
|
||||||
self.token = token
|
|
||||||
self.message = message
|
|
||||||
self.message_kind = message_kind
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
lines = [f'HTTPie {self.message_kind} Error: {self.message}']
|
|
||||||
if self.token is not None:
|
|
||||||
lines.append(self.source)
|
|
||||||
lines.append(
|
|
||||||
' ' * (self.token.start)
|
|
||||||
+ HIGHLIGHTER * (self.token.end - self.token.start)
|
|
||||||
)
|
|
||||||
return '\n'.join(lines)
|
|
||||||
|
|
||||||
|
|
||||||
class TokenKind(Enum):
|
|
||||||
TEXT = auto()
|
|
||||||
NUMBER = auto()
|
|
||||||
LEFT_BRACKET = auto()
|
|
||||||
RIGHT_BRACKET = auto()
|
|
||||||
|
|
||||||
def to_name(self) -> str:
|
|
||||||
for key, value in OPERATORS.items():
|
|
||||||
if value is self:
|
|
||||||
return repr(key)
|
|
||||||
else:
|
|
||||||
return 'a ' + self.name.lower()
|
|
||||||
|
|
||||||
|
|
||||||
OPERATORS = {OPEN_BRACKET: TokenKind.LEFT_BRACKET, CLOSE_BRACKET: TokenKind.RIGHT_BRACKET}
|
|
||||||
SPECIAL_CHARS = OPERATORS.keys() | {BACKSLASH}
|
|
||||||
LITERAL_TOKENS = [TokenKind.TEXT, TokenKind.NUMBER]
|
|
||||||
|
|
||||||
|
|
||||||
class Token(NamedTuple):
|
|
||||||
kind: TokenKind
|
|
||||||
value: Union[str, int]
|
|
||||||
start: int
|
|
||||||
end: int
|
|
||||||
|
|
||||||
|
|
||||||
def assert_cant_happen() -> NoReturn:
|
|
||||||
raise ValueError('Unexpected value')
|
|
||||||
|
|
||||||
|
|
||||||
def check_escaped_int(value: str) -> str:
|
|
||||||
if not value.startswith(BACKSLASH):
|
|
||||||
raise ValueError('Not an escaped int')
|
|
||||||
|
|
||||||
try:
|
|
||||||
int(value[1:])
|
|
||||||
except ValueError as exc:
|
|
||||||
raise ValueError('Not an escaped int') from exc
|
|
||||||
else:
|
|
||||||
return value[1:]
|
|
||||||
|
|
||||||
|
|
||||||
def tokenize(source: str) -> Iterator[Token]:
|
|
||||||
cursor = 0
|
|
||||||
backslashes = 0
|
|
||||||
buffer = []
|
|
||||||
|
|
||||||
def send_buffer() -> Iterator[Token]:
|
|
||||||
nonlocal backslashes
|
|
||||||
if not buffer:
|
|
||||||
return None
|
|
||||||
|
|
||||||
value = ''.join(buffer)
|
|
||||||
kind = TokenKind.TEXT
|
|
||||||
if not backslashes:
|
|
||||||
for variation, kind in [
|
|
||||||
(int, TokenKind.NUMBER),
|
|
||||||
(check_escaped_int, TokenKind.TEXT),
|
|
||||||
]:
|
|
||||||
try:
|
|
||||||
value = variation(value)
|
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
|
|
||||||
yield Token(
|
|
||||||
kind, value, start=cursor - (len(buffer) + backslashes), end=cursor
|
|
||||||
)
|
|
||||||
buffer.clear()
|
|
||||||
backslashes = 0
|
|
||||||
|
|
||||||
def can_advance() -> bool:
|
|
||||||
return cursor < len(source)
|
|
||||||
|
|
||||||
while can_advance():
|
|
||||||
index = source[cursor]
|
|
||||||
if index in OPERATORS:
|
|
||||||
yield from send_buffer()
|
|
||||||
yield Token(OPERATORS[index], index, cursor, cursor + 1)
|
|
||||||
elif index == BACKSLASH and can_advance():
|
|
||||||
if source[cursor + 1] in SPECIAL_CHARS:
|
|
||||||
backslashes += 1
|
|
||||||
else:
|
|
||||||
buffer.append(index)
|
|
||||||
|
|
||||||
buffer.append(source[cursor + 1])
|
|
||||||
cursor += 1
|
|
||||||
else:
|
|
||||||
buffer.append(index)
|
|
||||||
|
|
||||||
cursor += 1
|
|
||||||
|
|
||||||
yield from send_buffer()
|
|
||||||
|
|
||||||
|
|
||||||
class PathAction(Enum):
|
|
||||||
KEY = auto()
|
|
||||||
INDEX = auto()
|
|
||||||
APPEND = auto()
|
|
||||||
|
|
||||||
# Pseudo action, used by the interpreter
|
|
||||||
SET = auto()
|
|
||||||
|
|
||||||
def to_string(self) -> str:
|
|
||||||
return self.name.lower()
|
|
||||||
|
|
||||||
|
|
||||||
class Path:
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
kind: PathAction,
|
|
||||||
accessor: Optional[Union[str, int]] = None,
|
|
||||||
tokens: Optional[List[Token]] = None,
|
|
||||||
is_root: bool = False,
|
|
||||||
):
|
|
||||||
self.kind = kind
|
|
||||||
self.accessor = accessor
|
|
||||||
self.tokens = tokens or []
|
|
||||||
self.is_root = is_root
|
|
||||||
|
|
||||||
def reconstruct(self) -> str:
|
|
||||||
if self.kind is PathAction.KEY:
|
|
||||||
if self.is_root:
|
|
||||||
return str(self.accessor)
|
|
||||||
return OPEN_BRACKET + self.accessor + CLOSE_BRACKET
|
|
||||||
elif self.kind is PathAction.INDEX:
|
|
||||||
return OPEN_BRACKET + str(self.accessor) + CLOSE_BRACKET
|
|
||||||
elif self.kind is PathAction.APPEND:
|
|
||||||
return OPEN_BRACKET + CLOSE_BRACKET
|
|
||||||
else:
|
|
||||||
assert_cant_happen()
|
|
||||||
|
|
||||||
|
|
||||||
def parse(source: str) -> Iterator[Path]:
|
|
||||||
"""
|
|
||||||
start: root_path path*
|
|
||||||
root_path: (literal | index_path | append_path)
|
|
||||||
literal: TEXT | NUMBER
|
|
||||||
|
|
||||||
path:
|
|
||||||
key_path
|
|
||||||
| index_path
|
|
||||||
| append_path
|
|
||||||
key_path: LEFT_BRACKET TEXT RIGHT_BRACKET
|
|
||||||
index_path: LEFT_BRACKET NUMBER RIGHT_BRACKET
|
|
||||||
append_path: LEFT_BRACKET RIGHT_BRACKET
|
|
||||||
"""
|
|
||||||
|
|
||||||
tokens = list(tokenize(source))
|
|
||||||
cursor = 0
|
|
||||||
|
|
||||||
def can_advance():
|
|
||||||
return cursor < len(tokens)
|
|
||||||
|
|
||||||
def expect(*kinds):
|
|
||||||
nonlocal cursor
|
|
||||||
|
|
||||||
assert len(kinds) > 0
|
|
||||||
if can_advance():
|
|
||||||
token = tokens[cursor]
|
|
||||||
cursor += 1
|
|
||||||
if token.kind in kinds:
|
|
||||||
return token
|
|
||||||
elif tokens:
|
|
||||||
token = tokens[-1]._replace(
|
|
||||||
start=tokens[-1].end + 0, end=tokens[-1].end + 1
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
token = None
|
|
||||||
|
|
||||||
if len(kinds) == 1:
|
|
||||||
suffix = kinds[0].to_name()
|
|
||||||
else:
|
|
||||||
suffix = ', '.join(kind.to_name() for kind in kinds[:-1])
|
|
||||||
suffix += ' or ' + kinds[-1].to_name()
|
|
||||||
|
|
||||||
message = f'Expecting {suffix}'
|
|
||||||
raise HTTPieSyntaxError(source, token, message)
|
|
||||||
|
|
||||||
def parse_root():
|
|
||||||
tokens = []
|
|
||||||
if not can_advance():
|
|
||||||
return Path(
|
|
||||||
PathAction.KEY,
|
|
||||||
EMPTY_STRING,
|
|
||||||
is_root=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# (literal | index_path | append_path)?
|
|
||||||
token = expect(*LITERAL_TOKENS, TokenKind.LEFT_BRACKET)
|
|
||||||
tokens.append(token)
|
|
||||||
|
|
||||||
if token.kind in LITERAL_TOKENS:
|
|
||||||
action = PathAction.KEY
|
|
||||||
value = str(token.value)
|
|
||||||
elif token.kind is TokenKind.LEFT_BRACKET:
|
|
||||||
token = expect(TokenKind.NUMBER, TokenKind.RIGHT_BRACKET)
|
|
||||||
tokens.append(token)
|
|
||||||
if token.kind is TokenKind.NUMBER:
|
|
||||||
action = PathAction.INDEX
|
|
||||||
value = token.value
|
|
||||||
tokens.append(expect(TokenKind.RIGHT_BRACKET))
|
|
||||||
elif token.kind is TokenKind.RIGHT_BRACKET:
|
|
||||||
action = PathAction.APPEND
|
|
||||||
value = None
|
|
||||||
else:
|
|
||||||
assert_cant_happen()
|
|
||||||
else:
|
|
||||||
assert_cant_happen()
|
|
||||||
|
|
||||||
return Path(
|
|
||||||
action,
|
|
||||||
value,
|
|
||||||
tokens=tokens,
|
|
||||||
is_root=True
|
|
||||||
)
|
|
||||||
|
|
||||||
yield parse_root()
|
|
||||||
|
|
||||||
# path*
|
|
||||||
while can_advance():
|
|
||||||
path_tokens = []
|
|
||||||
path_tokens.append(expect(TokenKind.LEFT_BRACKET))
|
|
||||||
|
|
||||||
token = expect(
|
|
||||||
TokenKind.TEXT, TokenKind.NUMBER, TokenKind.RIGHT_BRACKET
|
|
||||||
)
|
|
||||||
path_tokens.append(token)
|
|
||||||
if token.kind is TokenKind.RIGHT_BRACKET:
|
|
||||||
path = Path(PathAction.APPEND, tokens=path_tokens)
|
|
||||||
elif token.kind is TokenKind.TEXT:
|
|
||||||
path = Path(PathAction.KEY, token.value, tokens=path_tokens)
|
|
||||||
path_tokens.append(expect(TokenKind.RIGHT_BRACKET))
|
|
||||||
elif token.kind is TokenKind.NUMBER:
|
|
||||||
path = Path(PathAction.INDEX, token.value, tokens=path_tokens)
|
|
||||||
path_tokens.append(expect(TokenKind.RIGHT_BRACKET))
|
|
||||||
else:
|
|
||||||
assert_cant_happen()
|
|
||||||
yield path
|
|
||||||
|
|
||||||
|
|
||||||
JSON_TYPE_MAPPING = {
|
|
||||||
dict: 'object',
|
|
||||||
list: 'array',
|
|
||||||
int: 'number',
|
|
||||||
float: 'number',
|
|
||||||
str: 'string',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def interpret(context: Any, key: str, value: Any) -> Any:
|
|
||||||
cursor = context
|
|
||||||
|
|
||||||
paths = list(parse(key))
|
|
||||||
paths.append(Path(PathAction.SET, value))
|
|
||||||
|
|
||||||
def type_check(index: int, path: Path, expected_type: Type[Any]) -> None:
|
|
||||||
if not isinstance(cursor, expected_type):
|
|
||||||
if path.tokens:
|
|
||||||
pseudo_token = Token(
|
|
||||||
None, None, path.tokens[0].start, path.tokens[-1].end
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
pseudo_token = None
|
|
||||||
|
|
||||||
cursor_type = JSON_TYPE_MAPPING.get(
|
|
||||||
type(cursor), type(cursor).__name__
|
|
||||||
)
|
|
||||||
required_type = JSON_TYPE_MAPPING[expected_type]
|
|
||||||
|
|
||||||
message = f"Can't perform {path.kind.to_string()!r} based access on "
|
|
||||||
message += repr(
|
|
||||||
''.join(path.reconstruct() for path in paths[:index])
|
|
||||||
)
|
|
||||||
message += (
|
|
||||||
f' which has a type of {cursor_type!r} but this operation'
|
|
||||||
)
|
|
||||||
message += f' requires a type of {required_type!r}.'
|
|
||||||
raise HTTPieSyntaxError(
|
|
||||||
key, pseudo_token, message, message_kind='Type'
|
|
||||||
)
|
|
||||||
|
|
||||||
def object_for(kind: str) -> Any:
|
|
||||||
if kind is PathAction.KEY:
|
|
||||||
return {}
|
|
||||||
elif kind in {PathAction.INDEX, PathAction.APPEND}:
|
|
||||||
return []
|
|
||||||
else:
|
|
||||||
assert_cant_happen()
|
|
||||||
|
|
||||||
for index, (path, next_path) in enumerate(zip(paths, paths[1:])):
|
|
||||||
# If there is no context yet, set it.
|
|
||||||
if cursor is None:
|
|
||||||
context = cursor = object_for(path.kind)
|
|
||||||
|
|
||||||
if path.kind is PathAction.KEY:
|
|
||||||
type_check(index, path, dict)
|
|
||||||
if next_path.kind is PathAction.SET:
|
|
||||||
cursor[path.accessor] = next_path.accessor
|
|
||||||
break
|
|
||||||
|
|
||||||
cursor = cursor.setdefault(
|
|
||||||
path.accessor, object_for(next_path.kind)
|
|
||||||
)
|
|
||||||
elif path.kind is PathAction.INDEX:
|
|
||||||
type_check(index, path, list)
|
|
||||||
if path.accessor < 0:
|
|
||||||
raise HTTPieSyntaxError(
|
|
||||||
key,
|
|
||||||
path.tokens[1],
|
|
||||||
'Negative indexes are not supported.',
|
|
||||||
message_kind='Value',
|
|
||||||
)
|
|
||||||
cursor.extend([None] * (path.accessor - len(cursor) + 1))
|
|
||||||
if next_path.kind is PathAction.SET:
|
|
||||||
cursor[path.accessor] = next_path.accessor
|
|
||||||
break
|
|
||||||
|
|
||||||
if cursor[path.accessor] is None:
|
|
||||||
cursor[path.accessor] = object_for(next_path.kind)
|
|
||||||
|
|
||||||
cursor = cursor[path.accessor]
|
|
||||||
elif path.kind is PathAction.APPEND:
|
|
||||||
type_check(index, path, list)
|
|
||||||
if next_path.kind is PathAction.SET:
|
|
||||||
cursor.append(next_path.accessor)
|
|
||||||
break
|
|
||||||
|
|
||||||
cursor.append(object_for(next_path.kind))
|
|
||||||
cursor = cursor[-1]
|
|
||||||
else:
|
|
||||||
assert_cant_happen()
|
|
||||||
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
def wrap_with_dict(context):
|
|
||||||
if context is None:
|
|
||||||
return {}
|
|
||||||
elif isinstance(context, list):
|
|
||||||
return {EMPTY_STRING: NestedJSONArray(context)}
|
|
||||||
else:
|
|
||||||
assert isinstance(context, dict)
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
def interpret_nested_json(pairs):
|
|
||||||
context = None
|
|
||||||
for key, value in pairs:
|
|
||||||
context = interpret(context, key, value)
|
|
||||||
|
|
||||||
return wrap_with_dict(context)
|
|
20
httpie/cli/nested_json/__init__.py
Normal file
20
httpie/cli/nested_json/__init__.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
"""
|
||||||
|
A library for parsing the HTTPie nested JSON key syntax and constructing the resulting objects.
|
||||||
|
|
||||||
|
<https://httpie.io/docs/cli/nested-json>
|
||||||
|
|
||||||
|
It has no dependencies.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from .interpret import interpret_nested_json, unwrap_top_level_list_if_needed
|
||||||
|
from .errors import NestedJSONSyntaxError
|
||||||
|
from .tokens import EMPTY_STRING, NestedJSONArray
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'interpret_nested_json',
|
||||||
|
'unwrap_top_level_list_if_needed',
|
||||||
|
'EMPTY_STRING',
|
||||||
|
'NestedJSONArray',
|
||||||
|
'NestedJSONSyntaxError'
|
||||||
|
]
|
27
httpie/cli/nested_json/errors.py
Normal file
27
httpie/cli/nested_json/errors.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from .tokens import Token, HIGHLIGHTER
|
||||||
|
|
||||||
|
|
||||||
|
class NestedJSONSyntaxError(ValueError):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
source: str,
|
||||||
|
token: Optional[Token],
|
||||||
|
message: str,
|
||||||
|
message_kind: str = 'Syntax',
|
||||||
|
) -> None:
|
||||||
|
self.source = source
|
||||||
|
self.token = token
|
||||||
|
self.message = message
|
||||||
|
self.message_kind = message_kind
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
lines = [f'HTTPie {self.message_kind} Error: {self.message}']
|
||||||
|
if self.token is not None:
|
||||||
|
lines.append(self.source)
|
||||||
|
lines.append(
|
||||||
|
' ' * self.token.start
|
||||||
|
+ HIGHLIGHTER * (self.token.end - self.token.start)
|
||||||
|
)
|
||||||
|
return '\n'.join(lines)
|
129
httpie/cli/nested_json/interpret.py
Normal file
129
httpie/cli/nested_json/interpret.py
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
from typing import Type, Union, Any, Iterable, Tuple
|
||||||
|
|
||||||
|
from .parse import parse, assert_cant_happen
|
||||||
|
from .errors import NestedJSONSyntaxError
|
||||||
|
from .tokens import EMPTY_STRING, TokenKind, Token, PathAction, Path, NestedJSONArray
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'interpret_nested_json',
|
||||||
|
'unwrap_top_level_list_if_needed',
|
||||||
|
]
|
||||||
|
|
||||||
|
JSONType = Type[Union[dict, list, int, float, str]]
|
||||||
|
JSON_TYPE_MAPPING = {
|
||||||
|
dict: 'object',
|
||||||
|
list: 'array',
|
||||||
|
int: 'number',
|
||||||
|
float: 'number',
|
||||||
|
str: 'string',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def interpret_nested_json(pairs: Iterable[Tuple[str, str]]) -> dict:
|
||||||
|
context = None
|
||||||
|
for key, value in pairs:
|
||||||
|
context = interpret(context, key, value)
|
||||||
|
return wrap_with_dict(context)
|
||||||
|
|
||||||
|
|
||||||
|
def interpret(context: Any, key: str, value: Any) -> Any:
|
||||||
|
cursor = context
|
||||||
|
paths = list(parse(key))
|
||||||
|
paths.append(Path(PathAction.SET, value))
|
||||||
|
|
||||||
|
# noinspection PyShadowingNames
|
||||||
|
def type_check(index: int, path: Path, expected_type: JSONType):
|
||||||
|
if not isinstance(cursor, expected_type):
|
||||||
|
if path.tokens:
|
||||||
|
pseudo_token = Token(
|
||||||
|
kind=TokenKind.PSEUDO,
|
||||||
|
value='',
|
||||||
|
start=path.tokens[0].start,
|
||||||
|
end=path.tokens[-1].end,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
pseudo_token = None
|
||||||
|
cursor_type = JSON_TYPE_MAPPING.get(type(cursor), type(cursor).__name__)
|
||||||
|
required_type = JSON_TYPE_MAPPING[expected_type]
|
||||||
|
message = f'Cannot perform {path.kind.to_string()!r} based access on '
|
||||||
|
message += repr(''.join(path.reconstruct() for path in paths[:index]))
|
||||||
|
message += f' which has a type of {cursor_type!r} but this operation'
|
||||||
|
message += f' requires a type of {required_type!r}.'
|
||||||
|
raise NestedJSONSyntaxError(
|
||||||
|
source=key,
|
||||||
|
token=pseudo_token,
|
||||||
|
message=message,
|
||||||
|
message_kind='Type',
|
||||||
|
)
|
||||||
|
|
||||||
|
def object_for(kind: PathAction) -> Any:
|
||||||
|
if kind is PathAction.KEY:
|
||||||
|
return {}
|
||||||
|
elif kind in {PathAction.INDEX, PathAction.APPEND}:
|
||||||
|
return []
|
||||||
|
else:
|
||||||
|
assert_cant_happen()
|
||||||
|
|
||||||
|
for index, (path, next_path) in enumerate(zip(paths, paths[1:])):
|
||||||
|
# If there is no context yet, set it.
|
||||||
|
if cursor is None:
|
||||||
|
context = cursor = object_for(path.kind)
|
||||||
|
if path.kind is PathAction.KEY:
|
||||||
|
type_check(index, path, dict)
|
||||||
|
if next_path.kind is PathAction.SET:
|
||||||
|
cursor[path.accessor] = next_path.accessor
|
||||||
|
break
|
||||||
|
cursor = cursor.setdefault(path.accessor, object_for(next_path.kind))
|
||||||
|
elif path.kind is PathAction.INDEX:
|
||||||
|
type_check(index, path, list)
|
||||||
|
if path.accessor < 0:
|
||||||
|
raise NestedJSONSyntaxError(
|
||||||
|
source=key,
|
||||||
|
token=path.tokens[1],
|
||||||
|
message='Negative indexes are not supported.',
|
||||||
|
message_kind='Value',
|
||||||
|
)
|
||||||
|
cursor.extend([None] * (path.accessor - len(cursor) + 1))
|
||||||
|
if next_path.kind is PathAction.SET:
|
||||||
|
cursor[path.accessor] = next_path.accessor
|
||||||
|
break
|
||||||
|
if cursor[path.accessor] is None:
|
||||||
|
cursor[path.accessor] = object_for(next_path.kind)
|
||||||
|
cursor = cursor[path.accessor]
|
||||||
|
elif path.kind is PathAction.APPEND:
|
||||||
|
type_check(index, path, list)
|
||||||
|
if next_path.kind is PathAction.SET:
|
||||||
|
cursor.append(next_path.accessor)
|
||||||
|
break
|
||||||
|
cursor.append(object_for(next_path.kind))
|
||||||
|
cursor = cursor[-1]
|
||||||
|
else:
|
||||||
|
assert_cant_happen()
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
def wrap_with_dict(context):
|
||||||
|
if context is None:
|
||||||
|
return {}
|
||||||
|
elif isinstance(context, list):
|
||||||
|
return {
|
||||||
|
EMPTY_STRING: NestedJSONArray(context),
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
assert isinstance(context, dict)
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
def unwrap_top_level_list_if_needed(data: dict):
|
||||||
|
"""
|
||||||
|
Propagate the top-level list, if that’s what we got.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if len(data) == 1:
|
||||||
|
key, value = list(data.items())[0]
|
||||||
|
if isinstance(value, NestedJSONArray):
|
||||||
|
assert key == EMPTY_STRING
|
||||||
|
return value
|
||||||
|
return data
|
193
httpie/cli/nested_json/parse.py
Normal file
193
httpie/cli/nested_json/parse.py
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
from typing import Iterator
|
||||||
|
|
||||||
|
from .errors import NestedJSONSyntaxError
|
||||||
|
from .tokens import (
|
||||||
|
EMPTY_STRING,
|
||||||
|
BACKSLASH,
|
||||||
|
TokenKind,
|
||||||
|
OPERATORS,
|
||||||
|
SPECIAL_CHARS,
|
||||||
|
LITERAL_TOKENS,
|
||||||
|
Token,
|
||||||
|
PathAction,
|
||||||
|
Path,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'parse',
|
||||||
|
'assert_cant_happen',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def parse(source: str) -> Iterator[Path]:
|
||||||
|
"""
|
||||||
|
start: root_path path*
|
||||||
|
root_path: (literal | index_path | append_path)
|
||||||
|
literal: TEXT | NUMBER
|
||||||
|
|
||||||
|
path:
|
||||||
|
key_path
|
||||||
|
| index_path
|
||||||
|
| append_path
|
||||||
|
key_path: LEFT_BRACKET TEXT RIGHT_BRACKET
|
||||||
|
index_path: LEFT_BRACKET NUMBER RIGHT_BRACKET
|
||||||
|
append_path: LEFT_BRACKET RIGHT_BRACKET
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
tokens = list(tokenize(source))
|
||||||
|
cursor = 0
|
||||||
|
|
||||||
|
def can_advance():
|
||||||
|
return cursor < len(tokens)
|
||||||
|
|
||||||
|
# noinspection PyShadowingNames
|
||||||
|
def expect(*kinds):
|
||||||
|
nonlocal cursor
|
||||||
|
assert kinds
|
||||||
|
if can_advance():
|
||||||
|
token = tokens[cursor]
|
||||||
|
cursor += 1
|
||||||
|
if token.kind in kinds:
|
||||||
|
return token
|
||||||
|
elif tokens:
|
||||||
|
token = tokens[-1]._replace(
|
||||||
|
start=tokens[-1].end + 0,
|
||||||
|
end=tokens[-1].end + 1,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
token = None
|
||||||
|
if len(kinds) == 1:
|
||||||
|
suffix = kinds[0].to_name()
|
||||||
|
else:
|
||||||
|
suffix = ', '.join(kind.to_name() for kind in kinds[:-1])
|
||||||
|
suffix += ' or ' + kinds[-1].to_name()
|
||||||
|
message = f'Expecting {suffix}'
|
||||||
|
raise NestedJSONSyntaxError(source, token, message)
|
||||||
|
|
||||||
|
# noinspection PyShadowingNames
|
||||||
|
def parse_root():
|
||||||
|
tokens = []
|
||||||
|
if not can_advance():
|
||||||
|
return Path(
|
||||||
|
kind=PathAction.KEY,
|
||||||
|
accessor=EMPTY_STRING,
|
||||||
|
is_root=True
|
||||||
|
)
|
||||||
|
# (literal | index_path | append_path)?
|
||||||
|
token = expect(*LITERAL_TOKENS, TokenKind.LEFT_BRACKET)
|
||||||
|
tokens.append(token)
|
||||||
|
if token.kind in LITERAL_TOKENS:
|
||||||
|
action = PathAction.KEY
|
||||||
|
value = str(token.value)
|
||||||
|
elif token.kind is TokenKind.LEFT_BRACKET:
|
||||||
|
token = expect(TokenKind.NUMBER, TokenKind.RIGHT_BRACKET)
|
||||||
|
tokens.append(token)
|
||||||
|
if token.kind is TokenKind.NUMBER:
|
||||||
|
action = PathAction.INDEX
|
||||||
|
value = token.value
|
||||||
|
tokens.append(expect(TokenKind.RIGHT_BRACKET))
|
||||||
|
elif token.kind is TokenKind.RIGHT_BRACKET:
|
||||||
|
action = PathAction.APPEND
|
||||||
|
value = None
|
||||||
|
else:
|
||||||
|
assert_cant_happen()
|
||||||
|
else:
|
||||||
|
assert_cant_happen()
|
||||||
|
# noinspection PyUnboundLocalVariable
|
||||||
|
return Path(
|
||||||
|
kind=action,
|
||||||
|
accessor=value,
|
||||||
|
tokens=tokens,
|
||||||
|
is_root=True
|
||||||
|
)
|
||||||
|
|
||||||
|
yield parse_root()
|
||||||
|
|
||||||
|
# path*
|
||||||
|
while can_advance():
|
||||||
|
path_tokens = [expect(TokenKind.LEFT_BRACKET)]
|
||||||
|
token = expect(TokenKind.TEXT, TokenKind.NUMBER, TokenKind.RIGHT_BRACKET)
|
||||||
|
path_tokens.append(token)
|
||||||
|
if token.kind is TokenKind.RIGHT_BRACKET:
|
||||||
|
path = Path(PathAction.APPEND, tokens=path_tokens)
|
||||||
|
elif token.kind is TokenKind.TEXT:
|
||||||
|
path = Path(PathAction.KEY, token.value, tokens=path_tokens)
|
||||||
|
path_tokens.append(expect(TokenKind.RIGHT_BRACKET))
|
||||||
|
elif token.kind is TokenKind.NUMBER:
|
||||||
|
path = Path(PathAction.INDEX, token.value, tokens=path_tokens)
|
||||||
|
path_tokens.append(expect(TokenKind.RIGHT_BRACKET))
|
||||||
|
else:
|
||||||
|
assert_cant_happen()
|
||||||
|
# noinspection PyUnboundLocalVariable
|
||||||
|
yield path
|
||||||
|
|
||||||
|
|
||||||
|
def tokenize(source: str) -> Iterator[Token]:
|
||||||
|
cursor = 0
|
||||||
|
backslashes = 0
|
||||||
|
buffer = []
|
||||||
|
|
||||||
|
def send_buffer() -> Iterator[Token]:
|
||||||
|
nonlocal backslashes
|
||||||
|
if not buffer:
|
||||||
|
return None
|
||||||
|
|
||||||
|
value = ''.join(buffer)
|
||||||
|
kind = TokenKind.TEXT
|
||||||
|
if not backslashes:
|
||||||
|
for variation, kind in [
|
||||||
|
(int, TokenKind.NUMBER),
|
||||||
|
(check_escaped_int, TokenKind.TEXT),
|
||||||
|
]:
|
||||||
|
try:
|
||||||
|
value = variation(value)
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
yield Token(
|
||||||
|
kind=kind,
|
||||||
|
value=value,
|
||||||
|
start=cursor - (len(buffer) + backslashes),
|
||||||
|
end=cursor,
|
||||||
|
)
|
||||||
|
buffer.clear()
|
||||||
|
backslashes = 0
|
||||||
|
|
||||||
|
def can_advance() -> bool:
|
||||||
|
return cursor < len(source)
|
||||||
|
|
||||||
|
while can_advance():
|
||||||
|
index = source[cursor]
|
||||||
|
if index in OPERATORS:
|
||||||
|
yield from send_buffer()
|
||||||
|
yield Token(OPERATORS[index], index, cursor, cursor + 1)
|
||||||
|
elif index == BACKSLASH and can_advance():
|
||||||
|
if source[cursor + 1] in SPECIAL_CHARS:
|
||||||
|
backslashes += 1
|
||||||
|
else:
|
||||||
|
buffer.append(index)
|
||||||
|
buffer.append(source[cursor + 1])
|
||||||
|
cursor += 1
|
||||||
|
else:
|
||||||
|
buffer.append(index)
|
||||||
|
cursor += 1
|
||||||
|
|
||||||
|
yield from send_buffer()
|
||||||
|
|
||||||
|
|
||||||
|
def check_escaped_int(value: str) -> str:
|
||||||
|
if not value.startswith(BACKSLASH):
|
||||||
|
raise ValueError('Not an escaped int')
|
||||||
|
try:
|
||||||
|
int(value[1:])
|
||||||
|
except ValueError as exc:
|
||||||
|
raise ValueError('Not an escaped int') from exc
|
||||||
|
else:
|
||||||
|
return value[1:]
|
||||||
|
|
||||||
|
|
||||||
|
def assert_cant_happen():
|
||||||
|
raise ValueError('Unexpected value')
|
80
httpie/cli/nested_json/tokens.py
Normal file
80
httpie/cli/nested_json/tokens.py
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
from enum import Enum, auto
|
||||||
|
from typing import NamedTuple, Union, Optional, List
|
||||||
|
|
||||||
|
EMPTY_STRING = ''
|
||||||
|
HIGHLIGHTER = '^'
|
||||||
|
OPEN_BRACKET = '['
|
||||||
|
CLOSE_BRACKET = ']'
|
||||||
|
BACKSLASH = '\\'
|
||||||
|
|
||||||
|
|
||||||
|
class TokenKind(Enum):
|
||||||
|
TEXT = auto()
|
||||||
|
NUMBER = auto()
|
||||||
|
LEFT_BRACKET = auto()
|
||||||
|
RIGHT_BRACKET = auto()
|
||||||
|
PSEUDO = auto() # Not a real token, use when representing location only.
|
||||||
|
|
||||||
|
def to_name(self) -> str:
|
||||||
|
for key, value in OPERATORS.items():
|
||||||
|
if value is self:
|
||||||
|
return repr(key)
|
||||||
|
else:
|
||||||
|
return 'a ' + self.name.lower()
|
||||||
|
|
||||||
|
|
||||||
|
OPERATORS = {
|
||||||
|
OPEN_BRACKET: TokenKind.LEFT_BRACKET,
|
||||||
|
CLOSE_BRACKET: TokenKind.RIGHT_BRACKET,
|
||||||
|
}
|
||||||
|
SPECIAL_CHARS = OPERATORS.keys() | {BACKSLASH}
|
||||||
|
LITERAL_TOKENS = [
|
||||||
|
TokenKind.TEXT,
|
||||||
|
TokenKind.NUMBER,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class Token(NamedTuple):
|
||||||
|
kind: TokenKind
|
||||||
|
value: Union[str, int]
|
||||||
|
start: int
|
||||||
|
end: int
|
||||||
|
|
||||||
|
|
||||||
|
class PathAction(Enum):
|
||||||
|
KEY = auto()
|
||||||
|
INDEX = auto()
|
||||||
|
APPEND = auto()
|
||||||
|
# Pseudo action, used by the interpreter
|
||||||
|
SET = auto()
|
||||||
|
|
||||||
|
def to_string(self) -> str:
|
||||||
|
return self.name.lower()
|
||||||
|
|
||||||
|
|
||||||
|
class Path:
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
kind: PathAction,
|
||||||
|
accessor: Optional[Union[str, int]] = None,
|
||||||
|
tokens: Optional[List[Token]] = None,
|
||||||
|
is_root: bool = False,
|
||||||
|
):
|
||||||
|
self.kind = kind
|
||||||
|
self.accessor = accessor
|
||||||
|
self.tokens = tokens or []
|
||||||
|
self.is_root = is_root
|
||||||
|
|
||||||
|
def reconstruct(self) -> str:
|
||||||
|
if self.kind is PathAction.KEY:
|
||||||
|
if self.is_root:
|
||||||
|
return str(self.accessor)
|
||||||
|
return OPEN_BRACKET + self.accessor + CLOSE_BRACKET
|
||||||
|
elif self.kind is PathAction.INDEX:
|
||||||
|
return OPEN_BRACKET + str(self.accessor) + CLOSE_BRACKET
|
||||||
|
elif self.kind is PathAction.APPEND:
|
||||||
|
return OPEN_BRACKET + CLOSE_BRACKET
|
||||||
|
|
||||||
|
|
||||||
|
class NestedJSONArray(list):
|
||||||
|
"""Denotes a top-level JSON array."""
|
@ -35,17 +35,6 @@ def drop_keys(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _get_first_line(source: str) -> str:
|
|
||||||
parts = []
|
|
||||||
for line in source.strip().splitlines():
|
|
||||||
line = line.strip()
|
|
||||||
parts.append(line)
|
|
||||||
if line.endswith("."):
|
|
||||||
break
|
|
||||||
|
|
||||||
return " ".join(parts)
|
|
||||||
|
|
||||||
|
|
||||||
PARSER_SPEC_VERSION = '0.0.1a0'
|
PARSER_SPEC_VERSION = '0.0.1a0'
|
||||||
|
|
||||||
|
|
||||||
@ -55,6 +44,8 @@ class ParserSpec:
|
|||||||
description: Optional[str] = None
|
description: Optional[str] = None
|
||||||
epilog: Optional[str] = None
|
epilog: Optional[str] = None
|
||||||
groups: List['Group'] = field(default_factory=list)
|
groups: List['Group'] = field(default_factory=list)
|
||||||
|
man_page_hint: Optional[str] = None
|
||||||
|
source_file: Optional[str] = None
|
||||||
|
|
||||||
def finalize(self) -> 'ParserSpec':
|
def finalize(self) -> 'ParserSpec':
|
||||||
if self.description:
|
if self.description:
|
||||||
@ -248,10 +239,11 @@ def to_data(abstract_options: ParserSpec) -> Dict[str, Any]:
|
|||||||
return {'version': PARSER_SPEC_VERSION, 'spec': abstract_options.serialize()}
|
return {'version': PARSER_SPEC_VERSION, 'spec': abstract_options.serialize()}
|
||||||
|
|
||||||
|
|
||||||
def parser_to_parser_spec(parser: argparse.ArgumentParser) -> ParserSpec:
|
def parser_to_parser_spec(parser: argparse.ArgumentParser, **kwargs) -> ParserSpec:
|
||||||
"""Take an existing argparse parser, and create a spec from it."""
|
"""Take an existing argparse parser, and create a spec from it."""
|
||||||
return ParserSpec(
|
return ParserSpec(
|
||||||
program=parser.prog,
|
program=parser.prog,
|
||||||
description=parser.description,
|
description=parser.description,
|
||||||
epilog=parser.epilog
|
epilog=parser.epilog,
|
||||||
|
**kwargs
|
||||||
)
|
)
|
||||||
|
@ -18,7 +18,7 @@ from .dicts import (
|
|||||||
)
|
)
|
||||||
from .exceptions import ParseError
|
from .exceptions import ParseError
|
||||||
from .nested_json import interpret_nested_json
|
from .nested_json import interpret_nested_json
|
||||||
from ..utils import get_content_type, load_json_preserve_order_and_dupe_keys, split
|
from ..utils import get_content_type, load_json_preserve_order_and_dupe_keys, split_iterable
|
||||||
|
|
||||||
|
|
||||||
class RequestItems:
|
class RequestItems:
|
||||||
@ -78,25 +78,28 @@ class RequestItems:
|
|||||||
instance.data,
|
instance.data,
|
||||||
),
|
),
|
||||||
SEPARATOR_DATA_RAW_JSON: (
|
SEPARATOR_DATA_RAW_JSON: (
|
||||||
json_only(instance, process_data_raw_json_embed_arg),
|
convert_json_value_to_form_if_needed(
|
||||||
|
in_json_mode=instance.is_json,
|
||||||
|
processor=process_data_raw_json_embed_arg
|
||||||
|
),
|
||||||
instance.data,
|
instance.data,
|
||||||
),
|
),
|
||||||
SEPARATOR_DATA_EMBED_RAW_JSON_FILE: (
|
SEPARATOR_DATA_EMBED_RAW_JSON_FILE: (
|
||||||
json_only(instance, process_data_embed_raw_json_file_arg),
|
convert_json_value_to_form_if_needed(
|
||||||
|
in_json_mode=instance.is_json,
|
||||||
|
processor=process_data_embed_raw_json_file_arg,
|
||||||
|
),
|
||||||
instance.data,
|
instance.data,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
if instance.is_json:
|
if instance.is_json:
|
||||||
json_item_args, request_item_args = split(
|
json_item_args, request_item_args = split_iterable(
|
||||||
request_item_args,
|
iterable=request_item_args,
|
||||||
lambda arg: arg.sep in SEPARATOR_GROUP_NESTED_JSON_ITEMS
|
key=lambda arg: arg.sep in SEPARATOR_GROUP_NESTED_JSON_ITEMS
|
||||||
)
|
)
|
||||||
if json_item_args:
|
if json_item_args:
|
||||||
pairs = [
|
pairs = [(arg.key, rules[arg.sep][0](arg)) for arg in json_item_args]
|
||||||
(arg.key, rules[arg.sep][0](arg))
|
|
||||||
for arg in json_item_args
|
|
||||||
]
|
|
||||||
processor_func, target_dict = rules[SEPARATOR_GROUP_NESTED_JSON_ITEMS]
|
processor_func, target_dict = rules[SEPARATOR_GROUP_NESTED_JSON_ITEMS]
|
||||||
value = processor_func(pairs)
|
value = processor_func(pairs)
|
||||||
target_dict.update(value)
|
target_dict.update(value)
|
||||||
@ -159,6 +162,30 @@ def process_file_upload_arg(arg: KeyValueArg) -> Tuple[str, IO, str]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_json_value_to_form_if_needed(in_json_mode: bool, processor: Callable[[KeyValueArg], JSONType]) -> Callable[[], str]:
|
||||||
|
"""
|
||||||
|
We allow primitive values to be passed to forms via JSON key/value syntax.
|
||||||
|
|
||||||
|
But complex values lead to an error because there’s no clear way to serialize them.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if in_json_mode:
|
||||||
|
return processor
|
||||||
|
|
||||||
|
@functools.wraps(processor)
|
||||||
|
def wrapper(*args, **kwargs) -> str:
|
||||||
|
try:
|
||||||
|
output = processor(*args, **kwargs)
|
||||||
|
except ParseError:
|
||||||
|
output = None
|
||||||
|
if isinstance(output, (str, int, float)):
|
||||||
|
return str(output)
|
||||||
|
else:
|
||||||
|
raise ParseError('Cannot use complex JSON value types with --form/--multipart.')
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
def process_data_item_arg(arg: KeyValueArg) -> str:
|
def process_data_item_arg(arg: KeyValueArg) -> str:
|
||||||
return arg.value
|
return arg.value
|
||||||
|
|
||||||
@ -167,29 +194,6 @@ def process_data_embed_file_contents_arg(arg: KeyValueArg) -> str:
|
|||||||
return load_text_file(arg)
|
return load_text_file(arg)
|
||||||
|
|
||||||
|
|
||||||
def json_only(items: RequestItems, func: Callable[[KeyValueArg], JSONType]) -> str:
|
|
||||||
if items.is_json:
|
|
||||||
return func
|
|
||||||
|
|
||||||
@functools.wraps(func)
|
|
||||||
def wrapper(*args, **kwargs) -> str:
|
|
||||||
try:
|
|
||||||
ret = func(*args, **kwargs)
|
|
||||||
except ParseError:
|
|
||||||
ret = None
|
|
||||||
|
|
||||||
# If it is a basic type, then allow it
|
|
||||||
if isinstance(ret, (str, int, float)):
|
|
||||||
return str(ret)
|
|
||||||
else:
|
|
||||||
raise ParseError(
|
|
||||||
'Can\'t use complex JSON value types with '
|
|
||||||
'--form/--multipart.'
|
|
||||||
)
|
|
||||||
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
def process_data_embed_raw_json_file_arg(arg: KeyValueArg) -> JSONType:
|
def process_data_embed_raw_json_file_arg(arg: KeyValueArg) -> JSONType:
|
||||||
contents = load_text_file(arg)
|
contents = load_text_file(arg)
|
||||||
value = load_json(arg, contents)
|
value = load_json(arg, contents)
|
||||||
|
@ -10,11 +10,14 @@ from urllib.parse import urlparse, urlunparse
|
|||||||
import requests
|
import requests
|
||||||
# noinspection PyPackageRequirements
|
# noinspection PyPackageRequirements
|
||||||
import urllib3
|
import urllib3
|
||||||
|
from urllib3.util import SKIP_HEADER, SKIPPABLE_HEADERS
|
||||||
|
|
||||||
from . import __version__
|
from . import __version__
|
||||||
from .adapters import HTTPieHTTPAdapter
|
from .adapters import HTTPieHTTPAdapter
|
||||||
|
from .cli.constants import HTTP_OPTIONS
|
||||||
|
from .cli.dicts import HTTPHeadersDict
|
||||||
|
from .cli.nested_json import unwrap_top_level_list_if_needed
|
||||||
from .context import Environment
|
from .context import Environment
|
||||||
from .cli.constants import EMPTY_STRING, HTTP_OPTIONS
|
|
||||||
from .cli.dicts import HTTPHeadersDict, NestedJSONArray
|
|
||||||
from .encoding import UTF8
|
from .encoding import UTF8
|
||||||
from .models import RequestsMessage
|
from .models import RequestsMessage
|
||||||
from .plugins.registry import plugin_manager
|
from .plugins.registry import plugin_manager
|
||||||
@ -140,7 +143,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/httpie/cli/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')
|
||||||
@ -196,8 +199,12 @@ def finalize_headers(headers: HTTPHeadersDict) -> HTTPHeadersDict:
|
|||||||
# 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/httpie/cli/issues/212>
|
||||||
value = value.encode()
|
value = value.encode()
|
||||||
|
elif name.lower() in SKIPPABLE_HEADERS:
|
||||||
|
# Some headers get overwritten by urllib3 when set to `None`
|
||||||
|
# and should be replaced with the `SKIP_HEADER` constant.
|
||||||
|
value = SKIP_HEADER
|
||||||
final_headers.add(name, value)
|
final_headers.add(name, value)
|
||||||
return final_headers
|
return final_headers
|
||||||
|
|
||||||
@ -305,21 +312,13 @@ def make_send_kwargs_mergeable_from_env(args: argparse.Namespace) -> dict:
|
|||||||
|
|
||||||
|
|
||||||
def json_dict_to_request_body(data: Dict[str, Any]) -> str:
|
def json_dict_to_request_body(data: Dict[str, Any]) -> str:
|
||||||
# Propagate the top-level list if there is only one
|
data = unwrap_top_level_list_if_needed(data)
|
||||||
# item in the object, with an en empty key.
|
|
||||||
if len(data) == 1:
|
|
||||||
[(key, value)] = data.items()
|
|
||||||
if isinstance(value, NestedJSONArray):
|
|
||||||
assert key == EMPTY_STRING
|
|
||||||
data = value
|
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
data = json.dumps(data)
|
data = json.dumps(data)
|
||||||
else:
|
else:
|
||||||
# We need to set data to an empty string to prevent requests
|
# We need to set data to an empty string to prevent requests
|
||||||
# from assigning an empty list to `response.request.data`.
|
# from assigning an empty list to `response.request.data`.
|
||||||
data = ''
|
data = ''
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
@ -382,7 +381,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/httpie/cli/issues/895>
|
||||||
|
|
||||||
|
|
||||||
<https://ec.haxx.se/http/http-basics#path-as-is>
|
<https://ec.haxx.se/http/http-basics#path-as-is>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import sys
|
import sys
|
||||||
|
from ssl import SSLContext
|
||||||
from typing import Any, Optional, Iterable
|
from typing import Any, Optional, Iterable
|
||||||
|
|
||||||
from httpie.cookies import HTTPieCookiePolicy
|
from httpie.cookies import HTTPieCookiePolicy
|
||||||
from http import cookiejar # noqa
|
from http import cookiejar # noqa
|
||||||
|
|
||||||
|
|
||||||
# Request does not carry the original policy attached to the
|
# Request does not carry the original policy attached to the
|
||||||
@ -10,7 +11,6 @@ from http import cookiejar # noqa
|
|||||||
# policy. <https://github.com/psf/requests/issues/5449>
|
# policy. <https://github.com/psf/requests/issues/5449>
|
||||||
cookiejar.DefaultCookiePolicy = HTTPieCookiePolicy
|
cookiejar.DefaultCookiePolicy = HTTPieCookiePolicy
|
||||||
|
|
||||||
|
|
||||||
is_windows = 'win32' in str(sys.platform).lower()
|
is_windows = 'win32' in str(sys.platform).lower()
|
||||||
is_frozen = getattr(sys, 'frozen', False)
|
is_frozen = getattr(sys, 'frozen', False)
|
||||||
|
|
||||||
@ -66,7 +66,6 @@ except ImportError:
|
|||||||
res = instance.__dict__[self.name] = self.func(instance)
|
res = instance.__dict__[self.name] = self.func(instance)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
# importlib_metadata was a provisional module, so the APIs changed quite a few times
|
# importlib_metadata was a provisional module, so the APIs changed quite a few times
|
||||||
# between 3.8-3.10. It was also not included in the standard library until 3.8, so
|
# between 3.8-3.10. It was also not included in the standard library until 3.8, so
|
||||||
# we install the backport for <3.8.
|
# we install the backport for <3.8.
|
||||||
@ -100,3 +99,15 @@ def get_dist_name(entry_point: importlib_metadata.EntryPoint) -> Optional[str]:
|
|||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return metadata.get('name')
|
return metadata.get('name')
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_default_certs_loaded(ssl_context: SSLContext) -> None:
|
||||||
|
"""
|
||||||
|
Workaround for a bug in Requests 2.32.3
|
||||||
|
|
||||||
|
See <https://github.com/httpie/cli/issues/1583>
|
||||||
|
|
||||||
|
"""
|
||||||
|
if hasattr(ssl_context, 'load_default_certs'):
|
||||||
|
if not ssl_context.get_ca_certs():
|
||||||
|
ssl_context.load_default_certs()
|
||||||
|
@ -149,6 +149,24 @@ class Config(BaseConfigDict):
|
|||||||
def default_options(self) -> list:
|
def default_options(self) -> list:
|
||||||
return self['default_options']
|
return self['default_options']
|
||||||
|
|
||||||
|
def _configured_path(self, config_option: str, default: str) -> None:
|
||||||
|
return Path(
|
||||||
|
self.get(config_option, self.directory / default)
|
||||||
|
).expanduser().resolve()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def plugins_dir(self) -> Path:
|
def plugins_dir(self) -> Path:
|
||||||
return Path(self.get('plugins_dir', self.directory / 'plugins')).resolve()
|
return self._configured_path('plugins_dir', 'plugins')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def version_info_file(self) -> Path:
|
||||||
|
return self._configured_path('version_info_file', 'version_info.json')
|
||||||
|
|
||||||
|
@property
|
||||||
|
def developer_mode(self) -> bool:
|
||||||
|
"""This is a special setting for the development environment. It is
|
||||||
|
different from the --debug mode in the terms that it might change
|
||||||
|
the behavior for certain parameters (e.g updater system) that
|
||||||
|
we usually ignore."""
|
||||||
|
|
||||||
|
return self.get('developer_mode')
|
||||||
|
@ -145,7 +145,7 @@ class Environment:
|
|||||||
try:
|
try:
|
||||||
config.load()
|
config.load()
|
||||||
except ConfigFileError as e:
|
except ConfigFileError as e:
|
||||||
self.log_error(e, level='warning')
|
self.log_error(e, level=LogLevel.WARNING)
|
||||||
return config
|
return config
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -174,7 +174,7 @@ class Environment:
|
|||||||
stderr = self._orig_stderr
|
stderr = self._orig_stderr
|
||||||
rich_console = self._make_rich_console(file=stderr, force_terminal=stderr.isatty())
|
rich_console = self._make_rich_console(file=stderr, force_terminal=stderr.isatty())
|
||||||
rich_console.print(
|
rich_console.print(
|
||||||
f'\n{self.program_name}: {level}: {msg}\n\n',
|
f'\n{self.program_name}: {level.value}: {msg}\n\n',
|
||||||
style=LOG_LEVEL_COLORS[level],
|
style=LOG_LEVEL_COLORS[level],
|
||||||
markup=False,
|
markup=False,
|
||||||
highlight=False,
|
highlight=False,
|
||||||
|
@ -11,7 +11,7 @@ from requests import __version__ as requests_version
|
|||||||
|
|
||||||
from . import __version__ as httpie_version
|
from . import __version__ as httpie_version
|
||||||
from .cli.constants import OUT_REQ_BODY
|
from .cli.constants import OUT_REQ_BODY
|
||||||
from .cli.nested_json import HTTPieSyntaxError
|
from .cli.nested_json import NestedJSONSyntaxError
|
||||||
from .client import collect_messages
|
from .client import collect_messages
|
||||||
from .context import Environment, LogLevel
|
from .context import Environment, LogLevel
|
||||||
from .downloads import Downloader
|
from .downloads import Downloader
|
||||||
@ -24,6 +24,8 @@ from .output.writer import write_message, write_stream, write_raw_data, MESSAGE_
|
|||||||
from .plugins.registry import plugin_manager
|
from .plugins.registry import plugin_manager
|
||||||
from .status import ExitStatus, http_status_to_exit_status
|
from .status import ExitStatus, http_status_to_exit_status
|
||||||
from .utils import unwrap_context
|
from .utils import unwrap_context
|
||||||
|
from .internal.update_warnings import check_updates
|
||||||
|
from .internal.daemon_runner import is_daemon_mode, run_daemon_task
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyDefaultArgument
|
# noinspection PyDefaultArgument
|
||||||
@ -37,6 +39,10 @@ def raw_main(
|
|||||||
program_name, *args = args
|
program_name, *args = args
|
||||||
env.program_name = os.path.basename(program_name)
|
env.program_name = os.path.basename(program_name)
|
||||||
args = decode_raw_args(args, env.stdin_encoding)
|
args = decode_raw_args(args, env.stdin_encoding)
|
||||||
|
|
||||||
|
if is_daemon_mode(args):
|
||||||
|
return run_daemon_task(env, args)
|
||||||
|
|
||||||
plugin_manager.load_installed_plugins(env.config.plugins_dir)
|
plugin_manager.load_installed_plugins(env.config.plugins_dir)
|
||||||
|
|
||||||
if use_default_options and env.config.default_options:
|
if use_default_options and env.config.default_options:
|
||||||
@ -72,7 +78,7 @@ def raw_main(
|
|||||||
args=args,
|
args=args,
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
except HTTPieSyntaxError as exc:
|
except NestedJSONSyntaxError as exc:
|
||||||
env.stderr.write(str(exc) + "\n")
|
env.stderr.write(str(exc) + "\n")
|
||||||
if include_traceback:
|
if include_traceback:
|
||||||
raise
|
raise
|
||||||
@ -89,6 +95,7 @@ def raw_main(
|
|||||||
raise
|
raise
|
||||||
exit_status = ExitStatus.ERROR
|
exit_status = ExitStatus.ERROR
|
||||||
else:
|
else:
|
||||||
|
check_updates(env)
|
||||||
try:
|
try:
|
||||||
exit_status = main_program(
|
exit_status = main_program(
|
||||||
args=parsed_args,
|
args=parsed_args,
|
||||||
|
@ -217,7 +217,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/httpie/cli/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):
|
||||||
|
5
httpie/internal/__build_channel__.py
Normal file
5
httpie/internal/__build_channel__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Represents the packaging method. This file should
|
||||||
|
# be overridden by every build system we support on
|
||||||
|
# the packaging step.
|
||||||
|
|
||||||
|
BUILD_CHANNEL = 'unknown'
|
0
httpie/internal/__init__.py
Normal file
0
httpie/internal/__init__.py
Normal file
50
httpie/internal/daemon_runner.py
Normal file
50
httpie/internal/daemon_runner.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import argparse
|
||||||
|
from contextlib import redirect_stderr, redirect_stdout
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from httpie.context import Environment
|
||||||
|
from httpie.internal.update_warnings import _fetch_updates, _get_suppress_context
|
||||||
|
from httpie.status import ExitStatus
|
||||||
|
|
||||||
|
STATUS_FILE = '.httpie-test-daemon-status'
|
||||||
|
|
||||||
|
|
||||||
|
def _check_status(env):
|
||||||
|
# This function is used only for the testing (test_update_warnings).
|
||||||
|
# Since we don't want to trigger the fetch_updates (which would interact
|
||||||
|
# with real world resources), we'll only trigger this pseudo task
|
||||||
|
# and check whether the STATUS_FILE is created or not.
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
status_file = Path(tempfile.gettempdir()) / STATUS_FILE
|
||||||
|
status_file.touch()
|
||||||
|
|
||||||
|
|
||||||
|
DAEMONIZED_TASKS = {
|
||||||
|
'check_status': _check_status,
|
||||||
|
'fetch_updates': _fetch_updates,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_options(args: List[str]) -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('task_id')
|
||||||
|
parser.add_argument('--daemon', action='store_true')
|
||||||
|
return parser.parse_known_args(args)[0]
|
||||||
|
|
||||||
|
|
||||||
|
def is_daemon_mode(args: List[str]) -> bool:
|
||||||
|
return '--daemon' in args
|
||||||
|
|
||||||
|
|
||||||
|
def run_daemon_task(env: Environment, args: List[str]) -> ExitStatus:
|
||||||
|
options = _parse_options(args)
|
||||||
|
|
||||||
|
assert options.daemon
|
||||||
|
assert options.task_id in DAEMONIZED_TASKS
|
||||||
|
with redirect_stdout(env.devnull), redirect_stderr(env.devnull):
|
||||||
|
with _get_suppress_context(env):
|
||||||
|
DAEMONIZED_TASKS[options.task_id](env)
|
||||||
|
|
||||||
|
return ExitStatus.SUCCESS
|
121
httpie/internal/daemons.py
Normal file
121
httpie/internal/daemons.py
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
"""
|
||||||
|
This module provides an interface to spawn a detached task to be
|
||||||
|
run with httpie.internal.daemon_runner on a separate process. It is
|
||||||
|
based on DVC's daemon system.
|
||||||
|
https://github.com/iterative/dvc/blob/main/dvc/daemon.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import inspect
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import sys
|
||||||
|
import httpie.__main__
|
||||||
|
from contextlib import suppress
|
||||||
|
from subprocess import Popen, DEVNULL
|
||||||
|
from typing import Dict, List
|
||||||
|
from httpie.compat import is_frozen, is_windows
|
||||||
|
|
||||||
|
|
||||||
|
ProcessContext = Dict[str, str]
|
||||||
|
|
||||||
|
|
||||||
|
def _start_process(cmd: List[str], **kwargs) -> Popen:
|
||||||
|
prefix = [sys.executable]
|
||||||
|
# If it is frozen, sys.executable points to the binary (http).
|
||||||
|
# Otherwise it points to the python interpreter.
|
||||||
|
if not is_frozen:
|
||||||
|
main_entrypoint = httpie.__main__.__file__
|
||||||
|
prefix += [main_entrypoint]
|
||||||
|
return Popen(prefix + cmd, close_fds=True, shell=False, stdout=DEVNULL, stderr=DEVNULL, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def _spawn_windows(cmd: List[str], process_context: ProcessContext) -> None:
|
||||||
|
from subprocess import (
|
||||||
|
CREATE_NEW_PROCESS_GROUP,
|
||||||
|
CREATE_NO_WINDOW,
|
||||||
|
STARTF_USESHOWWINDOW,
|
||||||
|
STARTUPINFO,
|
||||||
|
)
|
||||||
|
|
||||||
|
# https://stackoverflow.com/a/7006424
|
||||||
|
# https://bugs.python.org/issue41619
|
||||||
|
creationflags = CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW
|
||||||
|
|
||||||
|
startupinfo = STARTUPINFO()
|
||||||
|
startupinfo.dwFlags |= STARTF_USESHOWWINDOW
|
||||||
|
|
||||||
|
_start_process(
|
||||||
|
cmd,
|
||||||
|
env=process_context,
|
||||||
|
creationflags=creationflags,
|
||||||
|
startupinfo=startupinfo,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _spawn_posix(args: List[str], process_context: ProcessContext) -> None:
|
||||||
|
"""
|
||||||
|
Perform a double fork procedure* to detach from the parent
|
||||||
|
process so that we don't block the user even if their original
|
||||||
|
command's execution is done but the release fetcher is not.
|
||||||
|
|
||||||
|
[1]: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap11.html#tag_11_01_03
|
||||||
|
"""
|
||||||
|
|
||||||
|
from httpie.core import main
|
||||||
|
|
||||||
|
try:
|
||||||
|
pid = os.fork()
|
||||||
|
if pid > 0:
|
||||||
|
return
|
||||||
|
except OSError:
|
||||||
|
os._exit(1)
|
||||||
|
|
||||||
|
os.setsid()
|
||||||
|
|
||||||
|
try:
|
||||||
|
pid = os.fork()
|
||||||
|
if pid > 0:
|
||||||
|
os._exit(0)
|
||||||
|
except OSError:
|
||||||
|
os._exit(1)
|
||||||
|
|
||||||
|
# Close all standard inputs/outputs
|
||||||
|
sys.stdin.close()
|
||||||
|
sys.stdout.close()
|
||||||
|
sys.stderr.close()
|
||||||
|
|
||||||
|
if platform.system() == 'Darwin':
|
||||||
|
# Double-fork is not reliable on MacOS, so we'll use a subprocess
|
||||||
|
# to ensure the task is isolated properly.
|
||||||
|
process = _start_process(args, env=process_context)
|
||||||
|
# Unlike windows, since we already completed the fork procedure
|
||||||
|
# we can simply join the process and wait for it.
|
||||||
|
process.communicate()
|
||||||
|
else:
|
||||||
|
os.environ.update(process_context)
|
||||||
|
with suppress(BaseException):
|
||||||
|
main(['http'] + args)
|
||||||
|
|
||||||
|
os._exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
def _spawn(args: List[str], process_context: ProcessContext) -> None:
|
||||||
|
"""
|
||||||
|
Spawn a new process to run the given command.
|
||||||
|
"""
|
||||||
|
if is_windows:
|
||||||
|
_spawn_windows(args, process_context)
|
||||||
|
else:
|
||||||
|
_spawn_posix(args, process_context)
|
||||||
|
|
||||||
|
|
||||||
|
def spawn_daemon(task: str) -> None:
|
||||||
|
args = [task, '--daemon']
|
||||||
|
process_context = os.environ.copy()
|
||||||
|
if not is_frozen:
|
||||||
|
file_path = os.path.abspath(inspect.stack()[0][1])
|
||||||
|
process_context['PYTHONPATH'] = os.path.dirname(
|
||||||
|
os.path.dirname(os.path.dirname(file_path))
|
||||||
|
)
|
||||||
|
|
||||||
|
_spawn(args, process_context)
|
171
httpie/internal/update_warnings.py
Normal file
171
httpie/internal/update_warnings.py
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
import json
|
||||||
|
from contextlib import nullcontext, suppress
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Optional, Callable
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
import httpie
|
||||||
|
from httpie.context import Environment, LogLevel
|
||||||
|
from httpie.internal.__build_channel__ import BUILD_CHANNEL
|
||||||
|
from httpie.internal.daemons import spawn_daemon
|
||||||
|
from httpie.utils import is_version_greater, open_with_lockfile
|
||||||
|
|
||||||
|
# Automatically updated package version index.
|
||||||
|
PACKAGE_INDEX_LINK = 'https://packages.httpie.io/latest.json'
|
||||||
|
|
||||||
|
FETCH_INTERVAL = timedelta(weeks=2)
|
||||||
|
WARN_INTERVAL = timedelta(weeks=1)
|
||||||
|
|
||||||
|
UPDATE_MESSAGE_FORMAT = """\
|
||||||
|
A new HTTPie release ({last_released_version}) is available.
|
||||||
|
To see how you can update, please visit https://httpie.io/docs/cli/{installation_method}
|
||||||
|
"""
|
||||||
|
|
||||||
|
ALREADY_UP_TO_DATE_MESSAGE = """\
|
||||||
|
You are already up-to-date.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def _read_data_error_free(file: Path) -> Any:
|
||||||
|
# If the file is broken / non-existent, ignore it.
|
||||||
|
try:
|
||||||
|
with open(file) as stream:
|
||||||
|
return json.load(stream)
|
||||||
|
except (ValueError, OSError):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def _fetch_updates(env: Environment) -> str:
|
||||||
|
file = env.config.version_info_file
|
||||||
|
data = _read_data_error_free(file)
|
||||||
|
|
||||||
|
response = requests.get(PACKAGE_INDEX_LINK, verify=False)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
data.setdefault('last_warned_date', None)
|
||||||
|
data['last_fetched_date'] = datetime.now().isoformat()
|
||||||
|
data['last_released_versions'] = response.json()
|
||||||
|
|
||||||
|
with open_with_lockfile(file, 'w') as stream:
|
||||||
|
json.dump(data, stream)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_updates(env: Environment, lazy: bool = True):
|
||||||
|
if lazy:
|
||||||
|
spawn_daemon('fetch_updates')
|
||||||
|
else:
|
||||||
|
_fetch_updates(env)
|
||||||
|
|
||||||
|
|
||||||
|
def maybe_fetch_updates(env: Environment) -> None:
|
||||||
|
if env.config.get('disable_update_warnings'):
|
||||||
|
return None
|
||||||
|
|
||||||
|
data = _read_data_error_free(env.config.version_info_file)
|
||||||
|
|
||||||
|
if data:
|
||||||
|
current_date = datetime.now()
|
||||||
|
last_fetched_date = datetime.fromisoformat(data['last_fetched_date'])
|
||||||
|
earliest_fetch_date = last_fetched_date + FETCH_INTERVAL
|
||||||
|
if current_date < earliest_fetch_date:
|
||||||
|
return None
|
||||||
|
|
||||||
|
fetch_updates(env)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_suppress_context(env: Environment) -> Any:
|
||||||
|
"""Return a context manager that suppress
|
||||||
|
all possible errors.
|
||||||
|
|
||||||
|
Note: if you have set the developer_mode=True in
|
||||||
|
your config, then it will show all errors for easier
|
||||||
|
debugging."""
|
||||||
|
if env.config.developer_mode:
|
||||||
|
return nullcontext()
|
||||||
|
else:
|
||||||
|
return suppress(BaseException)
|
||||||
|
|
||||||
|
|
||||||
|
def _update_checker(
|
||||||
|
func: Callable[[Environment], None]
|
||||||
|
) -> Callable[[Environment], None]:
|
||||||
|
"""Control the execution of the update checker (suppress errors, trigger
|
||||||
|
auto updates etc.)"""
|
||||||
|
|
||||||
|
def wrapper(env: Environment) -> None:
|
||||||
|
with _get_suppress_context(env):
|
||||||
|
func(env)
|
||||||
|
|
||||||
|
with _get_suppress_context(env):
|
||||||
|
maybe_fetch_updates(env)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def _get_update_status(env: Environment) -> Optional[str]:
|
||||||
|
"""If there is a new update available, return the warning text.
|
||||||
|
Otherwise just return None."""
|
||||||
|
file = env.config.version_info_file
|
||||||
|
if not file.exists():
|
||||||
|
return None
|
||||||
|
|
||||||
|
with _get_suppress_context(env):
|
||||||
|
# If the user quickly spawns multiple httpie processes
|
||||||
|
# we don't want to end in a race.
|
||||||
|
with open_with_lockfile(file) as stream:
|
||||||
|
version_info = json.load(stream)
|
||||||
|
|
||||||
|
available_channels = version_info['last_released_versions']
|
||||||
|
if BUILD_CHANNEL not in available_channels:
|
||||||
|
return None
|
||||||
|
|
||||||
|
current_version = httpie.__version__
|
||||||
|
last_released_version = available_channels[BUILD_CHANNEL]
|
||||||
|
if not is_version_greater(last_released_version, current_version):
|
||||||
|
return None
|
||||||
|
|
||||||
|
text = UPDATE_MESSAGE_FORMAT.format(
|
||||||
|
last_released_version=last_released_version,
|
||||||
|
installation_method=BUILD_CHANNEL,
|
||||||
|
)
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
def get_update_status(env: Environment) -> str:
|
||||||
|
return _get_update_status(env) or ALREADY_UP_TO_DATE_MESSAGE
|
||||||
|
|
||||||
|
|
||||||
|
@_update_checker
|
||||||
|
def check_updates(env: Environment) -> None:
|
||||||
|
if env.config.get('disable_update_warnings'):
|
||||||
|
return None
|
||||||
|
|
||||||
|
file = env.config.version_info_file
|
||||||
|
update_status = _get_update_status(env)
|
||||||
|
|
||||||
|
if not update_status:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# If the user quickly spawns multiple httpie processes
|
||||||
|
# we don't want to end in a race.
|
||||||
|
with open_with_lockfile(file) as stream:
|
||||||
|
version_info = json.load(stream)
|
||||||
|
|
||||||
|
# We don't want to spam the user with too many warnings,
|
||||||
|
# so we'll only warn every once a while (WARN_INTERNAL).
|
||||||
|
current_date = datetime.now()
|
||||||
|
last_warned_date = version_info['last_warned_date']
|
||||||
|
if last_warned_date is not None:
|
||||||
|
earliest_warn_date = (
|
||||||
|
datetime.fromisoformat(last_warned_date) + WARN_INTERVAL
|
||||||
|
)
|
||||||
|
if current_date < earliest_warn_date:
|
||||||
|
return None
|
||||||
|
|
||||||
|
env.log_error(update_status, level=LogLevel.INFO)
|
||||||
|
version_info['last_warned_date'] = current_date.isoformat()
|
||||||
|
|
||||||
|
with open_with_lockfile(file, 'w') as stream:
|
||||||
|
json.dump(version_info, stream)
|
@ -52,7 +52,6 @@ def program():
|
|||||||
try:
|
try:
|
||||||
exit_status = main()
|
exit_status = main()
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
from httpie.status import ExitStatus
|
|
||||||
exit_status = ExitStatus.ERROR_CTRL_C
|
exit_status = ExitStatus.ERROR_CTRL_C
|
||||||
|
|
||||||
return exit_status
|
return exit_status
|
||||||
|
@ -20,9 +20,13 @@ COMMANDS = {
|
|||||||
{
|
{
|
||||||
'flags': ['-f', '--format'],
|
'flags': ['-f', '--format'],
|
||||||
'choices': ['json'],
|
'choices': ['json'],
|
||||||
|
'help': 'Format to export in.',
|
||||||
'default': 'json'
|
'default': 'json'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
'check-updates': [
|
||||||
|
'Check for updates'
|
||||||
|
],
|
||||||
'sessions': {
|
'sessions': {
|
||||||
'help': 'Manage HTTPie sessions',
|
'help': 'Manage HTTPie sessions',
|
||||||
'upgrade': [
|
'upgrade': [
|
||||||
@ -166,5 +170,12 @@ parser.add_argument(
|
|||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
|
|
||||||
options = parser_to_parser_spec(parser)
|
man_page_hint = '''
|
||||||
|
If you are looking for the man pages of http/https commands, try one of the following:
|
||||||
|
$ man http
|
||||||
|
$ man https
|
||||||
|
|
||||||
|
'''
|
||||||
|
|
||||||
|
options = parser_to_parser_spec(parser, man_page_hint=man_page_hint, source_file=__file__)
|
||||||
generate_subparsers(parser, parser, COMMANDS, options)
|
generate_subparsers(parser, parser, COMMANDS, options)
|
||||||
|
@ -43,7 +43,6 @@ def _discover_system_pip() -> List[str]:
|
|||||||
|
|
||||||
|
|
||||||
def _run_pip_subprocess(pip_executable: List[str], args: List[str]) -> bytes:
|
def _run_pip_subprocess(pip_executable: List[str], args: List[str]) -> bytes:
|
||||||
import subprocess
|
|
||||||
|
|
||||||
cmd = [*pip_executable, *args]
|
cmd = [*pip_executable, *args]
|
||||||
try:
|
try:
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
from httpie.manager.tasks.sessions import cli_sessions
|
from httpie.manager.tasks.sessions import cli_sessions
|
||||||
from httpie.manager.tasks.export_args import cli_export_args
|
from httpie.manager.tasks.export_args import cli_export_args
|
||||||
from httpie.manager.tasks.plugins import cli_plugins
|
from httpie.manager.tasks.plugins import cli_plugins
|
||||||
|
from httpie.manager.tasks.check_updates import cli_check_updates
|
||||||
|
|
||||||
CLI_TASKS = {
|
CLI_TASKS = {
|
||||||
'sessions': cli_sessions,
|
'sessions': cli_sessions,
|
||||||
'export-args': cli_export_args,
|
'export-args': cli_export_args,
|
||||||
'plugins': cli_plugins,
|
'plugins': cli_plugins,
|
||||||
|
'check-updates': cli_check_updates
|
||||||
}
|
}
|
||||||
|
10
httpie/manager/tasks/check_updates.py
Normal file
10
httpie/manager/tasks/check_updates.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import argparse
|
||||||
|
from httpie.context import Environment
|
||||||
|
from httpie.status import ExitStatus
|
||||||
|
from httpie.internal.update_warnings import fetch_updates, get_update_status
|
||||||
|
|
||||||
|
|
||||||
|
def cli_check_updates(env: Environment, args: argparse.Namespace) -> ExitStatus:
|
||||||
|
fetch_updates(env, lazy=False)
|
||||||
|
env.stdout.write(get_update_status(env))
|
||||||
|
return ExitStatus.SUCCESS
|
@ -1,11 +1,11 @@
|
|||||||
import argparse
|
import argparse
|
||||||
from typing import Tuple
|
|
||||||
|
|
||||||
from httpie.sessions import SESSIONS_DIR_NAME, get_httpie_session
|
from httpie.sessions import SESSIONS_DIR_NAME, get_httpie_session
|
||||||
from httpie.status import ExitStatus
|
from httpie.status import ExitStatus
|
||||||
from httpie.context import Environment
|
from httpie.context import Environment
|
||||||
from httpie.legacy import v3_1_0_session_cookie_format, v3_2_0_session_header_format
|
from httpie.legacy import v3_1_0_session_cookie_format, v3_2_0_session_header_format
|
||||||
from httpie.manager.cli import missing_subcommand, parser
|
from httpie.manager.cli import missing_subcommand, parser
|
||||||
|
from httpie.utils import is_version_greater
|
||||||
|
|
||||||
|
|
||||||
FIXERS_TO_VERSIONS = {
|
FIXERS_TO_VERSIONS = {
|
||||||
@ -27,25 +27,6 @@ def cli_sessions(env: Environment, args: argparse.Namespace) -> ExitStatus:
|
|||||||
raise ValueError(f'Unexpected action: {action}')
|
raise ValueError(f'Unexpected action: {action}')
|
||||||
|
|
||||||
|
|
||||||
def is_version_greater(version_1: str, version_2: str) -> bool:
|
|
||||||
# In an ideal scenario, we would depend on `packaging` in order
|
|
||||||
# to offer PEP 440 compatible parsing. But since it might not be
|
|
||||||
# commonly available for outside packages, and since we are only
|
|
||||||
# going to parse HTTPie's own version it should be fine to compare
|
|
||||||
# this in a SemVer subset fashion.
|
|
||||||
|
|
||||||
def split_version(version: str) -> Tuple[int, ...]:
|
|
||||||
parts = []
|
|
||||||
for part in version.split('.')[:3]:
|
|
||||||
try:
|
|
||||||
parts.append(int(part))
|
|
||||||
except ValueError:
|
|
||||||
break
|
|
||||||
return tuple(parts)
|
|
||||||
|
|
||||||
return split_version(version_1) > split_version(version_2)
|
|
||||||
|
|
||||||
|
|
||||||
def upgrade_session(env: Environment, args: argparse.Namespace, hostname: str, session_name: str):
|
def upgrade_session(env: Environment, args: argparse.Namespace, hostname: str, session_name: str):
|
||||||
session = get_httpie_session(
|
session = get_httpie_session(
|
||||||
env=env,
|
env=env,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from time import monotonic
|
from time import monotonic
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
from urllib3.util import SKIP_HEADER, SKIPPABLE_HEADERS
|
||||||
|
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from typing import Iterable, Union, NamedTuple
|
from typing import Iterable, Union, NamedTuple
|
||||||
@ -16,7 +17,6 @@ from .cli.constants import (
|
|||||||
from .compat import cached_property
|
from .compat import cached_property
|
||||||
from .utils import split_cookies, parse_content_type_header
|
from .utils import split_cookies, parse_content_type_header
|
||||||
|
|
||||||
|
|
||||||
ELAPSED_TIME_LABEL = 'Elapsed time'
|
ELAPSED_TIME_LABEL = 'Elapsed time'
|
||||||
|
|
||||||
|
|
||||||
@ -67,27 +67,10 @@ class HTTPResponse(HTTPMessage):
|
|||||||
def iter_lines(self, chunk_size):
|
def iter_lines(self, chunk_size):
|
||||||
return ((line, b'\n') for line in self._orig.iter_lines(chunk_size))
|
return ((line, b'\n') for line in self._orig.iter_lines(chunk_size))
|
||||||
|
|
||||||
# noinspection PyProtectedMember
|
|
||||||
@property
|
@property
|
||||||
def headers(self):
|
def headers(self):
|
||||||
try:
|
|
||||||
raw = self._orig.raw
|
|
||||||
if getattr(raw, '_original_response', None):
|
|
||||||
raw_version = raw._original_response.version
|
|
||||||
else:
|
|
||||||
raw_version = raw.version
|
|
||||||
except AttributeError:
|
|
||||||
# Assume HTTP/1.1
|
|
||||||
raw_version = 11
|
|
||||||
version = {
|
|
||||||
9: '0.9',
|
|
||||||
10: '1.0',
|
|
||||||
11: '1.1',
|
|
||||||
20: '2.0',
|
|
||||||
}[raw_version]
|
|
||||||
|
|
||||||
original = self._orig
|
original = self._orig
|
||||||
status_line = f'HTTP/{version} {original.status_code} {original.reason}'
|
status_line = f'HTTP/{self.version} {original.status_code} {original.reason}'
|
||||||
headers = [status_line]
|
headers = [status_line]
|
||||||
headers.extend(
|
headers.extend(
|
||||||
': '.join(header)
|
': '.join(header)
|
||||||
@ -117,6 +100,32 @@ class HTTPResponse(HTTPMessage):
|
|||||||
for key, value in data.items()
|
for key, value in data.items()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def version(self) -> str:
|
||||||
|
"""
|
||||||
|
Return the HTTP version used by the server, e.g. '1.1'.
|
||||||
|
|
||||||
|
Assume HTTP/1.1 if version is not available.
|
||||||
|
|
||||||
|
"""
|
||||||
|
mapping = {
|
||||||
|
9: '0.9',
|
||||||
|
10: '1.0',
|
||||||
|
11: '1.1',
|
||||||
|
20: '2.0',
|
||||||
|
}
|
||||||
|
fallback = 11
|
||||||
|
version = None
|
||||||
|
try:
|
||||||
|
raw = self._orig.raw
|
||||||
|
if getattr(raw, '_original_response', None):
|
||||||
|
version = raw._original_response.version
|
||||||
|
else:
|
||||||
|
version = raw.version
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
return mapping[version or fallback]
|
||||||
|
|
||||||
|
|
||||||
class HTTPRequest(HTTPMessage):
|
class HTTPRequest(HTTPMessage):
|
||||||
"""A :class:`requests.models.Request` wrapper."""
|
"""A :class:`requests.models.Request` wrapper."""
|
||||||
@ -144,6 +153,7 @@ class HTTPRequest(HTTPMessage):
|
|||||||
headers = [
|
headers = [
|
||||||
f'{name}: {value if isinstance(value, str) else value.decode()}'
|
f'{name}: {value if isinstance(value, str) else value.decode()}'
|
||||||
for name, value in headers.items()
|
for name, value in headers.items()
|
||||||
|
if not (name.lower() in SKIPPABLE_HEADERS and value == SKIP_HEADER)
|
||||||
]
|
]
|
||||||
|
|
||||||
headers.insert(0, request_line)
|
headers.insert(0, request_line)
|
||||||
|
@ -4,28 +4,45 @@ import subprocess
|
|||||||
import os
|
import os
|
||||||
from httpie.context import Environment
|
from httpie.context import Environment
|
||||||
|
|
||||||
|
|
||||||
MAN_COMMAND = 'man'
|
MAN_COMMAND = 'man'
|
||||||
NO_MAN_PAGES = os.getenv('HTTPIE_NO_MAN_PAGES', False)
|
NO_MAN_PAGES = os.getenv('HTTPIE_NO_MAN_PAGES', False)
|
||||||
|
|
||||||
|
# On some systems, HTTP(n) might exist, but we are only interested in HTTP(1).
|
||||||
|
# For more information on man page sections: <https://unix.stackexchange.com/a/138643>
|
||||||
|
MAN_PAGE_SECTION = '1'
|
||||||
|
|
||||||
|
|
||||||
def is_available(program: str) -> bool:
|
def is_available(program: str) -> bool:
|
||||||
"""Check whether HTTPie's man pages are available in this system."""
|
"""
|
||||||
|
Check whether `program`'s man pages are available on this system.
|
||||||
|
|
||||||
|
"""
|
||||||
if NO_MAN_PAGES or os.system == 'nt':
|
if NO_MAN_PAGES or os.system == 'nt':
|
||||||
return False
|
return False
|
||||||
|
try:
|
||||||
process = subprocess.run(
|
process = subprocess.run(
|
||||||
[MAN_COMMAND, program],
|
[MAN_COMMAND, MAN_PAGE_SECTION, program],
|
||||||
shell=False,
|
shell=False,
|
||||||
stdout=subprocess.DEVNULL,
|
stdout=subprocess.DEVNULL,
|
||||||
stderr=subprocess.DEVNULL,
|
stderr=subprocess.DEVNULL
|
||||||
)
|
)
|
||||||
return process.returncode == 0
|
except Exception:
|
||||||
|
# There might be some errors outside the process, e.g
|
||||||
|
# a permission error to execute something that is not an
|
||||||
|
# executable.
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return process.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
def display_for(env: Environment, program: str) -> None:
|
def display_for(env: Environment, program: str) -> None:
|
||||||
"""Display the man page for the given command (http/https)."""
|
"""
|
||||||
|
Open the system man page for the given command (http/https/httpie).
|
||||||
|
|
||||||
|
"""
|
||||||
subprocess.run(
|
subprocess.run(
|
||||||
[MAN_COMMAND, program], stdout=env.stdout, stderr=env.stderr
|
[MAN_COMMAND, MAN_PAGE_SECTION, program],
|
||||||
|
stdout=env.stdout,
|
||||||
|
stderr=env.stderr
|
||||||
)
|
)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
from dataclasses import dataclass, field
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
from typing import Optional
|
from typing import Optional, List
|
||||||
|
|
||||||
|
|
||||||
PYGMENTS_BRIGHT_BLACK = 'ansibrightblack'
|
PYGMENTS_BRIGHT_BLACK = 'ansibrightblack'
|
||||||
@ -34,7 +35,21 @@ class ColorString(str):
|
|||||||
|
|
||||||
E.g: PieColor.BLUE | BOLD | ITALIC
|
E.g: PieColor.BLUE | BOLD | ITALIC
|
||||||
"""
|
"""
|
||||||
return ColorString(self + ' ' + other)
|
if isinstance(other, str):
|
||||||
|
# In case of PieColor.BLUE | SOMETHING
|
||||||
|
# we just create a new string.
|
||||||
|
return ColorString(self + ' ' + other)
|
||||||
|
elif isinstance(other, GenericColor):
|
||||||
|
# If we see a GenericColor, then we'll wrap it
|
||||||
|
# in with the desired property in a different class.
|
||||||
|
return _StyledGenericColor(other, styles=self.split())
|
||||||
|
elif isinstance(other, _StyledGenericColor):
|
||||||
|
# And if it is already wrapped, we'll just extend the
|
||||||
|
# list of properties.
|
||||||
|
other.styles.extend(self.split())
|
||||||
|
return other
|
||||||
|
else:
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
|
||||||
class PieColor(ColorString, Enum):
|
class PieColor(ColorString, Enum):
|
||||||
@ -86,6 +101,12 @@ class GenericColor(Enum):
|
|||||||
return exposed_color
|
return exposed_color
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class _StyledGenericColor:
|
||||||
|
color: 'GenericColor'
|
||||||
|
styles: List[str] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
# noinspection PyDictCreation
|
# noinspection PyDictCreation
|
||||||
COLOR_PALETTE = {
|
COLOR_PALETTE = {
|
||||||
# Copy the brand palette
|
# Copy the brand palette
|
||||||
|
@ -26,6 +26,7 @@ STYLE_BOLD = 'bold'
|
|||||||
MAX_CHOICE_CHARS = 80
|
MAX_CHOICE_CHARS = 80
|
||||||
|
|
||||||
LEFT_PADDING_2 = (0, 0, 0, 2)
|
LEFT_PADDING_2 = (0, 0, 0, 2)
|
||||||
|
LEFT_PADDING_3 = (0, 0, 0, 3)
|
||||||
LEFT_PADDING_4 = (0, 0, 0, 4)
|
LEFT_PADDING_4 = (0, 0, 0, 4)
|
||||||
LEFT_PADDING_5 = (0, 0, 0, 4)
|
LEFT_PADDING_5 = (0, 0, 0, 4)
|
||||||
|
|
||||||
@ -33,6 +34,12 @@ LEFT_INDENT_2 = (1, 0, 0, 2)
|
|||||||
LEFT_INDENT_3 = (1, 0, 0, 3)
|
LEFT_INDENT_3 = (1, 0, 0, 3)
|
||||||
LEFT_INDENT_BOTTOM_3 = (0, 0, 1, 3)
|
LEFT_INDENT_BOTTOM_3 = (0, 0, 1, 3)
|
||||||
|
|
||||||
|
MORE_INFO_COMMANDS = """
|
||||||
|
To learn more, you can try:
|
||||||
|
-> running 'http --manual'
|
||||||
|
-> visiting our full documentation at https://httpie.io/docs/cli
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class OptionsHighlighter(RegexHighlighter):
|
class OptionsHighlighter(RegexHighlighter):
|
||||||
highlights = [
|
highlights = [
|
||||||
@ -213,6 +220,10 @@ def to_help_message(
|
|||||||
Text('More Information', style=STYLE_SWITCH),
|
Text('More Information', style=STYLE_SWITCH),
|
||||||
LEFT_INDENT_2,
|
LEFT_INDENT_2,
|
||||||
)
|
)
|
||||||
|
yield Padding(
|
||||||
|
MORE_INFO_COMMANDS.rstrip('\n'),
|
||||||
|
LEFT_PADDING_3
|
||||||
|
)
|
||||||
yield Padding(
|
yield Padding(
|
||||||
spec.epilog.rstrip('\n'),
|
spec.epilog.rstrip('\n'),
|
||||||
LEFT_INDENT_BOTTOM_3,
|
LEFT_INDENT_BOTTOM_3,
|
||||||
|
@ -4,20 +4,22 @@ from typing import TYPE_CHECKING, Any, Optional
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from rich.theme import Theme
|
from rich.theme import Theme
|
||||||
|
|
||||||
from httpie.output.ui.palette import GenericColor, PieStyle, Styles # noqa
|
from httpie.output.ui.palette import GenericColor, PieStyle, Styles, ColorString, _StyledGenericColor # noqa
|
||||||
|
|
||||||
|
RICH_BOLD = ColorString('bold')
|
||||||
|
|
||||||
# Rich-specific color code declarations
|
# Rich-specific color code declarations
|
||||||
# <https://github.com/Textualize/rich/blob/fcd684dd3a482977cab620e71ccaebb94bf13ac9/rich/default_styles.py>
|
# <https://github.com/Textualize/rich/blob/fcd684dd3a482977cab620e71ccaebb94bf13ac9/rich/default_styles.py>
|
||||||
CUSTOM_STYLES = {
|
CUSTOM_STYLES = {
|
||||||
'progress.description': GenericColor.WHITE,
|
'progress.description': RICH_BOLD | GenericColor.WHITE,
|
||||||
'progress.data.speed': GenericColor.GREEN,
|
'progress.data.speed': RICH_BOLD | GenericColor.GREEN,
|
||||||
'progress.percentage': GenericColor.AQUA,
|
'progress.percentage': RICH_BOLD | GenericColor.AQUA,
|
||||||
'progress.download': GenericColor.AQUA,
|
'progress.download': RICH_BOLD | GenericColor.AQUA,
|
||||||
'progress.remaining': GenericColor.ORANGE,
|
'progress.remaining': RICH_BOLD | GenericColor.ORANGE,
|
||||||
'bar.complete': GenericColor.PURPLE,
|
'bar.complete': RICH_BOLD | GenericColor.PURPLE,
|
||||||
'bar.finished': GenericColor.GREEN,
|
'bar.finished': RICH_BOLD | GenericColor.GREEN,
|
||||||
'bar.pulse': GenericColor.PURPLE,
|
'bar.pulse': RICH_BOLD | GenericColor.PURPLE,
|
||||||
'option': GenericColor.PINK,
|
'option': RICH_BOLD | GenericColor.PINK,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -40,7 +42,7 @@ class _GenericColorCaster(dict):
|
|||||||
return super().get(self._translate(key))
|
return super().get(self._translate(key))
|
||||||
|
|
||||||
|
|
||||||
def _make_rich_color_theme(style_name: Optional[str]) -> 'Theme':
|
def _make_rich_color_theme(style_name: Optional[str] = None) -> 'Theme':
|
||||||
from rich.style import Style
|
from rich.style import Style
|
||||||
from rich.theme import Theme
|
from rich.theme import Theme
|
||||||
|
|
||||||
@ -55,9 +57,15 @@ def _make_rich_color_theme(style_name: Optional[str]) -> 'Theme':
|
|||||||
for color, color_set in ChainMap(
|
for color, color_set in ChainMap(
|
||||||
GenericColor.__members__, CUSTOM_STYLES
|
GenericColor.__members__, CUSTOM_STYLES
|
||||||
).items():
|
).items():
|
||||||
|
if isinstance(color_set, _StyledGenericColor):
|
||||||
|
properties = dict.fromkeys(color_set.styles, True)
|
||||||
|
color_set = color_set.color
|
||||||
|
else:
|
||||||
|
properties = {}
|
||||||
|
|
||||||
theme.styles[color.lower()] = Style(
|
theme.styles[color.lower()] = Style(
|
||||||
color=color_set.apply_style(style, style_name=style_name),
|
color=color_set.apply_style(style, style_name=style_name),
|
||||||
bold=style is Styles.PIE,
|
**properties,
|
||||||
)
|
)
|
||||||
|
|
||||||
# E.g translate GenericColor.BLUE into blue on key access
|
# E.g translate GenericColor.BLUE into blue on key access
|
||||||
|
@ -6,13 +6,15 @@ from contextlib import contextmanager
|
|||||||
from rich.console import Console, RenderableType
|
from rich.console import Console, RenderableType
|
||||||
from rich.highlighter import Highlighter
|
from rich.highlighter import Highlighter
|
||||||
|
|
||||||
|
from httpie.output.ui.rich_palette import _make_rich_color_theme
|
||||||
|
|
||||||
|
|
||||||
def render_as_string(renderable: RenderableType) -> str:
|
def render_as_string(renderable: RenderableType) -> str:
|
||||||
"""Render any `rich` object in a fake console and
|
"""Render any `rich` object in a fake console and
|
||||||
return a *style-less* version of it as a string."""
|
return a *style-less* version of it as a string."""
|
||||||
|
|
||||||
with open(os.devnull, 'w') as null_stream:
|
with open(os.devnull, 'w') as null_stream:
|
||||||
fake_console = Console(file=null_stream, record=True)
|
fake_console = Console(file=null_stream, record=True, theme=_make_rich_color_theme())
|
||||||
fake_console.print(renderable)
|
fake_console.print(renderable)
|
||||||
return fake_console.export_text()
|
return fake_console.export_text()
|
||||||
|
|
||||||
@ -22,7 +24,7 @@ def enable_highlighter(
|
|||||||
console: Console,
|
console: Console,
|
||||||
highlighter: Highlighter,
|
highlighter: Highlighter,
|
||||||
) -> Iterator[Console]:
|
) -> Iterator[Console]:
|
||||||
"""Enable a higlighter temporarily."""
|
"""Enable a highlighter temporarily."""
|
||||||
|
|
||||||
original_highlighter = console.highlighter
|
original_highlighter = console.highlighter
|
||||||
try:
|
try:
|
||||||
|
@ -17,6 +17,7 @@ from .processing import Conversion, Formatting
|
|||||||
from .streams import (
|
from .streams import (
|
||||||
BaseStream, BufferedPrettyStream, EncodedStream, PrettyStream, RawStream,
|
BaseStream, BufferedPrettyStream, EncodedStream, PrettyStream, RawStream,
|
||||||
)
|
)
|
||||||
|
from ..utils import parse_content_type_header
|
||||||
|
|
||||||
|
|
||||||
MESSAGE_SEPARATOR = '\n\n'
|
MESSAGE_SEPARATOR = '\n\n'
|
||||||
@ -163,7 +164,10 @@ def get_stream_type_and_kwargs(
|
|||||||
if not is_stream and message_type is HTTPResponse:
|
if not is_stream and message_type is HTTPResponse:
|
||||||
# If this is a response, then check the headers for determining
|
# If this is a response, then check the headers for determining
|
||||||
# auto-streaming.
|
# auto-streaming.
|
||||||
is_stream = headers.get('Content-Type') == 'text/event-stream'
|
raw_content_type_header = headers.get('Content-Type', None)
|
||||||
|
if raw_content_type_header:
|
||||||
|
content_type_header, _ = parse_content_type_header(raw_content_type_header)
|
||||||
|
is_stream = (content_type_header == 'text/event-stream')
|
||||||
|
|
||||||
if not env.stdout_isatty and not prettify_groups:
|
if not env.stdout_isatty and not prettify_groups:
|
||||||
stream_class = RawStream
|
stream_class = RawStream
|
||||||
|
@ -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/httpie/cli/issues/212
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
import ssl
|
import ssl
|
||||||
from typing import NamedTuple, Optional
|
from typing import NamedTuple, Optional
|
||||||
|
|
||||||
from httpie.adapters import HTTPAdapter
|
|
||||||
# noinspection PyPackageRequirements
|
# noinspection PyPackageRequirements
|
||||||
from urllib3.util.ssl_ import (
|
from urllib3.util.ssl_ import (
|
||||||
DEFAULT_CIPHERS, create_urllib3_context,
|
create_urllib3_context,
|
||||||
resolve_ssl_version,
|
resolve_ssl_version,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .adapters import HTTPAdapter
|
||||||
|
from .compat import ensure_default_certs_loaded
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_SSL_CIPHERS = DEFAULT_CIPHERS
|
|
||||||
SSL_VERSION_ARG_MAPPING = {
|
SSL_VERSION_ARG_MAPPING = {
|
||||||
'ssl2.3': 'PROTOCOL_SSLv23',
|
'ssl2.3': 'PROTOCOL_SSLv23',
|
||||||
'ssl3': 'PROTOCOL_SSLv3',
|
'ssl3': 'PROTOCOL_SSLv3',
|
||||||
@ -33,7 +34,7 @@ class HTTPieCertificate(NamedTuple):
|
|||||||
def to_raw_cert(self):
|
def to_raw_cert(self):
|
||||||
"""Synthesize a requests-compatible (2-item tuple of cert and key file)
|
"""Synthesize a requests-compatible (2-item tuple of cert and key file)
|
||||||
object from HTTPie's internal representation of a certificate."""
|
object from HTTPie's internal representation of a certificate."""
|
||||||
return (self.cert_file, self.key_file)
|
return self.cert_file, self.key_file
|
||||||
|
|
||||||
|
|
||||||
class HTTPieHTTPSAdapter(HTTPAdapter):
|
class HTTPieHTTPSAdapter(HTTPAdapter):
|
||||||
@ -72,7 +73,7 @@ class HTTPieHTTPSAdapter(HTTPAdapter):
|
|||||||
ssl_version: str = None,
|
ssl_version: str = None,
|
||||||
ciphers: str = None,
|
ciphers: str = None,
|
||||||
) -> 'ssl.SSLContext':
|
) -> 'ssl.SSLContext':
|
||||||
return create_urllib3_context(
|
ssl_context = create_urllib3_context(
|
||||||
ciphers=ciphers,
|
ciphers=ciphers,
|
||||||
ssl_version=resolve_ssl_version(ssl_version),
|
ssl_version=resolve_ssl_version(ssl_version),
|
||||||
# Since we are using a custom SSL context, we need to pass this
|
# Since we are using a custom SSL context, we need to pass this
|
||||||
@ -80,6 +81,12 @@ class HTTPieHTTPSAdapter(HTTPAdapter):
|
|||||||
# in `super().cert_verify()`.
|
# in `super().cert_verify()`.
|
||||||
cert_reqs=ssl.CERT_REQUIRED if verify else ssl.CERT_NONE
|
cert_reqs=ssl.CERT_REQUIRED if verify else ssl.CERT_NONE
|
||||||
)
|
)
|
||||||
|
ensure_default_certs_loaded(ssl_context)
|
||||||
|
return ssl_context
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_default_ciphers_names(cls):
|
||||||
|
return [cipher['name'] for cipher in cls._create_ssl_context(verify=False).get_ciphers()]
|
||||||
|
|
||||||
|
|
||||||
def _is_key_file_encrypted(key_file):
|
def _is_key_file_encrypted(key_file):
|
||||||
@ -94,3 +101,9 @@ def _is_key_file_encrypted(key_file):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# We used to import the default set of TLS ciphers from urllib3, but they removed it.
|
||||||
|
# Instead, now urllib3 uses the list of ciphers configured by the system.
|
||||||
|
# <https://github.com/httpie/cli/pull/1501>
|
||||||
|
DEFAULT_SSL_CIPHERS_STRING = ':'.join(HTTPieHTTPSAdapter.get_default_ciphers_names())
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
|
import os
|
||||||
|
import base64
|
||||||
import json
|
import json
|
||||||
import mimetypes
|
import mimetypes
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import tempfile
|
||||||
import sysconfig
|
import sysconfig
|
||||||
|
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
from contextlib import contextmanager
|
||||||
from http.cookiejar import parse_ns_headers
|
from http.cookiejar import parse_ns_headers
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pprint import pformat
|
from pprint import pformat
|
||||||
from urllib.parse import urlsplit
|
from urllib.parse import urlsplit
|
||||||
from typing import Any, List, Optional, Tuple, Callable, Iterable, TypeVar
|
from typing import Any, List, Optional, Tuple, Generator, Callable, Iterable, IO, TypeVar
|
||||||
|
|
||||||
import requests.auth
|
import requests.auth
|
||||||
|
|
||||||
@ -241,7 +245,7 @@ def get_site_paths(path: Path) -> Iterable[Path]:
|
|||||||
yield as_site(path)
|
yield as_site(path)
|
||||||
|
|
||||||
|
|
||||||
def split(iterable: Iterable[T], key: Callable[[T], bool]) -> Tuple[List[T], List[T]]:
|
def split_iterable(iterable: Iterable[T], key: Callable[[T], bool]) -> Tuple[List[T], List[T]]:
|
||||||
left, right = [], []
|
left, right = [], []
|
||||||
for item in iterable:
|
for item in iterable:
|
||||||
if key(item):
|
if key(item):
|
||||||
@ -261,3 +265,45 @@ def unwrap_context(exc: Exception) -> Optional[Exception]:
|
|||||||
|
|
||||||
def url_as_host(url: str) -> str:
|
def url_as_host(url: str) -> str:
|
||||||
return urlsplit(url).netloc.split('@')[-1]
|
return urlsplit(url).netloc.split('@')[-1]
|
||||||
|
|
||||||
|
|
||||||
|
class LockFileError(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def open_with_lockfile(file: Path, *args, **kwargs) -> Generator[IO[Any], None, None]:
|
||||||
|
file_id = base64.b64encode(os.fsencode(file)).decode()
|
||||||
|
target_file = Path(tempfile.gettempdir()) / file_id
|
||||||
|
|
||||||
|
# Have an atomic-like touch here, so we'll tighten the possibility of
|
||||||
|
# a race occurring between multiple processes accessing the same file.
|
||||||
|
try:
|
||||||
|
target_file.touch(exist_ok=False)
|
||||||
|
except FileExistsError as exc:
|
||||||
|
raise LockFileError("Can't modify a locked file.") from exc
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open(file, *args, **kwargs) as stream:
|
||||||
|
yield stream
|
||||||
|
finally:
|
||||||
|
target_file.unlink()
|
||||||
|
|
||||||
|
|
||||||
|
def is_version_greater(version_1: str, version_2: str) -> bool:
|
||||||
|
# In an ideal scenario, we would depend on `packaging` in order
|
||||||
|
# to offer PEP 440 compatible parsing. But since it might not be
|
||||||
|
# commonly available for outside packages, and since we are only
|
||||||
|
# going to parse HTTPie's own version it should be fine to compare
|
||||||
|
# this in a SemVer subset fashion.
|
||||||
|
|
||||||
|
def split_version(version: str) -> Tuple[int, ...]:
|
||||||
|
parts = []
|
||||||
|
for part in version.split('.')[:3]:
|
||||||
|
try:
|
||||||
|
parts.append(int(part))
|
||||||
|
except ValueError:
|
||||||
|
break
|
||||||
|
return tuple(parts)
|
||||||
|
|
||||||
|
return split_version(version_1) > split_version(version_2)
|
||||||
|
92
setup.cfg
92
setup.cfg
@ -1,6 +1,6 @@
|
|||||||
# Please keep all characters in this file in ASCII
|
# Please keep all characters in this file in ASCII
|
||||||
# distutils uses system's locale to interpret it and not everybody
|
# distutils uses system's locale to interpret it and not everybody
|
||||||
# uses UTF-8. See https://github.com/httpie/httpie/issues/1039
|
# uses UTF-8. See https://github.com/httpie/cli/issues/1039
|
||||||
# for an example
|
# for an example
|
||||||
[wheel]
|
[wheel]
|
||||||
|
|
||||||
@ -12,9 +12,99 @@ norecursedirs = tests/fixtures
|
|||||||
addopts = --tb=native --doctest-modules --verbose
|
addopts = --tb=native --doctest-modules --verbose
|
||||||
xfail_strict = True
|
xfail_strict = True
|
||||||
|
|
||||||
|
[metadata]
|
||||||
|
name = httpie
|
||||||
|
version = attr: httpie.__version__
|
||||||
|
author = Jakub Roztocil
|
||||||
|
author_email = jakub@roztocil.co
|
||||||
|
license = BSD
|
||||||
|
description = HTTPie: modern, user-friendly command-line HTTP client for the API era.
|
||||||
|
url = https://httpie.io/
|
||||||
|
long_description = file: README.md
|
||||||
|
long_description_content_type = text/markdown
|
||||||
|
classifiers =
|
||||||
|
Development Status :: 5 - Production/Stable
|
||||||
|
Programming Language :: Python
|
||||||
|
Programming Language :: Python :: 3 :: Only
|
||||||
|
Environment :: Console
|
||||||
|
Intended Audience :: Developers
|
||||||
|
Intended Audience :: System Administrators
|
||||||
|
License :: OSI Approved :: BSD License
|
||||||
|
Topic :: Internet :: WWW/HTTP
|
||||||
|
Topic :: Software Development
|
||||||
|
Topic :: System :: Networking
|
||||||
|
Topic :: Terminals
|
||||||
|
Topic :: Text Processing
|
||||||
|
Topic :: Utilities
|
||||||
|
project_urls =
|
||||||
|
GitHub = https://github.com/httpie/cli
|
||||||
|
Twitter = https://twitter.com/httpie
|
||||||
|
Discord = https://httpie.io/discord
|
||||||
|
Documentation = https://httpie.io/docs
|
||||||
|
Online Demo = https://httpie.io/run
|
||||||
|
|
||||||
|
|
||||||
|
[options]
|
||||||
|
packages = find:
|
||||||
|
install_requires =
|
||||||
|
pip
|
||||||
|
charset_normalizer>=2.0.0
|
||||||
|
defusedxml>=0.6.0
|
||||||
|
requests[socks] >=2.22.0
|
||||||
|
Pygments>=2.5.2
|
||||||
|
requests-toolbelt>=0.9.1
|
||||||
|
multidict>=4.7.0
|
||||||
|
setuptools
|
||||||
|
importlib-metadata>=1.4.0; python_version<"3.8"
|
||||||
|
rich>=9.10.0
|
||||||
|
colorama>=0.2.4; sys_platform=="win32"
|
||||||
|
python_requires = >=3.7
|
||||||
|
|
||||||
|
|
||||||
[flake8]
|
[flake8]
|
||||||
# <https://flake8.pycqa.org/en/latest/user/error-codes.html>
|
# <https://flake8.pycqa.org/en/latest/user/error-codes.html>
|
||||||
# E501 - line too long
|
# E501 - line too long
|
||||||
# W503 - line break before binary operator
|
# W503 - line break before binary operator
|
||||||
ignore = E501,W503
|
ignore = E501,W503
|
||||||
|
|
||||||
|
[options.packages.find]
|
||||||
|
include =
|
||||||
|
httpie
|
||||||
|
httpie.*
|
||||||
|
|
||||||
|
[options.entry_points]
|
||||||
|
console_scripts =
|
||||||
|
http = httpie.__main__:main
|
||||||
|
https = httpie.__main__:main
|
||||||
|
httpie = httpie.manager.__main__:main
|
||||||
|
|
||||||
|
[options.extras_require]
|
||||||
|
dev =
|
||||||
|
pytest
|
||||||
|
pytest-httpbin>=0.0.6
|
||||||
|
responses
|
||||||
|
pytest-mock
|
||||||
|
werkzeug<2.1.0
|
||||||
|
flake8
|
||||||
|
flake8-comprehensions
|
||||||
|
flake8-deprecated
|
||||||
|
flake8-mutable
|
||||||
|
flake8-tuple
|
||||||
|
pyopenssl
|
||||||
|
pytest-cov
|
||||||
|
pyyaml
|
||||||
|
twine
|
||||||
|
wheel
|
||||||
|
Jinja2
|
||||||
|
test =
|
||||||
|
pytest
|
||||||
|
pytest-httpbin>=0.0.6
|
||||||
|
responses
|
||||||
|
pytest-mock
|
||||||
|
werkzeug<2.1.0
|
||||||
|
|
||||||
|
[options.data_files]
|
||||||
|
share/man/man1 =
|
||||||
|
extras/man/http.1
|
||||||
|
extras/man/https.1
|
||||||
|
extras/man/httpie.1
|
||||||
|
121
setup.py
121
setup.py
@ -1,120 +1,3 @@
|
|||||||
# This is purely the result of trial and error.
|
from setuptools import setup
|
||||||
|
|
||||||
import sys
|
setup()
|
||||||
|
|
||||||
from setuptools import setup, find_packages
|
|
||||||
|
|
||||||
import httpie
|
|
||||||
|
|
||||||
|
|
||||||
# Note: keep requirements here to ease distributions packaging
|
|
||||||
tests_require = [
|
|
||||||
'pytest',
|
|
||||||
'pytest-httpbin>=0.0.6',
|
|
||||||
'pytest-lazy-fixture>=0.0.6',
|
|
||||||
'responses',
|
|
||||||
'werkzeug<2.1.0'
|
|
||||||
]
|
|
||||||
dev_require = [
|
|
||||||
*tests_require,
|
|
||||||
'flake8',
|
|
||||||
'flake8-comprehensions',
|
|
||||||
'flake8-deprecated',
|
|
||||||
'flake8-mutable',
|
|
||||||
'flake8-tuple',
|
|
||||||
'pyopenssl',
|
|
||||||
'pytest-cov',
|
|
||||||
'pyyaml',
|
|
||||||
'twine',
|
|
||||||
'wheel',
|
|
||||||
'Jinja2'
|
|
||||||
]
|
|
||||||
install_requires = [
|
|
||||||
'pip',
|
|
||||||
'charset_normalizer>=2.0.0',
|
|
||||||
'defusedxml>=0.6.0',
|
|
||||||
'requests[socks]>=2.22.0',
|
|
||||||
'Pygments>=2.5.2',
|
|
||||||
'requests-toolbelt>=0.9.1',
|
|
||||||
'multidict>=4.7.0',
|
|
||||||
'setuptools',
|
|
||||||
'importlib-metadata>=1.4.0; python_version < "3.8"',
|
|
||||||
'rich>=9.10.0'
|
|
||||||
]
|
|
||||||
install_requires_win_only = [
|
|
||||||
'colorama>=0.2.4',
|
|
||||||
]
|
|
||||||
|
|
||||||
# Conditional dependencies:
|
|
||||||
|
|
||||||
# sdist
|
|
||||||
if 'bdist_wheel' not in sys.argv:
|
|
||||||
|
|
||||||
if 'win32' in str(sys.platform).lower():
|
|
||||||
# Terminal colors for Windows
|
|
||||||
install_requires.extend(install_requires_win_only)
|
|
||||||
|
|
||||||
|
|
||||||
# bdist_wheel
|
|
||||||
extras_require = {
|
|
||||||
'dev': dev_require,
|
|
||||||
'test': tests_require,
|
|
||||||
# https://wheel.readthedocs.io/en/latest/#defining-conditional-dependencies
|
|
||||||
':sys_platform == "win32"': install_requires_win_only,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def long_description():
|
|
||||||
with open('README.md', encoding='utf-8') as f:
|
|
||||||
return f.read()
|
|
||||||
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name='httpie',
|
|
||||||
version=httpie.__version__,
|
|
||||||
description=httpie.__doc__.strip(),
|
|
||||||
long_description=long_description(),
|
|
||||||
long_description_content_type='text/markdown',
|
|
||||||
url='https://httpie.io/',
|
|
||||||
download_url=f'https://github.com/httpie/httpie/archive/{httpie.__version__}.tar.gz',
|
|
||||||
author=httpie.__author__,
|
|
||||||
author_email='jakub@roztocil.co',
|
|
||||||
license=httpie.__licence__,
|
|
||||||
packages=find_packages(include=['httpie', 'httpie.*']),
|
|
||||||
entry_points={
|
|
||||||
'console_scripts': [
|
|
||||||
'http = httpie.__main__:main',
|
|
||||||
'https = httpie.__main__:main',
|
|
||||||
'httpie = httpie.manager.__main__:main',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
python_requires='>=3.7',
|
|
||||||
extras_require=extras_require,
|
|
||||||
install_requires=install_requires,
|
|
||||||
classifiers=[
|
|
||||||
'Development Status :: 5 - Production/Stable',
|
|
||||||
'Programming Language :: Python',
|
|
||||||
'Programming Language :: Python :: 3 :: Only',
|
|
||||||
'Environment :: Console',
|
|
||||||
'Intended Audience :: Developers',
|
|
||||||
'Intended Audience :: System Administrators',
|
|
||||||
'License :: OSI Approved :: BSD License',
|
|
||||||
'Topic :: Internet :: WWW/HTTP',
|
|
||||||
'Topic :: Software Development',
|
|
||||||
'Topic :: System :: Networking',
|
|
||||||
'Topic :: Terminals',
|
|
||||||
'Topic :: Text Processing',
|
|
||||||
'Topic :: Utilities'
|
|
||||||
],
|
|
||||||
project_urls={
|
|
||||||
'GitHub': 'https://github.com/httpie/httpie',
|
|
||||||
'Twitter': 'https://twitter.com/httpie',
|
|
||||||
'Discord': 'https://httpie.io/discord',
|
|
||||||
'Documentation': 'https://httpie.io/docs',
|
|
||||||
'Online Demo': 'https://httpie.io/run',
|
|
||||||
},
|
|
||||||
data_files=[
|
|
||||||
('share/man/man1', ['extras/man/http.1']),
|
|
||||||
('share/man/man1', ['extras/man/https.1']),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
@ -31,7 +31,7 @@ description: |
|
|||||||
Links
|
Links
|
||||||
- Documentation: https://httpie.io/docs
|
- Documentation: https://httpie.io/docs
|
||||||
- Try in browser: https://httpie.io/run
|
- Try in browser: https://httpie.io/run
|
||||||
- GitHub: https://github.com/httpie/httpie
|
- GitHub: https://github.com/httpie/cli
|
||||||
- Twitter: https://twitter.com/httpie
|
- Twitter: https://twitter.com/httpie
|
||||||
- Discord: https://httpie.io/chat
|
- Discord: https://httpie.io/chat
|
||||||
license: BSD-3-Clause-LBNL
|
license: BSD-3-Clause-LBNL
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
# HTTPie Test Suite
|
# HTTPie Test Suite
|
||||||
|
|
||||||
Please see [CONTRIBUTING](https://github.com/httpie/httpie/blob/master/CONTRIBUTING.md) for contribution and testing guidelines.
|
Please see [CONTRIBUTING](https://github.com/httpie/cli/blob/master/CONTRIBUTING.md) for contribution and testing guidelines.
|
||||||
|
@ -2,15 +2,16 @@ import socket
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from pytest_httpbin import certs
|
from pytest_httpbin import certs
|
||||||
|
from pytest_httpbin.serve import Server as PyTestHttpBinServer
|
||||||
|
|
||||||
from .utils import ( # noqa
|
from .utils import ( # noqa
|
||||||
HTTPBIN_WITH_CHUNKED_SUPPORT_DOMAIN,
|
HTTPBIN_WITH_CHUNKED_SUPPORT_DOMAIN,
|
||||||
HTTPBIN_WITH_CHUNKED_SUPPORT,
|
HTTPBIN_WITH_CHUNKED_SUPPORT,
|
||||||
REMOTE_HTTPBIN_DOMAIN,
|
REMOTE_HTTPBIN_DOMAIN,
|
||||||
IS_PYOPENSSL,
|
IS_PYOPENSSL,
|
||||||
mock_env
|
mock_env
|
||||||
)
|
)
|
||||||
from .utils.plugins_cli import ( # noqa
|
from .utils.plugins_cli import ( # noqa
|
||||||
broken_plugin,
|
broken_plugin,
|
||||||
dummy_plugin,
|
dummy_plugin,
|
||||||
dummy_plugins,
|
dummy_plugins,
|
||||||
@ -18,7 +19,11 @@ from .utils.plugins_cli import ( # noqa
|
|||||||
httpie_plugins_success,
|
httpie_plugins_success,
|
||||||
interface,
|
interface,
|
||||||
)
|
)
|
||||||
from .utils.http_server import http_server, localhost_http_server # noqa
|
from .utils.http_server import http_server, localhost_http_server # noqa
|
||||||
|
|
||||||
|
|
||||||
|
# Patch to support `url = str(server)` in addition to `url = server + '/foo'`.
|
||||||
|
PyTestHttpBinServer.__str__ = lambda self: self.url
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope='function', autouse=True)
|
@pytest.fixture(scope='function', autouse=True)
|
||||||
|
@ -42,7 +42,7 @@ def test_bearer_auth(httpbin_both, token):
|
|||||||
new=lambda self, prompt: 'password')
|
new=lambda self, prompt: 'password')
|
||||||
def test_password_prompt(httpbin):
|
def test_password_prompt(httpbin):
|
||||||
r = http('--auth', 'user',
|
r = http('--auth', 'user',
|
||||||
'GET', httpbin.url + '/basic-auth/user/password')
|
'GET', httpbin + '/basic-auth/user/password')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json == {'authenticated': True, 'user': 'user'}
|
assert r.json == {'authenticated': True, 'user': 'user'}
|
||||||
|
|
||||||
@ -71,7 +71,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/httpie/cli/issues/242
|
||||||
|
|
||||||
"""
|
"""
|
||||||
args = httpie.cli.definition.parser.parse_args(args=[url], env=MockEnvironment())
|
args = httpie.cli.definition.parser.parse_args(args=[url], env=MockEnvironment())
|
||||||
|
@ -15,18 +15,18 @@ class TestBinaryRequestData:
|
|||||||
stdin_isatty=False,
|
stdin_isatty=False,
|
||||||
stdout_isatty=False
|
stdout_isatty=False
|
||||||
)
|
)
|
||||||
r = http('--print=B', 'POST', httpbin.url + '/post', env=env)
|
r = http('--print=B', 'POST', httpbin + '/post', env=env)
|
||||||
assert r == BIN_FILE_CONTENT
|
assert r == BIN_FILE_CONTENT
|
||||||
|
|
||||||
def test_binary_file_path(self, httpbin):
|
def test_binary_file_path(self, httpbin):
|
||||||
env = MockEnvironment(stdin_isatty=True, stdout_isatty=False)
|
env = MockEnvironment(stdin_isatty=True, stdout_isatty=False)
|
||||||
r = http('--print=B', 'POST', httpbin.url + '/post',
|
r = http('--print=B', 'POST', httpbin + '/post',
|
||||||
'@' + BIN_FILE_PATH_ARG, env=env)
|
'@' + BIN_FILE_PATH_ARG, env=env)
|
||||||
assert r == BIN_FILE_CONTENT
|
assert r == BIN_FILE_CONTENT
|
||||||
|
|
||||||
def test_binary_file_form(self, httpbin):
|
def test_binary_file_form(self, httpbin):
|
||||||
env = MockEnvironment(stdin_isatty=True, stdout_isatty=False)
|
env = MockEnvironment(stdin_isatty=True, stdout_isatty=False)
|
||||||
r = http('--print=B', '--form', 'POST', httpbin.url + '/post',
|
r = http('--print=B', '--form', 'POST', httpbin + '/post',
|
||||||
'test@' + BIN_FILE_PATH_ARG, env=env)
|
'test@' + BIN_FILE_PATH_ARG, env=env)
|
||||||
assert bytes(BIN_FILE_CONTENT) in bytes(r)
|
assert bytes(BIN_FILE_CONTENT) in bytes(r)
|
||||||
|
|
||||||
|
@ -151,17 +151,17 @@ class TestItemParsing:
|
|||||||
|
|
||||||
class TestQuerystring:
|
class TestQuerystring:
|
||||||
def test_query_string_params_in_url(self, httpbin):
|
def test_query_string_params_in_url(self, httpbin):
|
||||||
r = http('--print=Hhb', 'GET', httpbin.url + '/get?a=1&b=2')
|
r = http('--print=Hhb', 'GET', httpbin + '/get?a=1&b=2')
|
||||||
path = '/get?a=1&b=2'
|
path = '/get?a=1&b=2'
|
||||||
url = httpbin.url + path
|
url = httpbin + path
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert f'GET {path} HTTP/1.1' in r
|
assert f'GET {path} HTTP/1.1' in r
|
||||||
assert f'"url": "{url}"' in r
|
assert f'"url": "{url}"' in r
|
||||||
|
|
||||||
def test_query_string_params_items(self, httpbin):
|
def test_query_string_params_items(self, httpbin):
|
||||||
r = http('--print=Hhb', 'GET', httpbin.url + '/get', 'a==1')
|
r = http('--print=Hhb', 'GET', httpbin + '/get', 'a==1')
|
||||||
path = '/get?a=1'
|
path = '/get?a=1'
|
||||||
url = httpbin.url + path
|
url = httpbin + path
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert f'GET {path} HTTP/1.1' in r
|
assert f'GET {path} HTTP/1.1' in r
|
||||||
assert f'"url": "{url}"' in r
|
assert f'"url": "{url}"' in r
|
||||||
@ -169,9 +169,9 @@ class TestQuerystring:
|
|||||||
def test_query_string_params_in_url_and_items_with_duplicates(self,
|
def test_query_string_params_in_url_and_items_with_duplicates(self,
|
||||||
httpbin):
|
httpbin):
|
||||||
r = http('--print=Hhb', 'GET',
|
r = http('--print=Hhb', 'GET',
|
||||||
httpbin.url + '/get?a=1&a=1', 'a==1', 'a==1')
|
httpbin + '/get?a=1&a=1', 'a==1', 'a==1')
|
||||||
path = '/get?a=1&a=1&a=1&a=1'
|
path = '/get?a=1&a=1&a=1&a=1'
|
||||||
url = httpbin.url + path
|
url = httpbin + path
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert f'GET {path} HTTP/1.1' in r
|
assert f'GET {path} HTTP/1.1' in r
|
||||||
assert f'"url": "{url}"' in r
|
assert f'"url": "{url}"' in r
|
||||||
@ -320,11 +320,11 @@ class TestArgumentParser:
|
|||||||
class TestNoOptions:
|
class TestNoOptions:
|
||||||
|
|
||||||
def test_valid_no_options(self, httpbin):
|
def test_valid_no_options(self, httpbin):
|
||||||
r = http('--verbose', '--no-verbose', 'GET', httpbin.url + '/get')
|
r = http('--verbose', '--no-verbose', 'GET', httpbin + '/get')
|
||||||
assert 'GET /get HTTP/1.1' not in r
|
assert 'GET /get HTTP/1.1' not in r
|
||||||
|
|
||||||
def test_invalid_no_options(self, httpbin):
|
def test_invalid_no_options(self, httpbin):
|
||||||
r = http('--no-war', 'GET', httpbin.url + '/get',
|
r = http('--no-war', 'GET', httpbin + '/get',
|
||||||
tolerate_error_exit_status=True)
|
tolerate_error_exit_status=True)
|
||||||
assert r.exit_status == ExitStatus.ERROR
|
assert r.exit_status == ExitStatus.ERROR
|
||||||
assert 'unrecognized arguments: --no-war' in r.stderr
|
assert 'unrecognized arguments: --no-war' in r.stderr
|
||||||
@ -338,13 +338,13 @@ class TestStdin:
|
|||||||
stdin=StdinBytesIO(FILE_PATH.read_bytes()),
|
stdin=StdinBytesIO(FILE_PATH.read_bytes()),
|
||||||
stdin_isatty=False,
|
stdin_isatty=False,
|
||||||
)
|
)
|
||||||
r = http('--ignore-stdin', '--verbose', httpbin.url + '/get', env=env)
|
r = http('--ignore-stdin', '--verbose', httpbin + '/get', env=env)
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert 'GET /get HTTP' in r, "Don't default to POST."
|
assert 'GET /get HTTP' in r, "Don't default to POST."
|
||||||
assert FILE_CONTENT not in r, "Don't send stdin data."
|
assert FILE_CONTENT not in r, "Don't send stdin data."
|
||||||
|
|
||||||
def test_ignore_stdin_cannot_prompt_password(self, httpbin):
|
def test_ignore_stdin_cannot_prompt_password(self, httpbin):
|
||||||
r = http('--ignore-stdin', '--auth=no-password', httpbin.url + '/get',
|
r = http('--ignore-stdin', '--auth=no-password', httpbin + '/get',
|
||||||
tolerate_error_exit_status=True)
|
tolerate_error_exit_status=True)
|
||||||
assert r.exit_status == ExitStatus.ERROR
|
assert r.exit_status == ExitStatus.ERROR
|
||||||
assert 'because --ignore-stdin' in r.stderr
|
assert 'because --ignore-stdin' in r.stderr
|
||||||
|
@ -29,14 +29,14 @@ def assert_decompressed_equal(base64_compressed_data, expected_str):
|
|||||||
|
|
||||||
|
|
||||||
def test_cannot_combine_compress_with_chunked(httpbin):
|
def test_cannot_combine_compress_with_chunked(httpbin):
|
||||||
r = http('--compress', '--chunked', httpbin.url + '/get',
|
r = http('--compress', '--chunked', httpbin + '/get',
|
||||||
tolerate_error_exit_status=True)
|
tolerate_error_exit_status=True)
|
||||||
assert r.exit_status == ExitStatus.ERROR
|
assert r.exit_status == ExitStatus.ERROR
|
||||||
assert 'cannot combine --compress and --chunked' in r.stderr
|
assert 'cannot combine --compress and --chunked' in r.stderr
|
||||||
|
|
||||||
|
|
||||||
def test_cannot_combine_compress_with_multipart(httpbin):
|
def test_cannot_combine_compress_with_multipart(httpbin):
|
||||||
r = http('--compress', '--multipart', httpbin.url + '/get',
|
r = http('--compress', '--multipart', httpbin + '/get',
|
||||||
tolerate_error_exit_status=True)
|
tolerate_error_exit_status=True)
|
||||||
assert r.exit_status == ExitStatus.ERROR
|
assert r.exit_status == ExitStatus.ERROR
|
||||||
assert 'cannot combine --compress and --multipart' in r.stderr
|
assert 'cannot combine --compress and --multipart' in r.stderr
|
||||||
|
@ -17,7 +17,7 @@ def test_default_options(httpbin):
|
|||||||
env = MockEnvironment()
|
env = MockEnvironment()
|
||||||
env.config['default_options'] = ['--form']
|
env.config['default_options'] = ['--form']
|
||||||
env.config.save()
|
env.config.save()
|
||||||
r = http(httpbin.url + '/post', 'foo=bar', env=env)
|
r = http(httpbin + '/post', 'foo=bar', env=env)
|
||||||
assert r.json['form'] == {
|
assert r.json['form'] == {
|
||||||
"foo": "bar"
|
"foo": "bar"
|
||||||
}
|
}
|
||||||
@ -51,7 +51,7 @@ def test_default_options_overwrite(httpbin):
|
|||||||
env = MockEnvironment()
|
env = MockEnvironment()
|
||||||
env.config['default_options'] = ['--form']
|
env.config['default_options'] = ['--form']
|
||||||
env.config.save()
|
env.config.save()
|
||||||
r = http('--json', httpbin.url + '/post', 'foo=bar', env=env)
|
r = http('--json', httpbin + '/post', 'foo=bar', env=env)
|
||||||
assert r.json['json'] == {
|
assert r.json['json'] == {
|
||||||
"foo": "bar"
|
"foo": "bar"
|
||||||
}
|
}
|
||||||
|
@ -2,54 +2,47 @@ import pytest
|
|||||||
from .utils import http
|
from .utils import http
|
||||||
|
|
||||||
|
|
||||||
def _stringify(fixture):
|
@pytest.mark.parametrize('target_httpbin', [
|
||||||
return fixture + ''
|
'httpbin',
|
||||||
|
'remote_httpbin',
|
||||||
|
|
||||||
@pytest.mark.parametrize('instance', [
|
|
||||||
pytest.lazy_fixture('httpbin'),
|
|
||||||
pytest.lazy_fixture('remote_httpbin'),
|
|
||||||
])
|
])
|
||||||
def test_explicit_user_set_cookie(httpbin, instance):
|
def test_explicit_user_set_cookie(httpbin, target_httpbin, request):
|
||||||
# User set cookies ARE NOT persisted within redirects
|
"""User set cookies ARE NOT persisted within redirects when there is no session, even on the same domain."""
|
||||||
# when there is no session, even on the same domain.
|
target_httpbin = request.getfixturevalue(target_httpbin)
|
||||||
|
|
||||||
r = http(
|
r = http(
|
||||||
'--follow',
|
'--follow',
|
||||||
httpbin + '/redirect-to',
|
httpbin + '/redirect-to',
|
||||||
f'url=={_stringify(instance)}/cookies',
|
f'url=={target_httpbin}/cookies',
|
||||||
'Cookie:a=b'
|
'Cookie:a=b'
|
||||||
)
|
)
|
||||||
assert r.json == {'cookies': {}}
|
assert r.json == {'cookies': {}}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('instance', [
|
@pytest.mark.parametrize('target_httpbin', [
|
||||||
pytest.lazy_fixture('httpbin'),
|
'httpbin',
|
||||||
pytest.lazy_fixture('remote_httpbin'),
|
'remote_httpbin',
|
||||||
])
|
])
|
||||||
def test_explicit_user_set_cookie_in_session(tmp_path, httpbin, instance):
|
def test_explicit_user_set_cookie_in_session(tmp_path, httpbin, target_httpbin, request):
|
||||||
# User set cookies ARE persisted within redirects
|
"""User set cookies ARE persisted within redirects when there is A session, even on the same domain."""
|
||||||
# when there is A session, even on the same domain.
|
target_httpbin = request.getfixturevalue(target_httpbin)
|
||||||
|
|
||||||
r = http(
|
r = http(
|
||||||
'--follow',
|
'--follow',
|
||||||
'--session',
|
'--session',
|
||||||
str(tmp_path / 'session.json'),
|
str(tmp_path / 'session.json'),
|
||||||
httpbin + '/redirect-to',
|
httpbin + '/redirect-to',
|
||||||
f'url=={_stringify(instance)}/cookies',
|
f'url=={target_httpbin}/cookies',
|
||||||
'Cookie:a=b'
|
'Cookie:a=b'
|
||||||
)
|
)
|
||||||
assert r.json == {'cookies': {'a': 'b'}}
|
assert r.json == {'cookies': {'a': 'b'}}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('instance', [
|
@pytest.mark.parametrize('target_httpbin', [
|
||||||
pytest.lazy_fixture('httpbin'),
|
'httpbin',
|
||||||
pytest.lazy_fixture('remote_httpbin'),
|
'remote_httpbin',
|
||||||
])
|
])
|
||||||
def test_saved_user_set_cookie_in_session(tmp_path, httpbin, instance):
|
def test_saved_user_set_cookie_in_session(tmp_path, httpbin, target_httpbin, request):
|
||||||
# User set cookies ARE persisted within redirects
|
"""User set cookies ARE persisted within redirects when there is A session, even on the same domain."""
|
||||||
# when there is A session, even on the same domain.
|
target_httpbin = request.getfixturevalue(target_httpbin)
|
||||||
|
|
||||||
http(
|
http(
|
||||||
'--follow',
|
'--follow',
|
||||||
'--session',
|
'--session',
|
||||||
@ -62,32 +55,33 @@ def test_saved_user_set_cookie_in_session(tmp_path, httpbin, instance):
|
|||||||
'--session',
|
'--session',
|
||||||
str(tmp_path / 'session.json'),
|
str(tmp_path / 'session.json'),
|
||||||
httpbin + '/redirect-to',
|
httpbin + '/redirect-to',
|
||||||
f'url=={_stringify(instance)}/cookies',
|
f'url=={target_httpbin}/cookies',
|
||||||
)
|
)
|
||||||
assert r.json == {'cookies': {'a': 'b'}}
|
assert r.json == {'cookies': {'a': 'b'}}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('instance', [
|
@pytest.mark.parametrize('target_httpbin', [
|
||||||
pytest.lazy_fixture('httpbin'),
|
'httpbin',
|
||||||
pytest.lazy_fixture('remote_httpbin'),
|
'remote_httpbin',
|
||||||
])
|
])
|
||||||
@pytest.mark.parametrize('session', [True, False])
|
@pytest.mark.parametrize('session', [True, False])
|
||||||
def test_explicit_user_set_headers(httpbin, tmp_path, instance, session):
|
def test_explicit_user_set_headers(httpbin, tmp_path, target_httpbin, session, request):
|
||||||
# User set headers ARE persisted within redirects
|
"""
|
||||||
# even on different domains domain with or without
|
User set headers ARE persisted within redirects even on different domains domain with or without an active session.
|
||||||
# an active session.
|
|
||||||
|
"""
|
||||||
|
target_httpbin = request.getfixturevalue(target_httpbin)
|
||||||
session_args = []
|
session_args = []
|
||||||
if session:
|
if session:
|
||||||
session_args.extend([
|
session_args.extend([
|
||||||
'--session',
|
'--session',
|
||||||
str(tmp_path / 'session.json')
|
str(tmp_path / 'session.json')
|
||||||
])
|
])
|
||||||
|
|
||||||
r = http(
|
r = http(
|
||||||
'--follow',
|
'--follow',
|
||||||
*session_args,
|
*session_args,
|
||||||
httpbin + '/redirect-to',
|
httpbin + '/redirect-to',
|
||||||
f'url=={_stringify(instance)}/get',
|
f'url=={target_httpbin}/get',
|
||||||
'X-Custom-Header:value'
|
'X-Custom-Header:value'
|
||||||
)
|
)
|
||||||
assert 'X-Custom-Header' in r.json['headers']
|
assert 'X-Custom-Header' in r.json['headers']
|
||||||
@ -95,16 +89,13 @@ def test_explicit_user_set_headers(httpbin, tmp_path, instance, session):
|
|||||||
|
|
||||||
@pytest.mark.parametrize('session', [True, False])
|
@pytest.mark.parametrize('session', [True, False])
|
||||||
def test_server_set_cookie_on_redirect_same_domain(tmp_path, httpbin, session):
|
def test_server_set_cookie_on_redirect_same_domain(tmp_path, httpbin, session):
|
||||||
# Server set cookies ARE persisted on the same domain
|
"""Server set cookies ARE persisted on the same domain when they are forwarded."""
|
||||||
# when they are forwarded.
|
|
||||||
|
|
||||||
session_args = []
|
session_args = []
|
||||||
if session:
|
if session:
|
||||||
session_args.extend([
|
session_args.extend([
|
||||||
'--session',
|
'--session',
|
||||||
str(tmp_path / 'session.json')
|
str(tmp_path / 'session.json')
|
||||||
])
|
])
|
||||||
|
|
||||||
r = http(
|
r = http(
|
||||||
'--follow',
|
'--follow',
|
||||||
*session_args,
|
*session_args,
|
||||||
@ -136,8 +127,7 @@ def test_server_set_cookie_on_redirect_different_domain(tmp_path, http_server, h
|
|||||||
|
|
||||||
|
|
||||||
def test_saved_session_cookies_on_same_domain(tmp_path, httpbin):
|
def test_saved_session_cookies_on_same_domain(tmp_path, httpbin):
|
||||||
# Saved session cookies ARE persisted when making a new
|
"""Saved session cookies ARE persisted when making a new request to the same domain."""
|
||||||
# request to the same domain.
|
|
||||||
http(
|
http(
|
||||||
'--session',
|
'--session',
|
||||||
str(tmp_path / 'session.json'),
|
str(tmp_path / 'session.json'),
|
||||||
@ -152,8 +142,7 @@ def test_saved_session_cookies_on_same_domain(tmp_path, httpbin):
|
|||||||
|
|
||||||
|
|
||||||
def test_saved_session_cookies_on_different_domain(tmp_path, httpbin, remote_httpbin):
|
def test_saved_session_cookies_on_different_domain(tmp_path, httpbin, remote_httpbin):
|
||||||
# Saved session cookies ARE persisted when making a new
|
"""Saved session cookies ARE persisted when making a new request to a different domain."""
|
||||||
# request to a different domain.
|
|
||||||
http(
|
http(
|
||||||
'--session',
|
'--session',
|
||||||
str(tmp_path / 'session.json'),
|
str(tmp_path / 'session.json'),
|
||||||
@ -167,45 +156,49 @@ def test_saved_session_cookies_on_different_domain(tmp_path, httpbin, remote_htt
|
|||||||
assert r.json == {'cookies': {}}
|
assert r.json == {'cookies': {}}
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('initial_domain, first_request_domain, second_request_domain, expect_cookies', [
|
@pytest.mark.parametrize(['initial_domain', 'first_request_domain', 'second_request_domain', 'expect_cookies'], [
|
||||||
(
|
(
|
||||||
# Cookies are set by Domain A
|
# Cookies are set by Domain A
|
||||||
# Initial domain is Domain A
|
# Initial domain is Domain A
|
||||||
# Redirected domain is Domain A
|
# Redirected domain is Domain A
|
||||||
pytest.lazy_fixture('httpbin'),
|
'httpbin',
|
||||||
pytest.lazy_fixture('httpbin'),
|
'httpbin',
|
||||||
pytest.lazy_fixture('httpbin'),
|
'httpbin',
|
||||||
True,
|
True,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
# Cookies are set by Domain A
|
# Cookies are set by Domain A
|
||||||
# Initial domain is Domain B
|
# Initial domain is Domain B
|
||||||
# Redirected domain is Domain B
|
# Redirected domain is Domain B
|
||||||
pytest.lazy_fixture('httpbin'),
|
'httpbin',
|
||||||
pytest.lazy_fixture('remote_httpbin'),
|
'remote_httpbin',
|
||||||
pytest.lazy_fixture('remote_httpbin'),
|
'remote_httpbin',
|
||||||
False,
|
False,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
# Cookies are set by Domain A
|
# Cookies are set by Domain A
|
||||||
# Initial domain is Domain A
|
# Initial domain is Domain A
|
||||||
# Redirected domain is Domain B
|
# Redirected domain is Domain B
|
||||||
pytest.lazy_fixture('httpbin'),
|
'httpbin',
|
||||||
pytest.lazy_fixture('httpbin'),
|
'httpbin',
|
||||||
pytest.lazy_fixture('remote_httpbin'),
|
'remote_httpbin',
|
||||||
False,
|
False,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
# Cookies are set by Domain A
|
# Cookies are set by Domain A
|
||||||
# Initial domain is Domain B
|
# Initial domain is Domain B
|
||||||
# Redirected domain is Domain A
|
# Redirected domain is Domain A
|
||||||
pytest.lazy_fixture('httpbin'),
|
'httpbin',
|
||||||
pytest.lazy_fixture('remote_httpbin'),
|
'remote_httpbin',
|
||||||
pytest.lazy_fixture('httpbin'),
|
'httpbin',
|
||||||
True,
|
True,
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
def test_saved_session_cookies_on_redirect(tmp_path, initial_domain, first_request_domain, second_request_domain, expect_cookies):
|
def test_saved_session_cookies_on_redirect(
|
||||||
|
tmp_path, initial_domain, first_request_domain, second_request_domain, expect_cookies, request):
|
||||||
|
initial_domain = request.getfixturevalue(initial_domain)
|
||||||
|
first_request_domain = request.getfixturevalue(first_request_domain)
|
||||||
|
second_request_domain = request.getfixturevalue(second_request_domain)
|
||||||
http(
|
http(
|
||||||
'--session',
|
'--session',
|
||||||
str(tmp_path / 'session.json'),
|
str(tmp_path / 'session.json'),
|
||||||
@ -216,7 +209,7 @@ def test_saved_session_cookies_on_redirect(tmp_path, initial_domain, first_reque
|
|||||||
str(tmp_path / 'session.json'),
|
str(tmp_path / 'session.json'),
|
||||||
'--follow',
|
'--follow',
|
||||||
first_request_domain + '/redirect-to',
|
first_request_domain + '/redirect-to',
|
||||||
f'url=={_stringify(second_request_domain)}/cookies'
|
f'url=={second_request_domain}/cookies'
|
||||||
)
|
)
|
||||||
if expect_cookies:
|
if expect_cookies:
|
||||||
expected_data = {'cookies': {'a': 'b'}}
|
expected_data = {'cookies': {'a': 'b'}}
|
||||||
|
@ -11,12 +11,12 @@ 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/httpie/cli/issues/644>
|
||||||
"""
|
"""
|
||||||
r = http(
|
r = http(
|
||||||
'--debug',
|
'--debug',
|
||||||
'--print=H',
|
'--print=H',
|
||||||
httpbin.url + '/post',
|
httpbin + '/post',
|
||||||
'CONTENT-TYPE:application/json-patch+json',
|
'CONTENT-TYPE:application/json-patch+json',
|
||||||
'a=b',
|
'a=b',
|
||||||
)
|
)
|
||||||
@ -27,26 +27,26 @@ def test_default_headers_case_insensitive(httpbin):
|
|||||||
# noinspection PyPep8Naming
|
# noinspection PyPep8Naming
|
||||||
class TestImplicitHTTPMethod:
|
class TestImplicitHTTPMethod:
|
||||||
def test_implicit_GET(self, httpbin):
|
def test_implicit_GET(self, httpbin):
|
||||||
r = http(httpbin.url + '/get')
|
r = http(httpbin + '/get')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
|
|
||||||
def test_implicit_GET_with_headers(self, httpbin):
|
def test_implicit_GET_with_headers(self, httpbin):
|
||||||
r = http(httpbin.url + '/headers', 'Foo:bar')
|
r = http(httpbin + '/headers', 'Foo:bar')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json['headers']['Foo'] == 'bar'
|
assert r.json['headers']['Foo'] == 'bar'
|
||||||
|
|
||||||
def test_implicit_POST_json(self, httpbin):
|
def test_implicit_POST_json(self, httpbin):
|
||||||
r = http(httpbin.url + '/post', 'hello=world')
|
r = http(httpbin + '/post', 'hello=world')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json['json'] == {'hello': 'world'}
|
assert r.json['json'] == {'hello': 'world'}
|
||||||
|
|
||||||
def test_implicit_POST_form(self, httpbin):
|
def test_implicit_POST_form(self, httpbin):
|
||||||
r = http('--form', httpbin.url + '/post', 'foo=bar')
|
r = http('--form', httpbin + '/post', 'foo=bar')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json['form'] == {'foo': 'bar'}
|
assert r.json['form'] == {'foo': 'bar'}
|
||||||
|
|
||||||
def test_implicit_POST_raw(self, httpbin):
|
def test_implicit_POST_raw(self, httpbin):
|
||||||
r = http('--raw', 'foo bar', httpbin.url + '/post')
|
r = http('--raw', 'foo bar', httpbin + '/post')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json['data'] == 'foo bar'
|
assert r.json['data'] == 'foo bar'
|
||||||
|
|
||||||
@ -55,7 +55,7 @@ class TestImplicitHTTPMethod:
|
|||||||
stdin_isatty=False,
|
stdin_isatty=False,
|
||||||
stdin=BytesIO(FILE_PATH.read_bytes())
|
stdin=BytesIO(FILE_PATH.read_bytes())
|
||||||
)
|
)
|
||||||
r = http('--form', httpbin.url + '/post', env=env)
|
r = http('--form', httpbin + '/post', env=env)
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
|
|
||||||
|
|
||||||
@ -68,42 +68,42 @@ 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/httpie/cli/issues/62
|
||||||
r = http('GET', httpbin.url + '/headers')
|
r = http('GET', httpbin + '/headers')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json['headers']['Accept'] == '*/*'
|
assert r.json['headers']['Accept'] == '*/*'
|
||||||
assert 'Content-Type' not in r.json['headers']
|
assert 'Content-Type' not in r.json['headers']
|
||||||
|
|
||||||
def test_POST_no_data_no_auto_headers(self, httpbin):
|
def test_POST_no_data_no_auto_headers(self, httpbin):
|
||||||
# JSON headers shouldn't be automatically set for POST with no data.
|
# JSON headers shouldn't be automatically set for POST with no data.
|
||||||
r = http('POST', httpbin.url + '/post')
|
r = http('POST', httpbin + '/post')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert '"Accept": "*/*"' in r
|
assert '"Accept": "*/*"' in r
|
||||||
assert '"Content-Type": "application/json' not in r
|
assert '"Content-Type": "application/json' not in r
|
||||||
|
|
||||||
def test_POST_with_data_auto_JSON_headers(self, httpbin):
|
def test_POST_with_data_auto_JSON_headers(self, httpbin):
|
||||||
r = http('POST', httpbin.url + '/post', 'a=b')
|
r = http('POST', httpbin + '/post', 'a=b')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json['headers']['Accept'] == JSON_ACCEPT
|
assert r.json['headers']['Accept'] == JSON_ACCEPT
|
||||||
assert r.json['headers']['Content-Type'] == 'application/json'
|
assert r.json['headers']['Content-Type'] == 'application/json'
|
||||||
|
|
||||||
def test_GET_with_data_auto_JSON_headers(self, httpbin):
|
def test_GET_with_data_auto_JSON_headers(self, httpbin):
|
||||||
# JSON headers should automatically be set also for GET with data.
|
# JSON headers should automatically be set also for GET with data.
|
||||||
r = http('POST', httpbin.url + '/post', 'a=b')
|
r = http('POST', httpbin + '/post', 'a=b')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert r.json['headers']['Accept'] == JSON_ACCEPT
|
assert r.json['headers']['Accept'] == JSON_ACCEPT
|
||||||
assert r.json['headers']['Content-Type'] == 'application/json'
|
assert r.json['headers']['Content-Type'] == 'application/json'
|
||||||
|
|
||||||
def test_POST_explicit_JSON_JSON_ACCEPT(self, httpbin):
|
def test_POST_explicit_JSON_JSON_ACCEPT(self, httpbin):
|
||||||
r = http('--json', 'POST', httpbin.url + '/post')
|
r = http('--json', 'POST', httpbin + '/post')
|
||||||
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/httpie/cli/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):
|
||||||
r = http('--json', 'GET', httpbin.url + '/headers',
|
r = http('--json', 'GET', httpbin + '/headers',
|
||||||
'Accept:application/xml',
|
'Accept:application/xml',
|
||||||
'Content-Type:application/xml')
|
'Content-Type:application/xml')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
@ -111,22 +111,22 @@ class TestAutoContentTypeAndAcceptHeaders:
|
|||||||
assert '"Content-Type": "application/xml"' in r
|
assert '"Content-Type": "application/xml"' in r
|
||||||
|
|
||||||
def test_POST_form_auto_Content_Type(self, httpbin):
|
def test_POST_form_auto_Content_Type(self, httpbin):
|
||||||
r = http('--form', 'POST', httpbin.url + '/post')
|
r = http('--form', 'POST', httpbin + '/post')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert '"Content-Type": "application/x-www-form-urlencoded' in r
|
assert '"Content-Type": "application/x-www-form-urlencoded' in r
|
||||||
|
|
||||||
def test_POST_form_Content_Type_override(self, httpbin):
|
def test_POST_form_Content_Type_override(self, httpbin):
|
||||||
r = http('--form', 'POST', httpbin.url + '/post',
|
r = http('--form', 'POST', httpbin + '/post',
|
||||||
'Content-Type:application/xml')
|
'Content-Type:application/xml')
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
assert '"Content-Type": "application/xml"' in r
|
assert '"Content-Type": "application/xml"' in r
|
||||||
|
|
||||||
def test_print_only_body_when_stdout_redirected_by_default(self, httpbin):
|
def test_print_only_body_when_stdout_redirected_by_default(self, httpbin):
|
||||||
env = MockEnvironment(stdin_isatty=True, stdout_isatty=False)
|
env = MockEnvironment(stdin_isatty=True, stdout_isatty=False)
|
||||||
r = http('GET', httpbin.url + '/get', env=env)
|
r = http('GET', httpbin + '/get', env=env)
|
||||||
assert 'HTTP/' not in r
|
assert 'HTTP/' not in r
|
||||||
|
|
||||||
def test_print_overridable_when_stdout_redirected(self, httpbin):
|
def test_print_overridable_when_stdout_redirected(self, httpbin):
|
||||||
env = MockEnvironment(stdin_isatty=True, stdout_isatty=False)
|
env = MockEnvironment(stdin_isatty=True, stdout_isatty=False)
|
||||||
r = http('--print=h', 'GET', httpbin.url + '/get', env=env)
|
r = http('--print=h', 'GET', httpbin + '/get', env=env)
|
||||||
assert HTTP_OK in r
|
assert HTTP_OK in r
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user