diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..559e66fe3d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# https://editorconfig.org +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.yml] +indent_size = 2 + +[Makefile] +indent_style = tab +indent_size = 8 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..76d78512c5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,44 @@ +--- +name: Bug report +about: Report a possible bug in HTTPie +title: '' +labels: "new, bug" +assignees: '' + +--- + +## Checklist + +- [ ] I've searched for similar issues. +- [ ] I'm using the latest version of HTTPie. + +--- + +## Minimal reproduction code and steps + +1. +2. +3. + +## Current result + +… + +## Expected result + +… + +--- + +## Debug output + +Please re-run the command with `--debug`, then copy the entire command & output and paste both below: + +```bash +$ http --debug + +``` + +## Additional information, screenshots, or code examples + +… diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..329c06a2ae --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,30 @@ +--- +name: Feature request +about: Suggest an enhancement for HTTPie +title: '' +labels: "new, enhancement" +assignees: '' + +--- + +## Checklist + +- [ ] I've searched for similar feature requests. + +--- + +## Enhancement request + +… + +--- + +## Problem it solves + +E.g. “I'm always frustrated when […]”, “I’m trying to do […] so that […]”. + +--- + +## Additional information, screenshots, or code examples + +… diff --git a/.github/ISSUE_TEMPLATE/other.md b/.github/ISSUE_TEMPLATE/other.md new file mode 100644 index 0000000000..bfad744e09 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/other.md @@ -0,0 +1,10 @@ +--- +name: Other +about: Anything else that isn't a feature or a bug +title: '' +labels: "new" +assignees: '' + +--- + +If you have a general question, please consider asking on Discord: https://httpie.io/chat diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..cde947df7d --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: + # GitHub Actions + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily + assignees: + - BoboTiG diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 0000000000..dd168f0114 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,52 @@ +name: Benchmark + +on: + pull_request: + types: [ labeled ] + +permissions: + issues: write + pull-requests: write + +jobs: + test: + if: github.event.label.name == 'benchmark' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.9" + + - id: benchmarks + name: Run Benchmarks + run: | + python -m pip install pyperf>=2.3.0 + python extras/profiling/run.py --fresh --complex --min-speed=6 --file output.txt + body=$(cat output.txt) + body="${body//'%'/'%25'}" + body="${body//$'\n'/'%0A'}" + body="${body//$'\r'/'%0D'}" + echo "::set-output name=body::$body" + + - name: Find Comment + uses: peter-evans/find-comment@v2 + id: fc + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: '# Benchmarks' + + - name: Create or update comment + uses: peter-evans/create-or-update-comment@v2 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + body: | + # Benchmarks + ${{ steps.benchmarks.outputs.body }} + edit-mode: replace + + - uses: actions-ecosystem/action-remove-labels@v1 + with: + labels: benchmark diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml new file mode 100644 index 0000000000..779bf09b8b --- /dev/null +++ b/.github/workflows/code-style.yml @@ -0,0 +1,21 @@ +name: Code Style Check + +on: + pull_request: + paths: + - .github/workflows/code-style.yml + - extras/*.py + - httpie/**/*.py + - setup.py + - tests/**/*.py + +jobs: + code-style: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.9 + - run: make venv + - run: make codestyle diff --git a/.github/workflows/content.yml b/.github/workflows/content.yml new file mode 100644 index 0000000000..d5d6dbcfa4 --- /dev/null +++ b/.github/workflows/content.yml @@ -0,0 +1,22 @@ +name: Update Generated Content +on: + push: + branches: + - master +jobs: + update-content: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: 3.9 + - run: make content + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v4 + with: + commit-message: "[automated] Update generated content" + title: "[automated] Update generated content" + delete-branch: true + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 0000000000..f4a21794d1 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,24 @@ +name: Coverage + +on: + pull_request: + paths: + - .github/workflows/coverage.yml + - httpie/**/*.py + - setup.* + - tests/**/*.py + +jobs: + coverage: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v4 + with: + python-version: "3.10" + - run: make install + - run: make test-cover + - run: make codecov-upload + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_REPO_TOKEN }} + - run: make test-dist diff --git a/.github/workflows/docs-check-markdown.yml b/.github/workflows/docs-check-markdown.yml new file mode 100644 index 0000000000..a19c25916a --- /dev/null +++ b/.github/workflows/docs-check-markdown.yml @@ -0,0 +1,21 @@ +name: Check Markdown Style + +on: + pull_request: + paths: + - "*.md" + - "**/*.md" + +jobs: + doc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 2.7 + - name: Install the linter + run: sudo gem install mdl + - name: Check files + run: make doc-check diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml new file mode 100644 index 0000000000..797bdc56e0 --- /dev/null +++ b/.github/workflows/docs-deploy.yml @@ -0,0 +1,22 @@ +name: Deploy Documentation + +on: + push: + branches: + - master + paths: + - docs/README.md + - docs/config.json + release: + types: + - published + - unpublished + - deleted +jobs: + trigger-doc-build: + runs-on: ubuntu-latest + steps: + - name: Install HTTPie + run: sudo pip install httpie + - name: Trigger new documentation build + run: http --ignore-stdin POST ${{ secrets.DOCS_UPDATE_VERCEL_HOOK }} diff --git a/.github/workflows/release-brew.yml b/.github/workflows/release-brew.yml new file mode 100644 index 0000000000..d58e6b6e3d --- /dev/null +++ b/.github/workflows/release-brew.yml @@ -0,0 +1,26 @@ +name: Release on Homebrew + +on: + workflow_dispatch: + inputs: + branch: + description: "The branch, tag or SHA to release from" + required: true + default: "master" + +jobs: + brew-release: + name: Release the Homebrew Package + runs-on: macos-latest + + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.inputs.branch }} + + - uses: mislav/bump-homebrew-formula-action@v2 + with: + formula-name: httpie + tag-name: ${{ github.events.inputs.branch }} + env: + COMMITTER_TOKEN: ${{ secrets.BREW_UPDATE_TOKEN }} diff --git a/.github/workflows/release-choco.yml b/.github/workflows/release-choco.yml new file mode 100644 index 0000000000..01e0c40642 --- /dev/null +++ b/.github/workflows/release-choco.yml @@ -0,0 +1,61 @@ +name: Release on Chocolatey + +on: + workflow_dispatch: + inputs: + branch: + description: "The branch, tag or SHA to release from" + required: true + default: "master" + +jobs: + brew-release: + name: Release the Chocolatey + runs-on: windows-2019 + env: + package-dir: docs\packaging\windows-chocolatey + + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.inputs.branch }} + + # Chocolatey comes already installed on the Windows GHA image + - name: Build the Choco package + shell: cmd + run: choco pack -v + working-directory: ${{ env.package-dir }} + + - name: Check the Choco package + run: choco info httpie -s . + working-directory: ${{ env.package-dir }} + + - name: Local installation + run: | + 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 + https --version + httpie --version + choco uninstall -y httpie + working-directory: ${{ env.package-dir }} + + - name: Publish on Chocolatey + shell: bash + env: + CHOCO_API_KEY: ${{ secrets.CHOCO_API_KEY }} + run: | + choco apikey --key $CHOCO_API_KEY --source https://push.chocolatey.org/ + choco push httpie*.nupkg --source https://push.chocolatey.org/ + working-directory: ${{ env.package-dir }} diff --git a/.github/workflows/release-linux-standalone.yml b/.github/workflows/release-linux-standalone.yml new file mode 100644 index 0000000000..01821c9a91 --- /dev/null +++ b/.github/workflows/release-linux-standalone.yml @@ -0,0 +1,77 @@ +name: Release as Standalone Linux Package + +on: + workflow_dispatch: + inputs: + branch: + description: "The branch, tag or SHA to release from" + required: true + default: "master" + tag_name: + description: "Which release to upload the artifacts to (e.g., 3.0)" + required: true + + release: + types: [released, prereleased] + + +jobs: + binary-build-and-release: + name: Build and Release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.inputs.branch }} + + - uses: actions/setup-python@v4 + with: + python-version: 3.9 + + - name: Build Artifacts + run: | + cd extras/packaging/linux + ./get_release_artifacts.sh + + - uses: actions/upload-artifact@v3 + with: + name: http + path: extras/packaging/linux/artifacts/dist/http + + - uses: actions/upload-artifact@v3 + with: + name: httpie.deb + path: extras/packaging/linux/artifacts/dist/*.deb + + - uses: actions/upload-artifact@v3 + with: + name: httpie.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 + uses: actions/upload-release-asset@v1.0.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.release_id.outputs.UPLOAD_URL }} + asset_path: extras/packaging/linux/artifacts/dist/httpie_${{ github.event.inputs.tag_name }}_amd64.deb + asset_name: httpie-${{ github.event.inputs.tag_name }}.deb + asset_content_type: binary/octet-stream + + - name: Publish Single Executable + uses: actions/upload-release-asset@v1.0.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.release_id.outputs.UPLOAD_URL }} + asset_path: extras/packaging/linux/artifacts/dist/http + asset_name: http + asset_content_type: binary/octet-stream diff --git a/.github/workflows/release-pypi.yml b/.github/workflows/release-pypi.yml new file mode 100644 index 0000000000..28ad081712 --- /dev/null +++ b/.github/workflows/release-pypi.yml @@ -0,0 +1,30 @@ +name: Release on PyPI + +on: + workflow_dispatch: + inputs: + branch: + description: "The branch, tag or SHA to release from" + required: true + default: "master" + +jobs: + pypi-build-and-release: + name: Build and Release + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.inputs.branch }} + + - uses: actions/setup-python@v4 + with: + python-version: 3.9 + + - name: Build a binary wheel and a source tarball + run: make install && make build + + - name: Release on PyPI + uses: pypa/gh-action-pypi-publish@master + with: + password: ${{ secrets.PYPI_TOKEN }} diff --git a/.github/workflows/release-snap.yml b/.github/workflows/release-snap.yml new file mode 100644 index 0000000000..ad25186e88 --- /dev/null +++ b/.github/workflows/release-snap.yml @@ -0,0 +1,41 @@ +name: Release on Snap + +on: + workflow_dispatch: + inputs: + branch: + description: "The branch, tag or SHA to release from" + required: true + default: "master" + +jobs: + snap-build-and-release: + name: Build & Release the Snap Package + runs-on: ubuntu-latest + + strategy: + # If any of the stages fail, then we'll stop the action + # to give release manager time to investigate the underlying + # issue. + fail-fast: true + matrix: + level: [edge, beta, candidate, stable] + + # Set the concurrency level for this version, so + # that we'll release one by one. + concurrency: ${{ github.event.inputs.branch }} + + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.inputs.branch }} + + - uses: snapcore/action-build@v1 + id: build + + - uses: snapcore/action-publish@v1 + env: + SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_STORE_LOGIN }} + with: + snap: ${{ steps.build.outputs.snap }} + release: ${{ matrix.level }} diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000000..7aa2e24555 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,26 @@ +name: Mark stale pull requests + +on: workflow_dispatch + +permissions: + pull-requests: write + +jobs: + stale: + runs-on: ubuntu-latest + + steps: + - uses: actions/stale@v8 + 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.' + stale-pr-label: 'stale' + + days-before-stale: -1 + days-before-issue-stale: -1 + days-before-pr-stale: 30 + + days-before-close: -1 + days-before-issue-close: -1 + days-before-pr-close: 0 + + operations-per-run: 300 diff --git a/.github/workflows/test-package-linux-snap.yml b/.github/workflows/test-package-linux-snap.yml new file mode 100644 index 0000000000..ac9640a06d --- /dev/null +++ b/.github/workflows/test-package-linux-snap.yml @@ -0,0 +1,28 @@ +name: Test Snap Package (Linux) + + +on: + pull_request: + paths: + - .github/workflows/test-package-linux-snap.yml + - snapcraft.yaml + workflow_dispatch: + +jobs: + snap: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Build + uses: snapcore/action-build@v1 + id: snapcraft + - name: Install + run: sudo snap install --dangerous ${{ steps.snapcraft.outputs.snap }} + - name: Test + run: | + httpie.http --version + httpie.https --version + httpie --version + # Auto-aliases cannot be tested when installing a snap outside the store. + # http --version + # https --version diff --git a/.github/workflows/test-package-mac-brew.yml b/.github/workflows/test-package-mac-brew.yml new file mode 100644 index 0000000000..babdaa5def --- /dev/null +++ b/.github/workflows/test-package-mac-brew.yml @@ -0,0 +1,20 @@ +name: Test Brew Package (MacOS) + +on: + pull_request: + paths: + - .github/workflows/test-package-mac-brew.yml + - docs/packaging/brew/httpie.rb + workflow_dispatch: + +jobs: + brew: + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + - name: Setup brew + run: | + brew developer on + brew update + - name: Build and test the formula + run: make brew-test diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000000..d98b63ae69 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,56 @@ +name: Tests +concurrency: + group: ${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +on: + push: + branches: + - master + paths: + - .github/workflows/tests.yml + - httpie/**/*.py + - setup.* + - tests/**/*.py + pull_request: + paths: + - .github/workflows/tests.yml + - httpie/**/*.py + - setup.* + - tests/**/*.py + +jobs: + test: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-13, windows-latest] + python-version: + - '3.12' + - '3.11' + - '3.10' + - '3.9' + - '3.8' + - '3.7' + pyopenssl: [0, 1] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Windows setup + if: matrix.os == 'windows-latest' + run: | + python -m pip install --upgrade pip wheel + python -m pip install --upgrade '.[dev]' + python -m pytest --verbose ./httpie ./tests + env: + HTTPIE_TEST_WITH_PYOPENSSL: ${{ matrix.pyopenssl }} + - name: Linux & Mac setup + if: matrix.os != 'windows-latest' + run: | + make install + make test + env: + HTTPIE_TEST_WITH_PYOPENSSL: ${{ matrix.pyopenssl }} diff --git a/.gitignore b/.gitignore index c585625650..aee3301cc3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,155 @@ -dist -httpie.egg-info -build +.DS_Store +.idea/ +*.egg-info +.cache/ *.pyc +htmlcov + + +############################################################################## +# The below is GitHub template for Python project. gitignore. +# +############################################################################## + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg *.egg -.tox -README.html +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.spec +*.manifest + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.nox/ .coverage -htmlcov -.idea -.DS_Store +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +venv*/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# Packit +/httpie.spec +/httpie-*.rpm +/httpie-*.tar.gz + +# VS Code +.vscode/ + +# Windows Chocolatey +*.nupkg + +artifacts/ diff --git a/.packit.yaml b/.packit.yaml new file mode 100644 index 0000000000..015fdf8e57 --- /dev/null +++ b/.packit.yaml @@ -0,0 +1,14 @@ +# See the documentation for more information: +# https://packit.dev/docs/configuration/ +specfile_path: httpie.spec +actions: + # get the current Fedora Rawhide specfile: + post-upstream-clone: "wget https://src.fedoraproject.org/rpms/httpie/raw/rawhide/f/httpie.spec -O httpie.spec" + # Use this when the latest spec is not up-to-date. + # post-upstream-clone: "cp docs/packaging/linux-fedora/httpie.spec.txt httpie.spec" +jobs: +- job: propose_downstream + trigger: release + metadata: + dist_git_branches: + - rawhide diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 05a3b1556b..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,16 +0,0 @@ -# https://travis-ci.org/jakubroztocil/httpie -os: - - linux - - osx -language: python -python: - - 2.6 - - 2.7 - - pypy - - 3.3 - - 3.4 -script: - - make -after_success: - - pip install python-coveralls - - coveralls diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000000..5351307d84 --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,41 @@ +# HTTPie authors + +- [Jakub Roztocil](https://github.com/jakubroztocil) + +## Patches, features, ideas + +[Complete list of contributors on GitHub](https://github.com/httpie/cli/graphs/contributors) + +- [Cláudia T. Delgado](https://github.com/claudiatd) +- [Hank Gay](https://github.com/gthank) +- [Jake Basile](https://github.com/jakebasile) +- [Vladimir Berkutov](https://github.com/dair-targ) +- [Jakob Kramer](https://github.com/gandaro) +- [Chris Faulkner](https://github.com/faulkner) +- [Alen Mujezinovic](https://github.com/flashingpumpkin) +- [Praful Mathur](https://github.com/tictactix) +- [Marc Abramowitz](https://github.com/msabramo) +- [Ismail Badawi](https://github.com/isbadawi) +- [Laurent Bachelier](https://github.com/laurentb) +- [Isman Firmansyah](https://github.com/iromli) +- [Simon Olofsson](https://github.com/simono) +- [Churkin Oleg](https://github.com/Bahus) +- [Jökull Sólberg Auðunsson](https://github.com/jokull) +- [Matthew M. Boedicker](https://github.com/mmb) +- [marblar](https://github.com/marblar) +- [Tomek Wójcik](https://github.com/tomekwojcik) +- [Davey Shafik](https://github.com/dshafik) +- [cido](https://github.com/cido) +- [Justin Bonnar](https://github.com/jargonjustin) +- [Nathan LaFreniere](https://github.com/nlf) +- [Matthias Lehmann](https://github.com/matleh) +- [Dennis Brakhane](https://github.com/brakhane) +- [Matt Layman](https://github.com/mblayman) +- [Edward Yang](https://github.com/honorabrutroll) +- [Aleksandr Vinokurov](https://github.com/aleksandr-vin) +- [Jeff Byrnes](https://github.com/jeffbyrnes) +- [Denis Belavin](https://github.com/LuckyDenis) +- [Mickaël Schoentgen](https://github.com/BoboTiG) +- [Elena Lape](https://github.com/elenalape) +- [Rohit Sehgal](https://github.com/r0hi7) +- [Bartłomiej Jacak](https://github.com/bartekjacak) diff --git a/AUTHORS.rst b/AUTHORS.rst deleted file mode 100644 index 9d445427b2..0000000000 --- a/AUTHORS.rst +++ /dev/null @@ -1,33 +0,0 @@ -============== -HTTPie authors -============== - -* `Jakub Roztocil `_ - - -Patches and ideas ------------------ - -* `Cláudia T. Delgado `_ (logo) -* `Hank Gay `_ -* `Jake Basile `_ -* `Vladimir Berkutov `_ -* `Jakob Kramer `_ -* `Chris Faulkner `_ -* `Alen Mujezinovic `_ -* `Praful Mathur `_ -* `Marc Abramowitz `_ -* `Ismail Badawi `_ -* `Laurent Bachelier `_ -* `Isman Firmansyah `_ -* `Simon Olofsson `_ -* `Churkin Oleg `_ -* `Jökull Sólberg Auðunsson `_ -* `Matthew M. Boedicker `_ -* `marblar `_ -* `Tomek Wójcik `_ -* `Davey Shafik `_ -* `cido `_ -* `Justin Bonnar `_ -* `Nathan LaFreniere `_ -* `Matthias Lehmann `_ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..0497ac3508 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,462 @@ +# Change Log + +This document records all notable changes to [HTTPie](https://httpie.io). +This project adheres to [Semantic Versioning](https://semver.org/). + +## [3.2.4](https://github.com/httpie/cli/compare/3.2.3...3.2.4) (2024-11-01) + +- Fix default certs loading and unpin `requests`. ([#1596](https://github.com/httpie/cli/issues/1596)) + +## [3.2.3](https://github.com/httpie/cli/compare/3.2.2...3.2.3) (2024-07-10) + +- 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)) + +## [3.2.2](https://github.com/httpie/cli/compare/3.2.1...3.2.2) (2023-05-19) + +- 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) + +- 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/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) + +- 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/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) + +- Dropped support for Python 3.6. ([#1177](https://github.com/httpie/cli/issues/1177)) +- Improved startup time by 40%. ([#1211](https://github.com/httpie/cli/pull/1211)) +- 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/cli/issues/566)) +- 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/cli/issues/1195)) +- 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/cli/issues/1207)) +- 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/cli/issues/376)) +- 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/cli/issues/1237)) +- 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/cli/issues/1098)) +- 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/cli/issues/1204)) +- 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/cli/issues/1242)) +- 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/cli/issues/1266)) + +## [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) + +- 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/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/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/cli/issues/1168)) +- 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/cli/issues/1165)) +- Fixed duplicate keys preservation in JSON data. ([#1163](https://github.com/httpie/cli/issues/1163)) + +## [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) + +- Added `--raw` to allow specifying the raw request body without extra processing as + an alternative to `stdin`. ([#534](https://github.com/httpie/cli/issues/534)) +- 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/cli/issues/1094)) +- 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/cli/issues/1088)) +- 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/cli/compare/2.3.0...2.4.0) (2021-02-06) + +- Added support for `--session` cookie expiration based on `Set-Cookie: max-age=`. ([#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/cli/issues/1026)) +- 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/cli/issues/1006)). + +## [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/cli/issues/201)). +- 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 `--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 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 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/cli/issues/904)). +- Added `--quiet, -q` flag to enforce silent behaviour. +- 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/cli/issues/943)). + +## [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/cli/issues/668)). +- 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/cli/issues/853)). +- 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/cli/issues/128)) +- 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` + 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/cli/issues/925)). + +## [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 `/./`) + URL squashing ([#895](https://github.com/httpie/cli/issues/895)). +- Changed the default `Accept` header value for JSON requests from + `application/json, */*` to `application/json, */*;q=0.5` + to clearly indicate preference ([#488](https://github.com/httpie/cli/issues/488)). +- Fixed `--form` file upload mixed with redirected `stdin` error handling + ([#840](https://github.com/httpie/cli/issues/840)). + +## [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/). +- Added `--offline` to allow building an HTTP request and printing it but not + actually sending it over the network. +- Replaced the old collect-all-then-process handling of HTTP communication + with one-by-one processing of each HTTP request or response as they become + available. This means that you can see headers immediately, + see what is being sent even if the request fails, etc. +- Removed automatic config file creation to avoid concurrency issues. +- Removed the default 30-second connection `--timeout` limit. +- Removed Python’s default limit of 100 response headers. +- Added `--max-headers` to allow setting the max header limit. +- Added `--compress` to allow request body compression. +- Added `--ignore-netrc` to allow bypassing credentials from `.netrc`. +- Added `https` alias command with `https://` as the default scheme. +- Added `$ALL_PROXY` documentation. +- Added type annotations throughout the codebase. +- Added `tests/` to the PyPi package for the convenience of + downstream package maintainers. +- Fixed an error when `stdin` was a closed fd. +- Improved `--debug` output formatting. + +## [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 + `--download` requests without `--output` resulting in a redirect has + been changed to only consider the initial URL as the base for the generated + filename, and not the final one. This fixes a potential security issue under + the following scenario: + + 1. A `--download` request with no explicit `--output` is made (e.g., + `$ http -d example.org/file.txt`), instructing httpie to + [generate the output filename](https://httpie.org/doc#downloaded-filename) + from the `Content-Disposition` response header, or from the URL if the header + is not provided. + 2. The server handling the request has been modified by an attacker and + instead of the expected response the URL returns a redirect to another + URL, e.g., `attacker.example.org/.bash_profile`, whose response does + not provide a `Content-Disposition` header (i.e., the base for the + generated filename becomes `.bash_profile` instead of `file.txt`). + 3. Your current directory doesn’t already contain `.bash_profile` + (i.e., no unique suffix is added to the generated filename). + 4. You don’t notice the potentially unexpected output filename + as reported by httpie in the console output + (e.g., `Downloading 100.00 B to ".bash_profile"`). + + Reported by Raul Onitza and Giulio Comi. + +## [1.0.2](https://github.com/httpie/cli/compare/1.0.1...1.0.2) (2018-11-14) + +- Fixed tests for installation with pyOpenSSL. + +## [1.0.1](https://github.com/httpie/cli/compare/1.0.0...1.0.1) (2018-11-14) + +- Removed external URL calls from tests. + +## [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 support for selecting TLS 1.3 via `--ssl=tls1.3` + (available once implemented in upstream libraries). +- Added `true`/`false` as valid values for `--verify` + (in addition to `yes`/`no`) and the boolean value is case-insensitive. +- Changed the default `--style` from `solarized` to `auto` (on Windows it stays `fruity`). +- Fixed default headers being incorrectly case-sensitive. +- Removed Python 2.6 support. + +## [0.9.9](https://github.com/httpie/cli/compare/0.9.8...0.9.9) (2016-12-08) + +- Fixed README. + +## [0.9.8](https://github.com/httpie/cli/compare/0.9.6...0.9.8) (2016-12-08) + +- Extended auth plugin API. +- Added exit status code `7` for plugin errors. +- Added support for `curses`-less Python installations. +- Fixed `REQUEST_ITEM` arg incorrectly being reported as required. +- Improved `CTRL-C` interrupt handling. +- Added the standard exit status code `130` for keyboard interrupts. + +## [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 + to ensure some of the newer HTTP features work out of the box + for macOS users (starting with HTTPie 0.9.4.). +- Added the ability to unset a request header with `Header:`, and send an + empty value with `Header;`. +- Added `--default-scheme ` to enable things like + `$ alias https='http --default-scheme=https`. +- Added `-I` as a shortcut for `--ignore-stdin`. +- Added fish shell completion (located in `extras/httpie-completion.fish` + in the GitHub repo). +- Updated `requests` to 2.10.0 so that SOCKS support can be added via + `pip install requests[socks]`. +- Changed the default JSON `Accept` header from `application/json` + to `application/json, */*`. +- Changed the pre-processing of request HTTP headers so that any leading + and trailing whitespace is removed. + +## [0.9.4](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 `--ssl=` to specify the desired SSL/TLS protocol version + to use for HTTPS requests. +- Added JSON detection with `--json, -j` to work around incorrect + `Content-Type` +- Added `--all` to show intermediate responses such as redirects (with `--follow`) +- Added `--history-print, -P WHAT` to specify formatting of intermediate responses +- Added `--max-redirects=N` (default 30) +- Added `-A` as short name for `--auth-type` +- Added `-F` as short name for `--follow` +- Removed the `implicit_content_type` config option + (use `"default_options": ["--form"]` instead) +- Redirected `stdout` doesn't trigger an error anymore when `--output FILE` + is set +- Changed the default `--style` back to `solarized` for better support + of light and dark terminals +- Improved `--debug` output +- Fixed `--session` when used with `--download` +- Fixed `--download` to trim too long filenames before saving the file +- Fixed the handling of `Content-Type` with multiple `+subtype` parts +- Removed the XML formatter as the implementation suffered from multiple issues + +## [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` +- Added basic Bash autocomplete support (need to be installed manually) +- Added request details to connection error messages +- Fixed `'requests.packages.urllib3' has no attribute 'disable_warnings'` + errors that occurred in some installations +- Fixed colors and formatting on Windows +- Fixed `--auth` prompt on Windows + +## [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 +- Changed the default JSON `Content-Type` to `application/json` as UTF-8 + is the default JSON encoding + +## [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 + (see [httpie-unixsocket](https://github.com/httpie/httpie-unixsocket) + and [httpie-http2](https://github.com/httpie/httpie-http2)) + +## [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 + certificate and private key for SSL +- Improved unicode support +- Improved terminal color depth detection via `curses` +- To make it easier to deal with Windows paths in request items, `\` + now only escapes special characters (the ones that are used as key-value + separators by HTTPie) +- Switched from `unittest` to `pytest` +- Added Python `wheel` support +- Various test suite improvements +- Added `CONTRIBUTING` +- Fixed `User-Agent` overwriting when used within a session +- Fixed handling of empty passwords in URL credentials +- Fixed multiple file uploads with the same form field name +- Fixed `--output=/dev/null` on Linux +- Miscellaneous bugfixes + +## [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 + the contents of text and JSON files into request data +- Added curl-style shorthand for localhost +- Fixed request `Host` header value output so that it doesn't contain + credentials, if included in the URL + +## [0.7.1](https://github.com/httpie/cli/compare/0.6.0...0.7.1) (2013-09-24) + +- Added `--ignore-stdin` +- Added support for auth plugins +- Improved `--help` output +- Improved `Content-Disposition` parsing for `--download` mode +- Update to Requests 2.0.0 + +## [0.6.0](https://github.com/httpie/cli/compare/0.5.1...0.6.0) (2013-06-03) + +- XML data is now formatted +- `--session` and `--session-read-only` now also accept paths to + session files (eg. `http --session=/tmp/session.json example.org`) + +## [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 + anymore as they are request-specific + +## [0.5.0](https://github.com/httpie/cli/compare/0.4.1...0.5.0) (2013-04-27) + +- Added a download mode via `--download` +- Fixes miscellaneous bugs + +## [0.4.1](https://github.com/httpie/cli/compare/0.4.0...0.4.1) (2013-02-26) + +- Fixed `setup.py` + +## [0.4.0](https://github.com/httpie/cli/compare/0.3.0...0.4.0) (2013-02-22) + +- Added Python 3.3 compatibility +- Added Requests >= v1.0.4 compatibility +- Added support for credentials in URL +- Added `--no-option` for every `--option` to be config-friendly +- Mutually exclusive arguments can be specified multiple times. The + last value is used + +## [0.3.0](https://github.com/httpie/cli/compare/0.2.7...0.3.0) (2012-09-21) + +- Allow output redirection on Windows +- Added configuration file +- Added persistent session support +- Renamed `--allow-redirects` to `--follow` +- Improved the usability of `http --help` +- Fixed installation on Windows with Python 3 +- Fixed colorized output on Windows with Python 3 +- CRLF HTTP header field separation in the output +- Added exit status code `2` for timed-out requests +- Added the option to separate colorizing and formatting + (`--pretty=all`, `--pretty=colors` and `--pretty=format`) + `--ugly` has bee removed in favor of `--pretty=none` + +## [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 streamed terminal output. `--stream, -S` can be used to enable + streaming also with `--pretty` and to ensure a more frequent output + flushing +- Added support for efficient large file downloads +- Sort headers by name (unless `--pretty=none`) +- Response body is fetched only when needed (e.g., not with `--headers`) +- Improved content type matching +- Updated Solarized color scheme +- Windows: Added `--output FILE` to store output into a file + (piping results in corrupted data on Windows) +- Proper handling of binary requests and responses +- Fixed printing of `multipart/form-data` requests +- Renamed `--traceback` to `--debug` + +## [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 + removed, for usage use `--help`) +- Form data and URL parameters can have multiple fields with the same name + (e.g.,`http -f url a=1 a=2`) +- Added `--check-status` to exit with an error on HTTP 3xx, 4xx and + 5xx (3, 4, and 5, respectively) +- If the output is piped to another program or redirected to a file, + the default behaviour is to only print the response body + (It can still be overwritten via the `--print` flag.) +- Improved highlighting of HTTP headers +- Added query string parameters (`param==value`) +- Added support for terminal colors under Windows + +## [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 + improved readability +- --auth now prompts for a password if only a username provided +- Added support for request payloads from a file path with automatic + `Content-Type` (`http URL @/path`) +- Fixed missing query string when displaying the request headers via + `--verbose` +- Fixed Content-Type for requests with no data + +## [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 + `GET`, or to `POST` with data) +- Fixed --verbose --form +- Added support for Tox + +## [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` +- Dropped custom JSON and HTTP lexers in favor of the ones newly included + in `pygments-1.5` + +## [0.2.0](https://github.com/httpie/cli/compare/0.1.6...0.2.0) (2012-04-25) + +- Added Python 3 support +- Added the ability to print the HTTP request as well as the response + (see `--print` and `--verbose`) +- Added support for Digest authentication +- Added file upload support + (`http -f POST file_field_name@/path/to/file`) +- Improved syntax highlighting for JSON +- Added support for field name escaping +- Many bug fixes + +## [0.1.6](https://github.com/httpie/cli/compare/0.1.5...0.1.6) (2012-03-04) + +- Fixed `setup.py` + +## [0.1.5](https://github.com/httpie/cli/compare/0.1.4...0.1.5) (2012-03-04) + +- Many improvements and bug fixes + +## [0.1.4](https://github.com/httpie/cli/compare/b966efa...0.1.4) (2012-02-28) + +- Many improvements and bug fixes + +## [0.1.0](https://github.com/httpie/cli/commit/b966efa) (2012-02-25) + +- Initial public release diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..973efd8bc8 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic + address, without explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at jakub@roztocil.co. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), +version 1.4, available at + +For answers to common questions about this code of conduct, see + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..1360184a8b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,212 @@ +# Contributing to HTTPie + +Bug reports and code and documentation patches are welcome. You can +help this project also by using the development version of HTTPie +and by reporting any bugs you might encounter. + +## 1. Reporting bugs + +**It's important that you provide the full command argument list +as well as the output of the failing command.** + +Use the `--debug` flag and copy&paste both the command and its output +to your bug report, e.g.: + +```bash +$ http --debug + +``` + +## 2. Contributing Code and Docs + +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. + +If your change alters HTTPie’s behaviour or interface, it's a good idea to +discuss it before you start working on it. + +If you are fixing an issue, the first step should be to create a test case that +reproduces the incorrect behaviour. That will also help you to build an +understanding of the issue at hand. + +**Pull requests introducing code changes without tests +will generally not get merged. The same goes for PRs changing HTTPie’s +behaviour and not providing documentation.** + +Conversely, PRs consisting of documentation improvements or tests +for existing-yet-previously-untested behavior will very likely be merged. +Therefore, docs and tests improvements are a great candidate for your first +contribution. + +Consider also adding a [CHANGELOG](https://github.com/httpie/cli/blob/master/CHANGELOG.md) entry for your changes. + +### Development Environment + +#### Getting the code + +Go to and fork the project repository. + +```bash +# Clone your fork +$ git clone git@github.com:/httpie.git + +# Enter the project directory +$ cd httpie + +# Create a branch for your changes +$ git checkout -b my_topical_branch +``` + +#### Setup + +The [Makefile](https://github.com/httpie/cli/blob/master/Makefile) contains a bunch of tasks to get you started. +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` + (via the standard library [venv](https://docs.python.org/3/library/venv.html) tool); +- installs all dependencies and also installs HTTPie + (in editable mode so that the `http` command will point to your + working copy). +- and runs tests (It is the same as running `make install test`). + +```bash +$ make all +``` + +#### Python virtual environment + +Activate the Python virtual environment—created via the `make install` +task during [setup](#setup) for your active shell session using the following command: + +```bash +$ source venv/bin/activate +``` + +(If you use `virtualenvwrapper`, you can also use `workon httpie` to +activate the environment — we have created a symlink for you. It’s a bit of +a hack but it works™.) + +You should now see `(httpie)` next to your shell prompt, and +the `http` command should point to your development copy: + +```bash +(httpie) ~/Code/httpie $ which http +/Users//Code/httpie/venv/bin/http +(httpie) ~/Code/httpie $ http --version +2.0.0-dev +``` + +(Btw, you don’t need to activate the virtual environment if you just want +run some of the `make` tasks. You can also invoke the development +version of HTTPie directly with `./venv/bin/http` without having to activate +the environment first. The same goes for `./venv/bin/pytest`, etc.). + +### Making Changes + +Please make sure your changes conform to [Style Guide for Python Code](https://python.org/dev/peps/pep-0008/) (PEP8) +and that `make pycodestyle` passes. + +### Testing & CI + +Please add tests for any new features and bug fixes. + +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 + +HTTPie uses the [pytest](https://pytest.org/) runner. + +```bash +# Run tests on the current Python interpreter with coverage. +$ make test + +# Run tests with coverage +$ make test-cover + +# Test PEP8 compliance +$ make codestyle + +# Run extended tests — for code as well as .md files syntax, packaging, etc. +$ make test-all +``` + +#### Running specific tests + +After you have activated your virtual environment (see [setup](#setup)), you +can run specific tests from the terminal: + +```bash +# Run specific tests on the current Python +$ python -m pytest tests/test_uploads.py +$ python -m pytest tests/test_uploads.py::TestMultipartFormDataFileUpload +$ python -m pytest tests/test_uploads.py::TestMultipartFormDataFileUpload::test_upload_ok +``` + +See [Makefile](https://github.com/httpie/cli/blob/master/Makefile) for additional development utilities. + +#### Running benchmarks + +If you are trying to work on speeding up HTTPie and want to verify your results, you +can run the benchmark suite. The suite will compare the last commit of your branch +with the master branch of your repository (or a fresh checkout of HTTPie master, through +`--fresh`) and report the results back. + +```bash +$ python extras/profiling/run.py +``` + +The benchmarks can also be run on the CI. Since it is a long process, it requires manual +oversight. Ping one of the maintainers to get a `benchmark` label on your branch. + +#### Windows + +If you are on a Windows machine and not able to run `make`, +follow the next steps for a basic setup. As a prerequisite, you need to have +Python 3.7+ installed. + +Create a virtual environment and activate it: + +```powershell +C:\> python -m venv --prompt httpie venv +C:\> venv\Scripts\activate +``` + +Install HTTPie in editable mode with all the dependencies: + +```powershell +C:\> python -m pip install --upgrade -e .[dev] +``` + +You should now see `(httpie)` next to your shell prompt, and +the `http` command should point to your development copy: + +```powershell +# In PowerShell: +(httpie) PS C:\Users\\httpie> Get-Command http +CommandType Name Version Source +----------- ---- ------- ------ +Application http.exe 0.0.0.0 C:\Users\\httpie\venv\Scripts\http.exe +``` + +```bash +# In CMD: +(httpie) C:\Users\\httpie> where http +C:\Users\\httpie\venv\Scripts\http.exe +C:\Users\\AppData\Local\Programs\Python\Python38-32\Scripts\http.exe + +(httpie) C:\Users\\httpie> http --version +2.3.0-dev +``` + +Use `pytest` to run tests locally with an active virtual environment: + +```bash +# Run all tests +$ python -m pytest +``` + +______________________________________________________________________ + +Finally, feel free to add yourself to [AUTHORS](https://github.com/httpie/cli/blob/master/AUTHORS.md)! diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst deleted file mode 100644 index e63cbb8b88..0000000000 --- a/CONTRIBUTING.rst +++ /dev/null @@ -1,95 +0,0 @@ -Contributing to HTTPie -###################### - -Bug reports and code and documentation patches are greatly appretiated. You can -also help by using the development version of HTTPie and reporting any bugs you -might encounter. - -Bug Reports -=========== - -**It's important that you provide the full command argument list -as well as the output of the failing command.** -Use the ``--debug`` flag and copy&paste both the command and its output -to your bug report, e.g.: - -.. code-block:: bash - - $ http --debug [arguments that trigger the error] - [complete output] - - -Contributing Code and Documentation -=================================== - -Before working on a new feature or a bug, please browse `existing issues`_ -to see whether it has been previously discussed. If the change in question -is a bigger one, it's always good to discuss before your starting working on -it. - - -Development Environment ------------------------ - -.. code-block:: bash - - git clone https://github.com//httpie - - cd httpie - - git checkout -b my_topical_branch - - # (Recommended: create a new virtualenv) - - # Install dev. requirements and also HTTPie (in editable mode - # so that the `http' command will point to your working copy): - make - - -Making Changes --------------- - -Please make sure your changes conform to `Style Guide for Python Code`_ (PEP8). - - -Tests ------ - -Before opening a pull requests, please make sure the `test suite`_ passes -in all of the `supported Python environments`_. You should also **add tests -for any new features and bug fixes**. - -HTTPie uses `pytest`_ and `Tox`_. - -.. code-block:: bash - - ### Running all tests: - - # Current Python - make test - - # Current Python with coverage - make test-cover - - # All the supported and available Pythons via Tox - make test-tox - - ### Running specific tests: - - # Current Python - pytest tests/test_uploads.py - - # All Pythons - tox -- tests/test_uploads.py --verbose - - -Don't forget to add yourself to `AUTHORS.rst`_. - - -.. _Tox: http://tox.testrun.org -.. _supported Python environments: https://github.com/jakubroztocil/httpie/blob/master/tox.ini -.. _existing issues: https://github.com/jakubroztocil/httpie/issues?state=open -.. _AUTHORS.rst: https://github.com/jakubroztocil/httpie/blob/master/AUTHORS.rst -.. _pytest: http://pytest.org/ -.. _Style Guide for Python Code: http://python.org/dev/peps/pep-0008/ -.. _test suite: https://github.com/jakubroztocil/httpie/tree/master/tests diff --git a/LICENSE b/LICENSE index 027da677e9..4a53e9d679 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright © 2012 Jakub Roztocil +Copyright © 2012-2022 Jakub Roztocil Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -10,14 +10,14 @@ modification, are permitted provided that the following conditions are met: notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - 3. Neither the name of The author nor the names of its contributors may - be used to endorse or promote products derived from this software + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without specific prior written permission. -THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE FOR +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON diff --git a/MANIFEST.in b/MANIFEST.in index 0c738421d6..a09a3037eb 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1 +1,8 @@ -include README.rst LICENSE +include LICENSE +include README.md +include CHANGELOG.md +include AUTHORS.md +include docs/README.md + +# +recursive-include tests/ * diff --git a/Makefile b/Makefile index 1c8868bf3a..a2a80a1778 100644 --- a/Makefile +++ b/Makefile @@ -1,67 +1,246 @@ +############################################################################### +# See ./CONTRIBUTING.md +############################################################################### + +.PHONY: build + +ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) VERSION=$(shell grep __version__ httpie/__init__.py) -REQUIREMENTS="requirements-dev.txt" -TAG="\n\n\033[0;32m\#\#\# " -END=" \#\#\# \033[0m\n" +H1="\n\n\033[0;32m\#\#\# " +H1END=" \#\#\# \033[0m\n" -all: test -uninstall-httpie: - @echo $(TAG)Removing existing installation of HTTPie$(END) - - pip uninstall --yes httpie >/dev/null - ! which http +# Only used to create our venv. +SYSTEM_PYTHON=python3 + +VENV_ROOT=venv +VENV_BIN=$(VENV_ROOT)/bin +VENV_PIP=$(VENV_BIN)/pip3 +VENV_PYTHON=$(VENV_BIN)/python + + +export PATH := $(VENV_BIN):$(PATH) + + + +default: list-tasks + + +############################################################################### +# Default task to get a list of tasks when `make' is run without args. +# +############################################################################### + +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 -uninstall-all: uninstall-httpie - - pip uninstall --yes -r $(REQUIREMENTS) -init: uninstall-httpie - @echo $(TAG)Installing dev requirements$(END) - pip install --upgrade -r $(REQUIREMENTS) - @echo $(TAG)Installing HTTPie$(END) - pip install --upgrade --editable . +############################################################################### +# Installation +############################################################################### + +all: uninstall-httpie install test + + +install: venv install-reqs + + +install-reqs: + @echo $(H1)Updating package tools$(H1END) + $(VENV_PIP) install --upgrade pip wheel build + + @echo $(H1)Installing dev requirements$(H1END) + $(VENV_PIP) install --upgrade '.[dev]' '.[test]' + + @echo $(H1)Installing HTTPie$(H1END) + $(VENV_PIP) install --upgrade --editable . + @echo -test: init - @echo $(TAG)Running tests in on current Python in parallel and with coverage $(END) - py.test --cov ./httpie --cov ./tests -n 8 --doctest-modules --verbose ./httpie ./tests + +clean: + @echo $(H1)Cleaning up$(H1END) + rm -rf $(VENV_ROOT) + # Remove symlink for virtualenvwrapper, if we’ve created one. + [ -n "$(WORKON_HOME)" -a -L "$(WORKON_HOME)/httpie" -a -f "$(WORKON_HOME)/httpie" ] && rm $(WORKON_HOME)/httpie || true + rm -rf *.egg dist build .coverage .cache .pytest_cache httpie.egg-info + find . -name '__pycache__' -delete -o -name '*.pyc' -delete + @echo + + +venv: + @echo $(H1)Creating a Python environment $(VENV_ROOT) $(H1END) + + $(SYSTEM_PYTHON) -m venv --prompt httpie $(VENV_ROOT) + + @echo + @echo done. + @echo + @echo To active it manually, run: + @echo + @echo " source $(VENV_BIN)/activate" @echo + @echo '(learn more: https://docs.python.org/3/library/venv.html)' + @echo + @if [ -n "$(WORKON_HOME)" ]; then \ + echo $(ROOT_DIR) > $(VENV_ROOT)/.project; \ + if [ ! -d $(WORKON_HOME)/httpie -a ! -L $(WORKON_HOME)/httpie ]; then \ + ln -s $(ROOT_DIR)/$(VENV_ROOT) $(WORKON_HOME)/httpie ; \ + echo ''; \ + echo 'Since you use virtualenvwrapper, we created a symlink'; \ + echo 'so you can also use "workon httpie" to activate the venv.'; \ + echo ''; \ + fi; \ + fi + + +############################################################################### +# Testing +############################################################################### + + +test: + @echo $(H1)Running tests$(HEADER_EXTRA)$(H1END) + $(VENV_BIN)/python -m pytest $(COV) + @echo + + +test-cover: COV=--cov=httpie --cov=tests +test-cover: HEADER_EXTRA=' (with coverage)' +test-cover: test + -test-tox: init - @echo $(TAG)Running tests on all Pythons via Tox$(END) - tox +# test-all is meant to test everything — even this Makefile +test-all: clean install test test-dist codestyle @echo + test-dist: test-sdist test-bdist-wheel @echo -test-sdist: clean uninstall-httpie - @echo $(TAG)Testing sdist build an installation$(END) - python setup.py sdist - pip install --force-reinstall --upgrade dist/*.gz - which http + +test-sdist: clean venv + @echo $(H1)Testing sdist build an installation$(H1END) + $(VENV_PIP) install build + $(VENV_PYTHON) -m build --sdist + $(VENV_PIP) install --force-reinstall --upgrade dist/*.gz + $(VENV_BIN)/http --version + @echo + + +test-bdist-wheel: clean venv + @echo $(H1)Testing wheel build an installation$(H1END) + $(VENV_PIP) install build + $(VENV_PYTHON) -m build --wheel + $(VENV_PIP) install --force-reinstall --upgrade dist/*.whl + $(VENV_BIN)/http --version + @echo + + +twine-check: + twine check dist/* + + +# Kept for convenience, "make codestyle" is preferred though +pycodestyle: codestyle + + +codestyle: + @echo $(H1)Running flake8$(H1END) + @[ -f $(VENV_BIN)/flake8 ] || $(VENV_PIP) install --upgrade --editable '.[dev]' + $(VENV_BIN)/flake8 httpie/ tests/ extras/profiling/ docs/packaging/brew/ *.py @echo -test-bdist-wheel: clean uninstall-httpie - @echo $(TAG)Testing wheel build an installation$(END) - python setup.py bdist_wheel - pip install --force-reinstall --upgrade dist/*.whl - which http + +codecov-upload: + @echo $(H1)Running codecov$(H1END) + @[ -f $(VENV_BIN)/codecov ] || $(VENV_PIP) install codecov + # $(VENV_BIN)/codecov --required + $(VENV_BIN)/codecov @echo -# This tests everything, even this Makefile. -test-all: uninstall-all clean init test test-tox test-dist -publish: test-all - @echo $(TAG)Testing wheel build an installation$(END) +doc-check: + @echo $(H1)Running documentations checks$(H1END) + mdl --git-recurse --style docs/markdownlint.rb . + + +############################################################################### +# Publishing to PyPi +############################################################################### + + +build: + rm -rf build/ dist/ + 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-no-test: + @echo $(H1)Testing wheel build an installation$(H1END) @echo "$(VERSION)" - @echo "$(VERSION)" | grep -q "dev" && echo "!!!Not publishing dev version!!!" && exit 1 - python setup.py register - python setup.py sdist upload - python setup.py bdist_wheel upload + @echo "$(VERSION)" | grep -q "dev" && echo '!!!Not publishing dev version!!!' && exit 1 || echo ok + make build + make twine-check + $(VENV_BIN)/twine upload --repository=httpie dist/* @echo -clean: - @echo $(TAG)Cleaning up$(END) - rm -rf .tox *.egg dist build .coverage - find . -name '__pycache__' -delete -print -o -name '*.pyc' -delete -print + + +############################################################################### +# Uninstalling +############################################################################### + +uninstall-httpie: + @echo $(H1)Uninstalling httpie$(H1END) + - $(VENV_PIP) uninstall --yes httpie &2>/dev/null + + @echo "Verifying…" + cd .. && ! $(VENV_PYTHON) -m httpie --version &2>/dev/null + + @echo "Done" @echo + + +############################################################################### +# Homebrew +############################################################################### + +brew-deps: + docs/packaging/brew/brew-deps.py + +brew-test: + @echo $(H1)Uninstalling httpie$(H1END) + - brew uninstall httpie + + @echo $(H1)Building from source…$(H1END) + - brew install --HEAD --build-from-source ./docs/packaging/brew/httpie.rb + + @echo $(H1)Verifying…$(H1END) + http --version + https --version + + @echo $(H1)Auditing…$(H1END) + brew audit --strict httpie + +############################################################################### +# Generated content +############################################################################### + +content: man installation-docs + +man: install + @echo $(H1)Regenerate man pages$(H1END) + $(VENV_PYTHON) extras/scripts/generate_man_pages.py + +installation-docs: + @echo $(H1)Updating installation instructions in the docs$(H1END) + $(VENV_PYTHON) docs/installation/generate.py diff --git a/README.md b/README.md new file mode 100644 index 0000000000..13a3503b41 --- /dev/null +++ b/README.md @@ -0,0 +1,113 @@ +

+ + HTTPie + +
+ HTTPie CLI: human-friendly HTTP client for the API era +

+ +
+ +[![HTTPie for Desktop](https://img.shields.io/static/v1?label=HTTPie&message=Desktop&color=4B78E6)](https://httpie.io/product) +[![](https://img.shields.io/static/v1?label=HTTPie&message=Web%20%26%20Mobile&color=73DC8C)](https://httpie.io/app) +[![](https://img.shields.io/static/v1?label=HTTPie&message=CLI&color=FA9BFA)](https://httpie.io/cli) +[![Twitter](https://img.shields.io/twitter/follow/httpie?style=flat&color=%234B78E6&logoColor=%234B78E6)](https://twitter.com/httpie) +[![Chat](https://img.shields.io/discord/725351238698270761?style=flat&label=Chat%20on%20Discord&color=%23FA9BFA)](https://httpie.io/discord) + +
+ + +
+ +[![Docs](https://img.shields.io/badge/stable%20docs-httpie.io%2Fdocs%2Fcli-brightgreen?style=flat&color=%2373DC8C&label=Docs)](https://httpie.org/docs/cli) +[![Latest version](https://img.shields.io/pypi/v/httpie.svg?style=flat&label=Latest&color=%234B78E6&logo=&logoColor=white)](https://pypi.python.org/pypi/httpie) +[![Build](https://img.shields.io/github/actions/workflow/status/httpie/cli/tests.yml?branch=master&color=%23FA9BFA&label=Build)](https://github.com/httpie/cli/actions) +[![Coverage](https://img.shields.io/codecov/c/github/httpie/cli?style=flat&label=Coverage&color=%2373DC8C)](https://codecov.io/gh/httpie/cli) +[![PyPi downloads](https://img.shields.io/pepy/dt/httpie?style=flat&label=Downloads%20from%20PyPi%20only&color=4B78E6)](https://www.pepy.tech/projects/httpie) + +
+ +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. +HTTPie is designed for testing, debugging, and generally interacting with APIs & HTTP servers. +The `http` & `https` commands allow for creating and sending arbitrary HTTP requests. +They use simple and natural syntax and provide formatted and colorized output. + +
+ +HTTPie in action + + +
+ + + + +## We lost 54k GitHub stars + +Please note we recently accidentally made this repo private for a moment, and GitHub deleted our community that took a decade to build. Read the full story here: https://httpie.io/blog/stardust + +![](docs/stardust.png) + + +## Getting started + +- [Installation instructions →](https://httpie.io/docs#installation) +- [Full documentation →](https://httpie.io/docs) + +## Features + +- Expressive and intuitive syntax +- Formatted and colorized terminal output +- Built-in JSON support +- Forms and file uploads +- HTTPS, proxies, and authentication +- Arbitrary request data +- Custom headers +- Persistent sessions +- `wget`-like downloads + +[See all features →](https://httpie.io/docs) + +## Examples + +Hello World: + +```bash +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: + +```bash +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/cli/offline-mode): + +```bash +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/cli/issues/83) with [authentication](https://httpie.io/docs#authentication): + +```bash +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) + +## Community & support + +- Visit the [HTTPie website](https://httpie.io) for full documentation and useful links. +- 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. +- Use [StackOverflow](https://stackoverflow.com/questions/tagged/httpie) to ask questions and include a `httpie` tag. +- 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. + +## Contributing + +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/cli/blob/master/CONTRIBUTING.md) diff --git a/README.rst b/README.rst deleted file mode 100644 index 6497ad1596..0000000000 --- a/README.rst +++ /dev/null @@ -1,1408 +0,0 @@ -**************************************** -HTTPie: a CLI, cURL-like tool for humans -**************************************** - - -HTTPie (pronounced *aych-tee-tee-pie*) is a **command line HTTP client**. Its -goal is to make CLI interaction with web services as **human-friendly** as -possible. It provides a simple ``http`` command that allows for sending -arbitrary HTTP requests using a simple and natural syntax, and displays -colorized responses. HTTPie can be used for **testing, debugging**, and -generally **interacting** with HTTP servers. - - -.. image:: https://github.com/jakubroztocil/httpie/raw/master/httpie.png - :alt: HTTPie compared to cURL - :width: 835 - :height: 835 - :align: center - - ------- - - -.. image:: https://raw.github.com/claudiatd/httpie-artwork/master/images/httpie_logo_simple.png - :alt: HTTPie logo - :align: center - -HTTPie is written in Python, and under the hood it uses the excellent -`Requests`_ and `Pygments`_ libraries. - - -**Table of Contents** - - -.. contents:: - :local: - :depth: 1 - :backlinks: none - - -============= -Main Features -============= - -* Expressive and intuitive syntax -* Formatted and colorized terminal output -* Built-in JSON support -* Forms and file uploads -* HTTPS, proxies, and authentication -* Arbitrary request data -* Custom headers -* Persistent sessions -* Wget-like downloads -* Python 2.6, 2.7 and 3.x support -* Linux, Mac OS X and Windows support -* Documentation -* Test coverage - - -============ -Installation -============ - - - ------------------------- -Stable version |version| ------------------------- - -The **latest stable version** can always be installed or updated to via `pip`_: - -.. code-block:: bash - - $ pip install --upgrade httpie - - -If the above fails, please use ``easy_install`` instead: - -.. code-block:: bash - - $ easy_install httpie - - -Many Linux distributions also provide a package that can be installed via -system package manager, e.g. -``yum install httpie`` or ``apt-get install httpie``. - - - -------------------- -Development version -------------------- - -============= ============= -Mac/Linux Windows -|unix| |windows| -============= ============= - - -The **latest development version** can be installed directly from GitHub: - -.. code-block:: bash - - $ pip install --upgrade https://github.com/jakubroztocil/httpie/tarball/master - - - -===== -Usage -===== - - -Hello World: - - -.. code-block:: bash - - $ http httpie.org - - -Synopsis: - -.. code-block:: bash - - $ http [flags] [METHOD] URL [ITEM [ITEM]] - - -See also ``http --help``. - - --------- -Examples --------- - -Custom `HTTP method`_, `HTTP headers`_ and `JSON`_ data: - -.. code-block:: bash - - $ http PUT example.org X-API-Token:123 name=John - - -Submitting `forms`_: - -.. code-block:: bash - - $ http -f POST example.org hello=World - - -See the request that is being sent using one of the `output options`_: - -.. code-block:: bash - - $ http -v example.org - - -Use `Github API`_ to post a comment on an -`issue `_ -with `authentication`_: - -.. code-block:: bash - - $ http -a USERNAME POST https://api.github.com/repos/jakubroztocil/httpie/issues/83/comments body='HTTPie is awesome!' - - -Upload a file using `redirected input`_: - -.. code-block:: bash - - $ http example.org < file.json - - -Download a file and save it via `redirected output`_: - -.. code-block:: bash - - $ http example.org/file > file - - -Download a file ``wget`` style: - -.. code-block:: bash - - $ http --download example.org/file - -Use named `sessions`_ to make certain aspects or the communication persistent -between requests to the same host: - -.. code-block:: bash - - $ http --session=logged-in -a username:password httpbin.org/get API-Key:123 - - $ http --session=logged-in httpbin.org/headers - - -Set a custom ``Host`` header to work around missing DNS records: - -.. code-block:: bash - - $ http localhost:8000 Host:example.com - -.. - --------- - -*What follows is a detailed documentation. It covers the command syntax, -advanced usage, and also features additional examples.* - - -=========== -HTTP Method -=========== - -The name of the HTTP method comes right before the URL argument: - -.. code-block:: bash - - $ http DELETE example.org/todos/7 - - -Which looks similar to the actual ``Request-Line`` that is sent: - -.. code-block:: http - - DELETE /todos/7 HTTP/1.1 - - -When the ``METHOD`` argument is **omitted** from the command, HTTPie defaults to -either ``GET`` (with no request data) or ``POST`` (with request data). - - -=========== -Request URL -=========== - -The only information HTTPie needs to perform a request is a URL. -The default scheme is, somewhat unsurprisingly, ``http://``, -and can be omitted from the argument – ``http example.org`` works just fine. - -Additionally, curl-like shorthand for localhost is supported. -This means that, for example ``:3000`` would expand to ``http://localhost:3000`` -If the port is omitted, then port 80 is assumed. - -.. code-block:: bash - - $ http :/foo - - -.. code-block:: http - - GET /foo HTTP/1.1 - Host: localhost - - -.. code-block:: bash - - $ http :3000/bar - - -.. code-block:: http - - GET /bar HTTP/1.1 - Host: localhost:3000 - - -.. code-block:: bash - - $ http : - - -.. code-block:: http - - GET / HTTP/1.1 - Host: localhost - -If find yourself manually constructing URLs with **querystring parameters** -on the terminal, you may appreciate the ``param==value`` syntax for appending -URL parameters so that you don't have to worry about escaping the ``&`` -separators. To search for ``HTTPie`` on Google Images you could use this -command: - -.. code-block:: bash - - $ http GET www.google.com search==HTTPie tbm==isch - - -.. code-block:: http - - GET /?search=HTTPie&tbm=isch HTTP/1.1 - - -============= -Request Items -============= - -There are a few different *request item* types that provide a -convenient mechanism for specifying HTTP headers, simple JSON and -form data, files, and URL parameters. - -They are key/value pairs specified after the URL. All have in -common that they become part of the actual request that is sent and that -their type is distinguished only by the separator used: -``:``, ``=``, ``:=``, ``==``, ``@``, ``=@``, and ``:=@``. The ones with an -``@`` expect a file path as value. - -+-----------------------+-----------------------------------------------------+ -| Item Type | Description | -+=======================+=====================================================+ -| HTTP Headers | Arbitrary HTTP header, e.g. ``X-API-Token:123``. | -| ``Name:Value`` | | -+-----------------------+-----------------------------------------------------+ -| URL parameters | Appends the given name/value pair as a query | -| ``name==value`` | string parameter to the URL. | -| | The ``==`` separator is used | -+-----------------------+-----------------------------------------------------+ -| Data Fields | Request data fields to be serialized as a JSON | -| ``field=value``, | object (default), or to be form-encoded | -| ``field=@file.txt`` | (``--form, -f``). | -+-----------------------+-----------------------------------------------------+ -| Raw JSON fields | Useful when sending JSON and one or | -| ``field:=json``, | more fields need to be a ``Boolean``, ``Number``, | -| ``field:=@file.json`` | nested ``Object``, or an ``Array``, e.g., | -| | ``meals:='["ham","spam"]'`` or ``pies:=[1,2,3]`` | -| | (note the quotes). | -+-----------------------+-----------------------------------------------------+ -| Form File Fields | Only available with ``--form, -f``. | -| ``field@/dir/file`` | For example ``screenshot@~/Pictures/img.png``. | -| | The presence of a file field results | -| | in a ``multipart/form-data`` request. | -+-----------------------+-----------------------------------------------------+ - -You can use ``\`` to escape characters that shouldn't be used as separators -(or parts thereof). For instance, ``foo\==bar`` will become a data key/value -pair (``foo=`` and ``bar``) instead of a URL parameter. - -You can also quote values, e.g. ``foo="bar baz"``. - -Note that data fields aren't the only way to specify request data: -`Redirected input`_ allows for passing arbitrary data to be sent with the -request. - - -==== -JSON -==== - -JSON is the *lingua franca* of modern web services and it is also the -**implicit content type** HTTPie by default uses: - -If your command includes some data items, they are serialized as a JSON -object by default. HTTPie also automatically sets the following headers, -both of which can be overwritten: - -================ ======================================= -``Content-Type`` ``application/json; charset=utf-8`` -``Accept`` ``application/json`` -================ ======================================= - -You can use ``--json, -j`` to explicitly set ``Accept`` -to ``application/json`` regardless of whether you are sending data -(it's a shortcut for setting the header via the usual header notation – -``http url Accept:application/json``). - -Simple example: - -.. code-block:: bash - - $ http PUT example.org name=John email=john@example.org - -.. code-block:: http - - PUT / HTTP/1.1 - Accept: application/json - Accept-Encoding: identity, deflate, compress, gzip - Content-Type: application/json; charset=utf-8 - Host: example.org - - { - "name": "John", - "email": "john@example.org" - } - - -Non-string fields use the ``:=`` separator, which allows you to embed raw JSON -into the resulting object. Text and raw JSON files can also be embedded into -fields using ``=@`` and ``:=@``: - -.. code-block:: bash - - $ http PUT api.example.com/person/1 \ - name=John \ - age:=29 married:=false hobbies:='["http", "pies"]' \ # Raw JSON - description=@about-john.txt \ # Embed text file - bookmarks:=@bookmarks.json # Embed JSON file - - -.. code-block:: http - - PUT /person/1 HTTP/1.1 - Accept: application/json - Content-Type: application/json; charset=utf-8 - Host: api.example.com - - { - "age": 29, - "hobbies": [ - "http", - "pies" - ], - "description": "John is a nice guy who likes pies.", - "married": false, - "name": "John", - "bookmarks": { - "HTTPie": "http://httpie.org", - } - } - - -Send JSON data stored in a file (see `redirected input`_ for more examples): - -.. code-block:: bash - - $ http POST api.example.com/person/1 < person.json - - -===== -Forms -===== - -Submitting forms is very similar to sending `JSON`_ requests. Often the only -difference is in adding the ``--form, -f`` option, which ensures that -data fields are serialized as, and ``Content-Type`` is set to, -``application/x-www-form-urlencoded; charset=utf-8``. - -It is possible to make form data the implicit content type instead of JSON -via the `config`_ file. - - -------------- -Regular Forms -------------- - -.. code-block:: bash - - $ http --form POST api.example.org/person/1 name='John Smith' email=john@example.org cv=@~/Documents/cv.txt - - -.. code-block:: http - - POST /person/1 HTTP/1.1 - Content-Type: application/x-www-form-urlencoded; charset=utf-8 - - name=John+Smith&email=john%40example.org&cv=John's+CV+... - - ------------------ -File Upload Forms ------------------ - -If one or more file fields is present, the serialization and content type is -``multipart/form-data``: - -.. code-block:: bash - - $ http -f POST example.com/jobs name='John Smith' cv@~/Documents/cv.pdf - - -The request above is the same as if the following HTML form were -submitted: - -.. code-block:: html - -
- - -
- -Note that ``@`` is used to simulate a file upload form field, whereas -``=@`` just embeds the file content as a regular text field value. - - -============ -HTTP Headers -============ - -To set custom headers you can use the ``Header:Value`` notation: - -.. code-block:: bash - - $ http example.org User-Agent:Bacon/1.0 'Cookie:valued-visitor=yes;foo=bar' X-Foo:Bar Referer:http://httpie.org/ - - -.. code-block:: http - - GET / HTTP/1.1 - Accept: */* - Accept-Encoding: identity, deflate, compress, gzip - Cookie: valued-visitor=yes;foo=bar - Host: example.org - Referer: http://httpie.org/ - User-Agent: Bacon/1.0 - X-Foo: Bar - - -There are a couple of default headers that HTTPie sets: - -.. code-block:: http - - GET / HTTP/1.1 - Accept: */* - Accept-Encoding: identity, deflate, compress, gzip - User-Agent: HTTPie/ - Host: - - -Any of the default headers can be overwritten. - - -============== -Authentication -============== - -The currently supported authentication schemes are Basic and Digest -(see `auth plugins`_ for more). There are two flags that control authentication: - -=================== ====================================================== -``--auth, -a`` Pass a ``username:password`` pair as - the argument. Or, if you only specify a username - (``-a username``), you'll be prompted for - the password before the request is sent. - To send a an empty password, pass ``username:``. - The ``username:password@hostname`` URL syntax is - supported as well (but credentials passed via ``-a`` - have higher priority). - -``--auth-type`` Specify the auth mechanism. Possible values are - ``basic`` and ``digest``. The default value is - ``basic`` so it can often be omitted. -=================== ====================================================== - - - -Basic auth: - - -.. code-block:: bash - - $ http -a username:password example.org - - -Digest auth: - - -.. code-block:: bash - - $ http --auth-type=digest -a username:password example.org - - -With password prompt: - -.. code-block:: bash - - $ http -a username example.org - - -Authorization information from your ``~/.netrc`` file is honored as well: - -.. code-block:: bash - - $ cat ~/.netrc - machine httpbin.org - login httpie - password test - - $ http httpbin.org/basic-auth/httpie/test - HTTP/1.1 200 OK - [...] - - ------------- -Auth Plugins ------------- - -* `httpie-oauth `_: OAuth -* `httpie-ntlm `_: NTLM (NT LAN Manager) -* `httpie-negotiate `_: SPNEGO (GSS Negotiate) - - -======= -Proxies -======= - -You can specify proxies to be used through the ``--proxy`` argument for each -protocol (which is included in the value in case of redirects across protocols): - -.. code-block:: bash - - $ http --proxy=http:http://10.10.1.10:3128 --proxy=https:https://10.10.1.10:1080 example.org - - -With Basic authentication: - -.. code-block:: bash - - $ http --proxy=http:http://user:pass@10.10.1.10:3128 example.org - -You can also configure proxies by environment variables ``HTTP_PROXY`` and -``HTTPS_PROXY``, and the underlying Requests library will pick them up as well. -If you want to disable proxies configured through the environment variables for -certain hosts, you can specify them in ``NO_PROXY``. - -In your ``~/.bash_profile``: - -.. code-block:: bash - - export HTTP_PROXY=http://10.10.1.10:3128 - export HTTPS_PROXY=https://10.10.1.10:1080 - export NO_PROXY=localhost,example.com - - -===== -HTTPS -===== - -To skip the host's SSL certificate verification, you can pass ``--verify=no`` -(default is ``yes``). You can also use ``--verify`` to set a custom CA bundle -path. The path can also be configured via the environment variable -``REQUESTS_CA_BUNDLE``. - -To use a client side certificate for the SSL communication, you can pass the -path of the cert file with ``--cert``. If the private key is not contained -in the cert file you may pass the path of the key file with ``--certkey``. - -If you use Python 2.x and need to talk to servers that use **SNI (Server Name -Indication)** you need to install some additional dependencies: - -.. code-block:: bash - - $ pip install --upgrade pyopenssl pyasn1 ndg-httpsclient - - -You can use the following command to test SNI support: - -.. code-block:: bash - - $ http https://sni.velox.ch - - -============== -Output Options -============== - -By default, HTTPie outputs the whole response message (headers as well as the -body). - -You can control what should be printed via several options: - -================= ===================================================== -``--headers, -h`` Only the response headers are printed. -``--body, -b`` Only the response body is printed. -``--verbose, -v`` Print the whole HTTP exchange (request and response). -``--print, -p`` Selects parts of the HTTP exchange. -================= ===================================================== - -``--verbose`` can often be useful for debugging the request and generating -documentation examples: - -.. code-block:: bash - - $ http --verbose PUT httpbin.org/put hello=world - PUT /put HTTP/1.1 - Accept: application/json - Accept-Encoding: identity, deflate, compress, gzip - Content-Type: application/json; charset=utf-8 - Host: httpbin.org - User-Agent: HTTPie/0.2.7dev - - { - "hello": "world" - } - - - HTTP/1.1 200 OK - Connection: keep-alive - Content-Length: 477 - Content-Type: application/json - Date: Sun, 05 Aug 2012 00:25:23 GMT - Server: gunicorn/0.13.4 - - { - […] - } - - -All the other options are just a shortcut for ``--print, -p``. -It accepts a string of characters each of which represents a specific part of -the HTTP exchange: - -========== ================== -Character Stands for -========== ================== -``H`` Request headers. -``B`` Request body. -``h`` Response headers. -``b`` Response body. -========== ================== - -Print request and response headers: - -.. code-block:: bash - - $ http --print=Hh PUT httpbin.org/put hello=world - - -------------------------- -Conditional Body Download -------------------------- - -As an optimization, the response body is downloaded from the server -only if it's part of the output. This is similar to performing a ``HEAD`` -request, except that it applies to any HTTP method you use. - -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: - -.. code-block:: bash - - $ http --headers PATCH example.org/Really-Huge-Resource name='New Name' - - -Since we 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. - -The response headers are downloaded always, even if they are not part of -the output - - -================ -Redirected Input -================ - -**A universal method for passing request data is through redirected** ``stdin`` -(standard input). Such data is buffered and then with no further processing -used as the request body. There are multiple useful ways to use piping: - -Redirect from a file: - -.. code-block:: bash - - $ http PUT example.com/person/1 X-API-Token:123 < person.json - - -Or the output of another program: - -.. code-block:: bash - - $ grep /var/log/httpd/error_log '401 Unauthorized' | http POST example.org/intruders - - -You can use ``echo`` for simple data: - -.. code-block:: bash - - $ echo '{"name": "John"}' | http PATCH example.com/person/1 X-API-Token:123 - - -You can even pipe web services together using HTTPie: - -.. code-block:: bash - - $ http GET https://api.github.com/repos/jakubroztocil/httpie | http POST httpbin.org/post - - -You can use ``cat`` to enter multiline data on the terminal: - -.. code-block:: bash - - $ cat | http POST example.com - - ^D - - -.. code-block:: bash - - $ cat | http POST example.com/todos Content-Type:text/plain - - buy milk - - call parents - ^D - - -On OS X, you can send the contents of the clipboard with ``pbpaste``: - -.. code-block:: bash - - $ pbpaste | http PUT example.com - - -Passing data through ``stdin`` cannot be combined with data fields specified -on the command line: - - -.. code-block:: bash - - $ echo 'data' | http POST example.org more=data # This is invalid - - -To prevent HTTPie from reading ``stdin`` data you can use the -``--ignore-stdin`` option. - - -------------------------- -Body Data From a Filename -------------------------- - -**An alternative to redirected** ``stdin`` is specifying a filename (as -``@/path/to/file``) whose content is used as if it came from ``stdin``. - -It has the advantage that **the** ``Content-Type`` -**header is automatically set** to the appropriate value based on the -filename extension. For example, the following request sends the -verbatim contents of that XML file with ``Content-Type: application/xml``: - -.. code-block:: bash - - $ http PUT httpbin.org/put @/data/file.xml - - -=============== -Terminal Output -=============== - -HTTPie does several things by default in order to make its terminal output -easy to read. - - ---------------------- -Colors and Formatting ---------------------- - -Syntax highlighting is applied to HTTP headers and bodies (where it makes -sense). You can choose your prefered color scheme via the ``--style`` option -if you don't like the default one (see ``$ http --help`` for the possible -values). - -Also, the following formatting is applied: - -* HTTP headers are sorted by name. -* JSON data is indented, sorted by keys, and unicode escapes are converted - to the characters they represent. -* XML data is indented for better readability. - -One of these options can be used to control output processing: - -==================== ======================================================== -``--pretty=all`` Apply both colors and formatting. - Default for terminal output. -``--pretty=colors`` Apply colors. -``--pretty=format`` Apply formatting. -``--pretty=none`` Disables output processing. - Default for redirected output. -==================== ======================================================== - ------------ -Binary data ------------ - -Binary data is suppressed for terminal output, which makes it safe to perform -requests to URLs that send back binary data. Binary data is suppressed also in -redirected, but prettified output. The connection is closed as soon as we know -that the response body is binary, - -.. code-block:: bash - - $ http example.org/Movie.mov - - -You will nearly instantly see something like this: - -.. code-block:: http - - HTTP/1.1 200 OK - Accept-Ranges: bytes - Content-Encoding: gzip - Content-Type: video/quicktime - Transfer-Encoding: chunked - - +-----------------------------------------+ - | NOTE: binary data not shown in terminal | - +-----------------------------------------+ - - -================= -Redirected Output -================= - -HTTPie uses **different defaults** for redirected output than for -`terminal output`_: - -* Formatting and colors aren't applied (unless ``--pretty`` is specified). -* Only the response body is printed (unless one of the `output options`_ is set). -* Also, binary data isn't suppressed. - -The reason is to make piping HTTPie's output to another programs and -downloading files work with no extra flags. Most of the time, only the raw -response body is of an interest when the output is redirected. - -Download a file: - -.. code-block:: bash - - $ http example.org/Movie.mov > Movie.mov - - -Download an image of Octocat, resize it using ImageMagick, upload it elsewhere: - -.. code-block:: bash - - $ http octodex.github.com/images/original.jpg | convert - -resize 25% - | http example.org/Octocats - - -Force colorizing and formatting, and show both the request and the response in -``less`` pager: - -.. code-block:: bash - - $ http --pretty=all --verbose example.org | less -R - - -The ``-R`` flag tells ``less`` to interpret color escape sequences included -HTTPie`s output. - -You can create a shortcut for invoking HTTPie with colorized and paged output -by adding the following to your ``~/.bash_profile``: - -.. code-block:: bash - - function httpless { - # `httpless example.org' - http --pretty=all --print=hb "$@" | less -R; - } - - -============= -Download Mode -============= - -HTTPie features a download mode in which it acts similarly to ``wget``. - -When enabled using the ``--download, -d`` flag, response headers are printed to -the terminal (``stderr``), and a progress bar is shown while the response body -is being saved to a file. - -.. code-block:: bash - - $ http --download https://github.com/jakubroztocil/httpie/tarball/master - -.. code-block:: http - - HTTP/1.1 200 OK - Connection: keep-alive - Content-Disposition: attachment; filename=jakubroztocil-httpie-0.4.1-33-gfc4f70a.tar.gz - Content-Length: 505530 - Content-Type: application/x-gzip - Server: GitHub.com - Vary: Accept-Encoding - - Downloading 494.89 kB to "jakubroztocil-httpie-0.4.1-33-gfc4f70a.tar.gz" - / 21.01% 104.00 kB 47.55 kB/s 0:00:08 ETA - - -If not provided via ``--output, -o``, the output filename will be determined -from ``Content-Disposition`` (if available), or from the URL and -``Content-Type``. If the guessed filename already exists, HTTPie adds a unique -suffix to it. - -You can also redirect the response body to another program while the response -headers and progress are still shown in the terminal: - -.. code-block:: bash - - $ http -d https://github.com/jakubroztocil/httpie/tarball/master | tar zxf - - - -If ``--output, -o`` is specified, you can resume a partial download using the -``--continue, -c`` option. This only works with servers that support -``Range`` requests and ``206 Partial Content`` responses. If the server doesn't -support that, the whole file will simply be downloaded: - -.. code-block:: bash - - $ http -dco file.zip example.org/file - -Other notes: - -* The ``--download`` option only changes how the response body is treated. -* You can still set custom headers, use sessions, ``--verbose, -v``, etc. -* ``--download`` always implies ``--follow`` (redirects are followed). -* HTTPie exits with status code ``1`` (error) if the body hasn't been fully - downloaded. -* ``Accept-Encoding`` cannot be set with ``--download``. - - -================== -Streamed Responses -================== - -Responses are downloaded and printed in chunks, which allows for streaming -and large file downloads without using too much RAM. However, when -`colors and formatting`_ is applied, the whole response is buffered and only -then processed at once. - - -You can use the ``--stream, -S`` flag to make two things happen: - -1. The output is flushed in **much smaller chunks** without any buffering, - which makes HTTPie behave kind of like ``tail -f`` for URLs. - -2. Streaming becomes enabled even when the output is prettified: It will be - applied to **each line** of the response and flushed immediately. This makes - it possible to have a nice output for long-lived requests, such as one - to the Twitter streaming API. - - -Prettified streamed response: - -.. code-block:: bash - - $ http --stream -f -a YOUR-TWITTER-NAME https://stream.twitter.com/1/statuses/filter.json track='Justin Bieber' - - -Streamed output by small chunks alá ``tail -f``: - -.. code-block:: bash - - # Send each new tweet (JSON object) mentioning "Apple" to another - # server as soon as it arrives from the Twitter streaming API: - $ http --stream -f -a YOUR-TWITTER-NAME https://stream.twitter.com/1/statuses/filter.json track=Apple \ - | while read tweet; do echo "$tweet" | http POST example.org/tweets ; done - -======== -Sessions -======== - -By default, every request is completely independent of any previous ones. -HTTPie also supports persistent sessions, where custom headers (except for the -ones starting with ``Content-`` or ``If-``), authorization, and cookies -(manually specified or sent by the server) persist between requests -to the same host. - --------------- -Named Sessions --------------- - -Create a new session named ``user1`` for ``example.org``: - -.. code-block:: bash - - $ http --session=user1 -a user1:password example.org X-Foo:Bar - -Now you can refer to the session by its name, and the previously used -authorization and HTTP headers will automatically be set: - -.. code-block:: bash - - $ http --session=user1 example.org - -To create or reuse a different session, simple specify a different name: - -.. code-block:: bash - - $ http --session=user2 -a user2:password example.org X-Bar:Foo - -To use a session without updating it from the request/response exchange -once it is created, specify the session name via -``--session-read-only=SESSION_NAME`` instead. - -Named sessions' data is stored in JSON files in the directory -``~/.httpie/sessions//.json`` -(``%APPDATA%\httpie\sessions\\.json`` on Windows). - ------------------- -Anonymous Sessions ------------------- - -Instead of a name, you can also directly specify a path to a session file. This -allows for sessions to be re-used across multiple hosts: - -.. code-block:: bash - - $ http --session=/tmp/session.json example.org - $ http --session=/tmp/session.json admin.example.org - $ http --session=~/.httpie/sessions/another.example.org/test.json example.org - $ http --session-read-only=/tmp/session.json example.org - - -**Warning:** All session data, including credentials, cookie data, -and custom headers are stored in plain text. - -Note that session files can also be created and edited manually in a text -editor; they are plain JSON. - -See also `Config`_. - - -====== -Config -====== - -HTTPie uses a simple configuration file that contains a JSON object with the -following keys: - -========================= ================================================= -``__meta__`` HTTPie automatically stores some metadata here. - Do not change. - -``implicit_content_type`` A ``String`` specifying the implicit content type - for request data. The default value for this - option is ``json`` and can be changed to - ``form``. - -``default_options`` An ``Array`` (by default empty) of options - that should be applied to every request. - - For instance, you can use this option to change - the default style and output options: - ``"default_options": ["--style=fruity", "--body"]`` - - Another useful default option is - ``"--session=default"`` to make HTTPie always - use `sessions`_. - - Default options from config file can be unset - for a particular invocation via - ``--no-OPTION`` arguments passed on the - command line (e.g., ``--no-style`` - or ``--no-session``). -========================= ================================================= - -The default location of the configuration file is ``~/.httpie/config.json`` -(or ``%APPDATA%\httpie\config.json`` on Windows). - -The config directory location can be changed by setting the -``HTTPIE_CONFIG_DIR`` environment variable. - - -========= -Scripting -========= - -When using HTTPie from **shell scripts**, it can be handy to set the -``--check-status`` flag. It instructs HTTPie to exit with an error if the -HTTP status is one of ``3xx``, ``4xx``, or ``5xx``. The exit status will -be ``3`` (unless ``--follow`` is set), ``4``, or ``5``, -respectively. - -The ``--ignore-stdin`` option prevents HTTPie from reading data from ``stdin``, -which is usually not desirable during non-interactive invocations. - -Also, the ``--timeout`` option allows to overwrite the default 30s timeout: - -.. code-block:: bash - - #!/bin/bash - - if http --check-status --ignore-stdin --timeout=2.5 HEAD example.org/health &> /dev/null; then - echo 'OK!' - else - case $? in - 2) echo 'Request timed out!' ;; - 3) echo 'Unexpected HTTP 3xx Redirection!' ;; - 4) echo 'HTTP 4xx Client Error!' ;; - 5) echo 'HTTP 5xx Server Error!' ;; - *) echo 'Other Error!' ;; - esac - fi - - -================ -Interface Design -================ - -The syntax of the command arguments closely corresponds to the actual HTTP -requests sent over the wire. It has the advantage that it's easy to remember -and read. It is often possible to translate an HTTP request to an HTTPie -argument list just by inlining the request elements. For example, compare this -HTTP request: - -.. code-block:: http - - POST /collection HTTP/1.1 - X-API-Key: 123 - User-Agent: Bacon/1.0 - Content-Type: application/x-www-form-urlencoded - - name=value&name2=value2 - - -with the HTTPie command that sends it: - -.. code-block:: bash - - $ http -f POST example.org/collection \ - X-API-Key:123 \ - User-Agent:Bacon/1.0 \ - name=value \ - name2=value2 - - -Notice that both the order of elements and the syntax is very similar, -and that only a small portion of the command is used to control HTTPie and -doesn't directly correspond to any part of the request (here it's only ``-f`` -asking HTTPie to send a form request). - -The two modes, ``--pretty=all`` (default for terminal) and ``--pretty=none`` -(default for redirected output), allow for both user-friendly interactive use -and usage from scripts, where HTTPie serves as a generic HTTP client. - -As HTTPie is still under heavy development, the existing command line -syntax and some of the ``--OPTIONS`` may change slightly before -HTTPie reaches its final version ``1.0``. All changes are recorded in the -`changelog`_. - - -========== -Contribute -========== - -Please see `CONTRIBUTING`_. - - -==== -Logo -==== - -Please see `claudiatd/httpie-artwork`_ - -======= -Authors -======= - -`Jakub Roztocil`_ (`@jakubroztocil`_) created HTTPie and `these fine people`_ -have contributed. - -======= -Licence -======= - -Please see `LICENSE`_. - - -========= -Changelog -========= - -*You can click a version name to see a diff with the previous one.* - -* `0.9.0-dev`_ - * Added ``--cert`` and ``--certkey`` parameters to specify a client side - certificate and private key for SSL - * Improved unicode support. - * Fixed ``User-Agent`` overwriting when used within a session. - * Switched from ``unittest`` to ``pytest``. - * Various test suite improvements. - * Added `CONTRIBUTING`_. -* `0.8.0`_ (2014-01-25) - * Added ``field=@file.txt`` and ``field:=@file.json`` for embedding - the contents of text and JSON files into request data. - * Added curl-style shorthand for localhost. - * Fixed request ``Host`` header value output so that it doesn't contain - credentials, if included in the URL. -* `0.7.1`_ (2013-09-24) - * Added ``--ignore-stdin``. - * Added support for auth plugins. - * Improved ``--help`` output. - * Improved ``Content-Disposition`` parsing for ``--download`` mode. - * Update to Requests 2.0.0 -* `0.6.0`_ (2013-06-03) - * XML data is now formatted. - * ``--session`` and ``--session-read-only`` now also accept paths to - session files (eg. ``http --session=/tmp/session.json example.org``). -* `0.5.1`_ (2013-05-13) - * ``Content-*`` and ``If-*`` request headers are not stored in sessions - anymore as they are request-specific. -* `0.5.0`_ (2013-04-27) - * Added a `download mode`_ via ``--download``. - * Bugfixes. -* `0.4.1`_ (2013-02-26) - * Fixed ``setup.py``. -* `0.4.0`_ (2013-02-22) - * Python 3.3 compatibility. - * Requests >= v1.0.4 compatibility. - * Added support for credentials in URL. - * Added ``--no-option`` for every ``--option`` to be config-friendly. - * Mutually exclusive arguments can be specified multiple times. The - last value is used. -* `0.3.0`_ (2012-09-21) - * Allow output redirection on Windows. - * Added configuration file. - * Added persistent session support. - * Renamed ``--allow-redirects`` to ``--follow``. - * Improved the usability of ``http --help``. - * Fixed installation on Windows with Python 3. - * Fixed colorized output on Windows with Python 3. - * CRLF HTTP header field separation in the output. - * Added exit status code ``2`` for timed-out requests. - * Added the option to separate colorizing and formatting - (``--pretty=all``, ``--pretty=colors`` and ``--pretty=format``). - ``--ugly`` has bee removed in favor of ``--pretty=none``. -* `0.2.7`_ (2012-08-07) - * Compatibility with Requests 0.13.6. - * Streamed terminal output. ``--stream, -S`` can be used to enable - streaming also with ``--pretty`` and to ensure a more frequent output - flushing. - * Support for efficient large file downloads. - * Sort headers by name (unless ``--pretty=none``). - * Response body is fetched only when needed (e.g., not with ``--headers``). - * Improved content type matching. - * Updated Solarized color scheme. - * Windows: Added ``--output FILE`` to store output into a file - (piping results in corrupted data on Windows). - * Proper handling of binary requests and responses. - * Fixed printing of ``multipart/form-data`` requests. - * Renamed ``--traceback`` to ``--debug``. -* `0.2.6`_ (2012-07-26) - * The short option for ``--headers`` is now ``-h`` (``-t`` has been - removed, for usage use ``--help``). - * Form data and URL parameters can have multiple fields with the same name - (e.g.,``http -f url a=1 a=2``). - * Added ``--check-status`` to exit with an error on HTTP 3xx, 4xx and - 5xx (3, 4, and 5, respectively). - * If the output is piped to another program or redirected to a file, - the default behaviour is to only print the response body. - (It can still be overwritten via the ``--print`` flag.) - * Improved highlighting of HTTP headers. - * Added query string parameters (``param==value``). - * Added support for terminal colors under Windows. -* `0.2.5`_ (2012-07-17) - * Unicode characters in prettified JSON now don't get escaped for - improved readability. - * --auth now prompts for a password if only a username provided. - * Added support for request payloads from a file path with automatic - ``Content-Type`` (``http URL @/path``). - * Fixed missing query string when displaying the request headers via - ``--verbose``. - * Fixed Content-Type for requests with no data. -* `0.2.2`_ (2012-06-24) - * The ``METHOD`` positional argument can now be omitted (defaults to - ``GET``, or to ``POST`` with data). - * Fixed --verbose --form. - * Added support for `Tox`_. -* `0.2.1`_ (2012-06-13) - * Added compatibility with ``requests-0.12.1``. - * Dropped custom JSON and HTTP lexers in favor of the ones newly included - in ``pygments-1.5``. -* `0.2.0`_ (2012-04-25) - * Added Python 3 support. - * Added the ability to print the HTTP request as well as the response - (see ``--print`` and ``--verbose``). - * Added support for Digest authentication. - * Added file upload support - (``http -f POST file_field_name@/path/to/file``). - * Improved syntax highlighting for JSON. - * Added support for field name escaping. - * Many bug fixes. -* `0.1.6`_ (2012-03-04) - - -.. _Requests: http://python-requests.org -.. _Pygments: http://pygments.org/ -.. _pip: http://www.pip-installer.org/en/latest/index.html -.. _Github API: http://developer.github.com/v3/issues/comments/#create-a-comment -.. _these fine people: https://github.com/jakubroztocil/httpie/contributors -.. _Jakub Roztocil: http://subtleapps.com -.. _@jakubroztocil: https://twitter.com/jakubroztocil -.. _claudiatd/httpie-artwork: https://github.com/claudiatd/httpie-artwork -.. _0.1.6: https://github.com/jakubroztocil/httpie/compare/0.1.4...0.1.6 -.. _0.2.0: https://github.com/jakubroztocil/httpie/compare/0.1.6...0.2.0 -.. _0.2.1: https://github.com/jakubroztocil/httpie/compare/0.2.0...0.2.1 -.. _0.2.2: https://github.com/jakubroztocil/httpie/compare/0.2.1...0.2.2 -.. _0.2.5: https://github.com/jakubroztocil/httpie/compare/0.2.2...0.2.5 -.. _0.2.6: https://github.com/jakubroztocil/httpie/compare/0.2.5...0.2.6 -.. _0.2.7: https://github.com/jakubroztocil/httpie/compare/0.2.5...0.2.7 -.. _0.3.0: https://github.com/jakubroztocil/httpie/compare/0.2.7...0.3.0 -.. _0.4.0: https://github.com/jakubroztocil/httpie/compare/0.3.0...0.4.0 -.. _0.4.1: https://github.com/jakubroztocil/httpie/compare/0.4.0...0.4.1 -.. _0.5.0: https://github.com/jakubroztocil/httpie/compare/0.4.1...0.5.0 -.. _0.5.1: https://github.com/jakubroztocil/httpie/compare/0.5.0...0.5.1 -.. _0.6.0: https://github.com/jakubroztocil/httpie/compare/0.5.1...0.6.0 -.. _0.7.1: https://github.com/jakubroztocil/httpie/compare/0.6.0...0.7.1 -.. _0.8.0: https://github.com/jakubroztocil/httpie/compare/0.7.1...0.8.0 -.. _0.9.0-dev: https://github.com/jakubroztocil/httpie/compare/0.8.0...master -.. _LICENSE: https://github.com/jakubroztocil/httpie/blob/master/LICENSE -.. _Tox: http://tox.testrun.org -.. _CONTRIBUTING: https://github.com/jakubroztocil/httpie/blob/master/CONTRIBUTING.rst - - -.. |version| image:: https://badge.fury.io/py/httpie.svg - :target: http://badge.fury.io/py/httpie - -.. |unix| image:: https://api.travis-ci.org/jakubroztocil/httpie.svg - :target: http://travis-ci.org/jakubroztocil/httpie - :alt: Build Status of the master branch on Mac/Linux - -.. |windows| image:: https://ci.appveyor.com/api/projects/status/f7b5dogxuseq8srw - :target: https://ci.appveyor.com/project/jakubroztocil/httpie - :alt: Build Status of the master branch on Windows diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..542bcd7854 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,14 @@ +# Security policy + +## Reporting a vulnerability + +When you identify a vulnerability in HTTPie, please report it privately using one of the following channels: + +- Email to [`security@httpie.io`](mailto:security@httpie.io) +- Report on [huntr.dev](https://huntr.dev/) + +In addition to the description of the vulnerability, include the following information: + +- A short reproducer to verify it (it can be a small HTTP server, shell script, docker image, etc.) +- Your deemed severity level of the vulnerability (`LOW`/`MEDIUM`/`HIGH`/`CRITICAL`) +- [CWE](https://cwe.mitre.org/) ID, if available. diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 81e9ce88f0..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,17 +0,0 @@ -# https://ci.appveyor.com/project/jakubroztocil/httpie -build: false -environment: - matrix: - - PYTHON: "C:/Python27" - - PYTHON: "C:/Python34" -init: - - "ECHO %PYTHON%" - - ps: "ls C:/Python*" -install: - - ps: (new-object net.webclient).DownloadFile('https://raw.github.com/pypa/pip/master/contrib/get-pip.py', 'C:/get-pip.py') - - "%PYTHON%/python.exe C:/get-pip.py" - - "%PYTHON%/Scripts/pip.exe install -e ." -test_script: - - "%PYTHON%/Scripts/pip.exe --version" - - "%PYTHON%/Scripts/http.exe --debug" - - "%PYTHON%/python.exe setup.py test" diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..51dff51aec --- /dev/null +++ b/docs/README.md @@ -0,0 +1,2601 @@ +
+ +# HTTPie documentation + +
+ +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. +HTTPie is designed for testing, debugging, and generally interacting with APIs & HTTP servers. +The `http` & `https` commands allow for creating and sending arbitrary HTTP requests. +They use simple and natural syntax and provide formatted and colorized output. + +
+ +## About this document + +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. + +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/cli/blob/master/docs/README.md). + +
+ +## Main features + +- Expressive and intuitive syntax +- Formatted and colorized terminal output +- Built-in JSON support +- Forms and file uploads +- HTTPS, proxies, and authentication +- Arbitrary request data +- Custom headers +- Persistent sessions +- Wget-like downloads +- Linux, macOS, Windows, and FreeBSD support +- Plugins +- Documentation +- Test coverage + +## Installation + +
+ + + +- [Universal](#universal) +- [macOS](#macos) +- [Windows](#windows) +- [Linux](#linux) +- [FreeBSD](#freebsd) + +### Universal + +#### PyPI + +Please make sure you have Python 3.7 or newer (`python --version`). + +```bash +# Install httpie +$ python -m pip install --upgrade pip wheel +$ python -m pip install httpie +``` + +```bash +# Upgrade httpie +$ python -m pip install --upgrade pip wheel +$ python -m pip install --upgrade httpie +``` + +### macOS + +#### Homebrew + +To install [Homebrew](https://brew.sh/), see [its installation](https://docs.brew.sh/Installation). + +```bash +# Install httpie +$ brew update +$ brew install httpie +``` + +```bash +# Upgrade httpie +$ brew update +$ brew upgrade httpie +``` + +#### MacPorts + +To install [MacPorts](https://www.macports.org/), see [its installation](https://www.macports.org/install.php). + +```bash +# Install httpie +$ port selfupdate +$ port install httpie +``` + +```bash +# Upgrade httpie +$ port selfupdate +$ port upgrade httpie +``` + +### Windows + +#### Chocolatey + +To install [Chocolatey](https://chocolatey.org/), see [its installation](https://chocolatey.org/install). + +```bash +# Install httpie +$ choco install httpie +``` + +```bash +# Upgrade httpie +$ choco upgrade httpie +``` + +### 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) + +To install [Snapcraft](https://snapcraft.io/), see [its installation](https://snapcraft.io/docs/installing-snapd). + +```bash +# Install httpie +$ snap install httpie +``` + +```bash +# Upgrade httpie +$ snap refresh httpie +``` + +#### Linuxbrew + +To install [Linuxbrew](https://docs.brew.sh/Homebrew-on-Linux), see [its installation](https://docs.brew.sh/Homebrew-on-Linux#install). + +```bash +# Install httpie +$ brew update +$ brew install httpie +``` + +```bash +# Upgrade httpie +$ brew update +$ brew upgrade httpie +``` + +#### Arch Linux + +Also works for other Arch-derived distributions like ArcoLinux, EndeavourOS, Artix Linux, etc. + +```bash +# Install httpie +$ pacman -Syu httpie +``` + +```bash +# Upgrade httpie +$ pacman -Syu +``` + +### FreeBSD + +#### FreshPorts + +```bash +# Install httpie +$ pkg install www/py-httpie +``` + +```bash +# Upgrade httpie +$ pkg upgrade www/py-httpie +``` + + + +
+ +### Unstable version + +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. + +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 +$ python -m pip install --upgrade https://github.com/httpie/cli/archive/master.tar.gz +``` + +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 +$ brew uninstall --force httpie +$ brew install --HEAD httpie +``` + +You can install it using Snapcraft by running the following commands: + +```bash +$ snap remove httpie +$ snap install httpie --edge +``` + +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 +$ http --version +# 3.X.X.dev0 +``` + +Note that on your machine, the version name will have the `.dev0` suffix. + +## Usage + +Hello World: + +```bash +$ https httpie.io/hello +``` + +Synopsis: + +```bash +$ http [flags] [METHOD] URL [ITEM [ITEM]] +``` + +See also `http --help` (and for systems where man pages are available, you can use `man http`). + +### Examples + +Custom [HTTP method](#http-method), [HTTP headers](#http-headers) and [JSON](#json) data: + +```bash +$ http PUT pie.dev/put X-API-Token:123 name=John +``` + +Submitting [forms](#forms): + +```bash +$ http -f POST pie.dev/post hello=World +``` + +See the request that is being sent using one of the [output options](#output-options): + +```bash +$ http -v pie.dev/get +``` + +Build and print a request without sending it using [offline mode](#offline-mode): + +```bash +$ 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/cli/issues/83) with [authentication](#authentication): + +```bash +$ 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): + +```bash +$ http pie.dev/post < files/data.json +``` + +Download a file and save it via [redirected output](#redirected-output): + +```bash +$ http pie.dev/image/png > image.png +``` + +Download a file `wget` style: + +```bash +$ http --download pie.dev/image/png +``` + +Use named [sessions](#sessions) to make certain aspects of the communication persistent between requests to the same host: + +```bash +$ http --session=logged-in -a username:password pie.dev/get API-Key:123 +``` + +```bash +$ http --session=logged-in pie.dev/headers +``` + +Set a custom `Host` header to work around missing DNS records: + +```bash +$ http localhost:8000 Host:example.com +``` + +## HTTP method + +The name of the HTTP method comes right before the URL argument: + +```bash +$ http DELETE pie.dev/delete +``` + +Which looks similar to the actual `Request-Line` that is sent: + +```http +DELETE /delete HTTP/1.1 +``` + +In addition to the standard methods (`GET`, `POST`, `HEAD`, `PUT`, `PATCH`, `DELETE`, etc.), you can use custom method names, for example: + +```bash +$ http AHOY pie.dev/post +``` + +There are no restrictions regarding which request methods can include a body. You can send an empty `POST` request: + +```bash +$ http POST pie.dev/post +``` + +You can also make `GET` requests containing a body: + +```bash +$ http GET pie.dev/get hello=world +``` + +### Optional `GET` and `POST` + +The `METHOD` argument is optional, and when you don’t specify it, HTTPie defaults to: + +- `GET` for requests without body +- `POST` for requests with body + +Here we don’t specify any request data, so both commands will send the same `GET` request: + +```bash +$ http GET pie.dev/get +``` + +```bash +$ http pie.dev/get +``` + +Here, on the other hand, we do have some data, so both commands will make the same `POST` request: + +```bash +$ http POST pie.dev/post hello=world +``` + +```bash +$ http pie.dev/post hello=world +``` + +## Request URL + +The only information HTTPie needs to perform a request is a URL. + +The default scheme is `http://` and can be omitted from the argument: + +```bash +$ http example.org +# → http://example.org +``` + +HTTPie also installs an `https` executable, where the default scheme is `https://`: + +```bash +$ https example.org +# → https://example.org +``` + +When you paste a URL into the terminal, you can even keep the `://` bit in the URL argument to quickly convert the URL into an HTTPie call just by adding a space after the protocol name. + +```bash +$ https ://example.org +# → https://example.org +``` + +```bash +$ http ://example.org +# → http://example.org +``` + +### Querystring parameters + +If you find yourself manually constructing URLs with querystring parameters on the terminal, you may appreciate the `param==value` syntax for appending URL parameters. + +With that, you don’t have to worry about escaping the `&` separators for your shell. Additionally, any special characters in the parameter name or value get automatically URL-escaped (as opposed to the parameters specified in the full URL, which HTTPie doesn’t modify). + +```bash +$ http https://api.github.com/search/repositories q==httpie per_page==1 +``` + +```http +GET /search/repositories?q=httpie&per_page=1 HTTP/1.1 +``` + +You can even retrieve the `value` from a file by using the `param==@file` syntax. This would also effectively strip the newlines from the end. See [file based separators](#file-based-separators) for more examples. + +```bash +$ http pie.dev/get text==@files/text.txt +``` + +### URL shortcuts for `localhost` + +Additionally, curl-like shorthand for localhost is supported. +This means that, for example, `:3000` would expand to `http://localhost:3000`. +If the port is omitted, then port 80 is assumed. + +```bash +$ http :/foo +``` + +```http +GET /foo HTTP/1.1 +Host: localhost +``` + +```bash +$ http :3000/bar +``` + +```http +GET /bar HTTP/1.1 +Host: localhost:3000 +``` + +```bash +$ http : +``` + +```http +GET / HTTP/1.1 +Host: localhost +``` + +### Other default schemes + +When HTTPie is invoked as `https` then the default scheme is `https://` (`$ https example.org` will make a request to `https://example.org`). + +You can also use the `--default-scheme ` option to create shortcuts for other protocols than HTTP (possibly supported via [plugins](https://pypi.org/search/?q=httpie)). Example for the [httpie-unixsocket](https://github.com/httpie/httpie-unixsocket) plugin: + +```bash +# Before +$ http http+unix://%2Fvar%2Frun%2Fdocker.sock/info +``` + +```bash +# Create an alias +$ alias http-unix='http --default-scheme="http+unix"' +``` + +```bash +# Now the scheme can be omitted +$ http-unix %2Fvar%2Frun%2Fdocker.sock/info +``` + +### `--path-as-is` + +The standard behavior of HTTP clients is to normalize the path portion of URLs by squashing dot segments as a typical filesystem would: + +```bash +$ http -v example.org/./../../etc/password +``` + +```http +GET /etc/password HTTP/1.1 +``` + +The `--path-as-is` option allows you to disable this behavior: + +```bash +$ http --path-as-is -v example.org/./../../etc/password +``` + +```http +GET /../../etc/password HTTP/1.1 +``` + +## Request items + +There are a few different *request item* types that provide a convenient +mechanism for specifying HTTP headers, JSON and form data, files, +and URL parameters. This is a very practical way of constructing +HTTP requests from scratch on the CLI. + +Each *request item* is simply a key/value pair separated with the following +characters: `:` (headers), `=` (data field, e.g., JSON, form), `:=` (raw data field) +`==` (query parameters), `@` (file upload). + +```bash +$ http PUT pie.dev/put \ + X-Date:today \ # Header + token==secret \ # Query parameter + name=John \ # Data field + age:=29 # Raw JSON +``` + +| Item Type | Description | +|-------------------------------------------------------------:|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| 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. | +| 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) | +| 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: +[raw request body](#raw-request-body) is a mechanism for passing arbitrary request data. + +### 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 +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. + +```bash +$ http POST pie.dev/post \ + X-Data:@files/text.txt # Read a header from a file + token==@files/text.txt # Read a query parameter from a file + name=@files/text.txt # Read a data field’s value from a file + bookmarks:=@files/data.json # Embed a JSON object from a file +``` + +### Escaping rules + +You can use `\` to escape characters that shouldn’t be used as separators (or parts thereof). For instance, `foo\==bar` will become a data key/value pair (`foo=` and `bar`) instead of a URL parameter. + +Often it is necessary to quote the values, e.g. `foo='bar baz'`. + +If any of the field names or headers starts with a minus (e.g. `-fieldname`), you need to place all such items after the special token `--` to prevent confusion with `--arguments`: + +```bash +$ http pie.dev/post -- -name-starting-with-dash=foo -Unusual-Header:bar +``` + +```http +POST /post HTTP/1.1 +-Unusual-Header: bar +Content-Type: application/json + +{ + "-name-starting-with-dash": "foo" +} +``` + +## JSON + +JSON is the *lingua franca* of modern web services, and it is also the **implicit content type** HTTPie uses by default. + +Simple example: + +```bash +$ http PUT pie.dev/put name=John email=john@example.org +``` + +```http +PUT / HTTP/1.1 +Accept: application/json, */*;q=0.5 +Accept-Encoding: gzip, deflate +Content-Type: application/json +Host: pie.dev + +{ + "name": "John", + "email": "john@example.org" +} +``` + +### Default behavior + +If your command includes some data [request items](#request-items), they are serialized as a JSON object by default. HTTPie also automatically sets the following headers, both of which can be overwritten: + +| Header | Value | +|---------------:|-------------------------------| +| `Content-Type` | `application/json` | +| `Accept` | `application/json, */*;q=0.5` | + +### Explicit JSON + +You can use `--json, -j` to explicitly set `Accept` to `application/json` regardless of whether you are sending data (it’s a shortcut for setting the header via the usual header notation: `http url Accept:'application/json, */*;q=0.5'`). +Additionally, HTTPie will try to detect JSON responses even when the `Content-Type` is incorrectly `text/plain` or unknown. + +### Non-string JSON fields + +Non-string JSON fields use the `:=` separator, which allows you to embed arbitrary JSON data into the resulting JSON object. +Additionally, text and raw JSON files can also be embedded into fields using `=@` and `:=@`: + +```bash +$ http PUT pie.dev/put \ + name=John \ # String (default) + age:=29 \ # Raw JSON — Number + married:=false \ # Raw JSON — Boolean + hobbies:='["http", "pies"]' \ # Raw JSON — Array + favorite:='{"tool": "HTTPie"}' \ # Raw JSON — Object + bookmarks:=@files/data.json \ # Embed JSON file + description=@files/text.txt # Embed text file +``` + +```http +PUT /person/1 HTTP/1.1 +Accept: application/json, */*;q=0.5 +Content-Type: application/json +Host: pie.dev + +{ + "age": 29, + "hobbies": [ + "http", + "pies" + ], + "description": "John is a nice guy who likes pies.", + "married": false, + "name": "John", + "favorite": { + "tool": "HTTPie" + }, + "bookmarks": { + "HTTPie": "https://httpie.org", + } +} +``` + +The `:=`/`:=@` syntax is JSON-specific. You can switch your request to `--form` or `--multipart`, +and string, float, and number values will continue to be serialized (as string form values). +Other JSON types, however, are not allowed with `--form` or `--multipart`. + +### Nested JSON + +If your use case involves sending complex JSON objects as part of the request body, +HTTPie can help you build them right from your terminal. You still use the existing +data field operators (`=`/`:=`) but instead of specifying a top-level field name (like `key=value`), +you specify a path declaration. This tells HTTPie where and how to put the given value inside an object: + +```bash +http pie.dev/post \ + platform[name]=HTTPie \ + platform[about][mission]='Make APIs simple and intuitive' \ + platform[about][homepage]=httpie.io \ + platform[about][homepage]=httpie.io \ + platform[about][stars]:=54000 \ + platform[apps][]=Terminal \ + platform[apps][]=Desktop \ + platform[apps][]=Web \ + platform[apps][]=Mobile +``` + +```json +{ + "platform": { + "name": "HTTPie", + "about": { + "mission": "Make APIs simple and intuitive", + "homepage": "httpie.io", + "stars": 54000 + }, + "apps": [ + "Terminal", + "Desktop", + "Web", + "Mobile" + ] + } +} +``` + +#### Introduction + +Let’s start with a simple example, and build a simple search query: + +```bash +$ http --offline --print=B pie.dev/post \ + category=tools \ + search[type]=id \ + search[id]:=1 +``` + +In the example above, the `search[type]` is an instruction for creating an object called `search`, and setting the `type` field of it to the given value (`"id"`). + +Also note that, just as the regular syntax, you can use the `:=` operator to directly pass raw JSON values (e.g., numbers in the case above). + +```json +{ + "category": "tools", + "search": { + "id": 1, + "type": "id" + } +} +``` + +Building arrays is also possible, through `[]` suffix (an append operation). This tells HTTPie to create an array in the given path (if there is not one already), and append the given value to that array. + +```bash +$ http --offline --print=B pie.dev/post \ + category=tools \ + search[type]=keyword \ + search[keywords][]=APIs \ + search[keywords][]=CLI +``` + +```json +{ + "category": "tools", + "search": { + "keywords": [ + "APIs", + "CLI" + ], + "type": "keyword" + } +} +``` + +If you want to explicitly specify the position of elements inside an array, +you can simply pass the desired index as the path: + +```bash +$ http --offline --print=B pie.dev/post \ + category=tools \ + search[type]=keyword \ + search[keywords][1]=APIs \ + search[keywords][0]=CLI +``` + +```json +{ + "category": "tools", + "search": { + "keywords": [ + "CLIs", + "API" + ], + "type": "keyword" + } +} +``` + +If there are any missing indexes, HTTPie will nullify them in order to create a concrete object that can be sent: + +```bash +$ http --offline --print=B pie.dev/post \ + category=tools \ + search[type]=platforms \ + search[platforms][]=Terminal \ + search[platforms][1]=Desktop \ + search[platforms][3]=Mobile +``` + +```json +{ + "category": "tools", + "search": { + "platforms": [ + "Terminal", + "Desktop", + null, + "Mobile" + ], + "type": "platforms" + } +} +``` + +It is also possible to embed raw JSON to a nested structure, for example: + +```bash +$ http --offline --print=B pie.dev/post \ + category=tools \ + search[type]=platforms \ + 'search[platforms]:=["Terminal", "Desktop"]' \ + search[platforms][]=Web \ + search[platforms][]=Mobile +``` + +```json +{ + "category": "tools", + "search": { + "platforms": [ + "Terminal", + "Desktop", + "Web", + "Mobile" + ], + "type": "platforms" + } +} +``` + +And just to demonstrate all of these features together, let’s create a very deeply nested JSON object: + +```bash +$ http PUT pie.dev/put \ + shallow=value \ # Shallow key-value pair + object[key]=value \ # Nested key-value pair + array[]:=1 \ # Array — first item + array[1]:=2 \ # Array — second item + array[2]:=3 \ # Array — append (third item) + very[nested][json][3][httpie][power][]=Amaze # Nested object +``` + +#### Advanced usage + +##### Top level arrays + +If you want to send an array instead of a regular object, you can simply +do that by omitting the starting key: + +```bash +$ http --offline --print=B pie.dev/post \ + []:=1 \ + []:=2 \ + []:=3 +``` + +```json +[ + 1, + 2, + 3 +] +``` + +You can also apply the nesting to the items by referencing their index: + +```bash +http --offline --print=B pie.dev/post \ + [0][type]=platform [0][name]=terminal \ + [1][type]=platform [1][name]=desktop +``` + +```json +[ + { + "type": "platform", + "name": "terminal" + }, + { + "type": "platform", + "name": "desktop" + } +] +``` + +Sending scalar JSON types (a single `null`, `true`, `false`, string or number) as the top-level object is impossible using the key/value syntax. But you can still pass it via [`--raw=''`](#raw-request-body). + +##### Escaping behavior + +Nested JSON syntax uses the same [escaping rules](#escaping-rules) as +the terminal. There are 3 special characters, and 1 special token that you can escape. + +If you want to send a bracket as is, escape it with a backslash (`\`): + +```bash +$ http --offline --print=B pie.dev/post \ + 'foo\[bar\]:=1' \ + 'baz[\[]:=2' \ + 'baz[\]]:=3' +``` + +```json +{ + "baz": { + "[": 2, + "]": 3 + }, + "foo[bar]": 1 +} +``` + +If you want to send the literal backslash character (`\`), escape it with another backslash: + +```bash +$ http --offline --print=B pie.dev/post \ + 'backslash[\\]:=1' +``` + +```json +{ + "backslash": { + "\\": 1 + } +} +``` + +A regular integer in a path (e.g `[10]`) means an array index; but if you want it to be treated as +a string, you can escape the whole number by using a backslash (`\`) prefix. + +```bash +$ http --offline --print=B pie.dev/post \ + 'object[\1]=stringified' \ + 'object[\100]=same' \ + 'array[1]=indexified' +``` + +```json +{ + "array": [ + null, + "indexified" + ], + "object": { + "1": "stringified", + "100": "same" + } +} +``` + +##### Guiding syntax errors + +If you make a typo or forget to close a bracket, the errors will guide you to fix it. For example: + +```bash +$ http --offline --print=B pie.dev/post \ + 'foo[bar]=OK' \ + 'foo[baz][quux=FAIL' +``` + +```console +HTTPie Syntax Error: Expecting ']' +foo[baz][quux + ^ +``` + +You can follow to given instruction (adding a `]`) and repair your expression. + +##### Type safety + +Each container path (e.g., `x[y][z]` in `x[y][z][1]`) has a certain type, which gets defined with +the first usage and can’t be changed after that. If you try to do a key-based access to an array or +an index-based access to an object, HTTPie will error out: + +```bash +$ http --offline --print=B pie.dev/post \ + 'array[]:=1' \ + 'array[]:=2' \ + 'array[key]:=3' +HTTPie Type Error: Can't perform 'key' based access on 'array' which has a type of 'array' but this operation requires a type of 'object'. +array[key] + ^^^^^ +``` + +Type Safety does not apply to value overrides, for example: + +```bash +$ http --offline --print=B pie.dev/post \ + user[name]:=411 # Defined as an integer + user[name]=string # Overridden with a string +``` + +```json +{ + "user": { + "name": "string" + } +} +``` + +### Raw JSON + +For very complex JSON structures, it may be more convenient to [pass it as raw request body](#raw-request-body), for example: + +```bash +$ echo -n '{"hello": "world"}' | http POST pie.dev/post +``` + +```bash +$ http POST pie.dev/post < files/data.json +``` + +## Forms + +Submitting forms is very similar to sending [JSON](#json) requests. +Often the only difference is in adding the `--form, -f` option, which ensures that data fields are serialized as key-value tuples separated by '&', with a '=' between the key and the value. In addition `Content-Type` is set to `application/x-www-form-urlencoded; charset=utf-8`. +It is possible to make form data the implicit content type instead of JSON via the [config](#config) file. + +### Regular forms + +```bash +$ http --form POST pie.dev/post name='John Smith' +``` + +```http +POST /post HTTP/1.1 +Content-Type: application/x-www-form-urlencoded; charset=utf-8 + +name=John+Smith +``` + +### File upload forms + +If one or more file fields is present, the serialization and content type is `multipart/form-data`: + +```bash +$ 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: + +```html +
+ + +
+``` + +Please note that `@` is used to simulate a file upload form field, whereas `=@` just embeds the file content as a regular text field value. + +When uploading files, their content type is inferred from the file name. You can manually override the inferred content type: + +```bash +$ http -f POST pie.dev/post name='John Smith' cv@'~/files/data.bin;type=application/pdf' +``` + +To perform a `multipart/form-data` request even without any files, use `--multipart` instead of `--form`: + +```bash +$ http --multipart --offline example.org hello=world +``` + +```http +POST / HTTP/1.1 +Content-Length: 129 +Content-Type: multipart/form-data; boundary=c31279ab254f40aeb06df32b433cbccb +Host: example.org + +--c31279ab254f40aeb06df32b433cbccb +Content-Disposition: form-data; name="hello" + +world +--c31279ab254f40aeb06df32b433cbccb-- +``` + +File uploads are always streamed to avoid memory issues with large files. + +By default, HTTPie uses a random unique string as the multipart boundary, but you can use `--boundary` to specify a custom string instead: + +```bash +$ http --form --multipart --boundary=xoxo --offline example.org hello=world +``` + +```http +POST / HTTP/1.1 +Content-Length: 129 +Content-Type: multipart/form-data; boundary=xoxo +Host: example.org + +--xoxo +Content-Disposition: form-data; name="hello" + +world +--xoxo-- +``` + +If you specify a custom `Content-Type` header without including the boundary bit, HTTPie will add the boundary value (explicitly specified or auto-generated) to the header automatically: + +```bash +$ http --form --multipart --offline example.org hello=world Content-Type:multipart/letter +``` + +```http +POST / HTTP/1.1 +Content-Length: 129 +Content-Type: multipart/letter; boundary=c31279ab254f40aeb06df32b433cbccb +Host: example.org + +--c31279ab254f40aeb06df32b433cbccb +Content-Disposition: form-data; name="hello" + +world +--c31279ab254f40aeb06df32b433cbccb-- +``` + +## HTTP headers + +To set custom headers you can use the `Header:Value` notation: + +```bash +$ http pie.dev/headers User-Agent:Bacon/1.0 'Cookie:valued-visitor=yes;foo=bar' \ + X-Foo:Bar Referer:https://httpie.org/ +``` + +```http +GET /headers HTTP/1.1 +Accept: */* +Accept-Encoding: gzip, deflate +Cookie: valued-visitor=yes;foo=bar +Host: pie.dev +Referer: https://httpie.org/ +User-Agent: Bacon/1.0 +X-Foo: Bar +``` + +### Default request headers + +There are a couple of default headers that HTTPie sets: + +```http +GET / HTTP/1.1 +Accept: */* +Accept-Encoding: gzip, deflate +User-Agent: HTTPie/ +Host: +``` + +All of these can be overwritten or unset (see below). + +### Reading headers from a file + +You can read headers from a file by using the `:@` operator. This would also effectively strip the newlines from the end. See [file based separators](#file-based-separators) for more examples. + +```bash +$ http pie.dev/headers X-Data:@files/text.txt +``` + +### Empty headers and header un-setting + +To unset a previously specified header (such a one of the default headers), use `Header:`: + +```bash +$ http pie.dev/headers Accept: User-Agent: +``` + +To send a header with an empty value, use `Header;`, with a semicolon: + +```bash +$ http pie.dev/headers 'Header;' +``` + +Please note that some internal headers, such as `Content-Length`, can’t be unset if +they are automatically added by the client itself. + +### Multiple header values with the same name + +If the request is sent with multiple headers that are sharing the same name, then +the HTTPie will send them individually. + +```bash +http --offline example.org Cookie:one Cookie:two +``` + +```http +GET / HTTP/1.1 +Cookie: one +Cookie: two +``` + +It is also possible to pass a single header value pair, where the value is a comma +separated list of header values. Then the client will send it as a single header. + +```bash +http --offline example.org Numbers:one,two +``` + +```http +GET / HTTP/1.1 +Numbers: one,two +``` + +Also be aware that if the current session contains any headers they will get overwritten +by individual commands when sending a request instead of being joined together. + +### Limiting response headers + +The `--max-headers=n` option allows you to control the number of headers HTTPie reads before giving up (the default `0`, i.e., there’s no limit). + +```bash +$ http --max-headers=100 pie.dev/get +``` + +## Offline mode + +Use `--offline` to construct HTTP requests without sending them anywhere. +With `--offline`, HTTPie builds a request based on the specified options and arguments, prints it to `stdout`, and then exits. It works completely offline; no network connection is ever made. This has a number of use cases, including: + +Generating API documentation examples that you can copy & paste without sending a request: + +```bash +$ http --offline POST server.chess/api/games API-Key:ZZZ w=magnus b=hikaru t=180 i=2 +``` + +```bash +$ http --offline MOVE server.chess/api/games/123 API-Key:ZZZ p=b a=R1a3 t=77 +``` + +Generating raw requests that can be sent with any other client: + +```bash +# 1. save a raw request to a file: +$ http --offline POST pie.dev/post hello=world > request.http +``` + +```bash +# 2. send it over the wire with, for example, the fantastic netcat tool: +$ nc pie.dev 80 < request.http +``` + +You can also use the `--offline` mode for debugging and exploring HTTP and HTTPie, and for “dry runs”. + +`--offline` has the side effect of automatically activating `--print=HB`, i.e., both the request headers and the body +are printed. You can customize the output with the usual [output options](#output-options), with the exception where there +is no response to be printed. You can use `--offline` in combination with all the other options (e.g. `--session`). + +## Cookies + +HTTP clients send cookies to the server as regular [HTTP headers](#http-headers). +That means, HTTPie does not offer any special syntax for specifying cookies — the usual `Header:Value` notation is used: + +Send a single cookie: + +```bash +$ http pie.dev/cookies Cookie:sessionid=foo +``` + +```http +GET / HTTP/1.1 +Accept: */* +Accept-Encoding: gzip, deflate +Connection: keep-alive +Cookie: sessionid=foo +Host: pie.dev +User-Agent: HTTPie/0.9.9 +``` + +Send multiple cookies (note: the header is quoted to prevent the shell from interpreting the `;`): + +```bash +$ http pie.dev/cookies 'Cookie:sessionid=foo;another-cookie=bar' +``` + +```http +GET / HTTP/1.1 +Accept: */* +Accept-Encoding: gzip, deflate +Connection: keep-alive +Cookie: sessionid=foo;another-cookie=bar +Host: pie.dev +User-Agent: HTTPie/0.9.9 +``` + +If you often deal with cookies in your requests, then you’d appreciate +the [sessions](#sessions) feature. + +## Authentication + +The currently supported authentication schemes are Basic and Digest (see [auth plugins](#auth-plugins) for more). There are two flags that control authentication: + +| Flag | Arguments | +|------------------:|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `--auth, -a` | Pass either a `username:password` pair or a `token` as the argument. If the selected authenticated method requires username/password combination and if you only specify a username (`-a username`), you’ll be prompted for the password before the request is sent. To send an empty password, pass `username:`. The `username:password@hostname` URL syntax is supported as well (but credentials passed via `-a` have higher priority) | +| `--auth-type, -A` | Specify the auth mechanism. Possible values are `basic`, `digest`, `bearer` or the name of any [auth plugins](#auth-plugins) you have installed. The default value is `basic` so it can often be omitted | + +### Basic auth + +```bash +$ http -a username:password pie.dev/basic-auth/username/password +``` + +### Digest auth + +```bash +$ http -A digest -a username:password pie.dev/digest-auth/httpie/username/password +``` + +### Bearer auth + +```bash +https -A bearer -a token pie.dev/bearer +``` + +### Password prompt + +If you omit the password part of `--auth, -a`, HTTPie securely prompts you for it: + +```bash +$ http -a username pie.dev/basic-auth/username/password +``` + +Please note that when you use [`--session`](#sessions), prompted passwords are persisted in session files. + +### Empty password + +To send an empty password without being prompted for it, include a trailing colon in the credentials: + +```bash +$ http -a username: pie.dev/headers +``` + +### `.netrc` + +Authentication information from your `~/.netrc` file is by default honored as well. + +For example: + +```bash +$ cat ~/.netrc +machine pie.dev +login httpie +password test +``` + +```bash +$ http pie.dev/basic-auth/httpie/test +HTTP/1.1 200 OK +[...] +``` + +This can be disabled with the `--ignore-netrc` option: + +```bash +$ http --ignore-netrc pie.dev/basic-auth/httpie/test +HTTP/1.1 401 UNAUTHORIZED +[...] +``` + +### Auth plugins + +Additional authentication mechanism can be installed as plugins. +They can be found on the [Python Package Index](https://pypi.python.org/pypi?%3Aaction=search&term=httpie&submit=search). +Here are a few picks: + +- [httpie-api-auth](https://github.com/pd/httpie-api-auth): ApiAuth +- [httpie-aws-auth](https://github.com/httpie/httpie-aws-auth): AWS / Amazon S3 +- [httpie-edgegrid](https://github.com/akamai-open/httpie-edgegrid): EdgeGrid +- [httpie-hmac-auth](https://github.com/guardian/httpie-hmac-auth): HMAC +- [httpie-jwt-auth](https://github.com/teracyhq/httpie-jwt-auth): JWTAuth (JSON Web Tokens) +- [httpie-negotiate](https://github.com/ndzou/httpie-negotiate): SPNEGO (GSS Negotiate) +- [httpie-ntlm](https://github.com/httpie/httpie-ntlm): NTLM (NT LAN Manager) +- [httpie-oauth1](https://github.com/qcif/httpie-oauth1): OAuth 1.0a +- [requests-hawk](https://github.com/mozilla-services/requests-hawk): Hawk + +See [plugin manager](#plugin-manager) for more details. + +## HTTP redirects + +By default, HTTP redirects are not followed and only the first +response is shown: + +```bash +$ http pie.dev/redirect/3 +``` + +### Follow `Location` + +To instruct HTTPie to follow the `Location` header of `30x` responses +and show the final response instead, use the `--follow, -F` option: + +```bash +$ http --follow pie.dev/redirect/3 +``` + +With `307 Temporary Redirect` and `308 Permanent Redirect`, the method and the body of the original request +are reused to perform the redirected request. Otherwise, a body-less `GET` request is performed. + +### Showing intermediary redirect responses + +If you wish to see the intermediary requests/responses, +then use the `--all` option: + +```bash +$ http --follow --all pie.dev/redirect/3 +``` + +### Limiting maximum redirects followed + +To change the default limit of maximum `30` redirects, use the `--max-redirects=` option: + +```bash +$ http --follow --all --max-redirects=2 pie.dev/redirect/3 +``` + +## Proxies + +You can specify proxies to be used through the `--proxy` argument for each protocol (which is included in the value in case of redirects across protocols): + +```bash +$ http --proxy=http:http://10.10.1.10:3128 --proxy=https:https://10.10.1.10:1080 example.org +``` + +With Basic authentication: + +```bash +$ http --proxy=http:http://user:pass@10.10.1.10:3128 example.org +``` + +### Environment variables + +You can also configure proxies by environment variables `ALL_PROXY`, `HTTP_PROXY` and `HTTPS_PROXY`, and the underlying +[Requests library](https://requests.readthedocs.io/en/latest/) will pick them up. +If you want to disable proxies configured through the environment variables for certain hosts, you can specify them in `NO_PROXY`. + +In your `~/.bash_profile`: + +```bash +export HTTP_PROXY=http://10.10.1.10:3128 +export HTTPS_PROXY=https://10.10.1.10:1080 +export NO_PROXY=localhost,example.com +``` + +### SOCKS + +Usage for SOCKS is the same as for other types of [proxies](#proxies): + +```bash +$ http --proxy=http:socks5://user:pass@host:port --proxy=https:socks5://user:pass@host:port example.org +``` + +## HTTPS + +### Server SSL certificate verification + +To skip the host’s SSL certificate verification, you can pass `--verify=no` (default is `yes`): + +```bash +$ http --verify=no https://pie.dev/get +``` + +### Custom CA bundle + +You can also use `--verify=` to set a custom CA bundle path: + +```bash +$ http --verify=/ssl/custom_ca_bundle https://example.org +``` + +### Client side SSL certificate + +To use a client side certificate for the SSL communication, you can pass +the path of the cert file with `--cert`: + +```bash +$ http --cert=client.pem https://example.org +``` + +If the private key is not contained in the cert file, you may pass the +path of the key file with `--cert-key`: + +```bash +$ http --cert=client.crt --cert-key=client.key https://example.org +``` + +If the given private key requires a passphrase, HTTPie will automatically detect it +and ask it through a prompt: + +```bash +$ http --cert=client.pem --cert-key=client.key https://example.org +http: passphrase for client.key: **** +``` + +If you don't want to see a prompt, you can supply the passphrase with the `--cert-key-pass` +argument: + +```bash +$ http --cert=client.pem --cert-key=client.key --cert-key-pass=my_password https://example.org +``` + +### SSL version + +Use the `--ssl=` option to specify the desired protocol version to use. +This will default to SSL v2.3 which will negotiate the highest protocol that both the server and your installation of OpenSSL support. +The available protocols are `ssl2.3`, `ssl3`, `tls1`, `tls1.1`, `tls1.2`, `tls1.3`. +(The actually available set of protocols may vary depending on your OpenSSL installation.) + +```bash +# Specify the vulnerable SSL v3 protocol to talk to an outdated server: +$ http --ssl=ssl3 https://vulnerable.example.org +``` + +### SSL ciphers + +You can specify the available ciphers with `--ciphers`. +It should be a string in the [OpenSSL cipher list format](https://www.openssl.org/docs/man1.1.0/man1/ciphers.html). + +```bash +$ http --ciphers=ECDHE-RSA-AES128-GCM-SHA256 https://pie.dev/get +``` + +Note: these cipher strings do not change the negotiated version of SSL or TLS, they only affect the list of available cipher suites. + +To see the default cipher string, run `http --help` and see the `--ciphers` section under SSL. + +## Output options + +By default, HTTPie only outputs the final response and the whole response +message is printed (headers as well as the body). You can control what should +be printed via several options: + +| Option | What is printed | +|---------------------------:|----------------------------------------------------------------------------------------------------| +| `--headers, -h` | Only the response headers are printed | +| `--body, -b` | Only the response body is printed | +| `--meta, -m` | Only the [response metadata](#response-meta) is printed | +| `--verbose, -v` | Print the whole HTTP exchange (request and response). This option also enables `--all` (see below) | +| `--verbose --verbose, -vv` | Just like `-v`, but also include the response metadata. | +| `--print, -p` | Selects parts of the HTTP exchange | +| `--quiet, -q` | Don’t print anything to `stdout` and `stderr` | + +### What parts of the HTTP exchange should be printed + +All the other [output options](#output-options) are under the hood just shortcuts for the more powerful `--print, -p`. +It accepts a string of characters each of which represents a specific part of the HTTP exchange: + +| Character | Stands for | +|----------:|---------------------------------| +| `H` | request headers | +| `B` | request body | +| `h` | response headers | +| `b` | response body | +| `m` | [response meta](#response-meta) | + +Print request and response headers: + +```bash +$ http --print=Hh PUT pie.dev/put hello=world +``` + +#### Response meta + +The response metadata section currently includes the total time elapsed. It’s the number of seconds between opening the network connection and downloading the last byte of response the body. + + +To _only_ show the response metadata, use `--meta, -m` (analogically to `--headers, -h` and `--body, -b`): + +```bash +$ http --meta pie.dev/delay/1 +``` + +```console +Elapsed time: 1.099171542s +``` + +The [extra verbose `-vv` output](#extra-verbose-output) includes the meta section by default. You can also show it in combination with other parts of the exchange via [`--print=m`](#what-parts-of-the-http-exchange-should-be-printed). For example, here we print it together with the response headers: + +```bash +$ http --print=hm pie.dev/get +``` + +```http +HTTP/1.1 200 OK +Content-Type: application/json + +Elapsed time: 0.077538375s +``` + + +Please note that it also includes time spent on formatting the output, which adds a small penalty. Also, if the body is not part of the output, [we don’t spend time downloading it](#conditional-body-download). + +If you [use `--style` with one of the Pie themes](#colors-and-formatting), you’ll see the time information color-coded (green/yellow/orange/red) based on how long the exchange took. + + +### Verbose output + +`--verbose` can often be useful for debugging the request and generating documentation examples: + +```bash +$ http --verbose PUT pie.dev/put hello=world +PUT /put HTTP/1.1 +Accept: application/json, */*;q=0.5 +Accept-Encoding: gzip, deflate +Content-Type: application/json +Host: pie.dev +User-Agent: HTTPie/0.2.7dev + +{ + "hello": "world" +} + +HTTP/1.1 200 OK +Connection: keep-alive +Content-Length: 477 +Content-Type: application/json +Date: Sun, 05 Aug 2012 00:25:23 GMT +Server: gunicorn/0.13.4 + +{ + […] +} +``` + +#### Extra verbose output + +If you run HTTPie with `-vv` or `--verbose --verbose`, then it would also display the [response metadata](#response-meta). + +```bash +# Just like the above, but with additional columns like the total elapsed time +$ http -vv pie.dev/get +``` + +### Quiet output + +`--quiet` redirects all output that would otherwise go to `stdout` and `stderr` to `/dev/null` (except for errors and warnings). +This doesn’t affect output to a file via `--output` or `--download`. + +```bash +# There will be no output: +$ http --quiet pie.dev/post enjoy='the silence' +``` + +If you’d like to silence warnings as well, use `-q` or `--quiet` twice: + +```bash +# There will be no output, even in case of an unexpected response status code: +$ http -qq --check-status pie.dev/post enjoy='the silence without warnings' +``` + +### Update warnings + +When there is a new release available for your platform (for example; if you installed HTTPie through `pip`, it will check the latest version on `PyPI`), HTTPie will regularly warn you about the new update (once a week). If you want to disable this behavior, you can set `disable_update_warnings` to `true` in your [config](#config) file. + +### Viewing intermediary requests/responses + +To see all the HTTP communication, i.e. the final request/response as well as any possible intermediary requests/responses, use the `--all` option. +The intermediary HTTP communication include followed redirects (with `--follow`), the first unauthorized request when HTTP digest authentication is used (`--auth=digest`), etc. + +```bash +# Include all responses that lead to the final one: +$ http --all --follow pie.dev/redirect/3 +``` + +The intermediary requests/responses are by default formatted according to `--print, -p` (and its shortcuts described above). + +### Conditional body download + +As an optimization, the response body is downloaded from the server only if it’s part of the output. +This is similar to performing a `HEAD` request, except that it applies to any HTTP method you use. + +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 +$ 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. +The response headers are downloaded always, even if they are not part of the output + +## Raw request body + +In addition to crafting structured [JSON](#json) and [forms](#forms) requests with the [request items](#request-items) syntax, you can provide a raw request body that will be sent without further processing. +These two approaches for specifying request data (i.e., structured and raw) cannot be combined. + +There are three methods for passing raw request data: piping via `stdin`, +`--raw='data'`, and `@/file/path`. + +### Redirected Input + +The universal method for passing request data is through redirected `stdin` +(standard input)—piping. + +By default, `stdin` data is buffered and then with no further processing used as the request body. +If you provide `Content-Length`, then the request body is streamed without buffering. +You may also use `--chunked` to enable streaming via [chunked transfer encoding](#chunked-transfer-encoding) +or `--compress, -x` to [compress the request body](#compressed-request-body). + +There are multiple useful ways to use piping: + +Redirect from a file: + +```bash +$ http PUT pie.dev/put X-API-Token:123 < files/data.json +``` + +Or the output of another program: + +```bash +$ grep '401 Unauthorized' /var/log/httpd/error_log | http POST pie.dev/post +``` + +You can use `echo` for simple data: + +```bash +$ echo -n '{"name": "John"}' | http PATCH pie.dev/patch X-API-Token:123 +``` + +You can also use a Bash *here string*: + +```bash +$ http pie.dev/post <<<'{"name": "John"}' +``` + +You can even pipe web services together using HTTPie: + +```bash +$ http GET https://api.github.com/repos/httpie/cli | http POST pie.dev/post +``` + +You can use `cat` to enter multiline data on the terminal: + +```bash +$ cat | http POST pie.dev/post + +^D +``` + +```bash +$ cat | http POST pie.dev/post Content-Type:text/plain +- buy milk +- call parents +^D +``` + +On macOS, you can send the contents of the clipboard with `pbpaste`: + +```bash +$ pbpaste | http PUT pie.dev/put +``` + +Passing data through `stdin` **can’t** be combined with data fields specified on the command line: + +```bash +$ echo -n 'data' | http POST example.org more=data # This is invalid +``` + +To prevent HTTPie from reading `stdin` data you can use the `--ignore-stdin` option. + +### Request data via `--raw` + +In a situation when piping data via `stdin` is not convenient (for example, +when generating API docs examples), you can specify the raw request body via +the `--raw` option. + +```bash +$ http --raw 'Hello, world!' pie.dev/post +``` + +```bash +$ http --raw '{"name": "John"}' pie.dev/post +``` + +### Request data from a filename + +An alternative to redirected `stdin` is specifying a filename (as `@/path/to/file`) whose content is used as if it came from `stdin`. + +It has the advantage that the `Content-Type` header is automatically set to the appropriate value based on the filename extension. +For example, the following request sends the verbatim contents of that XML file with `Content-Type: application/xml`: + +```bash +$ http PUT pie.dev/put @files/data.xml +``` + +File uploads are always streamed to avoid memory issues with large files. + +## Chunked transfer encoding + +You can use the `--chunked` flag to instruct HTTPie to use `Transfer-Encoding: chunked`: + +```bash +$ http --chunked PUT pie.dev/put hello=world +``` + +```bash +$ http --chunked --multipart PUT pie.dev/put hello=world foo@files/data.xml +``` + +```bash +$ http --chunked pie.dev/post @files/data.xml +``` + +```bash +$ cat files/data.xml | http --chunked pie.dev/post +``` + +## Compressed request body + +You can use the `--compress, -x` flag to instruct HTTPie to use `Content-Encoding: deflate` and compress the request data: + +```bash +$ http --compress pie.dev/post @files/data.xml +``` + +```bash +$ cat files/data.xml | http --compress pie.dev/post +``` + +If compressing the data does not save size, HTTPie sends it untouched. To always compress the data, specify `--compress, -x` twice: + +```bash +$ http -xx PUT pie.dev/put hello=world +``` + +## Terminal output + +HTTPie does several things by default in order to make its terminal output easy to read. + +### Colors and formatting + +Syntax highlighting is applied to HTTP headers and bodies (where it makes sense). +You can choose your preferred color scheme via the `--style` option if you don’t like the default one. +There are dozens of styles available, here are just a few notable ones: + +| Style | Description | +|------------:|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `auto` | Follows your terminal ANSI color styles. This is the default style used by HTTPie | +| `default` | Default styles of the underlying Pygments library. Not actually used by default by HTTPie. You can enable it with `--style=default` | +| `pie-dark` | HTTPie’s original brand style. Also used in [HTTPie for Web and Desktop](https://httpie.io/product). | +| `pie-light` | Like `pie-dark`, but for terminals with light background colors. | +| `pie` | A generic version of `pie-dark` and `pie-light` themes that can work with any terminal background. Its universality requires compromises in terms of legibility, but it’s useful if you frequently switch your terminal between dark and light backgrounds. | +| `monokai` | A popular color scheme. Enable with `--style=monokai` | +| `fruity` | A bold, colorful scheme. Enable with `--style=fruity` | +| … | See `$ http --help` for all the possible `--style` values | + +Use one of these options to control output processing: + +| Option | Description | +|------------------:|---------------------------------------------------------------| +| `--pretty=all` | Apply both colors and formatting. Default for terminal output | +| `--pretty=colors` | Apply colors | +| `--pretty=format` | Apply formatting | +| `--pretty=none` | Disables output processing. Default for redirected output | + +HTTPie looks at `Content-Type` to select the right syntax highlighter and formatter for each message body. If that fails (e.g., the server provides the wrong type), or you prefer a different treatment, you can manually overwrite the mime type for a response with `--response-mime`: + +```bash +$ http --response-mime=text/yaml pie.dev/get +``` + +Formatting has the following effects: + +- HTTP headers are sorted by name. +- JSON data is indented, sorted by keys, and unicode escapes are converted + to the characters they represent. +- XML and XHTML data is indented. + +Please note that sometimes there might be changes made by formatters on the actual response body (e.g., +collapsing empty tags on XML) but the end result will always be semantically indistinguishable. Some of +these formatting changes can be configured more granularly through [format options](#format-options). + +### Format options + +The `--format-options=opt1:value,opt2:value` option allows you to control how the output should be formatted +when formatting is applied. The following options are available: + +| Option | Default value | Shortcuts | +|-----------------:|:-------------:|--------------------------| +| `headers.sort` | `true` | `--sorted`, `--unsorted` | +| `json.format` | `true` | N/A | +| `json.indent` | `4` | N/A | +| `json.sort_keys` | `true` | `--sorted`, `--unsorted` | +| `xml.format` | `true` | N/A | +| `xml.indent` | `2` | N/A | + +For example, this is how you would disable the default header and JSON key +sorting, and specify a custom JSON indent size: + +```bash +$ http --format-options headers.sort:false,json.sort_keys:false,json.indent:2 pie.dev/get +``` + +There are also two shortcuts that allow you to quickly disable and re-enable +sorting-related format options (currently it means JSON keys and headers): +`--unsorted` and `--sorted`. + +This is something you will typically store as one of the default options in your [config](#config) file. + +### Redirected output + +HTTPie uses a different set of defaults for redirected output than for [terminal output](#terminal-output). +The differences being: + +- Formatting and colors aren’t applied (unless `--pretty` is specified). +- Only the response body is printed (unless one of the [output options](#output-options) is set). +- Also, binary data isn’t suppressed. + +The reason is to make piping HTTPie’s output to another programs and downloading files work with no extra flags. +Most of the time, only the raw response body is of an interest when the output is redirected. + +Download a file: + +```bash +$ http pie.dev/image/png > image.png +``` + +Download an image of an [Octocat](https://octodex.github.com/images/original.jpg), resize it using [ImageMagick](https://imagemagick.org/), and upload it elsewhere: + +```bash +$ http octodex.github.com/images/original.jpg | convert - -resize 25% - | http example.org/Octocats +``` + +Force colorizing and formatting, and show both the request and the response in `less` pager: + +```bash +$ http --pretty=all --verbose pie.dev/get | less -R +``` + +The `-R` flag tells `less` to interpret color escape sequences included HTTPie’s output. + +You can create a shortcut for invoking HTTPie with colorized and paged output by adding the following to your `~/.bash_profile`: + +```bash +function httpless { + # `httpless example.org' + http --pretty=all --print=hb "$@" | less -R; +} +``` + +### Binary data + +Binary data is suppressed for terminal output, which makes it safe to perform requests to URLs that send back binary data. +Binary data is also suppressed in redirected but prettified output. +The connection is closed as soon as we know that the response body is binary, + +```bash +$ http pie.dev/bytes/2000 +``` + +You will nearly instantly see something like this: + +```http +HTTP/1.1 200 OK +Content-Type: application/octet-stream + ++-----------------------------------------+ +| NOTE: binary data not shown in terminal | ++-----------------------------------------+ +``` + +### Display encoding + +HTTPie tries to do its best to decode message bodies when printing them to the terminal correctly. It uses the encoding specified in the `Content-Type` `charset` attribute. If a message doesn’t define its charset, we auto-detect it. For very short messages (1–32B), where auto-detection would be unreliable, we default to UTF-8. For cases when the response encoding is still incorrect, you can manually overwrite the response charset with `--response-charset`: + +```bash +$ http --response-charset=big5 pie.dev/get +``` + +## Download mode + +HTTPie features a download mode in which it acts similarly to `wget`. + +When enabled using the `--download, -d` flag, response headers are printed to the terminal (`stderr`), and a progress bar is shown while the response body is being saved to a file. + +```bash +$ http --download https://github.com/httpie/cli/archive/master.tar.gz +``` + +```http +HTTP/1.1 200 OK +Content-Disposition: attachment; filename=httpie-master.tar.gz +Content-Length: 257336 +Content-Type: application/x-gzip + +Downloading 251.30 kB to "httpie-master.tar.gz" +Done. 251.30 kB in 2.73862s (91.76 kB/s) +``` + +### Downloaded filename + +There are three mutually exclusive ways through which HTTPie determines +the output filename (with decreasing priority): + +1. You can explicitly provide it via `--output, -o`. The file gets overwritten if it already exists (or appended to with `--continue, -c`). +2. The server may specify the filename in the optional `Content-Disposition` response header. Any leading dots are stripped from a server-provided filename. +3. The last resort HTTPie uses is to generate the filename from a combination of the request URL and the response `Content-Type`. The initial URL is always used as the basis for the generated filename — even if there has been one or more redirects. + +To prevent data loss by overwriting, HTTPie adds a unique numerical suffix to the filename when necessary (unless specified with `--output, -o`). + +### 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: + +```bash +$ http -d https://github.com/httpie/cli/archive/master.tar.gz | tar zxf - +``` + +### Resuming downloads + +If `--output, -o` is specified, you can resume a partial download using the `--continue, -c` option. +This only works with servers that support `Range` requests and `206 Partial Content` responses. +If the server doesn’t support that, the whole file will simply be downloaded: + +```bash +$ http -dco file.zip example.org/file +``` + +`-dco` is shorthand for `--download` `--continue` `--output`. + +### Other notes + +- The `--download` option only changes how the response body is treated. +- You can still set custom headers, use sessions, `--verbose, -v`, etc. +- `--download` always implies `--follow` (redirects are followed). +- `--download` also implies `--check-status` (error HTTP status will result in a non-zero exist static code). +- HTTPie exits with status code `1` (error) if the body hasn’t been fully downloaded. +- `Accept-Encoding` can’t be set with `--download`. + +## Streamed responses + +Responses are downloaded and printed in chunks. +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. + +### Disabling buffering + +You can use the `--stream, -S` flag to make two things happen: + +1. The output is flushed in much smaller chunks without any buffering, which makes HTTPie behave kind of like `tail -f` for URLs. +2. Streaming becomes enabled even when the output is prettified: It will be applied to each line of the response and flushed immediately. This makes it possible to have a nice output for long-lived requests, such as one to the [Twitter streaming API](https://developer.twitter.com/en/docs/tutorials/consuming-streaming-data). + +The `--stream` option is automatically enabled when the response headers include `Content-Type: text/event-stream`. + +### Example use cases + +Prettified streamed response: + +```bash +$ http --stream pie.dev/stream/3 +``` + +Streamed output by small chunks à la `tail -f`: + +```bash +# Send each new line (JSON object) to another URL as soon as it arrives from a streaming API: +$ http --stream pie.dev/stream/3 | while read line; do echo "$line" | http pie.dev/post ; done +``` + +## Sessions + +By default, every request HTTPie makes is completely independent of any previous ones to the same host. + +However, HTTPie also supports persistent sessions via the `--session=SESSION_NAME_OR_PATH` option. +In a session, custom [HTTP headers](#http-headers) (except for the ones starting with `Content-` or `If-`), [authentication](#authentication), and [cookies](#cookies) (manually specified or sent by the server) persist between requests to the same host. + +```bash +# Create a new session: +$ http --session=./session.json pie.dev/headers API-Token:123 +``` + +```bash +# Inspect / edit the generated session file: +$ cat session.json +``` + +```bash +# Re-use the existing session — the API-Token header will be set: +$ http --session=./session.json pie.dev/headers +``` + +All session data, including credentials, prompted passwords, cookie data, and custom headers are stored in plain text. +That means session files can also be created and edited manually in a text editor—they are regular JSON. +It also means that they can be read by anyone who has access to the session file. + +### Named sessions + +You can create one or more named session per host. For example, this is how you can create a new session named `user1` for `pie.dev`: + +```bash +$ http --session=user1 -a user1:password pie.dev/get X-Foo:Bar +``` + +From now on, you can refer to the session by its name (`user1`). +When you choose to use the session again, all previously specified authentication or HTTP headers will automatically be set: + +```bash +$ http --session=user1 pie.dev/get +``` + +To create or reuse a different session, simply specify a different name: + +```bash +$ http --session=user2 -a user2:password pie.dev/get X-Bar:Foo +``` + +Named sessions’ data is stored in JSON files inside the `sessions` subdirectory of the [config](#config) directory, typically `~/.config/httpie/sessions//.json` (`%APPDATA%\httpie\sessions\\.json` on Windows). + +If you have executed the above commands on a Unix machine, you should be able to list the generated sessions files using: + +```bash +$ ls -l ~/.config/httpie/sessions/pie.dev +``` + +### Anonymous sessions + +Instead of giving it a name, you can also directly specify a path to a session file. +This allows for sessions to be re-used across multiple hosts: + +```bash +# Create a session: +$ http --session=/tmp/session.json example.org +``` + +```bash +# Use the session to make a request to another host: +$ http --session=/tmp/session.json admin.example.org +``` + +```bash +# You can also refer to a previously created named session: +$ http --session=~/.config/httpie/sessions/another.example.org/test.json example.org +``` + +When creating anonymous sessions, please remember to always include at least one `/`, even if the session files is located in the current directory (i.e. `--session=./session.json` instead of just `--session=session.json`), otherwise HTTPie assumes a named session instead. + +### Readonly session + +To use the original session file without updating it from the request/response exchange after it has been created, specify the session name via `--session-read-only=SESSION_NAME_OR_PATH` instead. + +```bash +# If the session file doesn’t exist, then it is created: +$ http --session-read-only=./ro-session.json pie.dev/headers Custom-Header:orig-value +``` + +```bash +# But it is not updated: +$ http --session-read-only=./ro-session.json pie.dev/headers Custom-Header:new-value +``` + +### Host-based cookie policy + +Cookies persisted in sessions files have a `domain` field. This _binds_ them to a specified hostname. For example: + +```json +{ + "cookies": [ + { + "domain": "pie.dev", + "name": "pie", + "value": "apple" + }, + { + "domain": "httpbin.org", + "name": "bin", + "value": "http" + } + ] +} +``` + +Using this session file, we include `Cookie: pie=apple` only in requests against `pie.dev` and subdomains (e.g., `foo.pie.dev` or `foo.bar.pie.dev`): + +```bash +$ http --session=./session.json pie.dev/cookies +``` + +```json +{ + "cookies": { + "pie": "apple" + } +} +``` + +To make a cookie domain _unbound_ (i.e., to make it available to all hosts, including throughout a cross-domain redirect chain), you can set the `domain` field to `null` in the session file: + +```json +{ + "cookies": [ + { + "domain": null, + "name": "unbound-cookie", + "value": "send-me-to-any-host" + } + ] +} +``` + +```bash +$ http --session=./session.json pie.dev/cookies +``` + +```json +{ + "cookies": { + "unbound-cookie": "send-me-to-any-host" + } +} +``` + + +### Cookie storage behavior + +There are three possible sources of persisted cookies within a session. They have the following storage priority: 1—response; 2—command line; 3—session file. + +1. Receive a response with a `Set-Cookie` header: + + ```bash + $ http --session=./session.json pie.dev/cookie/set?foo=bar + ``` + +2. Send a cookie specified on the command line as seen in [cookies](#cookies): + + ```bash + $ http --session=./session.json pie.dev/headers Cookie:foo=bar + ``` + +3. Manually set cookie parameters in the session file: + + ```json + { + "cookies": { + "foo": { + "expires": null, + "path": "/", + "secure": false, + "value": "bar" + } + } + } + ``` + +In summary: + +- Cookies set via the CLI overwrite cookies of the same name inside session files. +- Server-sent `Set-Cookie` header cookies overwrite any pre-existing ones with the same name. + +Cookie expiration handling: + +- When the server expires an existing cookie, HTTPie removes it from the session file. +- When a cookie in a session file expires, HTTPie removes it before sending a new request. + +### Upgrading sessions + +HTTPie may introduce changes in the session file format. When HTTPie detects an obsolete format, it shows a warning. You can upgrade your session files using the following commands: + +Upgrade all existing [named sessions](#named-sessions) inside the `sessions` subfolder of your [config directory](https://httpie.io/docs/cli/config-file-directory): + +```bash +$ httpie cli sessions upgrade-all +Upgraded 'api_auth' @ 'pie.dev' to v3.1.0 +Upgraded 'login_cookies' @ 'httpie.io' to v3.1.0 +``` + +Upgrading individual sessions requires you to specify the session's hostname. That allows HTTPie to find the correct file in the case of name sessions. Additionally, it allows it to correctly bind cookies when upgrading with [`--bind-cookies`](#session-upgrade-options). + +Upgrade a single [named session](#named-sessions): + +```bash +$ httpie cli sessions upgrade pie.dev api_auth +Upgraded 'api_auth' @ 'pie.dev' to v3.1.0 +``` + +Upgrade a single [anonymous session](#anonymous-sessions) using a file path: + +```bash +$ httpie cli sessions upgrade pie.dev ./session.json +Upgraded 'session.json' @ 'pie.dev' to v3.1.0 +``` + +#### Session upgrade options + +These flags are available for both `sessions upgrade` and `sessions upgrade-all`: + +| Option | Description | +|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `--bind-cookies` | Bind all previously [unbound cookies](#host-based-cookie-policy) to the session’s host ([context](https://github.com/httpie/cli/security/advisories/GHSA-9w4w-cpc8-h2fq)). | + + +## Config + +HTTPie uses a simple `config.json` file. +The file doesn’t exist by default, but you can create it manually. + +### Config file directory + +To see the exact location for your installation, run `http --debug` and look for `config_dir` in the output. + +The default location of the configuration file on most platforms is `$XDG_CONFIG_HOME/httpie/config.json` (defaulting to `~/.config/httpie/config.json`). + +For backward compatibility, if the directory `~/.httpie` exists, the configuration file there will be used instead. + +On Windows, the config file is located at `%APPDATA%\httpie\config.json`. + +The config directory can be changed by setting the `$HTTPIE_CONFIG_DIR` environment variable: + +```bash +$ export HTTPIE_CONFIG_DIR=/tmp/httpie +$ http pie.dev/get +``` + +### Configurable options + +Currently, HTTPie offers a single configurable option: + +#### `default_options` + +An `Array` (by default empty) of default options that should be applied to every invocation of HTTPie. + +For instance, you can use this config option to change your default color theme: + +```bash +$ cat ~/.config/httpie/config.json +``` + +```json +{ + "default_options": [ + "--style=fruity" + ] +} +``` + +Technically, it is possible to include any HTTPie options in there. +However, it is not recommended modifying the default behavior in a way that would break your compatibility with the wider world as that may become confusing. + +#### `plugins_dir` + +The directory where the plugins will be installed. HTTPie needs to have read/write access on that directory, since +`httpie cli plugins install` will download new plugins to there. See [plugin manager](#plugin-manager) for more information. + +### Un-setting previously specified options + +Default options from the config file, or specified any other way, can be unset for a particular invocation via `--no-OPTION` arguments passed via the command line (e.g., `--no-style` or `--no-session`). + +## Scripting + +When using HTTPie from shell scripts, it can be handy to set the `--check-status` flag. +It instructs HTTPie to exit with an error if the HTTP status is one of `3xx`, `4xx`, or `5xx`. +The exit status will be `3` (unless `--follow` is set), `4`, or `5`, respectively. + +```bash +#!/bin/bash + +if http --check-status --ignore-stdin --timeout=2.5 HEAD pie.dev/get &> /dev/null; then + echo 'OK!' +else + case $? in + 2) echo 'Request timed out!' ;; + 3) echo 'Unexpected HTTP 3xx Redirection!' ;; + 4) echo 'HTTP 4xx Client Error!' ;; + 5) echo 'HTTP 5xx Server Error!' ;; + 6) echo 'Exceeded --max-redirects= redirects!' ;; + *) echo 'Other Error!' ;; + esac +fi +``` + +### Best practices + +The default behavior of automatically reading `stdin` is typically not desirable during non-interactive invocations. +You most likely want to use the `--ignore-stdin` option to disable it. + +It's important to note that without the `--ignore-stdin` option, HTTPie may appear to have stopped working (hang). This happens because, in situations where HTTPie is invoked outside of an interactive session, such as from a cron job, `stdin` is not connected to a terminal. This means that the rules for [redirected input](#redirected-input) will be followed. When `stdin` is redirected, HTTPie assumes that the input will contain the request body, and it waits for the input to be provided. But, since there is neither any input data nor an end-of-file (`EOF`) signal, HTTPie gets stuck. To avoid this problem, the `--ignore-stdin` flag should be used in scripts, unless data is being piped to HTTPie. + +To prevent your program from becoming unresponsive when the server fails to respond, it's a good idea to use the `--timeout` option to set a connection timeout limit. + +## Plugin manager + +HTTPie offers extensibility through a [plugin API](https://github.com/httpie/cli/blob/master/httpie/plugins/base.py), +and there are dozens of plugins available to try! +They add things like new authentication methods ([akamai/httpie-edgegrid](https://github.com/akamai/httpie-edgegrid)), +transport mechanisms ([httpie/httpie-unixsocket](https://github.com/httpie/httpie-unixsocket)), +message convertors ([banteg/httpie-image](https://github.com/banteg/httpie-image)), or simply +change how a response is formatted. + +> Note: Plugins are usually made by our community members, and thus have no direct relationship with +> the HTTPie project. We do not control / review them at the moment, so use them at your own discretion. + +For managing these plugins; starting with 3.0, we are offering a new plugin manager. + +This command is currently in beta. + +### `httpie cli` + +#### `httpie cli check-updates` + +You can check whether a new update is available for your system by running `httpie cli check-updates`: + +```bash-termible +$ httpie cli check-updates +``` + +#### `httpie cli export-args` + +`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). + +Available formats to export in include: + +| Format | Description | +|--------|---------------------------------------------------------------------------------------------------------------------------------------------------| +| `json` | Export the parser spec in JSON. The schema includes a top-level `version` parameter which should be interpreted in [semver](https://semver.org/). | + +You can use any of these formats with `--format` parameter, but the default one is `json`. + +```bash +$ httpie cli export-args | jq '"Program: " + .spec.name + ", Version: " + .version' +"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, +but this can be modified through `plugins_dir` variable on the config. + +##### `httpie cli plugins install` + +For installing plugins from [PyPI](https://pypi.org/) or from local paths, `httpie cli plugins install` +can be used. + +```bash +$ httpie cli plugins install httpie-plugin +Installing httpie-plugin... +Successfully installed httpie-plugin-1.0.2 +``` + +> Tip: Generally HTTPie plugins start with `httpie-` prefix. Try searching for it on [PyPI](https://pypi.org/search/?q=httpie-) +> to find out all plugins from the community. + +##### `httpie cli plugins list` + +List all installed plugins. + +```bash +$ httpie cli plugins list +httpie_plugin (1.0.2) + httpie_plugin (httpie.plugins.auth.v1) +httpie_plugin_2 (1.0.6) + httpie_plugin_2 (httpie.plugins.auth.v1) +httpie_converter (1.0.0) + httpie_iterm_converter (httpie.plugins.converter.v1) + httpie_konsole_konverter (httpie.plugins.converter.v1) +``` + +##### `httpie cli plugins upgrade` + +For upgrading already installed plugins, use `httpie plugins upgrade`. + +```bash +$ httpie cli plugins upgrade httpie-plugin +``` + +##### `httpie cli plugins uninstall` + +Uninstall plugins from the isolated plugins directory. If the plugin is not installed +through `httpie cli plugins install`, it won’t uninstall it. + +```bash +$ httpie cli plugins uninstall httpie-plugin +``` + +## Meta + +### Interface design + +The syntax of the command arguments closely correspond to the actual HTTP requests sent over the wire. +It has the advantage that it’s easy to remember and read. +You can often translate an HTTP request to an HTTPie argument list just by inlining the request elements. +For example, compare this HTTP request: + +```http +POST /post HTTP/1.1 +Host: pie.dev +X-API-Key: 123 +User-Agent: Bacon/1.0 +Content-Type: application/x-www-form-urlencoded + +name=value&name2=value2 +``` + +with the HTTPie command that sends it: + +```bash +$ http -f POST pie.dev/post \ + X-API-Key:123 \ + User-Agent:Bacon/1.0 \ + name=value \ + name2=value2 +``` + +Notice that both the order of elements and the syntax are very similar, and that only a small portion of the command is used to control HTTPie and doesn’t directly correspond to any part of the request (here, it’s only `-f` asking HTTPie to send a form request). + +The two modes, `--pretty=all` (default for terminal) and `--pretty=none` (default for [redirected output](#redirected-output)), allow for both user-friendly interactive use and usage from scripts, where HTTPie serves as a generic HTTP client. + +In the future, the command line syntax and some of the `--OPTIONS` may change slightly, as HTTPie improves and new features are added. +All changes are recorded in the [change log](#change-log). + +### Community and Support + +HTTPie has the following community channels: + +- [GitHub Issues](https://github.com/httpie/cli/issues) for bug reports and feature requests +- [Discord server](https://httpie.io/discord) to ask questions, discuss features, and for general API development discussion +- [StackOverflow](https://stackoverflow.com) to ask questions (make sure to use the [httpie](https://stackoverflow.com/questions/tagged/httpie) tag) + +### Related projects + +#### Dependencies + +Under the hood, HTTPie uses these two amazing libraries: + +- [Requests](https://requests.readthedocs.io/en/latest/) — Python HTTP library for humans +- [Pygments](https://pygments.org/) — Python syntax highlighter + +#### HTTPie friends + +HTTPie plays exceptionally well with the following tools: + +- [http-prompt](https://github.com/httpie/http-prompt) — an interactive shell for HTTPie featuring autocomplete and command syntax highlighting +- [jq](https://stedolan.github.io/jq/) — CLI JSON processor that works great in conjunction with HTTPie + +Helpers to convert from other client tools: + +- [CurliPie](https://curlipie.open-api.vn) — library to convert cURL commands to HTTPie + +#### Alternatives + +- [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. + +### Contributing + +See [CONTRIBUTING](https://github.com/httpie/cli/blob/master/CONTRIBUTING.md). + +### Security policy + +See [github.com/httpie/cli/security/policy](https://github.com/httpie/cli/security/policy). + +### Change log + +See [CHANGELOG](https://github.com/httpie/cli/blob/master/CHANGELOG.md). + +### Artwork + +- [README Animation](https://github.com/httpie/cli/blob/master/docs/httpie-animation.gif) by [Allen Smith](https://github.com/loranallensmith). + +### Licence + +BSD-3-Clause: [LICENSE](https://github.com/httpie/cli/blob/master/LICENSE). + +### 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. + diff --git a/docs/config.json b/docs/config.json new file mode 100644 index 0000000000..4375a5a5e7 --- /dev/null +++ b/docs/config.json @@ -0,0 +1,5 @@ +{ + "website": { + "master_and_released_docs_differ_after": null + } +} diff --git a/docs/contributors/README.md b/docs/contributors/README.md new file mode 100644 index 0000000000..9a739cd543 --- /dev/null +++ b/docs/contributors/README.md @@ -0,0 +1,3 @@ +Here we maintain a database of contributors, from which we generate credits on release blog posts and social media. + +For the HTTPie blog see: . diff --git a/docs/contributors/fetch.py b/docs/contributors/fetch.py new file mode 100644 index 0000000000..ba94c28183 --- /dev/null +++ b/docs/contributors/fetch.py @@ -0,0 +1,281 @@ +""" +Generate the contributors database. + +FIXME: replace `requests` calls with the HTTPie API, when available. +""" +import json +import os +import re +import sys +from copy import deepcopy +from datetime import datetime +from pathlib import Path +from subprocess import check_output +from time import sleep +from typing import Any, Dict, Optional, Set + +import requests + +FullNames = Set[str] +GitHubLogins = Set[str] +Person = Dict[str, str] +People = Dict[str, Person] +UserInfo = Dict[str, Any] + +CO_AUTHORS = re.compile(r'Co-authored-by: ([^<]+) <').finditer +API_URL = 'https://api.github.com' +REPO = OWNER = 'httpie' +REPO_URL = f'{API_URL}/repos/{REPO}/{OWNER}' + +HERE = Path(__file__).parent +DB_FILE = HERE / 'people.json' + +DEFAULT_PERSON: Person = {'committed': [], 'reported': [], 'github': '', 'twitter': ''} +SKIPPED_LABELS = {'invalid'} + +GITHUB_TOKEN = os.getenv('GITHUB_TOKEN') +assert GITHUB_TOKEN, 'GITHUB_TOKEN envar is missing' + + +class FinishedForNow(Exception): + """Raised when remaining GitHub rate limit is zero.""" + + +def main(previous_release: str, current_release: str) -> int: + since = release_date(previous_release) + until = release_date(current_release) + + contributors = load_awesome_people() + try: + committers = find_committers(since, until) + reporters = find_reporters(since, until) + except Exception as exc: + # We want to save what we fetched so far. So pass. + print(' !! ', exc) + + try: + merge_all_the_people(current_release, contributors, committers, reporters) + fetch_missing_users_details(contributors) + except FinishedForNow: + # We want to save what we fetched so far. So pass. + print(' !! Committers:', committers) + print(' !! Reporters:', reporters) + exit_status = 1 + else: + exit_status = 0 + + save_awesome_people(contributors) + return exit_status + + +def find_committers(since: str, until: str) -> FullNames: + url = f'{REPO_URL}/commits' + page = 1 + per_page = 100 + params = { + 'since': since, + 'until': until, + 'per_page': per_page, + } + committers: FullNames = set() + + while 'there are commits': + params['page'] = page + data = fetch(url, params=params) + + for item in data: + commit = item['commit'] + committers.add(commit['author']['name']) + debug(' >>> Commit', item['html_url']) + for co_author in CO_AUTHORS(commit['message']): + name = co_author.group(1) + committers.add(name) + + if len(data) < per_page: + break + page += 1 + + return committers + + +def find_reporters(since: str, until: str) -> GitHubLogins: + url = f'{API_URL}/search/issues' + page = 1 + per_page = 100 + params = { + 'q': f'repo:{REPO}/{OWNER} is:issue closed:{since}..{until}', + 'per_page': per_page, + } + reporters: GitHubLogins = set() + + while 'there are issues': + params['page'] = page + data = fetch(url, params=params) + + for item in data['items']: + # Filter out unwanted labels. + if any(label['name'] in SKIPPED_LABELS for label in item['labels']): + continue + debug(' >>> Issue', item['html_url']) + reporters.add(item['user']['login']) + + if len(data['items']) < per_page: + break + page += 1 + + return reporters + + +def merge_all_the_people(release: str, contributors: People, committers: FullNames, reporters: GitHubLogins) -> None: + """ + >>> contributors = {'Alice': new_person(github='alice', twitter='alice')} + >>> merge_all_the_people('2.6.0', contributors, {}, {}) + >>> contributors + {'Alice': {'committed': [], 'reported': [], 'github': 'alice', 'twitter': 'alice'}} + + >>> contributors = {'Bob': new_person(github='bob', twitter='bob')} + >>> merge_all_the_people('2.6.0', contributors, {'Bob'}, {'bob'}) + >>> contributors + {'Bob': {'committed': ['2.6.0'], 'reported': ['2.6.0'], 'github': 'bob', 'twitter': 'bob'}} + + >>> contributors = {'Charlotte': new_person(github='charlotte', twitter='charlotte', committed=['2.5.0'], reported=['2.5.0'])} + >>> merge_all_the_people('2.6.0', contributors, {'Charlotte'}, {'charlotte'}) + >>> contributors + {'Charlotte': {'committed': ['2.5.0', '2.6.0'], 'reported': ['2.5.0', '2.6.0'], 'github': 'charlotte', 'twitter': 'charlotte'}} + + """ + # Update known contributors. + for name, details in contributors.items(): + if name in committers: + if release not in details['committed']: + details['committed'].append(release) + committers.remove(name) + if details['github'] in reporters: + if release not in details['reported']: + details['reported'].append(release) + reporters.remove(details['github']) + + # Add new committers. + for name in committers: + user_info = user(fullname=name) + contributors[name] = new_person( + github=user_info['login'], + twitter=user_info['twitter_username'], + committed=[release], + ) + if user_info['login'] in reporters: + contributors[name]['reported'].append(release) + reporters.remove(user_info['login']) + + # Add new reporters. + for github_username in reporters: + user_info = user(github_username=github_username) + contributors[user_info['name'] or user_info['login']] = new_person( + github=github_username, + twitter=user_info['twitter_username'], + reported=[release], + ) + + +def release_date(release: str) -> str: + date = check_output(['git', 'log', '-1', '--format=%ai', release], text=True).strip() + return datetime.strptime(date, '%Y-%m-%d %H:%M:%S %z').isoformat() + + +def load_awesome_people() -> People: + try: + with DB_FILE.open(encoding='utf-8') as fh: + return json.load(fh) + except (FileNotFoundError, ValueError): + return {} + + +def fetch(url: str, params: Optional[Dict[str, str]] = None) -> UserInfo: + headers = { + 'Accept': 'application/vnd.github.v3+json', + 'Authentication': f'token {GITHUB_TOKEN}' + } + for retry in range(1, 6): + debug(f'[{retry}/5]', f'{url = }', f'{params = }') + with requests.get(url, params=params, headers=headers) as req: + try: + req.raise_for_status() + except requests.exceptions.HTTPError as exc: + if exc.response.status_code == 403: + # 403 Client Error: rate limit exceeded for url: ... + now = int(datetime.utcnow().timestamp()) + xrate_limit_reset = int(exc.response.headers['X-RateLimit-Reset']) + wait = xrate_limit_reset - now + if wait > 20: + raise FinishedForNow() + debug(' !', 'Waiting', wait, 'seconds before another try ...') + sleep(wait) + continue + return req.json() + assert ValueError('Rate limit exceeded') + + +def new_person(**kwargs: str) -> Person: + data = deepcopy(DEFAULT_PERSON) + data.update(**kwargs) + return data + + +def user(fullname: Optional[str] = '', github_username: Optional[str] = '') -> UserInfo: + if github_username: + url = f'{API_URL}/users/{github_username}' + return fetch(url) + + url = f'{API_URL}/search/users' + for query in (f'fullname:{fullname}', f'user:{fullname}'): + params = { + 'q': f'repo:{REPO}/{OWNER} {query}', + 'per_page': 1, + } + user_info = fetch(url, params=params) + if user_info['items']: + user_url = user_info['items'][0]['url'] + return fetch(user_url) + + +def fetch_missing_users_details(people: People) -> None: + for name, details in people.items(): + if details['github'] and details['twitter']: + continue + user_info = user(github_username=details['github'], fullname=name) + if not details['github']: + details['github'] = user_info['login'] + if not details['twitter']: + details['twitter'] = user_info['twitter_username'] + + +def save_awesome_people(people: People) -> None: + with DB_FILE.open(mode='w', encoding='utf-8') as fh: + json.dump(people, fh, indent=4, sort_keys=True) + fh.write("\n") + + +def debug(*args: Any) -> None: + if os.getenv('DEBUG') == '1': + print(*args) + + +if __name__ == '__main__': + ret = 1 + try: + ret = main(*sys.argv[1:]) + except TypeError: + ret = 2 + print(f''' +Fetch contributors to a release. + +Usage: + python {sys.argv[0]} {sys.argv[0]} +Example: + python {sys.argv[0]} 2.4.0 2.5.0 + +Define the DEBUG=1 environment variable to enable verbose output. +''') + except KeyboardInterrupt: + ret = 255 + sys.exit(ret) diff --git a/docs/contributors/generate.py b/docs/contributors/generate.py new file mode 100644 index 0000000000..27470d5672 --- /dev/null +++ b/docs/contributors/generate.py @@ -0,0 +1,53 @@ +""" +Generate snippets to copy-paste. +""" +import sys + +from jinja2 import Template + +from fetch import HERE, load_awesome_people + +TPL_FILE = HERE / 'snippet.jinja2' + +HTTPIE_TEAM = { + 'claudiatd', + 'jakubroztocil', + 'jkbr', + 'isidentical' +} + +BOT_ACCOUNTS = { + 'dependabot-sr' +} + +IGNORE_ACCOUNTS = HTTPIE_TEAM | BOT_ACCOUNTS + + +def generate_snippets(release: str) -> str: + people = load_awesome_people() + contributors = { + name: details + for name, details in people.items() + if details['github'] not in IGNORE_ACCOUNTS + and (release in details['committed'] or release in details['reported']) + } + + template = Template(source=TPL_FILE.read_text(encoding='utf-8')) + output = template.render(contributors=contributors, release=release) + print(output) + return 0 + + +if __name__ == '__main__': + ret = 1 + try: + ret = generate_snippets(sys.argv[1]) + except (IndexError, TypeError): + ret = 2 + print(f''' +Generate snippets for contributors to a release. + +Usage: + python {sys.argv[0]} {sys.argv[0]} +''') + sys.exit(ret) diff --git a/docs/contributors/people.json b/docs/contributors/people.json new file mode 100644 index 0000000000..70514fcc2d --- /dev/null +++ b/docs/contributors/people.json @@ -0,0 +1,691 @@ +{ + "Aaron Miller": { + "committed": [], + "github": "aaronhmiller", + "reported": [ + "3.0.0" + ], + "twitter": "aaronmiller8" + }, + "Alexander Bogdanov": { + "committed": [], + "github": "ab-kily", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "Almad": { + "committed": [ + "2.5.0" + ], + "github": "Almad", + "reported": [ + "2.6.0", + "3.0.0" + ], + "twitter": "almadcz" + }, + "Andr\u00e1s Czig\u00e1ny": { + "committed": [], + "github": "andrascz", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "Annette Wilson": { + "committed": [], + "github": "annettejanewilson", + "reported": [ + "2.6.0", + "3.0.0" + ], + "twitter": null + }, + "Anton Emelyanov": { + "committed": [ + "2.5.0" + ], + "github": "king-menin", + "reported": [], + "twitter": null + }, + "Batuhan Taskaya": { + "committed": [ + "3.0.0", + "3.2.0" + ], + "github": "isidentical", + "reported": [ + "3.0.0", + "3.2.0" + ], + "twitter": "isidentical" + }, + "Brad Crittenden": { + "committed": [], + "github": "bac", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "Chad": { + "committed": [], + "github": "cythrawll", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "D8ger": { + "committed": [], + "github": "caofanCPU", + "reported": [ + "2.5.0" + ], + "twitter": null + }, + "Dave": { + "committed": [ + "2.6.0", + "3.0.0" + ], + "github": "davecheney", + "reported": [], + "twitter": "davecheney" + }, + "Dawid Ferenczy Rogo\u017ean": { + "committed": [], + "github": "ferenczy", + "reported": [ + "2.5.0" + ], + "twitter": "DawidFerenczy" + }, + "Ed Rooth": { + "committed": [], + "github": "sym3tri", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "Elena Lape": { + "committed": [ + "2.5.0" + ], + "github": "elenalape", + "reported": [], + "twitter": "elena_lape" + }, + "Ethan Mills": { + "committed": [ + "3.2.0" + ], + "github": "ethanmills", + "reported": [], + "twitter": null + }, + "Fabio Peruzzo": { + "committed": [], + "github": "peruzzof", + "reported": [ + "2.6.0", + "3.0.0" + ], + "twitter": null + }, + "F\u00fash\u0113ng": { + "committed": [], + "github": "lienide", + "reported": [ + "2.5.0" + ], + "twitter": null + }, + "Gabriel Cruz": { + "committed": [], + "github": "gmelodie", + "reported": [ + "3.0.0" + ], + "twitter": "gmelodiecruz" + }, + "Gaurav": { + "committed": [ + "3.0.0" + ], + "github": "gkcs", + "reported": [], + "twitter": null + }, + "Giampaolo Rodola": { + "committed": [], + "github": "giampaolo", + "reported": [ + "2.5.0" + ], + "twitter": null + }, + "Greg Myers": { + "committed": [ + "3.0.0" + ], + "github": "myersg86", + "reported": [], + "twitter": null + }, + "Hugh Williams": { + "committed": [], + "github": "hughpv", + "reported": [ + "2.5.0" + ], + "twitter": null + }, + "Ilya Sukhanov": { + "committed": [ + "2.5.0" + ], + "github": "IlyaSukhanov", + "reported": [ + "2.5.0" + ], + "twitter": null + }, + "Jakub Roztocil": { + "committed": [ + "2.5.0", + "2.6.0", + "3.0.0", + "3.2.0" + ], + "github": "jakubroztocil", + "reported": [ + "2.5.0", + "2.6.0", + "3.0.0" + ], + "twitter": "jakubroztocil" + }, + "Jan Bra\u0161na": { + "committed": [ + "3.0.0" + ], + "github": "janbrasna", + "reported": [], + "twitter": "janbrasna" + }, + "Jan Verbeek": { + "committed": [ + "2.5.0" + ], + "github": "blyxxyz", + "reported": [ + "3.0.0", + "3.2.0" + ], + "twitter": null + }, + "Jannik Vieten": { + "committed": [ + "2.5.0" + ], + "github": "exploide", + "reported": [], + "twitter": null + }, + "Jesper Holmberg": { + "committed": [], + "github": "strindberg", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "Kirill Krasnov": { + "committed": [], + "github": "Kirill", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "Marcel St\u00f6r": { + "committed": [ + "2.5.0" + ], + "github": "marcelstoer", + "reported": [], + "twitter": "frightanic" + }, + "Marco Seguri": { + "committed": [], + "github": "seguri", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "Mariano Ruiz": { + "committed": [], + "github": "mrsarm", + "reported": [ + "2.5.0" + ], + "twitter": "mrsarm82" + }, + "Mark Rosenbaum": { + "committed": [], + "github": "markrosenbaum", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "Micka\u00ebl Schoentgen": { + "committed": [ + "2.5.0", + "2.6.0", + "3.0.0" + ], + "github": "BoboTiG", + "reported": [ + "2.5.0", + "2.6.0", + "3.0.0" + ], + "twitter": "__tiger222__" + }, + "Mike DePalatis": { + "committed": [], + "github": "mivade", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "Miro Hron\u010dok": { + "committed": [ + "2.5.0", + "2.6.0", + "3.0.0" + ], + "github": "hroncok", + "reported": [], + "twitter": "hroncok" + }, + "Mohamed Daahir": { + "committed": [], + "github": "ducaale", + "reported": [ + "2.5.0", + "3.2.0" + ], + "twitter": null + }, + "Nanashi.": { + "committed": [], + "github": "sevenc-nanashi", + "reported": [ + "3.0.0" + ], + "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": { + "committed": [ + "2.6.0", + "3.0.0" + ], + "github": "om26er", + "reported": [ + "2.6.0", + "3.0.0" + ], + "twitter": "om26er" + }, + "Patrick Taylor": { + "committed": [], + "github": "pmeister", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "Paul Laffitte": { + "committed": [], + "github": "paullaffitte", + "reported": [ + "3.0.0" + ], + "twitter": "plaffitt" + }, + "Pavel Alexeev aka Pahan-Hubbitus": { + "committed": [], + "github": "Hubbitus", + "reported": [ + "2.5.0" + ], + "twitter": null + }, + "Roberto L\u00f3pez L\u00f3pez": { + "committed": [], + "github": "robertolopezlopez", + "reported": [ + "3.2.0" + ], + "twitter": null + }, + "Russell Shurts": { + "committed": [], + "github": "rshurts", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "Samuel Marks": { + "committed": [], + "github": "SamuelMarks", + "reported": [ + "2.5.0" + ], + "twitter": null + }, + "Sebastian Czech": { + "committed": [ + "3.0.0" + ], + "github": "sebastianczech", + "reported": [], + "twitter": "sebaczech" + }, + "Sullivan SENECHAL": { + "committed": [], + "github": "soullivaneuh", + "reported": [ + "2.5.0" + ], + "twitter": null + }, + "Thomas Klinger": { + "committed": [], + "github": "mosesontheweb", + "reported": [ + "2.5.0" + ], + "twitter": null + }, + "Vincent van \u2019t Zand": { + "committed": [], + "github": "vovtz", + "reported": [ + "2.6.0", + "3.0.0" + ], + "twitter": null + }, + "Vivaan Verma": { + "committed": [ + "3.0.0" + ], + "github": "doublevcodes", + "reported": [], + "twitter": "doublevcodes" + }, + "Vladimir Berkutov": { + "committed": [ + "3.0.0" + ], + "github": "dair-targ", + "reported": [], + "twitter": null + }, + "Will Rogers": { + "committed": [], + "github": "wjrogers", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "Yannic Schneider": { + "committed": [], + "github": "cynay", + "reported": [ + "2.5.0" + ], + "twitter": null + }, + "a1346054": { + "committed": [ + "2.5.0" + ], + "github": "a1346054", + "reported": [], + "twitter": null + }, + "arloan": { + "committed": [], + "github": "arloan", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "bl-ue": { + "committed": [ + "2.5.0" + ], + "github": "FiReBlUe45", + "reported": [], + "twitter": null + }, + "blueray453": { + "committed": [], + "github": "blueray453", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "claudiatd": { + "committed": [ + "2.6.0", + "3.0.0" + ], + "github": "claudiatd", + "reported": [], + "twitter": null + }, + "coldcoff": { + "committed": [], + "github": "coldcoff", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "dependabot[bot]": { + "committed": [ + "3.2.0" + ], + "github": "dependabot-sr", + "reported": [], + "twitter": null + }, + "dkreeft": { + "committed": [ + "2.6.0", + "3.0.0" + ], + "github": "dkreeft", + "reported": [], + "twitter": null + }, + "greg": { + "committed": [ + "3.0.0" + ], + "github": "gregkh", + "reported": [], + "twitter": null + }, + "henryhu712": { + "committed": [ + "2.5.0" + ], + "github": "henryhu712", + "reported": [], + "twitter": null + }, + "hosseingt": { + "committed": [ + "3.0.0" + ], + "github": "hosseingt", + "reported": [], + "twitter": null + }, + "jakubroztocil": { + "committed": [ + "2.6.0", + "3.0.0" + ], + "github": "jkbr", + "reported": [], + "twitter": null + }, + "josephworks": { + "committed": [], + "github": "josephworks", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "jungle-boogie": { + "committed": [], + "github": "jungle-boogie", + "reported": [ + "2.5.0" + ], + "twitter": null + }, + "luisuimi": { + "committed": [], + "github": "luisuimi", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "luzpaz": { + "committed": [ + "3.2.0" + ], + "github": "luzpaz", + "reported": [], + "twitter": null + }, + "nixbytes": { + "committed": [ + "2.5.0" + ], + "github": "nixbytes", + "reported": [], + "twitter": "linuxbyte3" + }, + "peterpt": { + "committed": [], + "github": "peterpt", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "qiulang": { + "committed": [], + "github": "qiulang", + "reported": [ + "2.5.0" + ], + "twitter": null + }, + "stonebig": { + "committed": [], + "github": "stonebig", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "whodidthis": { + "committed": [], + "github": "whodidthis", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "zhaohanqing95": { + "committed": [], + "github": "zhaohanqing95", + "reported": [ + "3.2.0" + ], + "twitter": null + }, + "zoulja": { + "committed": [], + "github": "zoulja", + "reported": [ + "3.0.0" + ], + "twitter": null + }, + "zwx00": { + "committed": [], + "github": "zwx00", + "reported": [ + "2.5.0" + ], + "twitter": null + }, + "\u5d14\u5c0f\u4e8c": { + "committed": [], + "github": "rogerdehe", + "reported": [ + "2.6.0", + "3.0.0" + ], + "twitter": null + }, + "\u9ec4\u6d77": { + "committed": [], + "github": "hh-in-zhuzhou", + "reported": [ + "2.6.0", + "3.0.0" + ], + "twitter": null + } +} diff --git a/docs/contributors/snippet.jinja2 b/docs/contributors/snippet.jinja2 new file mode 100644 index 0000000000..56e7d33fc1 --- /dev/null +++ b/docs/contributors/snippet.jinja2 @@ -0,0 +1,14 @@ + + +## Community contributions + +We’d like to thank these amazing people for their contributions to this release: +{% for name, details in contributors.items() -%} +- [{{ name }}](https://github.com/{{ details.github }}){{ '' if loop.last else '\n' }} +{%- endfor %} + + + +We’d like to thank these amazing people for their contributions to HTTPie {{ release }}: {% for name, details in contributors.items() if details.twitter -%} + @{{ details.twitter }}{{ '' if loop.last else ', ' }} +{%- endfor %} 🥧 diff --git a/docs/httpie-animation.gif b/docs/httpie-animation.gif new file mode 100644 index 0000000000..53e3cb84b3 Binary files /dev/null and b/docs/httpie-animation.gif differ diff --git a/docs/httpie-logo.svg b/docs/httpie-logo.svg new file mode 100644 index 0000000000..4a1f506151 --- /dev/null +++ b/docs/httpie-logo.svg @@ -0,0 +1 @@ + diff --git a/docs/installation/README.md b/docs/installation/README.md new file mode 100644 index 0000000000..8337663be4 --- /dev/null +++ b/docs/installation/README.md @@ -0,0 +1,5 @@ +Here we maintain a database of installation methods, from which we generate +the installation section in docs. If you’d like add or update an installation method, +edit [methods.yml](./methods.yml), do not edit the main docs directly. + +For HTTPie installation instructions see: . diff --git a/docs/installation/generate.py b/docs/installation/generate.py new file mode 100644 index 0000000000..0597a3a429 --- /dev/null +++ b/docs/installation/generate.py @@ -0,0 +1,85 @@ +import re +import sys +from pathlib import Path +from typing import Dict + +import yaml +from jinja2 import Template + +Database = Dict[str, dict] + +# Files +HERE = Path(__file__).parent +DB_FILE = HERE / 'methods.yml' +DOC_FILE = HERE.parent / 'README.md' +TPL_FILE = HERE / 'installation.jinja2' + +# Database keys +KEY_DOC_STRUCTURE = 'docs-structure' +KEY_TOOLS = 'tools' + +# Markers in-between content will be put. +MARKER_START = '
' +MARKER_END = '
' + + +def generate_documentation() -> str: + database = load_database() + structure = build_docs_structure(database) + template = Template(source=TPL_FILE.read_text(encoding='utf-8')) + output = template.render(structure=structure) + output = clean_template_output(output) + return output + + +def save_doc_file(content: str) -> None: + current_doc = load_doc_file() + marker_start = current_doc.find(MARKER_START) + len(MARKER_START) + assert marker_start > 0, 'cannot find the start marker' + marker_end = current_doc.find(MARKER_END, marker_start) + assert marker_start < marker_end, f'{marker_end=} < {marker_start=}' + updated_doc = ( + current_doc[:marker_start] + + '\n\n' + + content + + '\n\n' + + current_doc[marker_end:] + ) + if current_doc != updated_doc: + DOC_FILE.write_text(updated_doc, encoding='utf-8') + + +def build_docs_structure(database: Database): + tools = database[KEY_TOOLS] + assert len(tools) == len({tool['title'] for tool in tools.values()}), 'tool titles need to be unique' + tree = database[KEY_DOC_STRUCTURE] + structure = [] + for platform, tools_ids in tree.items(): + assert platform.isalnum(), f'{platform=} must be alphanumeric for generated links to work' + platform_tools = [tools[tool_id] for tool_id in tools_ids] + structure.append((platform, platform_tools)) + return structure + + +def clean_template_output(output): + output = '\n'.join(line.strip() for line in output.strip().splitlines()) + output = re.sub('\n{3,}', '\n\n', output) + return output + + +def load_database() -> Database: + return yaml.safe_load(DB_FILE.read_text(encoding='utf-8')) + + +def load_doc_file() -> str: + return DOC_FILE.read_text(encoding='utf-8') + + +def main() -> int: + content = generate_documentation() + save_doc_file(content) + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/docs/installation/installation.jinja2 b/docs/installation/installation.jinja2 new file mode 100644 index 0000000000..28671a7ed2 --- /dev/null +++ b/docs/installation/installation.jinja2 @@ -0,0 +1,37 @@ + +{% for platform, tools in structure %} + - [{{ platform }}](#{{ platform.lower() }}){% endfor %} {# <= keep `endfor` here to prevent unwanted `\n` #} + +{% for platform, tools in structure %} + + ### {{ platform }} + + {% for tool in tools %} + #### {{ tool.title }} + + {% if tool.note %} + {{ tool.note }} + {% endif %} + + {% if tool.links.setup %} + To install [{{ tool.name }}]({{ tool.links.homepage }}), see [its installation]({{ tool.links.setup }}). + {% endif %} + + ```bash + # Install httpie + $ {{ tool.commands.install|join('\n$ ') }} + ``` + + ```bash + # Upgrade httpie + $ {{ tool.commands.upgrade|join('\n$ ') }} + ``` + {% endfor %} + +{% endfor %} + diff --git a/docs/installation/methods.yml b/docs/installation/methods.yml new file mode 100644 index 0000000000..290b0e2436 --- /dev/null +++ b/docs/installation/methods.yml @@ -0,0 +1,197 @@ +# Database of HTTPie installation methods. Used to build the docs. +# +# We currently only include here methods for popular systems where we take care of the package, +# or have a good relationship with the maintainers. +# +# Each tool name should be unique (it becomes a linkable header). +# If a tools have `links.setup`, it also needs `links.homepage`. +# Some tools are available on multiple platforms, take into account when editing. +# + +docs-structure: + Universal: + - pypi + macOS: + - brew-mac + - port + Windows: + - chocolatey + Linux: + - apt + - dnf + - yum + - single-binary + - snap-linux + - brew-linux + - pacman + FreeBSD: + - pkg + +tools: + apt: + title: Debian and Ubuntu + note: 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. + name: APT + links: + homepage: https://en.wikipedia.org/wiki/APT_(software) + package: https://packages.debian.org/sid/web/httpie + commands: + install: + - curl -SsL https://packages.httpie.io/deb/KEY.gpg | sudo gpg --dearmor -o /usr/share/keyrings/httpie.gpg + # - 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: + - sudo apt update && sudo apt upgrade httpie + + brew-mac: + title: Homebrew + name: Homebrew + links: + homepage: https://brew.sh/ + setup: https://docs.brew.sh/Installation + package: https://formulae.brew.sh/formula/httpie + commands: + install: + - brew update + - brew install httpie + upgrade: + - brew update + - brew upgrade httpie + + brew-linux: + title: Linuxbrew + name: Linuxbrew + links: + homepage: https://docs.brew.sh/Homebrew-on-Linux + setup: https://docs.brew.sh/Homebrew-on-Linux#install + package: https://formulae.brew.sh/formula/httpie + commands: + install: + - brew update + - brew install httpie + upgrade: + - brew update + - brew upgrade httpie + + chocolatey: + title: Chocolatey + name: Chocolatey + links: + homepage: https://chocolatey.org/ + setup: https://chocolatey.org/install + package: https://community.chocolatey.org/packages/httpie/ + commands: + install: + - choco install httpie + upgrade: + - choco upgrade httpie + + dnf: + title: Fedora + name: DNF + links: + homepage: https://fedoraproject.org/wiki/DNF + package: https://src.fedoraproject.org/rpms/httpie + commands: + install: + - dnf install httpie + upgrade: + - dnf upgrade httpie + + pacman: + title: Arch Linux + name: pacman + note: Also works for other Arch-derived distributions like ArcoLinux, EndeavourOS, Artix Linux, etc. + links: + homepage: https://archlinux.org/pacman/ + package: https://archlinux.org/packages/community/any/httpie/ + commands: + install: + - pacman -Syu httpie + upgrade: + - pacman -Syu + + pkg: + title: FreshPorts + name: FreshPorts + links: + homepage: https://www.freebsd.org/cgi/man.cgi?query=pkg&sektion=8&n=1 + package: https://www.freshports.org/www/py-httpie/ + commands: + install: + - pkg install www/py-httpie + upgrade: + - pkg upgrade www/py-httpie + + port: + title: MacPorts + name: MacPorts + links: + homepage: https://www.macports.org/ + setup: https://www.macports.org/install.php + package: https://ports.macports.org/port/httpie/ + commands: + install: + - port selfupdate + - port install httpie + upgrade: + - port selfupdate + - port upgrade httpie + + pypi: + title: PyPI + name: pip + note: Please make sure you have Python 3.7 or newer (`python --version`). + links: + homepage: https://pypi.org/ + # setup: https://pip.pypa.io/en/stable/installation/ + package: https://pypi.org/project/httpie/ + commands: + install: + - python -m pip install --upgrade pip wheel + - python -m pip install httpie + upgrade: + - python -m pip install --upgrade pip wheel + - python -m pip install --upgrade httpie + + snap-linux: + title: Snapcraft (Linux) + name: Snapcraft + links: + homepage: https://snapcraft.io/ + setup: https://snapcraft.io/docs/installing-snapd + package: https://snapcraft.io/httpie + commands: + install: + - snap install httpie + upgrade: + - snap refresh httpie + + yum: + title: CentOS and RHEL + name: Yum + note: Also works for other RHEL-derived distributions like ClearOS, Oracle Linux, etc. + links: + homepage: http://yum.baseurl.org/ + package: https://src.fedoraproject.org/rpms/httpie + commands: + install: + - yum install epel-release + - yum install httpie + upgrade: + - 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 diff --git a/docs/markdownlint.rb b/docs/markdownlint.rb new file mode 100644 index 0000000000..cf2878c7b0 --- /dev/null +++ b/docs/markdownlint.rb @@ -0,0 +1,50 @@ +# Rules for + +# Load all rules by default +all + +# +# Tweak rules +# + +# MD002 First header should be a top level header +# Because we use HTML to hide them on the website. +exclude_rule 'MD002' + +# MD007 Allow unordered list indentation +exclude_rule 'MD007' + +# MD013 Line length +exclude_rule 'MD013' + +# MD014 Dollar signs used before commands without showing output +exclude_rule 'MD014' + +# MD028 Blank line inside blockquote +exclude_rule 'MD028' + +# MD012 Multiple consecutive blank lines +exclude_rule 'MD012' + +# Tell the linter to use ordered lists: +# 1. Foo +# 2. Bar +# 3. Baz +# +# Instead of: +# 1. Foo +# 1. Bar +# 1. Baz +rule 'MD029', :style => :ordered + +# MD033 Inline HTML +# TODO: Tweak elements when https://github.com/markdownlint/markdownlint/issues/118 will be done? +exclude_rule 'MD033' + +# MD034 Bare URL used +# TODO: Remove when https://github.com/markdownlint/markdownlint/issues/328 will be fixed. +exclude_rule 'MD034' + +# MD041 First line in file should be a top level header +# Because we use HTML to hide them on the website. +exclude_rule 'MD041' diff --git a/docs/packaging/README.md b/docs/packaging/README.md new file mode 100644 index 0000000000..e455742cc5 --- /dev/null +++ b/docs/packaging/README.md @@ -0,0 +1,47 @@ +# 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 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. + +## About + +You are looking at the HTTPie packaging documentation, where you will find valuable information about how we manage to release HTTPie to lots of OSes, including technical data that may be worth reading if you are a package maintainer. + +The overall release process starts simple: + +1. Bump the version identifiers in the following places: + - `httpie/__init__.py` + - `docs/packaging/windows-chocolatey/httpie.nuspec` + - `CHANGELOG.md` +2. Commit your changes and make a PR against the `master`. +3. Merge the PR, and tag the last commit with your version identifier. +4. Make a GitHub release (by copying the text in `CHANGELOG.md`) +5. Push that release to PyPI (dispatch the `Release PyPI` GitHub action). +6. Once PyPI is ready, push the release to the Snap, Homebrew and Chocolatey with their respective actions. +7. Go to the [`httpie/debian.httpie.io`](https://github.com/httpie/debian.httpie.io) repo and trigger the package index workflow. + +## Company-specific tasks + +- 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 HTTPie version bundled into [Termible](https://termible.io/) ([example](https://github.com/httpie/termible/pull/1)). + +## Finally, spread dowstream + +Find out how we do release new versions for each and every supported OS in the following table. +A more complete state of deployment can be found on [repology](https://repology.org/project/httpie/versions), including unofficial packages. + +| OS | Maintainer | +| -------------------------------------------: | -------------- | +| [Arch Linux, and derived](linux-arch/) | trusted person | +| [CentOS, RHEL, and derived](linux-centos/) | trusted person | +| [Fedora](linux-fedora/) | trusted person | +| [Debian, Ubuntu, and derived](linux-debian/) | **HTTPie** | +| [Homebrew, Linuxbrew](brew/) | **HTTPie** | +| [Snapcraft](snapcraft/) | **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/cli/issues/). diff --git a/docs/packaging/brew/README.md b/docs/packaging/brew/README.md new file mode 100644 index 0000000000..a06324ecd3 --- /dev/null +++ b/docs/packaging/brew/README.md @@ -0,0 +1,31 @@ +# HTTPie on Homebrew, and Linuxbrew + +Welcome to the documentation about **packaging HTTPie for Homebrew**. + +- If you do not know HTTPie, have a look [here](https://httpie.io/cli). +- If you are looking for HTTPie installation or upgrade instructions on Homebrew, then you can find them on [that page](https://httpie.io/docs#homebrew) ([that one](https://httpie.io/docs#linuxbrew) for Linuxbrew). +- If you are looking for technical information about the HTTPie packaging on Homebrew, then you are in a good place. + +## About + +This document contains technical details, where we describe how to create a patch for the latest HTTPie version for Homebrew. They apply to Linuxbrew as well. +We will discuss setting up the environment, installing development tools, installing and testing changes before submitting a patch downstream. + +## Overall process + +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. + +If it is needed to be done manually, the following command can be used: + +```console +$ brew bump-formula-pr httpie --version={TARGET_VERSION} +``` + +which will bump the formula, and create a PR against the package index. + +## Hacking + +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`) + diff --git a/docs/packaging/brew/httpie.rb b/docs/packaging/brew/httpie.rb new file mode 100644 index 0000000000..e249a5aa10 --- /dev/null +++ b/docs/packaging/brew/httpie.rb @@ -0,0 +1,84 @@ +class Httpie < Formula + include Language::Python::Virtualenv + + desc "User-friendly cURL replacement (command-line HTTP client)" + homepage "https://httpie.io/" + url "https://files.pythonhosted.org/packages/32/85/bb095699be20cc98731261cb80884e9458178f8fef2a38273530ce77c0a5/httpie-3.1.0.tar.gz" + sha256 "2e4a2040b84a912e65c01fb34f7aafe88cad2a3af2da8c685ca65080f376feda" + license "BSD-3-Clause" + head "https://github.com/httpie/cli.git", branch: "master" + + bottle do + sha256 cellar: :any_skip_relocation, arm64_monterey: "9bb6e8c1ef5ba8b019ddedd7e908dd2174da695351aa9a238dfb28b0f57ef005" + sha256 cellar: :any_skip_relocation, arm64_big_sur: "47ffccd3241155d863e1b4f6259d538a34d42a0cdeed8152bda257ee607b51be" + sha256 cellar: :any_skip_relocation, monterey: "dc4a04cb05a9cd1bfa6a632a0e4a21975905954af54ece41f9050c52474267be" + sha256 cellar: :any_skip_relocation, big_sur: "ae469e37864e967e0fd99fba15a78e719dcb351b462f98f3843c78ed1473df6d" + sha256 cellar: :any_skip_relocation, catalina: "291a3eaecb2a2cc845c1652686a9a14b21053d7e3a7d0115245b2150ca2e199e" + sha256 cellar: :any_skip_relocation, x86_64_linux: "710836e27c44c8e3ad181d668f4a9f78c4cb4c355d7b148a397599a7cd42713d" + end + + depends_on "python@3.10" + + resource "certifi" do + url "https://files.pythonhosted.org/packages/6c/ae/d26450834f0acc9e3d1f74508da6df1551ceab6c2ce0766a593362d6d57f/certifi-2021.10.8.tar.gz" + sha256 "78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872" + end + + resource "charset-normalizer" do + url "https://files.pythonhosted.org/packages/56/31/7bcaf657fafb3c6db8c787a865434290b726653c912085fbd371e9b92e1c/charset-normalizer-2.0.12.tar.gz" + sha256 "2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597" + end + + resource "defusedxml" do + url "https://files.pythonhosted.org/packages/0f/d5/c66da9b79e5bdb124974bfe172b4daf3c984ebd9c2a06e2b8a4dc7331c72/defusedxml-0.7.1.tar.gz" + sha256 "1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69" + end + + resource "idna" do + url "https://files.pythonhosted.org/packages/62/08/e3fc7c8161090f742f504f40b1bccbfc544d4a4e09eb774bf40aafce5436/idna-3.3.tar.gz" + sha256 "9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" + end + + resource "multidict" do + url "https://files.pythonhosted.org/packages/fa/a7/71c253cdb8a1528802bac7503bf82fe674367e4055b09c28846fdfa4ab90/multidict-6.0.2.tar.gz" + sha256 "5ff3bd75f38e4c43f1f470f2df7a4d430b821c4ce22be384e1459cb57d6bb013" + end + + resource "Pygments" do + url "https://files.pythonhosted.org/packages/94/9c/cb656d06950268155f46d4f6ce25d7ffc51a0da47eadf1b164bbf23b718b/Pygments-2.11.2.tar.gz" + sha256 "4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a" + end + + resource "PySocks" do + url "https://files.pythonhosted.org/packages/bd/11/293dd436aea955d45fc4e8a35b6ae7270f5b8e00b53cf6c024c83b657a11/PySocks-1.7.1.tar.gz" + sha256 "3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0" + end + + resource "requests" do + url "https://files.pythonhosted.org/packages/60/f3/26ff3767f099b73e0efa138a9998da67890793bfa475d8278f84a30fec77/requests-2.27.1.tar.gz" + sha256 "68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61" + end + + resource "requests-toolbelt" do + url "https://files.pythonhosted.org/packages/28/30/7bf7e5071081f761766d46820e52f4b16c8a08fef02d2eb4682ca7534310/requests-toolbelt-0.9.1.tar.gz" + sha256 "968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0" + end + + resource "urllib3" do + url "https://files.pythonhosted.org/packages/b0/b1/7bbf5181f8e3258efae31702f5eab87d8a74a72a0aa78bc8c08c1466e243/urllib3-1.26.8.tar.gz" + sha256 "0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c" + end + + def install + virtualenv_install_with_resources + end + + test do + assert_match version.to_s, shell_output("#{bin}/httpie --version") + assert_match version.to_s, shell_output("#{bin}/https --version") + assert_match version.to_s, shell_output("#{bin}/http --version") + + raw_url = "https://raw.githubusercontent.com/Homebrew/homebrew-core/HEAD/Formula/httpie.rb" + assert_match "PYTHONPATH", shell_output("#{bin}/http --ignore-stdin #{raw_url}") + end +end diff --git a/docs/packaging/brew/update.sh b/docs/packaging/brew/update.sh new file mode 100755 index 0000000000..7c26ae0ba4 --- /dev/null +++ b/docs/packaging/brew/update.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -xe + +rm -f httpie.rb +http --download https://raw.githubusercontent.com/Homebrew/homebrew-core/master/Formula/httpie.rb diff --git a/docs/packaging/linux-arch/PKGBUILD b/docs/packaging/linux-arch/PKGBUILD new file mode 100644 index 0000000000..5ecec154e8 --- /dev/null +++ b/docs/packaging/linux-arch/PKGBUILD @@ -0,0 +1,47 @@ +# Maintainer: Jelle van der Waa +# Maintainer: daurnimator +# Contributor: Daniel Micay +# Contributor: Thomas Weißschuh + +pkgname=httpie +pkgver=2.6.0 +pkgrel=1 +pkgdesc="human-friendly CLI HTTP client for the API era" +url="https://github.com/httpie/cli" +depends=('python-defusedxml' + 'python-pygments' + 'python-pysocks' + 'python-requests' + 'python-requests-toolbelt' + 'python-charset-normalizer') +makedepends=('python-setuptools') +checkdepends=('python-pytest' + 'python-pytest-httpbin' + 'python-responses') +conflicts=(python-httpie) +replaces=(python-httpie python2-httpie) +license=('BSD') +arch=('any') +source=($pkgname-$pkgver.tar.gz::"https://github.com/httpie/cli/archive/$pkgver.tar.gz") +sha256sums=('3bcd9a8cb2b11299da12d3af36c095c6d4b665e41c395898a07f1ae4d99fc14a') + +build() { + cd $pkgname-$pkgver + python3 setup.py build +} + +package() { + cd $pkgname-$pkgver + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/httpie/LICENSE" + python3 setup.py install --root="$pkgdir" --optimize=1 + + # Fix upstream, include them in MANIFEST.in and use data_files in setup.py to install them automatically + # TODO: add zsh support + install -Dm644 extras/httpie-completion.bash "$pkgdir"/usr/share/bash-completion/completions/http + install -Dm644 extras/httpie-completion.fish "$pkgdir"/usr/share/fish/vendor_completions.d/http.fish +} + +check() { + cd $pkgname-$pkgver + PYTHONDONTWRITEBYTECODE=1 pytest tests +} diff --git a/docs/packaging/linux-arch/README.md b/docs/packaging/linux-arch/README.md new file mode 100644 index 0000000000..36c985350b --- /dev/null +++ b/docs/packaging/linux-arch/README.md @@ -0,0 +1,22 @@ +# HTTPie on Arch Linux, and derived + +Welcome to the documentation about **packaging HTTPie for Arch Linux**. + +- If you do not know HTTPie, have a look [here](https://httpie.io/cli). +- If you are looking for HTTPie installation or upgrade instructions on Arch Linux, then you can find them on [that page](https://httpie.io/docs#arch-linux). +- If you are looking for technical information about the HTTPie packaging on Arch Linux, then you are in a good place. + +## About + +This document contains technical details, where we describe how to create a patch for the latest HTTPie version for Arch Linux. They apply to Arch-derived distributions as well, like ArcoLinux, EndeavourOS, Artix Linux, etc. +We will discuss setting up the environment, installing development tools, installing and testing changes before submitting a patch downstream. + +## Overall process + +Note: Sending patches downstream does not seem easy. We failed to find where is located the package file on . So we are relying on the last maintainer, daurnimator, and it works pretty well so far. + +Check and if the version is outdated, simply [report it](https://archlinux.org/packages/community/any/httpie/flag/). + +## Hacking + +Left blank on purpose, we will fill that section when we will have access to the downstream repository. diff --git a/docs/packaging/linux-centos/README.md b/docs/packaging/linux-centos/README.md new file mode 100644 index 0000000000..be809ea2a3 --- /dev/null +++ b/docs/packaging/linux-centos/README.md @@ -0,0 +1,26 @@ +# HTTPie on CentOS, RHEL, and derived + +Welcome to the documentation about **packaging HTTPie for CentOS and RHEL**. + +- If you do not know HTTPie, have a look [here](https://httpie.io/cli). +- If you are looking for HTTPie installation or upgrade instructions on CentOS, then you can find them on [that page](https://httpie.io/docs#centos-and-rhel). +- If you are looking for technical information about the HTTPie packaging on CentOS, then you are in a good place. + +## About + +This document contains technical details, where we describe how to create a patch for the latest HTTPie version for CentOS. They apply to RHEL as well, and any RHEL-derived distributions like ClearOS, Oracle Linux, etc. +We will discuss setting up the environment, installing development tools, installing and testing changes before submitting a patch downstream. + +The current maintainer is [Mikel Olasagasti](https://github.com/kaxero). + +## Overall process + +Same as [Fedora](../linux-fedora/README.md#overall-process). + +## Q/A with Mikel + +Q: What should we do to help seeing a new version on CentOS? + +A: When a new release is published Miro and I get notified by [release-monitoring](https://release-monitoring.org/project/1337/), that fills a BugZilla ticket reporting a new version being available. + +The system also tries to create a simple patch to update the spec file, but in the case of CentOS it needs some manual revision. For example for 2.5.0 `defuxedxml` dep is required. Maybe with CentOS-9 and some new macros that are available now in Fedora it can be automated same way. But even the bump can be automated, maintainers should check for license changes, new binaries/docs/ and so on. diff --git a/docs/packaging/linux-debian/README.md b/docs/packaging/linux-debian/README.md new file mode 100644 index 0000000000..4ec0c0e24e --- /dev/null +++ b/docs/packaging/linux-debian/README.md @@ -0,0 +1,26 @@ +# HTTPie on Debian, Ubuntu, and derived + +Welcome to the documentation about **packaging HTTPie for Debian GNU/Linux**. + +- If you do not know HTTPie, have a look [here](https://httpie.io/cli). +- If you are looking for HTTPie installation or upgrade instructions on Debian GNU/Linux, then you can find them on [that page](https://httpie.io/docs#debian-and-ubuntu). +- If you are looking for technical information about the HTTPie packaging on Debian GNU/Linux, then you are in a good place. + +## About + +This document contains technical details, where we describe how to create a patch for the latest HTTPie version for Debian GNU/Linux. They apply to Ubuntu as well, and any 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. +We will discuss setting up the environment, installing development tools, installing and testing changes before submitting a patch downstream. + +We create the standalone binaries (see this [for more details](../../../extras/packaging/linux/)) and package them with +[FPM](https://github.com/jordansissel/fpm)'s `dir` mode. The core `http`/`https` commands don't have any dependencies, but the `httpie` +command (due to the underlying `httpie cli plugins` interface) explicitly depends to the system Python (through `python3`/`python3-pip`). + +## Overall process + +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. + +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 +and trigger the [`Update Index`](https://github.com/httpie/debian.httpie.io/actions/workflows/update-index.yml) action. It will automatically +scrape all new debian packages from the release assets, properly update the indexes and create a new PR ([an example](https://github.com/httpie/debian.httpie.io/pull/1)) +which then will become active when merged. diff --git a/docs/packaging/linux-fedora/README.md b/docs/packaging/linux-fedora/README.md new file mode 100644 index 0000000000..e8bcf99039 --- /dev/null +++ b/docs/packaging/linux-fedora/README.md @@ -0,0 +1,48 @@ +# HTTPie on Fedora + +Welcome to the documentation about **packaging HTTPie for Fedora**. + +- If you do not know HTTPie, have a look [here](https://httpie.io/cli). +- If you are looking for HTTPie installation or upgrade instructions on Fedora, then you can find them on [that page](https://httpie.io/docs#fedora). +- If you are looking for technical information about the HTTPie packaging on Fedora, then you are in a good place. + +## About + +This document contains technical details, where we describe how to create a patch for the latest HTTPie version for Fedora. +We will discuss setting up the environment, installing development tools, installing and testing changes before submitting a patch downstream. + +The current maintainer is [Miro Hrončok](https://github.com/hroncok). + +## Overall process + +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. + +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. + +It is also possible to follow [user feedbacks](https://bodhi.fedoraproject.org/updates/?packages=httpie) for all builds. + +## Q/A with Miro + +Q: What would the command to install the latest stable version look like? + +A: Assuming the latest stable version is already propagated to Fedora: + +```bash +# Note that yum is an alias to dnf. +$ sudo dnf install httpie +``` + +Q: Will dnf/yum upgrade then update to the latest? + +A: Yes, assuming the same as above. + +Q: Are new versions backported automatically? + +A: No. The process is: + +1. A new HTTPie release is created on Github. +2. A pull request for Fedora `rawhide` (the development version of Fedora, currently Fedora 36) is created. +3. A Fedora packager (usually Miro) sanity checks the pull request and merges, builds. HTTPie is updated in `rawhide` within 24 hours (sometimes more, for unrelated issues). +4. A Fedora packager decides whether the upgrade is suitable for stable Fedora releases (currently 35, 34, 33), if so, merges the changes there. +5. (if the above is yes) The new version of HTTPie lands in `updates-testing` repo where it waits for user feedback and lands within ~1 week for broad availability. diff --git a/docs/packaging/linux-fedora/httpie.spec.txt b/docs/packaging/linux-fedora/httpie.spec.txt new file mode 100644 index 0000000000..3414b188c1 --- /dev/null +++ b/docs/packaging/linux-fedora/httpie.spec.txt @@ -0,0 +1,270 @@ +Name: httpie +Version: 3.1.0 +Release: 1%{?dist} +Summary: A Curl-like tool for humans + +License: BSD +URL: https://httpie.org/ +Source0: https://github.com/httpie/cli/archive/%{version}/%{name}-%{version}.tar.gz + +BuildArch: noarch + +BuildRequires: python3-devel +BuildRequires: pyproject-rpm-macros + +BuildRequires: help2man + +%description +HTTPie is a CLI HTTP utility built out of frustration with existing tools. The +goal is to make CLI interaction with HTTP-based services as human-friendly as +possible. + +HTTPie does so by providing an http command that allows for issuing arbitrary +HTTP requests using a simple and natural syntax and displaying colorized +responses. + + +%prep +%autosetup -p1 + + +%generate_buildrequires +%pyproject_buildrequires -rx test + + +%build +%pyproject_wheel + + +%install +%pyproject_install +%pyproject_save_files httpie + +# Bash completion +mkdir -p %{buildroot}%{_datadir}/bash-completion/completions +cp -a extras/httpie-completion.bash %{buildroot}%{_datadir}/bash-completion/completions/http +ln -s ./http %{buildroot}%{_datadir}/bash-completion/completions/https + +# Fish completion +mkdir -p %{buildroot}%{_datadir}/fish/vendor_completions.d/ +cp -a extras/httpie-completion.fish %{buildroot}%{_datadir}/fish/vendor_completions.d/http.fish +ln -s ./http.fish %{buildroot}%{_datadir}/fish/vendor_completions.d/https.fish + + +# Generate man pages for everything +export PYTHONPATH=%{buildroot}%{python3_sitelib} +mkdir -p %{buildroot}%{_mandir}/man1 +help2man %{buildroot}%{_bindir}/http > %{buildroot}%{_mandir}/man1/http.1 +help2man %{buildroot}%{_bindir}/https > %{buildroot}%{_mandir}/man1/https.1 +help2man %{buildroot}%{_bindir}/httpie > %{buildroot}%{_mandir}/man1/httpie.1 + + +%check +%pytest -v + + +%files -f %{pyproject_files} +%doc README.md +%license LICENSE +%{_bindir}/http +%{_bindir}/https +%{_bindir}/httpie +%{_mandir}/man1/http.1* +%{_mandir}/man1/https.1* +%{_mandir}/man1/httpie.1* +# we co-own the entire directory structures for bash/fish completion to avoid a dependency +%{_datadir}/bash-completion/ +%{_datadir}/fish/ + + +%changelog +* Tue Mar 08 2022 Miro Hrončok - 3.1.0-1 +- Update to 3.1.0 +- Fixes: rhbz#2061597 + +* Mon Jan 24 2022 Miro Hrončok - 3.0.2-1 +- Update to 3.0.2 +- Fixes: rhbz#2044572 + +* Mon Jan 24 2022 Miro Hrončok - 3.0.1-1 +- Update to 3.0.1 +- Fixes: rhbz#2044058 + +* Fri Jan 21 2022 Miro Hrončok - 3.0.0-1 +- Update to 3.0.0 +- Fixes: rhbz#2043680 + +* Thu Jan 20 2022 Fedora Release Engineering - 2.6.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_36_Mass_Rebuild + +* Fri Oct 15 2021 Miro Hrončok - 2.6.0-1 +- Update to 2.6.0 +- Fixes: rhbz#2014022 + +* Tue Sep 07 2021 Miro Hrončok - 2.5.0-1 +- Update to 2.5.0 +- Fixes: rhbz#2001693 + +* Thu Jul 22 2021 Fedora Release Engineering - 2.4.0-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_35_Mass_Rebuild + +* Fri Jun 04 2021 Python Maint - 2.4.0-3 +- Rebuilt for Python 3.10 + +* Thu May 27 2021 Miro Hrončok - 2.4.0-2 +- Add Bash and Fish completion +- Fixes rhbz#1834441 +- Run tests on build time + +* Wed Mar 24 2021 Mikel Olasagasti Uranga - 2.4.0-1 +- Update to 2.4.0 +- Use pypi_source macro + +* Tue Jan 26 2021 Fedora Release Engineering - 2.3.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + +* Thu Jan 21 2021 Nils Philippsen - 2.3.0-2 +- use macros for Python dependencies +- add missing Python dependencies needed for running help2man +- remove manual Python dependencies +- discard stderr when running help2man + +* Thu Dec 24 2020 Nils Philippsen - 2.3.0-1 +- version 2.3.0 +- Python 2 is no more +- use %%autosetup and Python build macros +- remove EL7-isms +- explicitly require sed for building + +* Tue Jul 28 2020 Fedora Release Engineering - 1.0.3-4 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_33_Mass_Rebuild + +* Tue May 26 2020 Miro Hrončok - 1.0.3-3 +- Rebuilt for Python 3.9 + +* Wed Jan 29 2020 Fedora Release Engineering - 1.0.3-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_32_Mass_Rebuild + +* Mon Sep 30 2019 Rick Elrod - 1.0.3-1 +- Latest upstream + +* Mon Aug 19 2019 Miro Hrončok - 0.9.4-15 +- Rebuilt for Python 3.8 + +* Thu Jul 25 2019 Fedora Release Engineering - 0.9.4-14 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_31_Mass_Rebuild + +* Fri Feb 01 2019 Fedora Release Engineering - 0.9.4-13 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_30_Mass_Rebuild + +* Fri Jul 13 2018 Fedora Release Engineering - 0.9.4-12 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_29_Mass_Rebuild + +* Tue Jun 19 2018 Miro Hrončok - 0.9.4-11 +- Rebuilt for Python 3.7 + +* Wed Feb 07 2018 Fedora Release Engineering - 0.9.4-10 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Wed Jul 26 2017 Fedora Release Engineering - 0.9.4-9 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Mar 10 2017 Ralph Bean - 0.9.4-8 +- Fix help2man usage with python3. + https://bugzilla.redhat.com/show_bug.cgi?id=1430733 + +* Mon Feb 27 2017 Ralph Bean - 0.9.4-7 +- Fix missing Requires. https://bugzilla.redhat.com/show_bug.cgi?id=1417730 + +* Fri Feb 10 2017 Fedora Release Engineering - 0.9.4-6 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Mon Jan 2 2017 Ricky Elrod - 0.9.4-5 +- Add missing Obsoletes. + +* Mon Jan 2 2017 Ricky Elrod - 0.9.4-4 +- Nuke python-version-specific subpackages. Just use py3 if we can. + +* Mon Dec 19 2016 Miro Hrončok - 0.9.4-3 +- Rebuild for Python 3.6 + +* Tue Jul 19 2016 Fedora Release Engineering - 0.9.4-2 +- https://fedoraproject.org/wiki/Changes/Automatic_Provides_for_Python_RPM_Packages + +* Tue Jul 05 2016 Ricky Elrod - 0.9.4-1 +- Update to latest upstream. + +* Fri Jun 03 2016 Ricky Elrod - 0.9.3-4 +- Add proper Obsoletes for rhbz#1329226. + +* Wed Feb 03 2016 Fedora Release Engineering - 0.9.3-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Mon Jan 04 2016 Ralph Bean - 0.9.3-2 +- Modernize python macros and subpackaging. +- Move LICENSE to %%license macro. +- Make python3 the default on modern Fedora. + +* Mon Jan 04 2016 Ralph Bean - 0.9.3-1 +- new version + +* Tue Nov 10 2015 Fedora Release Engineering - 0.9.2-3 +- Rebuilt for https://fedoraproject.org/wiki/Changes/python3.5 + +* Wed Jun 17 2015 Fedora Release Engineering - 0.9.2-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Thu Mar 26 2015 Ricky Elrod - 0.9.2-1 +- Latest upstream release. + +* Sat Jun 07 2014 Fedora Release Engineering - 0.8.0-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Wed May 28 2014 Kalev Lember - 0.8.0-2 +- Rebuilt for https://fedoraproject.org/wiki/Changes/Python_3.4 + +* Fri Jan 31 2014 Ricky Elrod - 0.8.0-1 +- Latest upstream release. + +* Fri Oct 4 2013 Ricky Elrod - 0.7.2-2 +- Add in patch to work without having python-requests 2.0.0. + +* Sat Sep 28 2013 Ricky Elrod - 0.7.2-1 +- Latest upstream release. + +* Thu Sep 5 2013 Ricky Elrod - 0.6.0-7 +- Only try building the manpage on Fedora, since RHEL's help2man doesn't + have the --no-discard-stderr flag. + +* Thu Sep 5 2013 Ricky Elrod - 0.6.0-6 +- Loosen the requirement on python-pygments. + +* Sat Aug 03 2013 Fedora Release Engineering - 0.6.0-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild + +* Tue Jul 2 2013 Ricky Elrod - 0.6.0-4 +- python-requests 1.2.3 exists in rawhide now. + +* Sun Jun 30 2013 Ricky Elrod - 0.6.0-3 +- Patch to use python-requests 1.1.0 for now. + +* Sat Jun 29 2013 Ricky Elrod - 0.6.0-2 +- Update to latest upstream release. + +* Mon Apr 29 2013 Ricky Elrod - 0.5.0-2 +- Fix changelog messup. + +* Mon Apr 29 2013 Ricky Elrod - 0.5.0-1 +- Update to latest upstream release. + +* Mon Apr 8 2013 Ricky Elrod - 0.4.1-3 +- Fix manpage generation by exporting PYTHONPATH. + +* Tue Mar 26 2013 Ricky Elrod - 0.4.1-2 +- Include Python3 support, and fix other review blockers. + +* Mon Mar 11 2013 Ricky Elrod - 0.4.1-1 +- Update to latest upstream release + +* Thu Jul 19 2012 Ricky Elrod - 0.2.5-1 +- Initial build. diff --git a/docs/packaging/linux-fedora/update.sh b/docs/packaging/linux-fedora/update.sh new file mode 100755 index 0000000000..773bef336c --- /dev/null +++ b/docs/packaging/linux-fedora/update.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -xe + +rm -f httpie.spec.txt +https --download src.fedoraproject.org/rpms/httpie/raw/rawhide/f/httpie.spec -o httpie.spec.txt diff --git a/docs/packaging/mac-ports/Portfile b/docs/packaging/mac-ports/Portfile new file mode 100644 index 0000000000..6d113727d3 --- /dev/null +++ b/docs/packaging/mac-ports/Portfile @@ -0,0 +1,50 @@ +# -*- coding: utf-8; mode: tcl; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8::et:sw=4:ts=4:sts=4 + +PortSystem 1.0 +PortGroup github 1.0 +PortGroup python 1.0 + +github.setup httpie httpie 2.6.0 + +maintainers {g5pw @g5pw} openmaintainer +categories net +description Modern, user-friendly command-line HTTP client for the API era +long_description HTTPie (pronounced aych-tee-tee-pie) is a command line HTTP \ + client. Its goal is to make CLI interaction with web \ + services as human-friendly as possible. It provides a simple \ + http command that allows for sending arbitrary HTTP requests \ + using a simple and natural syntax, and displays colorized \ + responses. HTTPie can be used for testing, debugging, and \ + generally interacting with HTTP servers. +platforms darwin +license BSD +homepage https://httpie.io/ + +variant python37 conflicts python36 python38 python39 python310 description "Use Python 3.7" {} +variant python38 conflicts python36 python37 python39 python310 description "Use Python 3.8" {} +variant python39 conflicts python36 python37 python38 python310 description "Use Python 3.9" {} +variant python310 conflicts python36 python37 python38 python39 description "Use Python 3.10" {} + +if {[variant_isset python37]} { + python.default_version 37 +} elseif {[variant_isset python39]} { + python.default_version 39 +} elseif {[variant_isset python310]} { + python.default_version 310 +} else { + default_variants +python38 + python.default_version 38 +} + +depends_lib-append port:py${python.version}-requests \ + port:py${python.version}-requests-toolbelt \ + port:py${python.version}-pygments \ + port:py${python.version}-socks \ + port:py${python.version}-charset-normalizer \ + port:py${python.version}-defusedxml + +checksums rmd160 07b1d1592da1c505ed3ee4ef3b6056215e16e9ff \ + sha256 63cf104bf3552305c68a74f16494a90172b15296610a875e17918e5e36373c0b \ + size 1133491 + +python.link_binaries_suffix diff --git a/docs/packaging/mac-ports/README.md b/docs/packaging/mac-ports/README.md new file mode 100644 index 0000000000..792638789f --- /dev/null +++ b/docs/packaging/mac-ports/README.md @@ -0,0 +1,40 @@ +# HTTPie on MacPorts + +Welcome to the documentation about **packaging HTTPie for MacPorts**. + +- If you do not know HTTPie, have a look [here](https://httpie.io/cli). +- If you are looking for HTTPie installation or upgrade instructions on MacPorts, then you can find them on [that page](https://httpie.io/docs#macports). +- If you are looking for technical information about the HTTPie packaging on MacPorts, then you are in a good place. + +## About + +This document contains technical details, where we describe how to create a patch for the latest HTTPie version for MacPorts. +We will discuss setting up the environment, installing development tools, installing and testing changes before submitting a patch downstream. + +## Overall process + +Open a pull request to update the [downstream file](https://github.com/macports/macports-ports/blob/master/net/httpie/Portfile) ([example](https://github.com/macports/macports-ports/pull/12583)). + +- Here is how to calculate the size and checksums (replace `2.5.0` with the correct version): + + ```bash + # Download the archive + $ wget https://api.github.com/repos/httpie/cli/tarball/2.5.0 + + # Size + $ stat --printf="%s\n" 2.5.0 + 1105185 + + # Checksums + $ openssl dgst -rmd160 2.5.0 + RIPEMD160(2.5.0)= 88d227d52199c232c0ddf704a219d1781b1e77ee + $ openssl dgst -sha256 2.5.0 + SHA256(2.5.0)= 00c4b7bbe7f65abe1473f37b39d9d9f8f53f44069a430ad143a404c01c2179fc + ``` + +- The commit message must be `httpie: update to XXX`. +- The commit must be signed-off (`git commit -s`). + +## Hacking + +:construction: Work in progress. diff --git a/docs/packaging/snapcraft/README.md b/docs/packaging/snapcraft/README.md new file mode 100644 index 0000000000..e7c37c4782 --- /dev/null +++ b/docs/packaging/snapcraft/README.md @@ -0,0 +1,60 @@ +# HTTPie on Snapcraft + +Welcome to the documentation about **packaging HTTPie for Snapcraft**. + +- If you do not know HTTPie, have a look [here](https://httpie.io/cli). +- If you are looking for HTTPie installation or upgrade instructions on Snapcraft, then you can find them on [that page](https://httpie.io/docs#snapcraft-linux) ([that one](https://httpie.io/docs#snapcraft-macos) for macOS). +- If you are looking for technical information about the HTTPie packaging on Snapcraft, then you are in a good place. + +## About + +This document contains technical details, where we describe how to create a patch for the latest HTTPie version for Snapcraft. They apply to Snapcraft on Linux, macOS, and Windows. +We will discuss setting up the environment, installing development tools, installing and testing changes before submitting a patch downstream. + +## Overall process + +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: + +- Edge +- Beta +- Candidate +- Stable + +If a push to any of them fail, all the release tasks for the following channels will be cancelled so that the +release manager can look into the underlying cause. + +## Hacking + +Launch the docker image: + +```bash +docker pull ubuntu/latest +docker run -it --rm ubuntu/latest +``` + +From inside the container: + +```bash +# Clone +git clone --depth=1 https://github.com/httpie/cli.git +cd httpie + +# Build +export SNAPCRAFT_BUILD_ENVIRONMENT_CPU=8 +export SNAPCRAFT_BUILD_ENVIRONMENT_MEMORY=16G +snapcraft --debug + +# Install +sudo snap install --dangerous httpie_XXX_amd64.snap + +# Test +httpie.http --version +httpie.https --version +# Auto-aliases cannot be tested when installing a snap outside the store. +# http --version +# https --version + +# Remove +sudo snap remove httpie +``` diff --git a/docs/packaging/windows-chocolatey/README.md b/docs/packaging/windows-chocolatey/README.md new file mode 100644 index 0000000000..33c5d35f11 --- /dev/null +++ b/docs/packaging/windows-chocolatey/README.md @@ -0,0 +1,50 @@ +# HTTPie on Chocolatey + +Welcome to the documentation about **packaging HTTPie for Chocolatey**. + +- If you do not know HTTPie, have a look [here](https://httpie.io/cli). +- If you are looking for HTTPie installation or upgrade instructions on Chocolatey, then you can find them on [that page](https://httpie.io/docs#chocolatey). +- If you are looking for technical information about the HTTPie packaging on Chocolatey, then you are in a good place. + +## About + +This document contains technical details, where we describe how to create a patch for the latest HTTPie version for Chocolatey. +We will discuss setting up the environment, installing development tools, installing and testing changes before submitting a patch downstream. + +## Overall process + +After having successfully [built and tested](#hacking) the package, either trigger the +[`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: + +```bash +# Replace 2.5.0 with the correct version +choco push httpie.2.5.0.nupkg -s https://push.chocolatey.org/ --api-key=API_KEY +``` + +Be aware that it might take multiple days until the release is approved, sine it goes through multiple +sets of reviews (some of them are done manually). + +## Hacking + +```bash +# Clone +git clone --depth=1 https://github.com/httpie/cli.git +cd httpie/docs/packaging/windows-chocolatey + +# Build +choco pack + +# Check metadata +choco info httpie -s . + +# Install +choco install httpie -y -dv -s "'.;https://community.chocolatey.org/api/v2/'" + +# Test +http --version +https --version + +# Remove +choco uninstall -y httpie +``` diff --git a/docs/packaging/windows-chocolatey/httpie.nuspec b/docs/packaging/windows-chocolatey/httpie.nuspec new file mode 100644 index 0000000000..1d8c69790a --- /dev/null +++ b/docs/packaging/windows-chocolatey/httpie.nuspec @@ -0,0 +1,50 @@ + + + + httpie + 3.2.2 + Modern, 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. +It comes with JSON support, syntax highlighting, persistent sessions, wget-like downloads, plugins, and more. + +The project's goal is to make CLI interaction with web services as human-friendly as possible. HTTPie is designed for testing, debugging, and generally interacting with APIs and HTTP servers. +The `http` and `https` commands allow for creating and sending arbitrary HTTP requests. They use simple and natural syntax and provide formatted and colorized output. + +Main features: + +- Built-in JSON support +- Colorized and formatted terminal output +- Sensible defaults for the API era +- Persistent sessions +- Forms and file uploads +- HTTPS, proxies, and authentication support +- Support for arbitrary request data and headers +- Wget-like downloads +- Extensions API +- Expressive and intuitive syntax +- Linux, macOS, Windows, and FreeBSD support +- All that and more in 2 simple commands: `http` + `https` + + HTTPie + HTTPie + jakubroztocil + 2012-2022 Jakub Roztocil + https://raw.githubusercontent.com/httpie/cli/master/LICENSE + https://pie-assets.s3.eu-central-1.amazonaws.com/LogoIcons/GB.png + false + See the [changelog](https://github.com/httpie/cli/releases/tag/3.2.2). + httpie http https rest api client curl python ssl cli foss oss url + https://httpie.io + https://github.com/httpie/cli/tree/master/docs/packaging/windows-chocolatey + https://github.com/httpie/cli + https://httpie.io/docs + https://github.com/httpie/cli/issues + + + + + + + + diff --git a/docs/packaging/windows-chocolatey/tools/chocolateyinstall.ps1 b/docs/packaging/windows-chocolatey/tools/chocolateyinstall.ps1 new file mode 100644 index 0000000000..e27045f342 --- /dev/null +++ b/docs/packaging/windows-chocolatey/tools/chocolateyinstall.ps1 @@ -0,0 +1,2 @@ +$ErrorActionPreference = 'Stop'; +py -m pip install $env:ChocolateyPackageName==$env:ChocolateyPackageVersion --disable-pip-version-check diff --git a/docs/packaging/windows-chocolatey/tools/chocolateyuninstall.ps1 b/docs/packaging/windows-chocolatey/tools/chocolateyuninstall.ps1 new file mode 100644 index 0000000000..59c660b513 --- /dev/null +++ b/docs/packaging/windows-chocolatey/tools/chocolateyuninstall.ps1 @@ -0,0 +1,2 @@ +$ErrorActionPreference = 'Stop'; +py -m pip uninstall -y $env:ChocolateyPackageName --disable-pip-version-check diff --git a/docs/stardust.png b/docs/stardust.png new file mode 100644 index 0000000000..f95821a425 Binary files /dev/null and b/docs/stardust.png differ diff --git a/extras/httpie-completion.bash b/extras/httpie-completion.bash new file mode 100644 index 0000000000..6abbc2176a --- /dev/null +++ b/extras/httpie-completion.bash @@ -0,0 +1,20 @@ +_http_complete() { + local cur_word=${COMP_WORDS[COMP_CWORD]} + local prev_word=${COMP_WORDS[COMP_CWORD - 1]} + + if [[ "$cur_word" == -* ]]; then + _http_complete_options "$cur_word" + fi +} + +complete -o default -F _http_complete http httpie.http httpie.https https + +_http_complete_options() { + local cur_word=$1 + local options="-j --json -f --form --pretty -s --style -p --print + -v --verbose -h --headers -b --body -S --stream -o --output -d --download + -c --continue --session --session-read-only -a --auth --auth-type --proxy + --follow --verify --cert --cert-key --timeout --check-status --ignore-stdin + --help --version --traceback --debug --raw" + COMPREPLY=( $( compgen -W "$options" -- "$cur_word" ) ) +} diff --git a/extras/httpie-completion.fish b/extras/httpie-completion.fish new file mode 100644 index 0000000000..c0cfc20ad9 --- /dev/null +++ b/extras/httpie-completion.fish @@ -0,0 +1,119 @@ +function __fish_httpie_styles + 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 +end + +function __fish_httpie_mime_types + test -r /usr/share/mime/types && cat /usr/share/mime/types +end + +function __fish_httpie_print_args + set -l arg (commandline -t) + string match -qe H "$arg" || echo -e $arg"H\trequest headers" + string match -qe B "$arg" || echo -e $arg"B\trequest body" + string match -qe h "$arg" || echo -e $arg"h\tresponse headers" + string match -qe b "$arg" || echo -e $arg"b\tresponse body" + string match -qe m "$arg" || echo -e $arg"m\tresponse metadata" +end + +function __fish_httpie_auth_types + echo -e "basic\tBasic HTTP auth" + echo -e "digest\tDigest HTTP auth" + echo -e "bearer\tBearer HTTP Auth" +end + +function __fish_http_verify_options + echo -e "yes\tEnable cert verification" + echo -e "no\tDisable cert verification" +end + + +# Predefined Content Types + +complete -c http -s j -l json -d 'Data items are serialized as a JSON object' +complete -c http -s f -l form -d 'Data items are serialized as form fields' +complete -c http -l multipart -d 'Always sends a multipart/form-data request' +complete -c http -l boundary -x -d 'Custom boundary string for multipart/form-data requests' +complete -c http -l raw -x -d 'Pass raw request data without extra processing' + + +# Content Processing Options + +complete -c http -s x -l compress -d 'Content compressed with Deflate algorithm' + + +# 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 -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 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 + +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 m -l meta -d 'Print only the response metadata' +complete -c http -s b -l body -d 'Print only the response body' +complete -c http -s v -l verbose -d 'Print the whole request as well as the response' +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 o -l output -F -d 'Save output to 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 q -l quiet -d 'Do not print to stdout or stderr' + + +# Sessions + +complete -c http -l session -F -d 'Create, or reuse and update a session' +complete -c http -l session-read-only -F -d 'Create or read a session without updating it' + + +# Authentication + +complete -c http -s a -l auth -x -d 'Username and password for authentication' +complete -c http -s A -l auth-type -xa "(__fish_httpie_auth_types)" -d 'The authentication mechanism to be used' +complete -c http -l ignore-netrc -d 'Ignore credentials from .netrc' + + +# Network + +complete -c http -l offline -d 'Build the request and print it but don\'t actually send it' +complete -c http -l proxy -x -d 'String mapping protocol to the URL of the proxy' +complete -c http -s F -l follow -d 'Follow 30x Location redirects' +complete -c http -l max-redirects -x -d 'Set maximum number of redirects' +complete -c http -l max-headers -x -d 'Maximum number of response headers to be read before giving up' +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 path-as-is -d 'Bypass dot segment URL squashing' +complete -c http -l chunked -d 'Enable streaming via chunked transfer encoding' + + +# SSL + +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 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-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 + +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 manual -d 'Show the full manual' +complete -c http -l version -d 'Show version' +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 debug -d 'Show debugging output' + + +# Alias for https to http + +complete -c https -w http diff --git a/extras/man/http.1 b/extras/man/http.1 new file mode 100644 index 0000000000..889e82b31d --- /dev/null +++ b/extras/man/http.1 @@ -0,0 +1,600 @@ +.\" 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-11-01" "HTTPie 3.2.4" "HTTPie Manual" +.SH NAME +http +.SH SYNOPSIS +http [METHOD] URL [REQUEST_ITEM ...] + +.SH DESCRIPTION +HTTPie: modern, user-friendly command-line HTTP client for the API era. +.SH Positional arguments + +These arguments come after any flags and in the order they are listed here. +Only URL is required. + +.IP "\fB\,METHOD\/\fR" + + +The HTTP method to be used for the request (GET, POST, PUT, DELETE, ...). + +This argument can be omitted in which case HTTPie will use POST if there +is some data to be sent, otherwise GET: + + $ http example.org # => GET + $ http example.org hello=world # => POST + + + +.IP "\fB\,URL\/\fR" + + +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) + +You can also use a shorthand for localhost + + $ http :3000 # => http://localhost:3000 + $ http :/foo # => http://localhost/foo + + + +.IP "\fB\,REQUEST_ITEM\/\fR" + + +Optional key-value pairs to be included in the request. The separator used +determines the type: + +\[aq]:\[aq] HTTP headers: + + Referer:https://httpie.io Cookie:foo=bar User-Agent:bacon/1.0 + +\[aq]==\[aq] URL parameters to be appended to the request URI: + + search==httpie + +\[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): + + name=HTTPie language=Python description=\[aq]CLI HTTP client\[aq] + +\[aq]:=\[aq] Non-string JSON data fields (only with \fB\,--json\/\fR, \fB\,-j\/\fR): + + awesome:=true amount:=42 colors:=\[aq][\[dq]red\[dq], \[dq]green\[dq], \[dq]blue\[dq]]\[aq] + +\[aq]@\[aq] Form file fields (only with \fB\,--form\/\fR or \fB\,--multipart\/\fR): + + cv@\(ti/Documents/CV.pdf + cv@\[aq]\(ti/Documents/CV.pdf;type=application/pdf\[aq] + +\[aq]=@\[aq] A data field like \[aq]=\[aq], but takes a file path and embeds its content: + + essay=@Documents/essay.txt + +\[aq]:=@\[aq] A raw JSON field like \[aq]:=\[aq], but takes a file path and embeds its content: + + package:=@./package.json + +You can use a backslash to escape a colliding separator in the field name: + + field-name-with\e:colon=value + + + +.PP +.SH Predefined content types +.IP "\fB\,--json\/\fR, \fB\,-j\/\fR" + + +(default) Data items from the command line are serialized as a JSON object. +The Content-Type and Accept headers are set to application/json +(if not specified). + + + +.IP "\fB\,--form\/\fR, \fB\,-f\/\fR" + + +Data items from the command line are serialized as form fields. + +The Content-Type is set to application/x-www-form-urlencoded (if not +specified). The presence of any file fields results in a +multipart/form-data request. + + + +.IP "\fB\,--multipart\/\fR" + + +Similar to \fB\,--form\/\fR, but always sends a multipart/form-data request (i.e., even without files). + + +.IP "\fB\,--boundary\/\fR" + + +Specify a custom boundary string for multipart/form-data requests. Only has effect only together with \fB\,--form\/\fR. + + +.IP "\fB\,--raw\/\fR" + + +This option allows you to pass raw request data without extra processing +(as opposed to the structured request items syntax): + + $ http \fB\,--raw\/\fR=\[aq]data\[aq] pie.dev/post + +You can achieve the same by piping the data via stdin: + + $ echo data | http pie.dev/post + +Or have HTTPie load the raw data from a file: + + $ http pie.dev/post @data.txt + + + + +.PP +.SH Content processing options +.IP "\fB\,--compress\/\fR, \fB\,-x\/\fR" + + +Content compressed (encoded) with Deflate algorithm. +The Content-Encoding header is set to deflate. + +Compression is skipped if it appears that compression ratio is +negative. Compression can be forced by repeating the argument. + + + +.PP +.SH Output processing +.IP "\fB\,--pretty\/\fR" + + +Controls output processing. The value can be \[dq]none\[dq] to not prettify +the output (default for redirected output), \[dq]all\[dq] to apply both colors +and formatting (default for terminal output), \[dq]colors\[dq], or \[dq]format\[dq]. + + + +.IP "\fB\,--style\/\fR, \fB\,-s\/\fR \fI\,STYLE\/\fR" + + +Output coloring style (default is \[dq]auto\[dq]). It can be one of: + + auto, pie, pie-dark, pie-light, solarized + + +For finding out all available styles in your system, try: + +$ http \fB\,--style\/\fR + +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 +$TERM environment variable is set to \[dq]xterm-256color\[dq] or similar +(e.g., via `export TERM=xterm-256color\[aq] in your \(ti/.bashrc). + +.IP "\fB\,--unsorted\/\fR" + + +Disables all sorting while formatting output. It is a shortcut for: + + \fB\,--format-options\/\fR=headers.sort:false,json.sort_keys:false + + + +.IP "\fB\,--sorted\/\fR" + + +Re-enables all sorting options while formatting output. It is a shortcut for: + + \fB\,--format-options\/\fR=headers.sort:true,json.sort_keys:true + + + +.IP "\fB\,--response-charset\/\fR \fI\,ENCODING\/\fR" + + +Override the response encoding for terminal display purposes, e.g.: + + \fB\,--response-charset\/\fR=utf8 + \fB\,--response-charset\/\fR=big5 + + + +.IP "\fB\,--response-mime\/\fR \fI\,MIME_TYPE\/\fR" + + +Override the response mime type for coloring and formatting for the terminal, e.g.: + + \fB\,--response-mime\/\fR=application/json + \fB\,--response-mime\/\fR=text/xml + + + +.IP "\fB\,--format-options\/\fR" + + +Controls output formatting. Only relevant when formatting is enabled +through (explicit or implied) \fB\,--pretty\/\fR=all or \fB\,--pretty\/\fR=format. +The following are the default options: + + headers.sort:true + json.format:true + json.indent:4 + json.sort_keys:true + xml.format:true + xml.indent:2 + +You may use this option multiple times, as well as specify multiple +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: + + \fB\,--format-options\/\fR json.sort_keys:false,json.indent:2 + +This is something you will typically put into your config file. + + + +.PP +.SH Output options +.IP "\fB\,--print\/\fR, \fB\,-p\/\fR \fI\,WHAT\/\fR" + + +String specifying what the output should contain: + + \[aq]H\[aq] request headers + \[aq]B\[aq] request body + \[aq]h\[aq] response headers + \[aq]b\[aq] response body + \[aq]m\[aq] response metadata + +The default behaviour is \[aq]hb\[aq] (i.e., the response +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 +response body is printed by default. + + + +.IP "\fB\,--headers\/\fR, \fB\,-h\/\fR" + + +Print only the response headers. Shortcut for \fB\,--print\/\fR=h. + + + +.IP "\fB\,--meta\/\fR, \fB\,-m\/\fR" + + +Print only the response metadata. Shortcut for \fB\,--print\/\fR=m. + + + +.IP "\fB\,--body\/\fR, \fB\,-b\/\fR" + + +Print only the response body. Shortcut for \fB\,--print\/\fR=b. + + + +.IP "\fB\,--verbose\/\fR, \fB\,-v\/\fR" + + +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 +requests/responses (such as redirects). For the second level and higher, +print these as well as the response metadata. + +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 + + +.IP "\fB\,--all\/\fR" + + +By default, only the final request/response is shown. Use this flag to show +any intermediary requests/responses as well. Intermediary requests include +followed redirects (with \fB\,--follow\/\fR), the first unauthorized request when +Digest auth is used (\fB\,--auth\/\fR=digest), etc. + + + +.IP "\fB\,--stream\/\fR, \fB\,-S\/\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), +HTTPie fetches the whole response before it outputs the processed data. + +Set this option when you want to continuously display a prettified +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 +more often and in smaller chunks. + + + +.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 +the response body is saved to FILE. Other parts of the HTTP exchange are +printed to stderr. + + + +.IP "\fB\,--download\/\fR, \fB\,-d\/\fR" + + +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 +[filename]. This action is similar to the default behaviour of wget. + + + +.IP "\fB\,--continue\/\fR, \fB\,-c\/\fR" + + +Resume an interrupted download. Note that the \fB\,--output\/\fR option needs to be +specified as well. + + + +.IP "\fB\,--quiet\/\fR, \fB\,-q\/\fR" + + +Do not print to stdout or stderr, except for errors and warnings when provided once. +Provide twice to suppress warnings as well. +stdout is still redirected if \fB\,--output\/\fR is specified. +Flag doesn\[aq]t affect behaviour of download beyond not printing to terminal. + + + +.PP +.SH Sessions +.IP "\fB\,--session\/\fR \fI\,SESSION_NAME_OR_PATH\/\fR" + + +Create, or reuse and update a session. Within a session, custom headers, +auth credential, as well as any cookies sent by the server persist between +requests. + +Session files are stored in: + + [HTTPIE_CONFIG_DIR]//.json. + +See the following page to find out your default HTTPIE_CONFIG_DIR: + + https://httpie.io/docs/cli/config-file-directory + + +.IP "\fB\,--session-read-only\/\fR \fI\,SESSION_NAME_OR_PATH\/\fR" + + +Create or read a session without updating it form the request/response +exchange. + + + +.PP +.SH Authentication +.IP "\fB\,--auth\/\fR, \fB\,-a\/\fR \fI\,USER[:PASS] | TOKEN\/\fR" + + +For username/password based authentication mechanisms (e.g +basic auth or digest auth) if only the username is provided +(\fB\,-a\/\fR username), HTTPie will prompt for the password. + + + +.IP "\fB\,--auth-type\/\fR, \fB\,-A\/\fR" + + +The authentication mechanism to be used. Defaults to \[dq]basic\[dq]. + +\[dq]basic\[dq]: Basic HTTP auth + +\[dq]digest\[dq]: Digest HTTP auth + +\[dq]bearer\[dq]: Bearer HTTP Auth + +To see all available auth types on your system, including ones installed via plugins, run: + +$ http \fB\,--auth-type\/\fR + +.IP "\fB\,--ignore-netrc\/\fR" + + +Ignore credentials from .netrc. + + +.PP +.SH Network +.IP "\fB\,--offline\/\fR" + + +Build the request and print it but don\(gat actually send it. + + +.IP "\fB\,--proxy\/\fR \fI\,PROTOCOL:PROXY_URL\/\fR" + + +String mapping protocol to the URL of the proxy +(e.g. http:http://foo.bar:3128). You can specify multiple proxies with +different protocols. The environment variables $ALL_PROXY, $HTTP_PROXY, +and $HTTPS_proxy are supported as well. + + + +.IP "\fB\,--follow\/\fR, \fB\,-F\/\fR" + + +Follow 30x Location redirects. + + +.IP "\fB\,--max-redirects\/\fR" + + +By default, requests have a limit of 30 redirects (works with \fB\,--follow\/\fR). + + + +.IP "\fB\,--max-headers\/\fR" + + +The maximum number of response headers to be read before giving up (default 0, i.e., no limit). + + +.IP "\fB\,--timeout\/\fR \fI\,SECONDS\/\fR" + + +The connection timeout of the request in seconds. +The default value is 0, i.e., there is no timeout limit. +This is not a time limit on the entire response download; +rather, an error is reported if the server has not issued a response for +timeout seconds (more precisely, if no bytes have been received on +the underlying socket for timeout seconds). + + + +.IP "\fB\,--check-status\/\fR" + + +By default, HTTPie exits with 0 when no network or other fatal errors +occur. This flag instructs HTTPie to also check the HTTP status code and +exit with an error if the status indicates one. + +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 +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. + + + +.IP "\fB\,--path-as-is\/\fR" + + +Bypass dot segment (/../ or /./) URL squashing. + + +.IP "\fB\,--chunked\/\fR" + + +Enable streaming via chunked transfer encoding. The Transfer-Encoding header is set to chunked. + + +.PP +.SH SSL +.IP "\fB\,--verify\/\fR" + + +Set to \[dq]no\[dq] (or \[dq]false\[dq]) to skip checking the host\[aq]s SSL certificate. +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 +variable instead.) + + +.IP "\fB\,--ssl\/\fR" + + +The desired protocol version to use. This will default to +SSL v2.3 which will negotiate the highest protocol that both +the server and your installation of OpenSSL support. Available protocols +may vary depending on OpenSSL installation (only the supported ones +are shown here). + + + +.IP "\fB\,--ciphers\/\fR" + + + +A string in the OpenSSL cipher list format. + + +See `http \fB\,--help\/\fR` for the default ciphers list on you system. + + + + + +.IP "\fB\,--cert\/\fR" + + +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 +specify \fB\,--cert-key\/\fR separately. + + + +.IP "\fB\,--cert-key\/\fR" + + +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. + + + +.IP "\fB\,--cert-key-pass\/\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. +If not provided, you\(gall be prompted interactively. + + +.PP +.SH Troubleshooting +.IP "\fB\,--ignore-stdin\/\fR, \fB\,-I\/\fR" + + +Do not attempt to read stdin + + +.IP "\fB\,--help\/\fR" + + +Show this help message and exit. + + +.IP "\fB\,--manual\/\fR" + + +Show the full manual. + + +.IP "\fB\,--version\/\fR" + + +Show version and exit. + + +.IP "\fB\,--traceback\/\fR" + + +Prints the exception traceback should one occur. + + +.IP "\fB\,--default-scheme\/\fR" + + +The default scheme to use if not specified in the URL. + + +.IP "\fB\,--debug\/\fR" + + +Prints the exception traceback should one occur, as well as other +information useful for debugging HTTPie itself and for reporting bugs. + + + +.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 \ No newline at end of file diff --git a/extras/man/httpie.1 b/extras/man/httpie.1 new file mode 100644 index 0000000000..b44172967b --- /dev/null +++ b/extras/man/httpie.1 @@ -0,0 +1,100 @@ +.\" 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-11-01" "HTTPie 3.2.4" "HTTPie Manual" +.SH NAME +httpie +.SH SYNOPSIS +httpie +.SH DESCRIPTION + +Managing interface for the HTTPie itself. + +Be aware that you might be looking for http/https commands for sending +HTTP requests. This command is only available for managing the HTTTPie +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 +Export available options for the CLI +.IP "\fB\,-f\/\fR, \fB\,--format\/\fR" + +Format to export in. + +.PP +.SH httpie cli check-updates +Check for updates +.PP +.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. +.IP "\fB\,HOSTNAME\/\fR" + +The host this session belongs. + +.IP "\fB\,SESSION_NAME_OR_PATH\/\fR" + +The name or the path for the session that will be upgraded. + +.IP "\fB\,--bind-cookies\/\fR" + +Bind domainless cookies to the host that session belongs. + +.PP +.SH httpie cli sessions upgrade-all +Upgrade all named sessions with the latest layout. A list of changes between different session versions can be found in the official documentation. +.IP "\fB\,--bind-cookies\/\fR" + +Bind domainless cookies to the host that session belongs. + +.PP +.SH httpie cli plugins install +Install the given targets from PyPI or from a local paths. +.IP "\fB\,TARGET\/\fR" + +targets to install + +.PP +.SH httpie cli plugins upgrade +Upgrade the given plugins +.IP "\fB\,TARGET\/\fR" + +targets to upgrade + +.PP +.SH httpie cli plugins uninstall +Uninstall the given HTTPie plugins. +.IP "\fB\,TARGET\/\fR" + +targets to install + +.PP +.SH httpie cli plugins list +List all installed HTTPie plugins. +.PP +.SH httpie plugins install +Install the given targets from PyPI or from a local paths. +.IP "\fB\,TARGET\/\fR" + +targets to install + +.PP +.SH httpie plugins upgrade +Upgrade the given plugins +.IP "\fB\,TARGET\/\fR" + +targets to upgrade + +.PP +.SH httpie plugins uninstall +Uninstall the given HTTPie plugins. +.IP "\fB\,TARGET\/\fR" + +targets to install + +.PP +.SH httpie plugins list +List all installed HTTPie plugins. +.PP \ No newline at end of file diff --git a/extras/man/https.1 b/extras/man/https.1 new file mode 100644 index 0000000000..14434b9a71 --- /dev/null +++ b/extras/man/https.1 @@ -0,0 +1,600 @@ +.\" 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-11-01" "HTTPie 3.2.4" "HTTPie Manual" +.SH NAME +https +.SH SYNOPSIS +https [METHOD] URL [REQUEST_ITEM ...] + +.SH DESCRIPTION +HTTPie: modern, user-friendly command-line HTTP client for the API era. +.SH Positional arguments + +These arguments come after any flags and in the order they are listed here. +Only URL is required. + +.IP "\fB\,METHOD\/\fR" + + +The HTTP method to be used for the request (GET, POST, PUT, DELETE, ...). + +This argument can be omitted in which case HTTPie will use POST if there +is some data to be sent, otherwise GET: + + $ http example.org # => GET + $ http example.org hello=world # => POST + + + +.IP "\fB\,URL\/\fR" + + +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) + +You can also use a shorthand for localhost + + $ http :3000 # => http://localhost:3000 + $ http :/foo # => http://localhost/foo + + + +.IP "\fB\,REQUEST_ITEM\/\fR" + + +Optional key-value pairs to be included in the request. The separator used +determines the type: + +\[aq]:\[aq] HTTP headers: + + Referer:https://httpie.io Cookie:foo=bar User-Agent:bacon/1.0 + +\[aq]==\[aq] URL parameters to be appended to the request URI: + + search==httpie + +\[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): + + name=HTTPie language=Python description=\[aq]CLI HTTP client\[aq] + +\[aq]:=\[aq] Non-string JSON data fields (only with \fB\,--json\/\fR, \fB\,-j\/\fR): + + awesome:=true amount:=42 colors:=\[aq][\[dq]red\[dq], \[dq]green\[dq], \[dq]blue\[dq]]\[aq] + +\[aq]@\[aq] Form file fields (only with \fB\,--form\/\fR or \fB\,--multipart\/\fR): + + cv@\(ti/Documents/CV.pdf + cv@\[aq]\(ti/Documents/CV.pdf;type=application/pdf\[aq] + +\[aq]=@\[aq] A data field like \[aq]=\[aq], but takes a file path and embeds its content: + + essay=@Documents/essay.txt + +\[aq]:=@\[aq] A raw JSON field like \[aq]:=\[aq], but takes a file path and embeds its content: + + package:=@./package.json + +You can use a backslash to escape a colliding separator in the field name: + + field-name-with\e:colon=value + + + +.PP +.SH Predefined content types +.IP "\fB\,--json\/\fR, \fB\,-j\/\fR" + + +(default) Data items from the command line are serialized as a JSON object. +The Content-Type and Accept headers are set to application/json +(if not specified). + + + +.IP "\fB\,--form\/\fR, \fB\,-f\/\fR" + + +Data items from the command line are serialized as form fields. + +The Content-Type is set to application/x-www-form-urlencoded (if not +specified). The presence of any file fields results in a +multipart/form-data request. + + + +.IP "\fB\,--multipart\/\fR" + + +Similar to \fB\,--form\/\fR, but always sends a multipart/form-data request (i.e., even without files). + + +.IP "\fB\,--boundary\/\fR" + + +Specify a custom boundary string for multipart/form-data requests. Only has effect only together with \fB\,--form\/\fR. + + +.IP "\fB\,--raw\/\fR" + + +This option allows you to pass raw request data without extra processing +(as opposed to the structured request items syntax): + + $ http \fB\,--raw\/\fR=\[aq]data\[aq] pie.dev/post + +You can achieve the same by piping the data via stdin: + + $ echo data | http pie.dev/post + +Or have HTTPie load the raw data from a file: + + $ http pie.dev/post @data.txt + + + + +.PP +.SH Content processing options +.IP "\fB\,--compress\/\fR, \fB\,-x\/\fR" + + +Content compressed (encoded) with Deflate algorithm. +The Content-Encoding header is set to deflate. + +Compression is skipped if it appears that compression ratio is +negative. Compression can be forced by repeating the argument. + + + +.PP +.SH Output processing +.IP "\fB\,--pretty\/\fR" + + +Controls output processing. The value can be \[dq]none\[dq] to not prettify +the output (default for redirected output), \[dq]all\[dq] to apply both colors +and formatting (default for terminal output), \[dq]colors\[dq], or \[dq]format\[dq]. + + + +.IP "\fB\,--style\/\fR, \fB\,-s\/\fR \fI\,STYLE\/\fR" + + +Output coloring style (default is \[dq]auto\[dq]). It can be one of: + + auto, pie, pie-dark, pie-light, solarized + + +For finding out all available styles in your system, try: + +$ http \fB\,--style\/\fR + +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 +$TERM environment variable is set to \[dq]xterm-256color\[dq] or similar +(e.g., via `export TERM=xterm-256color\[aq] in your \(ti/.bashrc). + +.IP "\fB\,--unsorted\/\fR" + + +Disables all sorting while formatting output. It is a shortcut for: + + \fB\,--format-options\/\fR=headers.sort:false,json.sort_keys:false + + + +.IP "\fB\,--sorted\/\fR" + + +Re-enables all sorting options while formatting output. It is a shortcut for: + + \fB\,--format-options\/\fR=headers.sort:true,json.sort_keys:true + + + +.IP "\fB\,--response-charset\/\fR \fI\,ENCODING\/\fR" + + +Override the response encoding for terminal display purposes, e.g.: + + \fB\,--response-charset\/\fR=utf8 + \fB\,--response-charset\/\fR=big5 + + + +.IP "\fB\,--response-mime\/\fR \fI\,MIME_TYPE\/\fR" + + +Override the response mime type for coloring and formatting for the terminal, e.g.: + + \fB\,--response-mime\/\fR=application/json + \fB\,--response-mime\/\fR=text/xml + + + +.IP "\fB\,--format-options\/\fR" + + +Controls output formatting. Only relevant when formatting is enabled +through (explicit or implied) \fB\,--pretty\/\fR=all or \fB\,--pretty\/\fR=format. +The following are the default options: + + headers.sort:true + json.format:true + json.indent:4 + json.sort_keys:true + xml.format:true + xml.indent:2 + +You may use this option multiple times, as well as specify multiple +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: + + \fB\,--format-options\/\fR json.sort_keys:false,json.indent:2 + +This is something you will typically put into your config file. + + + +.PP +.SH Output options +.IP "\fB\,--print\/\fR, \fB\,-p\/\fR \fI\,WHAT\/\fR" + + +String specifying what the output should contain: + + \[aq]H\[aq] request headers + \[aq]B\[aq] request body + \[aq]h\[aq] response headers + \[aq]b\[aq] response body + \[aq]m\[aq] response metadata + +The default behaviour is \[aq]hb\[aq] (i.e., the response +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 +response body is printed by default. + + + +.IP "\fB\,--headers\/\fR, \fB\,-h\/\fR" + + +Print only the response headers. Shortcut for \fB\,--print\/\fR=h. + + + +.IP "\fB\,--meta\/\fR, \fB\,-m\/\fR" + + +Print only the response metadata. Shortcut for \fB\,--print\/\fR=m. + + + +.IP "\fB\,--body\/\fR, \fB\,-b\/\fR" + + +Print only the response body. Shortcut for \fB\,--print\/\fR=b. + + + +.IP "\fB\,--verbose\/\fR, \fB\,-v\/\fR" + + +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 +requests/responses (such as redirects). For the second level and higher, +print these as well as the response metadata. + +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 + + +.IP "\fB\,--all\/\fR" + + +By default, only the final request/response is shown. Use this flag to show +any intermediary requests/responses as well. Intermediary requests include +followed redirects (with \fB\,--follow\/\fR), the first unauthorized request when +Digest auth is used (\fB\,--auth\/\fR=digest), etc. + + + +.IP "\fB\,--stream\/\fR, \fB\,-S\/\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), +HTTPie fetches the whole response before it outputs the processed data. + +Set this option when you want to continuously display a prettified +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 +more often and in smaller chunks. + + + +.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 +the response body is saved to FILE. Other parts of the HTTP exchange are +printed to stderr. + + + +.IP "\fB\,--download\/\fR, \fB\,-d\/\fR" + + +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 +[filename]. This action is similar to the default behaviour of wget. + + + +.IP "\fB\,--continue\/\fR, \fB\,-c\/\fR" + + +Resume an interrupted download. Note that the \fB\,--output\/\fR option needs to be +specified as well. + + + +.IP "\fB\,--quiet\/\fR, \fB\,-q\/\fR" + + +Do not print to stdout or stderr, except for errors and warnings when provided once. +Provide twice to suppress warnings as well. +stdout is still redirected if \fB\,--output\/\fR is specified. +Flag doesn\[aq]t affect behaviour of download beyond not printing to terminal. + + + +.PP +.SH Sessions +.IP "\fB\,--session\/\fR \fI\,SESSION_NAME_OR_PATH\/\fR" + + +Create, or reuse and update a session. Within a session, custom headers, +auth credential, as well as any cookies sent by the server persist between +requests. + +Session files are stored in: + + [HTTPIE_CONFIG_DIR]//.json. + +See the following page to find out your default HTTPIE_CONFIG_DIR: + + https://httpie.io/docs/cli/config-file-directory + + +.IP "\fB\,--session-read-only\/\fR \fI\,SESSION_NAME_OR_PATH\/\fR" + + +Create or read a session without updating it form the request/response +exchange. + + + +.PP +.SH Authentication +.IP "\fB\,--auth\/\fR, \fB\,-a\/\fR \fI\,USER[:PASS] | TOKEN\/\fR" + + +For username/password based authentication mechanisms (e.g +basic auth or digest auth) if only the username is provided +(\fB\,-a\/\fR username), HTTPie will prompt for the password. + + + +.IP "\fB\,--auth-type\/\fR, \fB\,-A\/\fR" + + +The authentication mechanism to be used. Defaults to \[dq]basic\[dq]. + +\[dq]basic\[dq]: Basic HTTP auth + +\[dq]digest\[dq]: Digest HTTP auth + +\[dq]bearer\[dq]: Bearer HTTP Auth + +To see all available auth types on your system, including ones installed via plugins, run: + +$ http \fB\,--auth-type\/\fR + +.IP "\fB\,--ignore-netrc\/\fR" + + +Ignore credentials from .netrc. + + +.PP +.SH Network +.IP "\fB\,--offline\/\fR" + + +Build the request and print it but don\(gat actually send it. + + +.IP "\fB\,--proxy\/\fR \fI\,PROTOCOL:PROXY_URL\/\fR" + + +String mapping protocol to the URL of the proxy +(e.g. http:http://foo.bar:3128). You can specify multiple proxies with +different protocols. The environment variables $ALL_PROXY, $HTTP_PROXY, +and $HTTPS_proxy are supported as well. + + + +.IP "\fB\,--follow\/\fR, \fB\,-F\/\fR" + + +Follow 30x Location redirects. + + +.IP "\fB\,--max-redirects\/\fR" + + +By default, requests have a limit of 30 redirects (works with \fB\,--follow\/\fR). + + + +.IP "\fB\,--max-headers\/\fR" + + +The maximum number of response headers to be read before giving up (default 0, i.e., no limit). + + +.IP "\fB\,--timeout\/\fR \fI\,SECONDS\/\fR" + + +The connection timeout of the request in seconds. +The default value is 0, i.e., there is no timeout limit. +This is not a time limit on the entire response download; +rather, an error is reported if the server has not issued a response for +timeout seconds (more precisely, if no bytes have been received on +the underlying socket for timeout seconds). + + + +.IP "\fB\,--check-status\/\fR" + + +By default, HTTPie exits with 0 when no network or other fatal errors +occur. This flag instructs HTTPie to also check the HTTP status code and +exit with an error if the status indicates one. + +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 +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. + + + +.IP "\fB\,--path-as-is\/\fR" + + +Bypass dot segment (/../ or /./) URL squashing. + + +.IP "\fB\,--chunked\/\fR" + + +Enable streaming via chunked transfer encoding. The Transfer-Encoding header is set to chunked. + + +.PP +.SH SSL +.IP "\fB\,--verify\/\fR" + + +Set to \[dq]no\[dq] (or \[dq]false\[dq]) to skip checking the host\[aq]s SSL certificate. +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 +variable instead.) + + +.IP "\fB\,--ssl\/\fR" + + +The desired protocol version to use. This will default to +SSL v2.3 which will negotiate the highest protocol that both +the server and your installation of OpenSSL support. Available protocols +may vary depending on OpenSSL installation (only the supported ones +are shown here). + + + +.IP "\fB\,--ciphers\/\fR" + + + +A string in the OpenSSL cipher list format. + + +See `http \fB\,--help\/\fR` for the default ciphers list on you system. + + + + + +.IP "\fB\,--cert\/\fR" + + +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 +specify \fB\,--cert-key\/\fR separately. + + + +.IP "\fB\,--cert-key\/\fR" + + +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. + + + +.IP "\fB\,--cert-key-pass\/\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. +If not provided, you\(gall be prompted interactively. + + +.PP +.SH Troubleshooting +.IP "\fB\,--ignore-stdin\/\fR, \fB\,-I\/\fR" + + +Do not attempt to read stdin + + +.IP "\fB\,--help\/\fR" + + +Show this help message and exit. + + +.IP "\fB\,--manual\/\fR" + + +Show the full manual. + + +.IP "\fB\,--version\/\fR" + + +Show version and exit. + + +.IP "\fB\,--traceback\/\fR" + + +Prints the exception traceback should one occur. + + +.IP "\fB\,--default-scheme\/\fR" + + +The default scheme to use if not specified in the URL. + + +.IP "\fB\,--debug\/\fR" + + +Prints the exception traceback should one occur, as well as other +information useful for debugging HTTPie itself and for reporting bugs. + + + +.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 \ No newline at end of file diff --git a/extras/packaging/linux/Dockerfile b/extras/packaging/linux/Dockerfile new file mode 100644 index 0000000000..ea441fd613 --- /dev/null +++ b/extras/packaging/linux/Dockerfile @@ -0,0 +1,33 @@ +# Use the oldest (but still supported) Ubuntu as the base for PyInstaller +# packages. This will prevent stuff like glibc from conflicting. +FROM ubuntu:18.04 + +RUN apt-get update +RUN apt-get install -y software-properties-common binutils +RUN apt-get install -y ruby-dev +RUN gem install fpm + +# Use deadsnakes for the latest Pythons (e.g 3.9) +RUN add-apt-repository ppa:deadsnakes/ppa +RUN apt-get update && apt-get install -y python3.9 python3.9-dev python3.9-venv + +# Install rpm as well, since we are going to build fedora dists too +RUN apt-get install -y rpm + +ADD . /app +WORKDIR /app/extras/packaging/linux + +ENV VIRTUAL_ENV=/opt/venv +RUN python3.9 -m venv $VIRTUAL_ENV +ENV PATH="$VIRTUAL_ENV/bin:$PATH" + +# Ensure that pip is renewed, otherwise we would be using distro-provided pip +# which strips vendored packages and doesn't work with PyInstaller. +RUN python -m pip install /app +RUN python -m pip install pyinstaller wheel +RUN python -m pip install --force-reinstall --upgrade pip + +RUN echo 'BUILD_CHANNEL="pypi"' > /app/httpie/internal/__build_channel__.py +RUN python build.py + +ENTRYPOINT ["mv", "/app/extras/packaging/linux/dist/", "/artifacts"] diff --git a/extras/packaging/linux/README.md b/extras/packaging/linux/README.md new file mode 100644 index 0000000000..ab18bfd02f --- /dev/null +++ b/extras/packaging/linux/README.md @@ -0,0 +1,52 @@ +# Standalone Linux Packages + +![packaging.png](https://user-images.githubusercontent.com/47358913/159950478-2d090d1b-69b9-4914-a1b4-d3e3d8e25fe0.png) + +This directory contains the build scripts for creating: + +- A self-contained binary executable for the HTTPie itself +- `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/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. + + +## Hacking + +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: + +- `build_binaries`, for the self-contained executables +- `build_packages`, for the OS-specific packages (which wrap the binaries) + +### `build_binaries` + +We use [PyInstaller](https://pyinstaller.readthedocs.io/en/stable/) for the binaries. Normally pyinstaller offers two different modes: + +- Single directory (harder to distribute, low redundancy. Library files are shared across different executables) +- Single binary (easier to distribute, higher redundancy. Same libraries are statically linked to different executables, so higher total size) + +Since our binary size (in total 20 MiBs) is not that big, we have decided to choose the single binary mode for the sake of easier distribution. + +We also disable `UPX`, which is a runtime decompression method since it adds some startup cost. + +### `build_packages` + +We build our OS-specific packages with [FPM](https://github.com/jordansissel/fpm) which offers a really nice abstraction. We use the `dir` mode, +and package `http`, `https` and `httpie` commands. More can be added to the `files` option. + +Since the `httpie` depends on having a pip executable, we explicitly depend on the system Python even though the core does not use it. + +### Docker Image + +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 +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. + +It also contains the Python version we package our HTTPie with, so it is the place if you need to change it. + +### `./get_release_artifacts.sh` + +If you make a change in the `build.py`, run the following script to test it out. It will return multiple files under `artifacts/dist` which +then you can test out and ensure their quality (it is also the script that we use in our automation). diff --git a/extras/packaging/linux/build.py b/extras/packaging/linux/build.py new file mode 100644 index 0000000000..9f02136923 --- /dev/null +++ b/extras/packaging/linux/build.py @@ -0,0 +1,109 @@ +import stat +import subprocess +from pathlib import Path +from typing import Iterator, Tuple + +BUILD_DIR = Path(__file__).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') +HOOKS_DIR = SCRIPT_DIR / 'hooks' + +DIST_DIR = BUILD_DIR / 'dist' + +TARGET_SCRIPTS = { + SCRIPT_DIR / 'http_cli.py': [], + SCRIPT_DIR / 'httpie_cli.py': ['--hidden-import=pip'], +} + + +def build_binaries() -> Iterator[Tuple[str, Path]]: + for target_script, extra_args in TARGET_SCRIPTS.items(): + subprocess.check_call( + [ + 'pyinstaller', + '--onefile', + '--noupx', + '-p', + HTTPIE_DIR, + '--additional-hooks-dir', + HOOKS_DIR, + *extra_args, + target_script, + ] + ) + + for executable_path in DIST_DIR.iterdir(): + if executable_path.suffix: + continue + stat_r = executable_path.stat() + executable_path.chmod(stat_r.st_mode | stat.S_IEXEC) + yield executable_path.stem, executable_path + + +def build_packages(http_binary: Path, httpie_binary: Path) -> None: + import httpie + + # Mapping of src_file -> dst_file + files = [ + (http_binary, '/usr/bin/http'), + (http_binary, '/usr/bin/https'), + (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 + deps = [ + 'python3 >= 3.7', + 'python3-pip' + ] + + processed_deps = [ + f'--depends={dep}' + for dep in deps + ] + processed_files = [ + '='.join([str(src.resolve()), dst]) for src, dst in files + ] + for target in ['deb', 'rpm']: + subprocess.check_call( + [ + 'fpm', + '--force', + '-s', + 'dir', + '-t', + target, + '--name', + 'httpie', + '--version', + httpie.__version__, + '--description', + httpie.__doc__.strip(), + '--license', + httpie.__licence__, + *processed_deps, + *processed_files, + ], + cwd=DIST_DIR, + ) + + +def main(): + binaries = dict(build_binaries()) + build_packages(binaries['http_cli'], binaries['httpie_cli']) + + # Rename http_cli/httpie_cli to http/httpie + binaries['http_cli'].rename(DIST_DIR / 'http') + binaries['httpie_cli'].rename(DIST_DIR / 'httpie') + + + +if __name__ == '__main__': + main() diff --git a/extras/packaging/linux/get_release_artifacts.sh b/extras/packaging/linux/get_release_artifacts.sh new file mode 100755 index 0000000000..b56e8b83d8 --- /dev/null +++ b/extras/packaging/linux/get_release_artifacts.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -xe + +REPO_ROOT=../../../ +ARTIFACTS_DIR=$(pwd)/artifacts + +# Reset the ARTIFACTS_DIR. +rm -rf $ARTIFACTS_DIR +mkdir -p $ARTIFACTS_DIR + +# Operate on the repository root to have the proper +# docker context. +pushd $REPO_ROOT + +# Build the PyInstaller image +docker build -t pyinstaller-httpie -f extras/packaging/linux/Dockerfile . + +# Copy the artifacts to the designated directory. +docker run --rm -i -v $ARTIFACTS_DIR:/artifacts pyinstaller-httpie:latest + +popd diff --git a/extras/packaging/linux/scripts/hooks/hook-pip.py b/extras/packaging/linux/scripts/hooks/hook-pip.py new file mode 100644 index 0000000000..1dac8fb17f --- /dev/null +++ b/extras/packaging/linux/scripts/hooks/hook-pip.py @@ -0,0 +1,14 @@ +from pathlib import Path +from PyInstaller.utils.hooks import collect_all + +def hook(hook_api): + for pkg in [ + 'pip', + 'setuptools', + 'distutils', + 'pkg_resources' + ]: + datas, binaries, hiddenimports = collect_all(pkg) + hook_api.add_datas(datas) + hook_api.add_binaries(binaries) + hook_api.add_imports(*hiddenimports) diff --git a/extras/packaging/linux/scripts/http_cli.py b/extras/packaging/linux/scripts/http_cli.py new file mode 100644 index 0000000000..12ac773fef --- /dev/null +++ b/extras/packaging/linux/scripts/http_cli.py @@ -0,0 +1,5 @@ +from httpie.__main__ import main + +if __name__ == '__main__': + import sys + sys.exit(main()) diff --git a/extras/packaging/linux/scripts/httpie_cli.py b/extras/packaging/linux/scripts/httpie_cli.py new file mode 100644 index 0000000000..1e979c9521 --- /dev/null +++ b/extras/packaging/linux/scripts/httpie_cli.py @@ -0,0 +1,5 @@ +from httpie.manager.__main__ import main + +if __name__ == '__main__': + import sys + sys.exit(main()) diff --git a/extras/profiling/README.md b/extras/profiling/README.md new file mode 100644 index 0000000000..aff4c68614 --- /dev/null +++ b/extras/profiling/README.md @@ -0,0 +1,39 @@ +# HTTPie Benchmarking Infrastructure + +This directory includes the benchmarks we use for testing HTTPie's speed and the +infrastructure to automate this testing across versions. + +## Usage + +Ensure the following requirements are satisfied: + +- Python 3.7+ +- `pyperf` + +Then, run the `extras/profiling/run.py`: + +```console +$ python extras/profiling/run.py +``` + +Without any options, this command will initially create an isolated environment +and install `httpie` from the latest commit. Then it will create a second +environment with the `master` of the current repository and run the benchmarks +on both of them. It will compare the results and print it as a markdown table: + +| Benchmark | master | this_branch | +| -------------------------------------- | :----: | :------------------: | +| `http --version` (startup) | 201 ms | 174 ms: 1.16x faster | +| `http --offline pie.dev/get` (startup) | 200 ms | 174 ms: 1.15x faster | +| Geometric mean | (ref) | 1.10x faster | + +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/cli` +repo from `GitHub` and use it as the baseline. + +You can customize these branches by passing `--local-repo`/`--target-branch`, +and customize the repos by passing `--local-repo`/`--target-repo` (can either +take a URL or a path). + +If you want to run a third environment with additional dependencies (such as +`pyOpenSSL`), you can pass `--complex`. diff --git a/extras/profiling/benchmarks.py b/extras/profiling/benchmarks.py new file mode 100644 index 0000000000..9d409debbe --- /dev/null +++ b/extras/profiling/benchmarks.py @@ -0,0 +1,202 @@ +""" +This file is the declaration of benchmarks for HTTPie. It +is also used to run them with the current environment. + +Each instance of BaseRunner class will be an individual +benchmark. And if run without any arguments, this file +will execute every benchmark instance and report the +timings. + +The benchmarks are run through 'pyperf', which allows to +do get very precise results. For micro-benchmarks like startup, +please run `pyperf system tune` to get even more accurate results. + +Examples: + + # Run everything as usual, the default is that we do 3 warm-up runs + # and 5 actual runs. + $ python extras/profiling/benchmarks.py + + # For retrieving results faster, pass --fast + $ python extras/profiling/benchmarks.py --fast + + # For verify everything works as expected, pass --debug-single-value. + # It will only run everything once, so the resuls are not reliable. But + # very useful when iterating on a benchmark + $ python extras/profiling/benchmarks.py --debug-single-value + + # If you want to run with a custom HTTPie command (for example with + # and HTTPie instance installed in another virtual environment), + # pass HTTPIE_COMMAND variable. + $ HTTPIE_COMMAND="/my/python /my/httpie" python extras/profiling/benchmarks.py +""" + +from __future__ import annotations + +import os +import shlex +import subprocess +import sys +import threading +from contextlib import ExitStack, contextmanager +from dataclasses import dataclass, field +from functools import cached_property, partial +from http.server import HTTPServer, SimpleHTTPRequestHandler +from tempfile import TemporaryDirectory +from typing import ClassVar, Final, List + +import pyperf + +# For download benchmarks, define a set of files. +# file: (block_size, count) => total_size = block_size * count +PREDEFINED_FILES: Final = {'3G': (3 * 1024 ** 2, 1024)} + + +class QuietSimpleHTTPServer(SimpleHTTPRequestHandler): + def log_message(self, *args, **kwargs): + pass + + +@contextmanager +def start_server(): + """Create a server to serve local files. It will create the + PREDEFINED_FILES through dd.""" + with TemporaryDirectory() as directory: + for file_name, (block_size, count) in PREDEFINED_FILES.items(): + subprocess.check_call( + [ + 'dd', + 'if=/dev/zero', + f'of={file_name}', + f'bs={block_size}', + f'count={count}', + ], + cwd=directory, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + handler = partial(QuietSimpleHTTPServer, directory=directory) + server = HTTPServer(('localhost', 0), handler) + + thread = threading.Thread(target=server.serve_forever) + thread.start() + yield '{}:{}'.format(*server.socket.getsockname()) + server.shutdown() + thread.join(timeout=0.5) + + +@dataclass +class Context: + benchmarks: ClassVar[List[BaseRunner]] = [] + stack: ExitStack = field(default_factory=ExitStack) + runner: pyperf.Runner = field(default_factory=pyperf.Runner) + + def run(self) -> pyperf.BenchmarkSuite: + results = [benchmark.run(self) for benchmark in self.benchmarks] + return pyperf.BenchmarkSuite(results) + + @property + def cmd(self) -> List[str]: + if cmd := os.getenv('HTTPIE_COMMAND'): + return shlex.split(cmd) + + http = os.path.join(os.path.dirname(sys.executable), 'http') + assert os.path.exists(http) + return [sys.executable, http] + + @cached_property + def server(self) -> str: + return self.stack.enter_context(start_server()) + + def __enter__(self): + return self + + def __exit__(self, *exc_info): + self.stack.close() + + +@dataclass +class BaseRunner: + """ + An individual benchmark case. By default it has the category + (e.g like startup or download) and a name. + """ + + category: str + title: str + + def __post_init__(self): + Context.benchmarks.append(self) + + def run(self, context: Context) -> pyperf.Benchmark: + raise NotImplementedError + + @property + def name(self) -> str: + return f'{self.title} ({self.category})' + + +@dataclass +class CommandRunner(BaseRunner): + """ + Run a single command, and benchmark it. + """ + + args: List[str] + + def run(self, context: Context) -> pyperf.Benchmark: + return context.runner.bench_command(self.name, [*context.cmd, *self.args]) + + +@dataclass +class DownloadRunner(BaseRunner): + """ + Benchmark downloading a single file from the + remote server. + """ + + file_name: str + + def run(self, context: Context) -> pyperf.Benchmark: + return context.runner.bench_command( + self.name, + [ + *context.cmd, + '--download', + 'GET', + f'{context.server}/{self.file_name}', + ], + ) + + +CommandRunner('startup', '`http --version`', ['--version']) +CommandRunner('startup', '`http --offline pie.dev/get`', ['--offline', 'pie.dev/get']) +for pretty in ['all', 'none']: + CommandRunner( + 'startup', + f'`http --pretty={pretty} pie.dev/stream/1000`', + [ + '--print=HBhb', + f'--pretty={pretty}', + 'httpbin.org/stream/1000' + ] + ) +DownloadRunner('download', '`http --download :/big_file.txt` (3GB)', '3G') + + +def main() -> None: + # PyPerf will bring it's own argument parser, so configure the script. + # The somewhat fast and also precise enough configuration is this. We run + # benchmarks 3 times to warm up (e.g especially for download benchmark, this + # is important). And then 5 actual runs where we record. + sys.argv.extend( + ['--worker', '--loops=1', '--warmup=3', '--values=5', '--processes=2'] + ) + + with Context() as context: + context.run() + + +if __name__ == '__main__': + main() diff --git a/extras/profiling/run.py b/extras/profiling/run.py new file mode 100644 index 0000000000..040e4389e7 --- /dev/null +++ b/extras/profiling/run.py @@ -0,0 +1,287 @@ +""" +Run the HTTPie benchmark suite with multiple environments. + +This script is configured in a way that, it will create +two (or more) isolated environments and compare the *last +commit* of this repository with it's master. + +> If you didn't commit yet, it won't be showing results. + +You can also pass --fresh, which would test the *last +commit* of this repository with a fresh copy of HTTPie +itself. This way even if you don't have an up-to-date +master branch, you can still compare it with the upstream's +master. + +You can also pass --complex to add 2 additional environments, +which would include additional dependencies like pyOpenSSL. + +Examples: + + # Run everything as usual, and compare last commit with master + $ python extras/profiling/run.py + + # Include complex environments + $ python extras/profiling/run.py --complex + + # Compare against a fresh copy + $ python extras/profiling/run.py --fresh + + # Compare against a custom branch of a custom repo + $ python extras/profiling/run.py --target-repo my_repo --target-branch my_branch + + # Debug changes made on this script (only run benchmarks once) + $ python extras/profiling/run.py --debug +""" + +import dataclasses +import shlex +import subprocess +import sys +import tempfile +import venv +from argparse import ArgumentParser, FileType +from contextlib import contextmanager +from dataclasses import dataclass +from pathlib import Path +from typing import (IO, Dict, Generator, Iterable, List, Optional, + Tuple) + +BENCHMARK_SCRIPT = Path(__file__).parent / 'benchmarks.py' +CURRENT_REPO = Path(__file__).parent.parent.parent + +GITHUB_URL = 'https://github.com/httpie/cli.git' +TARGET_BRANCH = 'master' + +# Additional dependencies for --complex +ADDITIONAL_DEPS = ('pyOpenSSL',) + + +def call(*args, **kwargs): + kwargs.setdefault('stdout', subprocess.DEVNULL) + return subprocess.check_call(*args, **kwargs) + + +class Environment: + """ + Each environment defines how to create an isolated instance + where we could install HTTPie and run benchmarks without any + environmental factors. + """ + + @contextmanager + def on_repo(self) -> Generator[Tuple[Path, Dict[str, str]], None, None]: + """ + Return the path to the python interpreter and the + environment variables (e.g HTTPIE_COMMAND) to be + used on the benchmarks. + """ + raise NotImplementedError + + +@dataclass +class HTTPieEnvironment(Environment): + repo_url: str + branch: Optional[str] = None + dependencies: Iterable[str] = () + + @contextmanager + def on_repo(self) -> Generator[Path, None, None]: + with tempfile.TemporaryDirectory() as directory_path: + directory = Path(directory_path) + + # Clone the repo + repo_path = directory / 'httpie' + call( + ['git', 'clone', self.repo_url, repo_path], + stderr=subprocess.DEVNULL, + ) + + if self.branch is not None: + call( + ['git', 'checkout', self.branch], + cwd=repo_path, + stderr=subprocess.DEVNULL, + ) + + # Prepare the environment + venv_path = directory / '.venv' + venv.create(venv_path, with_pip=True) + + # Install basic dependencies + python = venv_path / 'bin' / 'python' + call( + [ + python, + '-m', + 'pip', + 'install', + 'wheel', + 'pyperf==2.3.0', + *self.dependencies, + ] + ) + + # Create a wheel distribution of HTTPie + call([python, 'setup.py', 'bdist_wheel'], cwd=repo_path) + + # Install httpie + distribution_path = next((repo_path / 'dist').iterdir()) + call( + [python, '-m', 'pip', 'install', distribution_path], + cwd=repo_path, + ) + + http = venv_path / 'bin' / 'http' + yield python, {'HTTPIE_COMMAND': shlex.join([str(python), str(http)])} + + +@dataclass +class LocalCommandEnvironment(Environment): + local_command: str + + @contextmanager + def on_repo(self) -> Generator[Path, None, None]: + yield sys.executable, {'HTTPIE_COMMAND': self.local_command} + + +def dump_results( + results: List[str], + file: IO[str], + min_speed: Optional[str] = None +) -> None: + for result in results: + lines = result.strip().splitlines() + if min_speed is not None and "hidden" in lines[-1]: + lines[-1] = ( + 'Some benchmarks were hidden from this list ' + 'because their timings did not change in a ' + 'significant way (change was within the error ' + 'margin ±{margin}%).' + ).format(margin=min_speed) + result = '\n'.join(lines) + + print(result, file=file) + print("\n---\n", file=file) + + +def compare(*args, directory: Path, min_speed: Optional[str] = None): + compare_args = ['pyperf', 'compare_to', '--table', '--table-format=md', *args] + if min_speed: + compare_args.extend(['--min-speed', min_speed]) + return subprocess.check_output( + compare_args, + cwd=directory, + text=True, + ) + + +def run( + configs: List[Dict[str, Environment]], + file: IO[str], + debug: bool = False, + min_speed: Optional[str] = None, +) -> None: + result_directory = Path(tempfile.mkdtemp()) + results = [] + + current = 1 + total = sum(1 for config in configs for _ in config.items()) + + def iterate(env_name, status): + print( + f'Iteration: {env_name} ({current}/{total}) ({status})' + ' ' * 10, + end='\r', + flush=True, + ) + + for config in configs: + for env_name, env in config.items(): + iterate(env_name, 'setting up') + with env.on_repo() as (python, env_vars): + iterate(env_name, 'running benchmarks') + args = [python, BENCHMARK_SCRIPT, '-o', env_name] + if debug: + args.append('--debug-single-value') + call( + args, + cwd=result_directory, + env=env_vars, + ) + current += 1 + + results.append(compare( + *config.keys(), + directory=result_directory, + min_speed=min_speed + )) + + dump_results(results, file=file, min_speed=min_speed) + print('Results are available at:', result_directory) + + +def main() -> None: + parser = ArgumentParser() + parser.add_argument('--local-repo', default=CURRENT_REPO) + parser.add_argument('--local-branch', default=None) + parser.add_argument('--target-repo', default=CURRENT_REPO) + parser.add_argument('--target-branch', default=TARGET_BRANCH) + parser.add_argument( + '--fresh', + action='store_const', + const=GITHUB_URL, + dest='target_repo', + help='Clone the target repo from upstream GitHub URL', + ) + parser.add_argument( + '--complex', + action='store_true', + help='Add a second run, with a complex python environment.', + ) + parser.add_argument( + '--local-bin', + help='Run the suite with the given local binary in addition to' + ' existing runners. (E.g --local-bin $(command -v xh))', + ) + parser.add_argument( + '--file', + type=FileType('w'), + default=sys.stdout, + help='File to print the actual results', + ) + parser.add_argument( + '--min-speed', + help='Minimum of speed in percent to consider that a ' + 'benchmark is significant' + ) + parser.add_argument( + '--debug', + action='store_true', + ) + + options = parser.parse_args() + + configs = [] + + base_config = { + options.target_branch: HTTPieEnvironment(options.target_repo, options.target_branch), + 'this_branch': HTTPieEnvironment(options.local_repo, options.local_branch), + } + configs.append(base_config) + + if options.complex: + complex_config = { + env_name + + '-complex': dataclasses.replace(env, dependencies=ADDITIONAL_DEPS) + for env_name, env in base_config.items() + } + configs.append(complex_config) + + if options.local_bin: + base_config['binary'] = LocalCommandEnvironment(options.local_bin) + + run(configs, file=options.file, debug=options.debug, min_speed=options.min_speed) + + +if __name__ == '__main__': + main() diff --git a/extras/scripts/generate_man_pages.py b/extras/scripts/generate_man_pages.py new file mode 100644 index 0000000000..87939bd5b3 --- /dev/null +++ b/extras/scripts/generate_man_pages.py @@ -0,0 +1,188 @@ +import os +import re +from contextlib import contextmanager +from pathlib import Path +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 +from httpie.cli.definition import options as core_options, IS_MAN_PAGE +from httpie.cli.options import ParserSpec +from httpie.manager.cli import options as manager_options +from httpie.output.ui.rich_help import OptionsHighlighter, to_usage +from httpie.output.ui.rich_utils import render_as_string + + +assert IS_MAN_PAGE, 'CLI definition does not understand we’re building man pages' + +# Escape certain characters, so they are rendered properly on all terminals. +# +ESCAPE_MAP = { + '"': '\[dq]', + "'": '\[aq]', + '~': '\(ti', + '’': "\(ga", + '\\': '\e', +} +ESCAPE_MAP = {ord(key): value for key, value in ESCAPE_MAP.items()} + +EXTRAS_DIR = Path(__file__).parent.parent +MAN_PAGE_PATH = EXTRAS_DIR / 'man' +PROJECT_ROOT = EXTRAS_DIR.parent + +OPTION_HIGHLIGHT_RE = re.compile( + OptionsHighlighter.highlights[0] +) + + +class ManPageBuilder: + def __init__(self): + self.source = [] + + def title_line( + self, + full_name: str, + program_name: str, + program_version: str, + last_edit_date: str, + ) -> None: + self.source.append( + f'.TH {program_name} 1 "{last_edit_date}" ' + f'"{full_name} {program_version}" "{full_name} Manual"' + ) + + def set_name(self, program_name: str) -> None: + with self.section('NAME'): + self.write(program_name) + + def write(self, text: str, *, bold: bool = False) -> None: + if bold: + text = '.B ' + text + self.source.append(text) + + def separate(self) -> None: + 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: + text = ", ".join(map(self.boldify, options)) + if metavar: + text += f' {self.underline(metavar)}' + self.write(f'.IP "{text}"') + + def build(self) -> str: + return '\n'.join(self.source) + + @contextmanager + def section(self, section_name: str) -> Iterator[None]: + self.write(f'.SH {section_name}') + self.in_section = True + yield + self.in_section = False + + def underline(self, text: str) -> str: + return r'\fI\,{}\/\fR'.format(text) + + def boldify(self, text: str) -> str: + return r'\fB\,{}\/\fR'.format(text) + + +def _escape_and_dedent(text: str) -> str: + lines = [] + for should_act, line in enumerate(text.splitlines()): + # Only dedent after the first line. + if should_act: + if line.startswith(' '): + line = line[4:] + + lines.append(line) + return '\n'.join(lines).translate(ESCAPE_MAP) + + +def to_man_page(program_name: str, spec: ParserSpec, *, is_top_level_cmd: bool = False) -> str: + 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( + full_name='HTTPie', + program_name=program_name, + program_version=httpie.__version__, + last_edit_date=httpie.__date__, + ) + builder.set_name(program_name) + + with builder.section('SYNOPSIS'): + # `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'): + builder.write(spec.description) + if spec.man_page_hint: + builder.write(spec.man_page_hint) + + for index, group in enumerate(spec.groups, 1): + with builder.section(group.name): + if group.description: + builder.write(group.description) + + for argument in group.arguments: + if argument.is_hidden: + continue + + raw_arg = argument.serialize(isolation_mode=True) + + metavar = raw_arg.get('metavar') + if raw_arg.get('is_positional'): + # In case of positional arguments, metavar is always equal + # to the list of options (e.g `METHOD`). + metavar = None + builder.add_options(raw_arg['options'], metavar=metavar) + + desc = builder.format_desc(raw_arg.get('description', '')) + builder.write('\n' + desc + '\n') + + builder.separate() + + if spec.epilog: + with builder.section('SEE ALSO'): + builder.write(builder.format_desc(spec.epilog)) + + return builder.build() + + +def main() -> None: + for program_name, spec, config in [ + ('http', core_options, {}), + ('https', core_options, {}), + ('httpie', manager_options, {'is_top_level_cmd': True}), + ]: + with open((MAN_PAGE_PATH / program_name).with_suffix('.1'), 'w') as stream: + stream.write(to_man_page(program_name, spec, **config)) + + +if __name__ == '__main__': + main() diff --git a/httpie.png b/httpie.png deleted file mode 100644 index 0430ab8016..0000000000 Binary files a/httpie.png and /dev/null differ diff --git a/httpie/__init__.py b/httpie/__init__.py index 4971499970..a4dded9ced 100644 --- a/httpie/__init__.py +++ b/httpie/__init__.py @@ -1,19 +1,9 @@ """ -HTTPie - a CLI, cURL-like tool for humans. +HTTPie: modern, user-friendly command-line HTTP client for the API era. """ + +__version__ = '3.2.4' +__date__ = '2024-11-01' __author__ = 'Jakub Roztocil' -__version__ = '0.9.0-dev' __licence__ = 'BSD' - - -class ExitStatus: - """Exit status code constants.""" - OK = 0 - ERROR = 1 - ERROR_TIMEOUT = 2 - - # Used only when requested with --check-status: - ERROR_HTTP_3XX = 3 - ERROR_HTTP_4XX = 4 - ERROR_HTTP_5XX = 5 diff --git a/httpie/__main__.py b/httpie/__main__.py index bed0ec7b55..7b5042b800 100644 --- a/httpie/__main__.py +++ b/httpie/__main__.py @@ -1,10 +1,19 @@ -#!/usr/bin/env python """The main entry point. Invoke as `http' or `python -m httpie'. """ -import sys -from .core import main -if __name__ == '__main__': +def main(): + try: + from httpie.core import main + exit_status = main() + except KeyboardInterrupt: + from httpie.status import ExitStatus + exit_status = ExitStatus.ERROR_CTRL_C + + return exit_status.value + + +if __name__ == '__main__': # pragma: nocover + import sys sys.exit(main()) diff --git a/httpie/adapters.py b/httpie/adapters.py new file mode 100644 index 0000000000..8e2dd7397f --- /dev/null +++ b/httpie/adapters.py @@ -0,0 +1,13 @@ +from httpie.cli.dicts import HTTPHeadersDict +from requests.adapters import HTTPAdapter + + +class HTTPieHTTPAdapter(HTTPAdapter): + + def build_response(self, req, resp): + """Wrap the original headers with the `HTTPHeadersDict` + to preserve multiple headers that have the same name""" + + response = super().build_response(req, resp) + response.headers = HTTPHeadersDict(getattr(resp, 'headers', {})) + return response diff --git a/httpie/cli.py b/httpie/cli.py deleted file mode 100644 index 9b7a454e27..0000000000 --- a/httpie/cli.py +++ /dev/null @@ -1,570 +0,0 @@ -"""CLI arguments definition. - -NOTE: the CLI interface may change before reaching v1.0. - -""" -from textwrap import dedent, wrap -#noinspection PyCompatibility -from argparse import (RawDescriptionHelpFormatter, FileType, - OPTIONAL, ZERO_OR_MORE, SUPPRESS) - -from httpie import __doc__, __version__ -from httpie.plugins.builtin import BuiltinAuthPlugin -from httpie.plugins import plugin_manager -from httpie.sessions import DEFAULT_SESSIONS_DIR -from httpie.output.formatters.colors import AVAILABLE_STYLES, DEFAULT_STYLE -from httpie.input import (Parser, AuthCredentialsArgType, KeyValueArgType, - SEP_PROXY, SEP_CREDENTIALS, SEP_GROUP_ALL_ITEMS, - OUT_REQ_HEAD, OUT_REQ_BODY, OUT_RESP_HEAD, - OUT_RESP_BODY, OUTPUT_OPTIONS, - OUTPUT_OPTIONS_DEFAULT, PRETTY_MAP, - PRETTY_STDOUT_TTY_ONLY, SessionNameValidator, - readable_file_arg) - - -class HTTPieHelpFormatter(RawDescriptionHelpFormatter): - """A nicer help formatter. - - Help for arguments can be indented and contain new lines. - It will be de-dented and arguments in the help - will be separated by a blank line for better readability. - - - """ - def __init__(self, max_help_position=6, *args, **kwargs): - # A smaller indent for args help. - kwargs['max_help_position'] = max_help_position - super(HTTPieHelpFormatter, self).__init__(*args, **kwargs) - - def _split_lines(self, text, width): - text = dedent(text).strip() + '\n\n' - return text.splitlines() - -parser = Parser( - formatter_class=HTTPieHelpFormatter, - description='%s ' % __doc__.strip(), - epilog=dedent(""" - For every --OPTION there is also a --no-OPTION that reverts OPTION - to its default value. - - Suggestions and bug reports are greatly appreciated: - - https://github.com/jakubroztocil/httpie/issues - - """) -) - - -####################################################################### -# Positional arguments. -####################################################################### - -positional = parser.add_argument_group( - title='Positional Arguments', - description=dedent(""" - These arguments come after any flags and in the order they are listed here. - Only URL is required. - - """) -) -positional.add_argument( - 'method', - metavar='METHOD', - nargs=OPTIONAL, - default=None, - help=""" - The HTTP method to be used for the request (GET, POST, PUT, DELETE, ...). - - This argument can be omitted in which case HTTPie will use POST if there - is some data to be sent, otherwise GET: - - $ http example.org # => GET - $ http example.org hello=world # => POST - - """ -) -positional.add_argument( - 'url', - metavar='URL', - help=""" - The scheme defaults to 'http://' if the URL does not include one. - - You can also use a shorthand for localhost - - $ http :3000 # => http://localhost:3000 - $ http :/foo # => http://localhost/foo - - """ -) -positional.add_argument( - 'items', - metavar='REQUEST_ITEM', - nargs=ZERO_OR_MORE, - type=KeyValueArgType(*SEP_GROUP_ALL_ITEMS), - help=r""" - Optional key-value pairs to be included in the request. The separator used - determines the type: - - ':' HTTP headers: - - Referer:http://httpie.org Cookie:foo=bar User-Agent:bacon/1.0 - - '==' URL parameters to be appended to the request URI: - - search==httpie - - '=' Data fields to be serialized into a JSON object (with --json, -j) - or form data (with --form, -f): - - name=HTTPie language=Python description='CLI HTTP client' - - ':=' Non-string JSON data fields (only with --json, -j): - - awesome:=true amount:=42 colors:='["red", "green", "blue"]' - - '@' Form file fields (only with --form, -f): - - cs@~/Documents/CV.pdf - - '=@' A data field like '=', but takes a file path and embeds its content: - - essay=@Documents/essay.txt - - ':=@' A raw JSON field like ':=', but takes a file path and embeds its content: - - package:=@./package.json - - You can use a backslash to escape a colliding separator in the field name: - - field-name-with\:colon=value - - """ -) - - -####################################################################### -# Content type. -####################################################################### - -content_type = parser.add_argument_group( - title='Predefined Content Types', - description=None -) - -content_type.add_argument( - '--json', '-j', - action='store_true', - help=""" - (default) Data items from the command line are serialized as a JSON object. - The Content-Type and Accept headers are set to application/json - (if not specified). - - """ -) -content_type.add_argument( - '--form', '-f', - action='store_true', - help=""" - Data items from the command line are serialized as form fields. - - The Content-Type is set to application/x-www-form-urlencoded (if not - specified). The presence of any file fields results in a - multipart/form-data request. - - """ -) - - -####################################################################### -# Output processing -####################################################################### - -output_processing = parser.add_argument_group(title='Output Processing') - -output_processing.add_argument( - '--pretty', - dest='prettify', - default=PRETTY_STDOUT_TTY_ONLY, - choices=sorted(PRETTY_MAP.keys()), - help=""" - Controls output processing. The value can be "none" to not prettify - the output (default for redirected output), "all" to apply both colors - and formatting (default for terminal output), "colors", or "format". - - """ -) -output_processing.add_argument( - '--style', '-s', - dest='style', - metavar='STYLE', - default=DEFAULT_STYLE, - choices=AVAILABLE_STYLES, - help=""" - Output coloring style (default is "{default}"). One of: - -{available} - - For this option to work properly, please make sure that the $TERM - environment variable is set to "xterm-256color" or similar - (e.g., via `export TERM=xterm-256color' in your ~/.bashrc). - - """.format( - default=DEFAULT_STYLE, - available='\n'.join( - '{0}{1}'.format(8*' ', line.strip()) - for line in wrap(', '.join(sorted(AVAILABLE_STYLES)), 60) - ).rstrip(), - ) -) - - -####################################################################### -# Output options -####################################################################### -output_options = parser.add_argument_group(title='Output Options') - -output_options.add_argument( - '--print', '-p', - dest='output_options', - metavar='WHAT', - help=""" - String specifying what the output should contain: - - '{req_head}' request headers - '{req_body}' request body - '{res_head}' response headers - '{res_body}' response body - - The default behaviour is '{default}' (i.e., the response 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 response body is printed - by default. - - """ - .format( - req_head=OUT_REQ_HEAD, - req_body=OUT_REQ_BODY, - res_head=OUT_RESP_HEAD, - res_body=OUT_RESP_BODY, - default=OUTPUT_OPTIONS_DEFAULT, - ) -) -output_options.add_argument( - '--verbose', '-v', - dest='output_options', - action='store_const', - const=''.join(OUTPUT_OPTIONS), - help=""" - Print the whole request as well as the response. Shortcut for --print={0}. - - """ - .format(''.join(OUTPUT_OPTIONS)) -) -output_options.add_argument( - '--headers', '-h', - dest='output_options', - action='store_const', - const=OUT_RESP_HEAD, - help=""" - Print only the response headers. Shortcut for --print={0}. - - """ - .format(OUT_RESP_HEAD) -) -output_options.add_argument( - '--body', '-b', - dest='output_options', - action='store_const', - const=OUT_RESP_BODY, - help=""" - Print only the response body. Shortcut for --print={0}. - - """ - .format(OUT_RESP_BODY) -) - -output_options.add_argument( - '--stream', '-S', - action='store_true', - default=False, - help=""" - Always stream the output by line, i.e., behave like `tail -f'. - - Without --stream and with --pretty (either set or implied), - HTTPie fetches the whole response before it outputs the processed data. - - Set this option when you want to continuously display a prettified - long-lived response, such as one from the Twitter streaming API. - - It is useful also without --pretty: It ensures that the output is flushed - more often and in smaller chunks. - - """ -) -output_options.add_argument( - '--output', '-o', - type=FileType('a+b'), - dest='output_file', - metavar='FILE', - help=""" - Save output to FILE. If --download is set, then only the response body is - saved to the file. Other parts of the HTTP exchange are printed to stderr. - - """ - -) - -output_options.add_argument( - '--download', '-d', - action='store_true', - default=False, - help=""" - Do not print the response body to stdout. Rather, download it and store it - in a file. The filename is guessed unless specified with --output - [filename]. This action is similar to the default behaviour of wget. - - """ -) - -output_options.add_argument( - '--continue', '-c', - dest='download_resume', - action='store_true', - default=False, - help=""" - Resume an interrupted download. Note that the --output option needs to be - specified as well. - - """ -) - - -####################################################################### -# Sessions -####################################################################### - -sessions = parser.add_argument_group(title='Sessions')\ - .add_mutually_exclusive_group(required=False) - -session_name_validator = SessionNameValidator( - 'Session name contains invalid characters.' -) - -sessions.add_argument( - '--session', - metavar='SESSION_NAME_OR_PATH', - type=session_name_validator, - help=""" - Create, or reuse and update a session. Within a session, custom headers, - auth credential, as well as any cookies sent by the server persist between - requests. - - Session files are stored in: - - {session_dir}//.json. - - """ - .format(session_dir=DEFAULT_SESSIONS_DIR) -) -sessions.add_argument( - '--session-read-only', - metavar='SESSION_NAME_OR_PATH', - type=session_name_validator, - help=""" - Create or read a session without updating it form the request/response - exchange. - - """ -) - - -####################################################################### -# Authentication -####################################################################### - -# ``requests.request`` keyword arguments. -auth = parser.add_argument_group(title='Authentication') -auth.add_argument( - '--auth', '-a', - metavar='USER[:PASS]', - type=AuthCredentialsArgType(SEP_CREDENTIALS), - help=""" - If only the username is provided (-a username), HTTPie will prompt - for the password. - - """, -) - -_auth_plugins = plugin_manager.get_auth_plugins() -auth.add_argument( - '--auth-type', - choices=[plugin.auth_type for plugin in _auth_plugins], - default=_auth_plugins[0].auth_type, - help=""" - The authentication mechanism to be used. Defaults to "{default}". - - {types} - - """ - .format(default=_auth_plugins[0].auth_type, types='\n '.join( - '"{type}": {name}{package}{description}'.format( - type=plugin.auth_type, - name=plugin.name, - package=( - '' if issubclass(plugin, BuiltinAuthPlugin) - else ' (provided by %s)' % plugin.package_name - ), - description=( - '' if not plugin.description else - '\n ' + ('\n '.join(wrap(plugin.description))) - ) - ) - for plugin in _auth_plugins - )), -) - - -####################################################################### -# Network -####################################################################### - -network = parser.add_argument_group(title='Network') - -network.add_argument( - '--proxy', - default=[], - action='append', - metavar='PROTOCOL:PROXY_URL', - type=KeyValueArgType(SEP_PROXY), - help=""" - String mapping protocol to the URL of the proxy - (e.g. http:http://foo.bar:3128). You can specify multiple proxies with - different protocols. - - """ -) -network.add_argument( - '--follow', - default=False, - action='store_true', - help=""" - Set this flag if full redirects are allowed (e.g. re-POST-ing of data at - new Location). - - """ -) -network.add_argument( - '--verify', - default='yes', - help=""" - Set to "no" to skip checking the host's SSL certificate. You can also pass - the path to a CA_BUNDLE file for private certs. You can also set the - REQUESTS_CA_BUNDLE environment variable. Defaults to "yes". - - """ -) - -network.add_argument( - '--cert', - default=None, - type=readable_file_arg, - help=""" - 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 - specify --certkey separately. - - """ -) - -network.add_argument( - '--certkey', - default=None, - type=readable_file_arg, - help=""" - The private key to use with SSL. Only needed if --cert is given and the - certificate file does not contain the private key. - - """ -) - -network.add_argument( - '--timeout', - type=float, - default=30, - metavar='SECONDS', - help=""" - The connection timeout of the request in seconds. The default value is - 30 seconds. - - """ -) -network.add_argument( - '--check-status', - default=False, - action='store_true', - help=""" - By default, HTTPie exits with 0 when no network or other fatal errors - occur. This flag instructs HTTPie to also check the HTTP status code and - exit with an error if the status indicates one. - - 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 - 3xx (Redirect) and --follow hasn't been set, then the exit status is 3. - Also an error message is written to stderr if stdout is redirected. - - """ -) - - -####################################################################### -# Troubleshooting -####################################################################### - -troubleshooting = parser.add_argument_group(title='Troubleshooting') - -troubleshooting.add_argument( - '--ignore-stdin', - action='store_true', - default=False, - help=""" - Do not attempt to read stdin. - - """ -) -troubleshooting.add_argument( - '--help', - action='help', - default=SUPPRESS, - help=""" - Show this help message and exit. - - """ -) -troubleshooting.add_argument( - '--version', - action='version', - version=__version__, - help=""" - Show version and exit. - - """ -) -troubleshooting.add_argument( - '--traceback', - action='store_true', - default=False, - help=""" - Prints exception traceback should one occur. - - """ -) -troubleshooting.add_argument( - '--debug', - action='store_true', - default=False, - help=""" - Prints exception traceback should one occur, and also other information - that is useful for debugging HTTPie itself and for reporting bugs. - - """ -) diff --git a/httpie/cli/__init__.py b/httpie/cli/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/httpie/cli/argparser.py b/httpie/cli/argparser.py new file mode 100644 index 0000000000..9bf09b3b73 --- /dev/null +++ b/httpie/cli/argparser.py @@ -0,0 +1,613 @@ +import argparse +import errno +import os +import re +import sys +from argparse import RawDescriptionHelpFormatter +from textwrap import dedent +from urllib.parse import urlsplit + +from requests.utils import get_netrc_auth + +from .argtypes import ( + AuthCredentials, SSLCredentials, KeyValueArgType, + PARSED_DEFAULT_FORMAT_OPTIONS, + parse_auth, + parse_format_options, +) +from .constants import ( + HTTP_GET, HTTP_POST, BASE_OUTPUT_OPTIONS, OUTPUT_OPTIONS, OUTPUT_OPTIONS_DEFAULT, + OUTPUT_OPTIONS_DEFAULT_OFFLINE, OUTPUT_OPTIONS_DEFAULT_STDOUT_REDIRECTED, + OUT_RESP_BODY, PRETTY_MAP, PRETTY_STDOUT_TTY_ONLY, RequestType, + SEPARATOR_CREDENTIALS, + SEPARATOR_GROUP_ALL_ITEMS, SEPARATOR_GROUP_DATA_ITEMS, URL_SCHEME_RE, +) +from .exceptions import ParseError +from .requestitems import RequestItems +from ..context import Environment +from ..plugins.registry import plugin_manager +from ..utils import ExplicitNullAuth, get_content_type + + +class HTTPieHelpFormatter(RawDescriptionHelpFormatter): + """A nicer help formatter. + + Help for arguments can be indented and contain new lines. + It will be de-dented and arguments in the help + will be separated by a blank line for better readability. + + + """ + + def __init__(self, max_help_position=6, *args, **kwargs): + # A smaller indent for args help. + kwargs['max_help_position'] = max_help_position + super().__init__(*args, **kwargs) + + def _split_lines(self, text, width): + text = dedent(text).strip() + '\n\n' + return text.splitlines() + + def add_usage(self, usage, actions, groups, prefix=None): + # Only display the positional arguments + displayed_actions = [ + action + for action in actions + if not action.option_strings + ] + + _, exception, _ = sys.exc_info() + if ( + isinstance(exception, argparse.ArgumentError) + and len(exception.args) >= 1 + and isinstance(exception.args[0], argparse.Action) + ): + # add_usage path is also taken when you pass an invalid option, + # e.g --style=invalid. If something like that happens, we want + # to include to action that caused to the invalid usage into + # the list of actions we are displaying. + displayed_actions.insert(0, exception.args[0]) + + super().add_usage( + usage, + displayed_actions, + groups, + prefix="usage:\n " + ) + + +# TODO: refactor and design type-annotated data structures +# for raw args + parsed args and keep things immutable. +class BaseHTTPieArgumentParser(argparse.ArgumentParser): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.env = None + self.args = None + self.has_stdin_data = False + self.has_input_data = False + + # noinspection PyMethodOverriding + def parse_args( + self, + env: Environment, + args=None, + namespace=None + ) -> argparse.Namespace: + self.env = env + self.args, no_options = self.parse_known_args(args, namespace) + if self.args.debug: + self.args.traceback = True + self.has_stdin_data = ( + self.env.stdin + and not getattr(self.args, 'ignore_stdin', False) + and not self.env.stdin_isatty + ) + self.has_input_data = self.has_stdin_data or getattr(self.args, 'raw', None) is not None + return self.args + + # noinspection PyShadowingBuiltins + def _print_message(self, message, file=None): + # Sneak in our stderr/stdout. + if hasattr(self, 'root'): + env = self.root.env + else: + env = self.env + + if env is not None: + file = { + sys.stdout: env.stdout, + sys.stderr: env.stderr, + None: env.stderr + }.get(file, file) + + if not hasattr(file, 'buffer') and isinstance(message, str): + message = message.encode(env.stdout_encoding) + super()._print_message(message, file) + + +class HTTPieManagerArgumentParser(BaseHTTPieArgumentParser): + def parse_known_args(self, args=None, namespace=None): + try: + return super().parse_known_args(args, namespace) + except SystemExit as exc: + if not hasattr(self, 'root') and exc.code == 2: # Argument Parser Error + raise argparse.ArgumentError(None, None) + raise + + +class HTTPieArgumentParser(BaseHTTPieArgumentParser): + """Adds additional logic to `argparse.ArgumentParser`. + + Handles all input (CLI args, file args, stdin), applies defaults, + and performs extra validation. + + """ + + def __init__(self, *args, formatter_class=HTTPieHelpFormatter, **kwargs): + kwargs.setdefault('add_help', False) + super().__init__(*args, formatter_class=formatter_class, **kwargs) + + # noinspection PyMethodOverriding + def parse_args( + self, + env: Environment, + args=None, + namespace=None + ) -> argparse.Namespace: + self.env = env + self.env.args = namespace = namespace or argparse.Namespace() + self.args, no_options = super().parse_known_args(args, namespace) + if self.args.debug: + self.args.traceback = True + self.has_stdin_data = ( + self.env.stdin + and not self.args.ignore_stdin + and not self.env.stdin_isatty + ) + self.has_input_data = self.has_stdin_data or self.args.raw is not None + # Arguments processing and environment setup. + self._apply_no_options(no_options) + self._process_request_type() + self._process_download_options() + self._setup_standard_streams() + self._process_output_options() + self._process_pretty_options() + self._process_format_options() + self._guess_method() + self._parse_items() + self._process_url() + self._process_auth() + self._process_ssl_cert() + + if self.args.raw is not None: + self._body_from_input(self.args.raw) + elif self.has_stdin_data: + self._body_from_file(self.env.stdin) + + if self.args.compress: + # TODO: allow --compress with --chunked / --multipart + if self.args.chunked: + self.error('cannot combine --compress and --chunked') + if self.args.multipart: + self.error('cannot combine --compress and --multipart') + + return self.args + + def _process_request_type(self): + request_type = self.args.request_type + self.args.json = request_type is RequestType.JSON + self.args.multipart = request_type is RequestType.MULTIPART + self.args.form = request_type in { + RequestType.FORM, + RequestType.MULTIPART, + } + + def _process_url(self): + if self.args.url.startswith('://'): + # Paste URL & add space shortcut: `http ://pie.dev` → `http://pie.dev` + self.args.url = self.args.url[3:] + if not URL_SCHEME_RE.match(self.args.url): + if os.path.basename(self.env.program_name) == 'https': + scheme = 'https://' + else: + scheme = self.args.default_scheme + '://' + + # See if we're using curl style shorthand for localhost (:3000/foo) + shorthand = re.match(r'^:(?!:)(\d*)(/?.*)$', self.args.url) + if shorthand: + port = shorthand.group(1) + rest = shorthand.group(2) + self.args.url = scheme + 'localhost' + if port: + self.args.url += ':' + port + self.args.url += rest + else: + self.args.url = scheme + self.args.url + + def _setup_standard_streams(self): + """ + Modify `env.stdout` and `env.stdout_isatty` based on args, if needed. + + """ + + self.args.output_file_specified = bool(self.args.output_file) + if self.args.download: + # FIXME: Come up with a cleaner solution. + if not self.args.output_file and not self.env.stdout_isatty: + # Use stdout as the download output file. + self.args.output_file = self.env.stdout + # With `--download`, we write everything that would normally go to + # `stdout` to `stderr` instead. Let's replace the stream so that + # we don't have to use many `if`s throughout the codebase. + # The response body will be treated separately. + self.env.stdout = self.env.stderr + self.env.stdout_isatty = self.env.stderr_isatty + + elif self.args.output_file: + # When not `--download`ing, then `--output` simply replaces + # `stdout`. The file is opened for appending, which isn't what + # we want in this case. + self.args.output_file.seek(0) + try: + self.args.output_file.truncate() + except OSError as e: + if e.errno == errno.EINVAL: + # E.g. /dev/null on Linux. + pass + else: + raise + self.env.stdout = self.args.output_file + self.env.stdout_isatty = False + + if self.args.quiet: + self.env.quiet = self.args.quiet + self.env.stderr = self.env.devnull + if not (self.args.output_file_specified and not self.args.download): + self.env.stdout = self.env.devnull + self.env.apply_warnings_filter() + + def _process_ssl_cert(self): + from httpie.ssl_ import _is_key_file_encrypted + + if self.args.cert_key_pass is None: + self.args.cert_key_pass = SSLCredentials(None) + + if ( + self.args.cert_key is not None + and self.args.cert_key_pass.value is None + and _is_key_file_encrypted(self.args.cert_key) + ): + self.args.cert_key_pass.prompt_password(self.args.cert_key) + + def _process_auth(self): + # TODO: refactor & simplify this method. + self.args.auth_plugin = None + default_auth_plugin = plugin_manager.get_auth_plugins()[0] + auth_type_set = self.args.auth_type is not None + url = urlsplit(self.args.url) + + if self.args.auth is None and not auth_type_set: + if url.username is not None: + # Handle http://username:password@hostname/ + username = url.username + password = url.password or '' + self.args.auth = AuthCredentials( + key=username, + value=password, + sep=SEPARATOR_CREDENTIALS, + orig=SEPARATOR_CREDENTIALS.join([username, password]) + ) + + if self.args.auth is not None or auth_type_set: + if not self.args.auth_type: + self.args.auth_type = default_auth_plugin.auth_type + plugin = plugin_manager.get_auth_plugin(self.args.auth_type)() + + if (not self.args.ignore_netrc + and self.args.auth is None + and plugin.netrc_parse): + # Only host needed, so it’s OK URL not finalized. + netrc_credentials = get_netrc_auth(self.args.url) + if netrc_credentials: + self.args.auth = AuthCredentials( + key=netrc_credentials[0], + value=netrc_credentials[1], + sep=SEPARATOR_CREDENTIALS, + orig=SEPARATOR_CREDENTIALS.join(netrc_credentials) + ) + + if plugin.auth_require and self.args.auth is None: + self.error('--auth required') + + plugin.raw_auth = self.args.auth + self.args.auth_plugin = plugin + already_parsed = isinstance(self.args.auth, AuthCredentials) + + if self.args.auth is None or not plugin.auth_parse: + self.args.auth = plugin.get_auth() + else: + if already_parsed: + # from the URL + credentials = self.args.auth + else: + credentials = parse_auth(self.args.auth) + + if (not credentials.has_password() + and plugin.prompt_password): + if self.args.ignore_stdin: + # Non-tty stdin read by now + self.error( + 'Unable to prompt for passwords because' + ' --ignore-stdin is set.' + ) + credentials.prompt_password(url.netloc) + + if (credentials.key and credentials.value): + plugin.raw_auth = credentials.key + ":" + credentials.value + + self.args.auth = plugin.get_auth( + username=credentials.key, + password=credentials.value, + ) + if not self.args.auth and self.args.ignore_netrc: + # Set a no-op auth to force requests to ignore .netrc + # + self.args.auth = ExplicitNullAuth() + + def _apply_no_options(self, no_options): + """For every `--no-OPTION` in `no_options`, set `args.OPTION` to + its default value. This allows for un-setting of options, e.g., + specified in config. + + """ + invalid = [] + + for option in no_options: + if not option.startswith('--no-'): + invalid.append(option) + continue + + # --no-option => --option + inverted = '--' + option[5:] + for action in self._actions: + if inverted in action.option_strings: + setattr(self.args, action.dest, action.default) + break + else: + invalid.append(option) + + if invalid: + self.error(f'unrecognized arguments: {" ".join(invalid)}') + + def _body_from_file(self, fd): + """Read the data from a file-like object. + + Bytes are always read. + + """ + self._ensure_one_data_source(self.args.data, self.args.files) + self.args.data = getattr(fd, 'buffer', fd) + + def _body_from_input(self, data): + """Read the data from the CLI. + + """ + self._ensure_one_data_source(self.has_stdin_data, self.args.data, + self.args.files) + self.args.data = data.encode() + + def _ensure_one_data_source(self, *other_sources): + """There can only be one source of input request data. + + """ + if any(other_sources): + self.error('Request body (from stdin, --raw or a file) and request ' + 'data (key=value) cannot be mixed. Pass ' + '--ignore-stdin to let key/value take priority. ' + 'See https://httpie.io/docs#scripting for details.') + + def _guess_method(self): + """Set `args.method` if not specified to either POST or GET + based on whether the request has data or not. + + """ + if self.args.method is None: + # Invoked as `http URL'. + assert not self.args.request_items + if self.has_input_data: + self.args.method = HTTP_POST + else: + self.args.method = HTTP_GET + + # FIXME: False positive, e.g., "localhost" matches but is a valid URL. + elif not re.match('^[a-zA-Z]+$', self.args.method): + # Invoked as `http URL item+'. The URL is now in `args.method` + # and the first ITEM is now incorrectly in `args.url`. + try: + # Parse the URL as an ITEM and store it as the first ITEM arg. + self.args.request_items.insert(0, KeyValueArgType( + *SEPARATOR_GROUP_ALL_ITEMS).__call__(self.args.url)) + + except argparse.ArgumentTypeError as e: + if self.args.traceback: + raise + self.error(e.args[0]) + + else: + # Set the URL correctly + self.args.url = self.args.method + # Infer the method + has_data = ( + self.has_input_data + or any( + item.sep in SEPARATOR_GROUP_DATA_ITEMS + for item in self.args.request_items) + ) + self.args.method = HTTP_POST if has_data else HTTP_GET + + def _parse_items(self): + """ + Parse `args.request_items` into `args.headers`, `args.data`, + `args.params`, and `args.files`. + + """ + try: + request_items = RequestItems.from_args( + request_item_args=self.args.request_items, + request_type=self.args.request_type, + ) + except ParseError as e: + if self.args.traceback: + raise + self.error(e.args[0]) + else: + self.args.headers = request_items.headers + self.args.data = request_items.data + self.args.files = request_items.files + self.args.params = request_items.params + self.args.multipart_data = request_items.multipart_data + + if self.args.files and not self.args.form: + # `http url @/path/to/file` + request_file = None + for key, file in self.args.files.items(): + if key != '': + self.error( + 'Invalid file fields (perhaps you meant --form?):' + f' {",".join(self.args.files.keys())}') + if request_file is not None: + self.error("Can't read request from multiple files") + request_file = file + + fn, fd, ct = request_file + self.args.files = {} + + self._body_from_file(fd) + + if 'Content-Type' not in self.args.headers: + content_type = get_content_type(fn) + if content_type: + self.args.headers['Content-Type'] = content_type + + def _process_output_options(self): + """Apply defaults to output options, or validate the provided ones. + + The default output options are stdout-type-sensitive. + + """ + + def check_options(value, option): + unknown = set(value) - OUTPUT_OPTIONS + if unknown: + self.error(f'Unknown output options: {option}={",".join(unknown)}') + + if self.args.verbose: + self.args.all = True + + if self.args.output_options is None: + if self.args.verbose >= 2: + self.args.output_options = ''.join(OUTPUT_OPTIONS) + elif self.args.verbose == 1: + self.args.output_options = ''.join(BASE_OUTPUT_OPTIONS) + elif self.args.offline: + self.args.output_options = OUTPUT_OPTIONS_DEFAULT_OFFLINE + elif not self.env.stdout_isatty: + self.args.output_options = OUTPUT_OPTIONS_DEFAULT_STDOUT_REDIRECTED + else: + self.args.output_options = OUTPUT_OPTIONS_DEFAULT + + if self.args.output_options_history is None: + self.args.output_options_history = self.args.output_options + + check_options(self.args.output_options, '--print') + check_options(self.args.output_options_history, '--history-print') + + if self.args.download and OUT_RESP_BODY in self.args.output_options: + # Response body is always downloaded with --download and it goes + # through a different routine, so we remove it. + self.args.output_options = str( + set(self.args.output_options) - set(OUT_RESP_BODY)) + + def _process_pretty_options(self): + if self.args.prettify == PRETTY_STDOUT_TTY_ONLY: + self.args.prettify = PRETTY_MAP[ + 'all' if self.env.stdout_isatty else 'none'] + elif (self.args.prettify and self.env.is_windows + and self.args.output_file): + self.error('Only terminal output can be colorized on Windows.') + else: + # noinspection PyTypeChecker + self.args.prettify = PRETTY_MAP[self.args.prettify] + + def _process_download_options(self): + if self.args.offline: + self.args.download = False + self.args.download_resume = False + return + if not self.args.download: + if self.args.download_resume: + self.error('--continue only works with --download') + if self.args.download_resume and not ( + self.args.download and self.args.output_file): + self.error('--continue requires --output to be specified') + + def _process_format_options(self): + format_options = self.args.format_options or [] + parsed_options = PARSED_DEFAULT_FORMAT_OPTIONS + for options_group in format_options: + parsed_options = parse_format_options(options_group, defaults=parsed_options) + self.args.format_options = parsed_options + + def print_manual(self): + from httpie.output.ui import man_pages + + if man_pages.is_available(self.env.program_name): + man_pages.display_for(self.env, self.env.program_name) + return None + + text = self.format_help() + with self.env.rich_console.pager(): + self.env.rich_console.print( + text, + highlight=False + ) + + def print_usage(self, file): + from rich.text import Text + from httpie.output.ui import rich_help + + whitelist = set() + _, exception, _ = sys.exc_info() + if ( + isinstance(exception, argparse.ArgumentError) + and len(exception.args) >= 1 + and isinstance(exception.args[0], argparse.Action) + and exception.args[0].option_strings + ): + # add_usage path is also taken when you pass an invalid option, + # e.g --style=invalid. If something like that happens, we want + # to include to action that caused to the invalid usage into + # the list of actions we are displaying. + whitelist.add(exception.args[0].option_strings[0]) + + usage_text = Text('usage', style='bold') + usage_text.append(':\n ') + usage_text.append(rich_help.to_usage(self.spec, whitelist=whitelist)) + self.env.rich_error_console.print(usage_text) + + def error(self, message): + """Prints a usage message incorporating the message to stderr and + exits.""" + self.print_usage(sys.stderr) + self.env.rich_error_console.print( + dedent( + f''' + [bold]error[/bold]: + {message} + + [bold]for more information[/bold]: + run '{self.prog} --help' or visit https://httpie.io/docs/cli + '''.rstrip() + ) + ) + self.exit(2) diff --git a/httpie/cli/argtypes.py b/httpie/cli/argtypes.py new file mode 100644 index 0000000000..8f19c3c51e --- /dev/null +++ b/httpie/cli/argtypes.py @@ -0,0 +1,275 @@ +import argparse +import getpass +import os +import sys +from copy import deepcopy +from typing import List, Optional, Union + +from .constants import DEFAULT_FORMAT_OPTIONS, SEPARATOR_CREDENTIALS +from ..sessions import VALID_SESSION_NAME_PATTERN + + +class KeyValueArg: + """Base key-value pair parsed from CLI.""" + + def __init__(self, key: str, value: Optional[str], sep: str, orig: str): + self.key = key + self.value = value + self.sep = sep + self.orig = orig + + def __eq__(self, other: 'KeyValueArg'): + return self.__dict__ == other.__dict__ + + def __repr__(self): + return repr(self.__dict__) + + +class SessionNameValidator: + + def __init__(self, error_message: str): + self.error_message = error_message + + def __call__(self, value: str) -> str: + # Session name can be a path or just a name. + if (os.path.sep not in value + and not VALID_SESSION_NAME_PATTERN.search(value)): + raise argparse.ArgumentError(None, self.error_message) + return value + + +class Escaped(str): + """Represents an escaped character.""" + + def __repr__(self): + return f"Escaped({repr(str(self))})" + + +class KeyValueArgType: + """A key-value pair argument type used with `argparse`. + + Parses a key-value arg and constructs a `KeyValueArg` instance. + Used for headers, form data, and other key-value pair types. + + """ + + key_value_class = KeyValueArg + + def __init__(self, *separators: str): + self.separators = separators + self.special_characters = set() + for separator in separators: + self.special_characters.update(separator) + + def __call__(self, s: str) -> KeyValueArg: + """Parse raw string arg and return `self.key_value_class` instance. + + The best of `self.separators` is determined (first found, longest). + Back slash escaped characters aren't considered as separators + (or parts thereof). Literal back slash characters have to be escaped + as well (r'\\'). + + """ + tokens = self.tokenize(s) + + # Sorting by length ensures that the longest one will be + # chosen as it will overwrite any shorter ones starting + # at the same position in the `found` dictionary. + separators = sorted(self.separators, key=len) + + for i, token in enumerate(tokens): + + if isinstance(token, Escaped): + continue + + found = {} + for sep in separators: + pos = token.find(sep) + if pos != -1: + found[pos] = sep + + if found: + # Starting first, longest separator found. + sep = found[min(found.keys())] + + key, value = token.split(sep, 1) + + # Any preceding tokens are part of the key. + key = ''.join(tokens[:i]) + key + + # Any following tokens are part of the value. + value += ''.join(tokens[i + 1:]) + + break + + else: + raise argparse.ArgumentTypeError(f'{s!r} is not a valid value') + + return self.key_value_class(key=key, value=value, sep=sep, orig=s) + + def tokenize(self, s: str) -> List[Union[str, Escaped]]: + r"""Tokenize the raw arg string + + There are only two token types - strings and escaped characters: + + >>> KeyValueArgType('=').tokenize(r'foo\=bar\\baz') + ['foo', Escaped('='), 'bar\\\\baz'] + + """ + tokens = [''] + characters = iter(s) + for char in characters: + if char == '\\': + char = next(characters, '') + if char not in self.special_characters: + tokens[-1] += '\\' + char + else: + tokens.extend([Escaped(char), '']) + else: + tokens[-1] += char + return tokens + + +class PromptMixin: + def _prompt_password(self, prompt: str) -> str: + prompt_text = f'http: {prompt}: ' + try: + return self._getpass(prompt_text) + except (EOFError, KeyboardInterrupt): + sys.stderr.write('\n') + sys.exit(0) + + @staticmethod + def _getpass(prompt): + # To allow easy mocking. + return getpass.getpass(str(prompt)) + + +class SSLCredentials(PromptMixin): + """Represents the passphrase for the certificate's key.""" + + def __init__(self, value: Optional[str]) -> None: + self.value = value + + def prompt_password(self, key_file: str) -> None: + self.value = self._prompt_password(f'passphrase for {key_file}') + + +class AuthCredentials(KeyValueArg, PromptMixin): + """Represents parsed credentials.""" + + def has_password(self) -> bool: + return self.value is not None + + def prompt_password(self, host: str) -> None: + self.value = self._prompt_password(f'password for {self.key}@{host}:') + + +class AuthCredentialsArgType(KeyValueArgType): + """A key-value arg type that parses credentials.""" + + key_value_class = AuthCredentials + + def __call__(self, s): + """Parse credentials from `s`. + + ("username" or "username:password"). + + """ + try: + return super().__call__(s) + except argparse.ArgumentTypeError: + # No password provided, will prompt for it later. + return self.key_value_class( + key=s, + value=None, + sep=SEPARATOR_CREDENTIALS, + orig=s + ) + + +parse_auth = AuthCredentialsArgType(SEPARATOR_CREDENTIALS) + + +def readable_file_arg(filename): + try: + with open(filename, 'rb'): + return filename + except OSError as ex: + raise argparse.ArgumentTypeError(f'{ex.filename}: {ex.strerror}') + + +def parse_format_options(s: str, defaults: Optional[dict]) -> dict: + """ + Parse `s` and update `defaults` with the parsed values. + + >>> parse_format_options( + ... defaults={'json': {'indent': 4, 'sort_keys': True}}, + ... s='json.indent:2,json.sort_keys:False', + ... ) + {'json': {'indent': 2, 'sort_keys': False}} + + """ + value_map = { + 'true': True, + 'false': False, + } + options = deepcopy(defaults or {}) + for option in s.split(','): + try: + path, value = option.lower().split(':') + section, key = path.split('.') + except ValueError: + raise argparse.ArgumentTypeError(f'invalid option {option!r}') + + if value in value_map: + parsed_value = value_map[value] + else: + if value.isnumeric(): + parsed_value = int(value) + else: + parsed_value = value + + if defaults is None: + options.setdefault(section, {}) + else: + try: + default_value = defaults[section][key] + except KeyError: + raise argparse.ArgumentTypeError( + f'invalid key {path!r}') + + default_type, parsed_type = type(default_value), type(parsed_value) + if parsed_type is not default_type: + raise argparse.ArgumentTypeError( + 'invalid value' + f' {value!r} in {option!r}' + f' (expected {default_type.__name__}' + f' got {parsed_type.__name__})' + ) + + options[section][key] = parsed_value + + return options + + +PARSED_DEFAULT_FORMAT_OPTIONS = parse_format_options( + s=','.join(DEFAULT_FORMAT_OPTIONS), + defaults=None, +) + + +def response_charset_type(encoding: str) -> str: + try: + ''.encode(encoding) + except LookupError: + raise argparse.ArgumentTypeError( + f'{encoding!r} is not a supported encoding') + return encoding + + +def response_mime_type(mime_type: str) -> str: + if mime_type.count('/') != 1: + raise argparse.ArgumentTypeError( + f'{mime_type!r} doesn’t look like a mime type; use type/subtype') + return mime_type diff --git a/httpie/cli/constants.py b/httpie/cli/constants.py new file mode 100644 index 0000000000..09ca19e4af --- /dev/null +++ b/httpie/cli/constants.py @@ -0,0 +1,134 @@ +"""Parsing and processing of CLI input (args, auth credentials, files, stdin). + +""" +import enum +import re + + +URL_SCHEME_RE = re.compile(r'^[a-z][a-z0-9.+-]*://', re.IGNORECASE) + +HTTP_POST = 'POST' +HTTP_GET = 'GET' +HTTP_OPTIONS = 'OPTIONS' + +# Various separators used in args +SEPARATOR_HEADER = ':' +SEPARATOR_HEADER_EMPTY = ';' +SEPARATOR_CREDENTIALS = ':' +SEPARATOR_PROXY = ':' +SEPARATOR_HEADER_EMBED = ':@' +SEPARATOR_DATA_STRING = '=' +SEPARATOR_DATA_RAW_JSON = ':=' +SEPARATOR_FILE_UPLOAD = '@' +SEPARATOR_FILE_UPLOAD_TYPE = ';type=' # in already parsed file upload path only +SEPARATOR_DATA_EMBED_FILE_CONTENTS = '=@' +SEPARATOR_DATA_EMBED_RAW_JSON_FILE = ':=@' +SEPARATOR_QUERY_PARAM = '==' +SEPARATOR_QUERY_EMBED_FILE = '==@' + +# Separators that become request data +SEPARATOR_GROUP_DATA_ITEMS = frozenset({ + SEPARATOR_DATA_STRING, + SEPARATOR_DATA_RAW_JSON, + SEPARATOR_FILE_UPLOAD, + SEPARATOR_DATA_EMBED_FILE_CONTENTS, + SEPARATOR_DATA_EMBED_RAW_JSON_FILE +}) + +SEPARATORS_GROUP_MULTIPART = frozenset({ + SEPARATOR_DATA_STRING, + SEPARATOR_DATA_EMBED_FILE_CONTENTS, + SEPARATOR_FILE_UPLOAD, +}) + +# Separators for items whose value is a filename to be embedded +SEPARATOR_GROUP_DATA_EMBED_ITEMS = frozenset({ + SEPARATOR_HEADER_EMBED, + SEPARATOR_QUERY_EMBED_FILE, + SEPARATOR_DATA_EMBED_FILE_CONTENTS, + SEPARATOR_DATA_EMBED_RAW_JSON_FILE, +}) + +# Separators for nested JSON items +SEPARATOR_GROUP_NESTED_JSON_ITEMS = frozenset([ + SEPARATOR_DATA_STRING, + SEPARATOR_DATA_RAW_JSON, + SEPARATOR_DATA_EMBED_FILE_CONTENTS, + SEPARATOR_DATA_EMBED_RAW_JSON_FILE, +]) + +# Separators allowed in ITEM arguments +SEPARATOR_GROUP_ALL_ITEMS = frozenset({ + SEPARATOR_HEADER, + SEPARATOR_HEADER_EMPTY, + SEPARATOR_HEADER_EMBED, + SEPARATOR_QUERY_PARAM, + SEPARATOR_QUERY_EMBED_FILE, + SEPARATOR_DATA_STRING, + SEPARATOR_DATA_RAW_JSON, + SEPARATOR_FILE_UPLOAD, + SEPARATOR_DATA_EMBED_FILE_CONTENTS, + SEPARATOR_DATA_EMBED_RAW_JSON_FILE, +}) + +# Output options +OUT_REQ_HEAD = 'H' +OUT_REQ_BODY = 'B' +OUT_RESP_HEAD = 'h' +OUT_RESP_BODY = 'b' +OUT_RESP_META = 'm' + +BASE_OUTPUT_OPTIONS = frozenset({ + OUT_REQ_HEAD, + OUT_REQ_BODY, + OUT_RESP_HEAD, + OUT_RESP_BODY, +}) + +OUTPUT_OPTIONS = frozenset({ + *BASE_OUTPUT_OPTIONS, + OUT_RESP_META, +}) + +# Pretty + + +class PrettyOptions(enum.Enum): + STDOUT_TTY_ONLY = enum.auto() + + +PRETTY_MAP = { + 'all': ['format', 'colors'], + 'colors': ['colors'], + 'format': ['format'], + 'none': [] +} +PRETTY_STDOUT_TTY_ONLY = PrettyOptions.STDOUT_TTY_ONLY + + +DEFAULT_FORMAT_OPTIONS = [ + 'headers.sort:true', + 'json.format:true', + 'json.indent:4', + 'json.sort_keys:true', + 'xml.format:true', + 'xml.indent:2', +] +SORTED_FORMAT_OPTIONS = [ + 'headers.sort:true', + 'json.sort_keys:true', +] +SORTED_FORMAT_OPTIONS_STRING = ','.join(SORTED_FORMAT_OPTIONS) +UNSORTED_FORMAT_OPTIONS_STRING = ','.join( + option.replace('true', 'false') for option in SORTED_FORMAT_OPTIONS) + +# Defaults +OUTPUT_OPTIONS_DEFAULT = OUT_RESP_HEAD + OUT_RESP_BODY +OUTPUT_OPTIONS_DEFAULT_STDOUT_REDIRECTED = OUT_RESP_BODY +OUTPUT_OPTIONS_DEFAULT_OFFLINE = OUT_REQ_HEAD + OUT_REQ_BODY + + +class RequestType(enum.Enum): + FORM = enum.auto() + MULTIPART = enum.auto() + JSON = enum.auto() diff --git a/httpie/cli/definition.py b/httpie/cli/definition.py new file mode 100644 index 0000000000..843b29c9cf --- /dev/null +++ b/httpie/cli/definition.py @@ -0,0 +1,956 @@ +from __future__ import annotations + +import os +import textwrap +from argparse import FileType + +from httpie import __doc__, __version__ +from httpie.cli.argtypes import (KeyValueArgType, SessionNameValidator, + SSLCredentials, readable_file_arg, + response_charset_type, response_mime_type) +from httpie.cli.constants import (BASE_OUTPUT_OPTIONS, DEFAULT_FORMAT_OPTIONS, + OUT_REQ_BODY, OUT_REQ_HEAD, OUT_RESP_BODY, + OUT_RESP_HEAD, OUT_RESP_META, OUTPUT_OPTIONS, + OUTPUT_OPTIONS_DEFAULT, PRETTY_MAP, + PRETTY_STDOUT_TTY_ONLY, + SEPARATOR_GROUP_ALL_ITEMS, SEPARATOR_PROXY, + SORTED_FORMAT_OPTIONS_STRING, + UNSORTED_FORMAT_OPTIONS_STRING, RequestType) +from httpie.cli.options import ParserSpec, Qualifiers, to_argparse +from httpie.output.formatters.colors import (AUTO_STYLE, DEFAULT_STYLE, BUNDLED_STYLES, + get_available_styles) +from httpie.plugins.builtin import BuiltinAuthPlugin +from httpie.plugins.registry import plugin_manager +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( + 'http', + description=f'{__doc__.strip()} ', + epilog=""" + For every --OPTION there is also a --no-OPTION that reverts OPTION + to its default value. + + Suggestions and bug reports are greatly appreciated: + https://github.com/httpie/cli/issues + """, + source_file=__file__ +) + +####################################################################### +# Positional arguments. +####################################################################### + +positional_arguments = options.add_group( + 'Positional arguments', + description=""" + These arguments come after any flags and in the order they are listed here. + Only URL is required. + """, +) + +positional_arguments.add_argument( + dest='method', + metavar='METHOD', + nargs=Qualifiers.OPTIONAL, + default=None, + short_help='The HTTP method to be used for the request (GET, POST, PUT, DELETE, ...).', + help=""" + The HTTP method to be used for the request (GET, POST, PUT, DELETE, ...). + + This argument can be omitted in which case HTTPie will use POST if there + is some data to be sent, otherwise GET: + + $ http example.org # => GET + $ http example.org hello=world # => POST + + """, +) +positional_arguments.add_argument( + dest='url', + metavar='URL', + short_help='The request URL.', + help=""" + The request URL. Scheme defaults to 'http://' if the URL + does not include one. (You can override this with: --default-scheme=http/https) + + You can also use a shorthand for localhost + + $ http :3000 # => http://localhost:3000 + $ http :/foo # => http://localhost/foo + + """, +) +positional_arguments.add_argument( + dest='request_items', + metavar='REQUEST_ITEM', + nargs=Qualifiers.ZERO_OR_MORE, + default=None, + type=KeyValueArgType(*SEPARATOR_GROUP_ALL_ITEMS), + short_help=( + 'HTTPie’s request items syntax for specifying HTTP headers, JSON/Form' + 'data, files, and URL parameters.' + ), + nested_options=[ + ('HTTP Headers', 'Name:Value', 'Arbitrary HTTP header, e.g X-API-Token:123'), + ('URL Parameters', 'name==value', 'Querystring parameter to the URL, e.g limit==50'), + ('Data Fields', 'field=value', 'Data fields to be serialized as JSON (default) or Form Data (with --form)'), + ('Raw JSON Fields', 'field:=json', 'Data field for real JSON types.'), + ('File upload Fields', 'field@/dir/file', 'Path field for uploading a file.'), + ], + help=r""" + Optional key-value pairs to be included in the request. The separator used + determines the type: + + ':' HTTP headers: + + Referer:https://httpie.io Cookie:foo=bar User-Agent:bacon/1.0 + + '==' URL parameters to be appended to the request URI: + + search==httpie + + '=' Data fields to be serialized into a JSON object (with --json, -j) + or form data (with --form, -f): + + name=HTTPie language=Python description='CLI HTTP client' + + ':=' Non-string JSON data fields (only with --json, -j): + + awesome:=true amount:=42 colors:='["red", "green", "blue"]' + + '@' Form file fields (only with --form or --multipart): + + cv@~/Documents/CV.pdf + cv@'~/Documents/CV.pdf;type=application/pdf' + + '=@' A data field like '=', but takes a file path and embeds its content: + + essay=@Documents/essay.txt + + ':=@' A raw JSON field like ':=', but takes a file path and embeds its content: + + package:=@./package.json + + You can use a backslash to escape a colliding separator in the field name: + + field-name-with\:colon=value + + """, +) + +####################################################################### +# Content type. +####################################################################### + +content_types = options.add_group('Predefined content types') + +content_types.add_argument( + '--json', + '-j', + action='store_const', + const=RequestType.JSON, + dest='request_type', + short_help='(default) Serialize data items from the command line as a JSON object.', + help=""" + (default) Data items from the command line are serialized as a JSON object. + The Content-Type and Accept headers are set to application/json + (if not specified). + + """, +) +content_types.add_argument( + '--form', + '-f', + action='store_const', + const=RequestType.FORM, + dest='request_type', + short_help='Serialize data items from the command line as form field data.', + help=""" + Data items from the command line are serialized as form fields. + + The Content-Type is set to application/x-www-form-urlencoded (if not + specified). The presence of any file fields results in a + multipart/form-data request. + + """, +) +content_types.add_argument( + '--multipart', + action='store_const', + const=RequestType.MULTIPART, + dest='request_type', + short_help=( + 'Similar to --form, but always sends a multipart/form-data ' + 'request (i.e., even without files).' + ) +) +content_types.add_argument( + '--boundary', + short_help=( + 'Specify a custom boundary string for multipart/form-data requests. ' + 'Only has effect only together with --form.' + ) +) +content_types.add_argument( + '--raw', + short_help='Pass raw request data without extra processing.', + help=""" + This option allows you to pass raw request data without extra processing + (as opposed to the structured request items syntax): + + $ http --raw='data' pie.dev/post + + You can achieve the same by piping the data via stdin: + + $ echo data | http pie.dev/post + + Or have HTTPie load the raw data from a file: + + $ http pie.dev/post @data.txt + + + """, +) + +####################################################################### +# Content processing. +####################################################################### + +processing_options = options.add_group('Content processing options') + +processing_options.add_argument( + '--compress', + '-x', + action='count', + default=0, + short_help='Compress the content with Deflate algorithm.', + help=""" + Content compressed (encoded) with Deflate algorithm. + The Content-Encoding header is set to deflate. + + Compression is skipped if it appears that compression ratio is + negative. Compression can be forced by repeating the argument. + + """, +) + + +####################################################################### +# Output processing +####################################################################### + + +def format_style_help(available_styles, *, isolation_mode: bool = False): + text = """ + Output coloring style (default is "{default}"). It can be one of: + + {available_styles} + """ + if isolation_mode: + text += '\n\n' + text += 'For finding out all available styles in your system, try:\n\n' + text += ' $ http --style\n' + text += textwrap.dedent(""" + The "{auto_style}" style follows your terminal's ANSI color styles. + For non-{auto_style} styles to work properly, please make sure that the + $TERM environment variable is set to "xterm-256color" or similar + (e.g., via `export TERM=xterm-256color' in your ~/.bashrc). + """) + + if isolation_mode: + available_styles = sorted(BUNDLED_STYLES) + + available_styles_text = '\n'.join( + f' {line.strip()}' + for line in textwrap.wrap(', '.join(available_styles), 60) + ).strip() + return text.format( + default=DEFAULT_STYLE, + available_styles=available_styles_text, + auto_style=AUTO_STYLE, + ) + + +_sorted_kwargs = { + 'action': 'append_const', + 'const': SORTED_FORMAT_OPTIONS_STRING, + 'dest': 'format_options', +} +_unsorted_kwargs = { + 'action': 'append_const', + 'const': UNSORTED_FORMAT_OPTIONS_STRING, + 'dest': 'format_options', +} + +output_processing = options.add_group('Output processing') + +output_processing.add_argument( + '--pretty', + dest='prettify', + default=PRETTY_STDOUT_TTY_ONLY, + choices=sorted(PRETTY_MAP.keys()), + short_help='Control the processing of console outputs.', + help=""" + Controls output processing. The value can be "none" to not prettify + the output (default for redirected output), "all" to apply both colors + and formatting (default for terminal output), "colors", or "format". + + """, +) +output_processing.add_argument( + '--style', + '-s', + dest='style', + metavar='STYLE', + default=DEFAULT_STYLE, + action='lazy_choices', + getter=get_available_styles, + short_help=f'Output coloring style (default is "{DEFAULT_STYLE}").', + help_formatter=format_style_help, +) + +# The closest approx. of the documented resetting to default via --no-