Compare commits

...

20 Commits

Author SHA1 Message Date
46fa79eff8 Add PoC of --send-term-headers #805 2021-04-07 15:21:59 +02:00
fc45bf0fe3 Explicitly require setuptools, httpie/plugins/manager.py imports pkg_resources (#1049) 2021-03-30 22:54:53 +02:00
56c4ba1794 Warn against non-ascii in setup.cfg (#1041) 2021-02-24 14:56:57 +01:00
8f83bfe767 Replace typography quotes (#1040)
fixes #1039
2021-02-24 14:38:18 +01:00
a32ad344dd Update README.rst 2021-02-23 12:51:06 +01:00
c53fbe5ae3 Typo 2021-02-23 12:22:35 +01:00
070ba9fa5a Tweaks 2021-02-18 13:48:48 +01:00
82ee071362 Tweaks 2021-02-18 13:47:58 +01:00
97dffb35a2 Spacing 2021-02-18 13:46:10 +01:00
18af03ac18 Issue templates tweaks II 2021-02-18 13:44:29 +01:00
904dd4107a Issue templates tweaks 2021-02-18 13:42:59 +01:00
8efabc86e6 Issue templates (#1031)
* add bug report template

* add feature request template

* add other template

* a httpie to an httpie

Co-authored-by: bl-ue <54780737+bl-ue@users.noreply.github.com>

* Update .github/ISSUE_TEMPLATE/bug_report.md

Co-authored-by: bl-ue <54780737+bl-ue@users.noreply.github.com>

* Update .github/ISSUE_TEMPLATE/bug_report.md

Co-authored-by: bl-ue <54780737+bl-ue@users.noreply.github.com>

* Update .github/ISSUE_TEMPLATE/bug_report.md

Co-authored-by: bl-ue <54780737+bl-ue@users.noreply.github.com>

* minor changes

* add discord

Co-authored-by: bl-ue <54780737+bl-ue@users.noreply.github.com>
2021-02-18 12:55:15 +01:00
cc20488f49 Formatting 2021-02-14 13:34:20 +01:00
b918972862 CHANGELOG #1032 2021-02-14 13:33:38 +01:00
84c7327057 Correctly handle single-byte Content-Range (#1032)
HTTPie previously failed if it continued a download with a single byte left.
2021-02-14 13:30:58 +01:00
e944dbd7fa 2.5.0-dev 2021-02-06 13:34:04 +01:00
157f3a1840 2021 2021-02-06 13:29:02 +01:00
61dbadb730 Tests 2021-02-06 13:21:21 +01:00
7be25d0751 Update brew formula to 2.4.0 2021-02-06 11:54:15 +01:00
5d5a8b4091 Typo 2021-02-06 11:26:15 +01:00
17 changed files with 173 additions and 38 deletions

40
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,40 @@
---
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 the latest version of HTTPie.
---
**What are the steps to reproduce the problem?**
1.
2.
3.
**What is the expected result?**
**What happens instead?**
**Debug output**
Please re-run the command with `--debug`, then copy the entire command & output and paste both below:
```
$ http --debug <COMPLETE ARGUMENT LIST THAT TRIGGERS THE ERROR>
<COMPLETE OUTPUT>
```
**Provide any additional information, screenshots, or code examples below:**

View File

@ -0,0 +1,24 @@
---
name: Feature request
about: Suggest an enhancement for HTTPie
title: ''
labels: "new, enhancement"
assignees: ''
---
**Checklist**
- [ ] I've searched for similar feature requests.
---
**What enhancement would you like to see?**
**What problem does it solve?**
E.g. “I'm always frustrated when [...]”, “Im trying to do […] so that […]”.
**Provide any additional information, screenshots, or code examples below:**

10
.github/ISSUE_TEMPLATE/other.md vendored Normal file
View File

@ -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

View File

@ -7,10 +7,15 @@ This project adheres to `Semantic Versioning <https://semver.org/>`_.
`2.5.0-dev`_ (unreleased)
-------------------------
* Fixed ``--continue --download`` with a single byte to be downloaded left. (`#1032`_)
`2.4.0`_ (2021-02-06) `2.4.0`_ (2021-02-06)
--------------------- ---------------------
* Added support for ``--session`` cookie expiration based on ``Set-Cookie: max-age=<n>``. (`#1029`_) * Added support for ``--session`` cookie expiration based on ``Set-Cookie: max-age=<n>``. (`#1029`_)
* Show a ``--check-status`` warning with ``--quiet`` as well, not only when the output si redirected. (`#1026`_) * Show a ``--check-status`` warning with ``--quiet`` as well, not only when the output is redirected. (`#1026`_)
* Fixed upload with ``--session`` (`#1020`_). * Fixed upload with ``--session`` (`#1020`_).
* Fixed a missing blank line between request and response (`#1006`_). * Fixed a missing blank line between request and response (`#1006`_).
@ -492,3 +497,4 @@ This project adheres to `Semantic Versioning <https://semver.org/>`_.
.. _#1020: https://github.com/httpie/httpie/issues/1020 .. _#1020: https://github.com/httpie/httpie/issues/1020
.. _#1026: https://github.com/httpie/httpie/issues/1026 .. _#1026: https://github.com/httpie/httpie/issues/1026
.. _#1029: https://github.com/httpie/httpie/issues/1029 .. _#1029: https://github.com/httpie/httpie/issues/1029
.. _#1032: https://github.com/httpie/httpie/issues/1032

View File

@ -1,4 +1,4 @@
Copyright © 2012-2020 Jakub Roztocil <jakub@roztocil.co> Copyright © 2012-2021 Jakub Roztocil <jakub@roztocil.co>
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:

View File

@ -12,6 +12,13 @@ import hashlib
import requests import requests
VERSIONS = {
# By default, we use the latest packages. But sometimes Requests has a maximum supported versions.
# Take a look here before making a release: <https://github.com/psf/requests/blob/master/setup.py>
'idna': '2.10',
}
PACKAGES = [ PACKAGES = [
'httpie', 'httpie',
'Pygments', 'Pygments',
@ -26,10 +33,17 @@ PACKAGES = [
def get_package_meta(package_name): def get_package_meta(package_name):
api_url = f'https://pypi.python.org/pypi/{package_name}/json' api_url = f'https://pypi.org/pypi/{package_name}/json'
resp = requests.get(api_url).json() resp = requests.get(api_url).json()
hasher = hashlib.sha256() hasher = hashlib.sha256()
for release in resp['urls']: version = VERSIONS.get(package_name)
if package_name not in VERSIONS:
# Latest version
release_bundle = resp['urls']
else:
release_bundle = resp['releases'][version]
for release in release_bundle:
download_url = release['url'] download_url = release['url']
if download_url.endswith('.tar.gz'): if download_url.endswith('.tar.gz'):
hasher.update(requests.get(download_url).content) hasher.update(requests.get(download_url).content)

View File

@ -8,26 +8,31 @@ class Httpie < Formula
include Language::Python::Virtualenv include Language::Python::Virtualenv
desc "User-friendly cURL replacement (command-line HTTP client)" desc "User-friendly cURL replacement (command-line HTTP client)"
homepage "https://httpie.org/" homepage "https://httpie.io/"
url "https://files.pythonhosted.org/packages/b4/d4/712645808103f2d15c281b9eacd184c88754ef7e9a322d9a30ba343fd341/httpie-2.3.0.tar.gz" url "https://files.pythonhosted.org/packages/17/3a/90fb6702e600f5ba7d38d147bbc0b0a1e47159e3e244737319c98c140420/httpie-2.4.0.tar.gz"
sha256 "d540571991d07329d217c31bf1ff95fd217957da2aa2def09bcfa0c0fca0cf96" sha256 "4d1bf5779cf6c9007351cfcaa20bd19947267dc026af09246db6006a8927d8c6"
license "BSD-3-Clause" license "BSD-3-Clause"
head "https://github.com/httpie/httpie.git" head "https://github.com/httpie/httpie.git"
livecheck do bottle do
url :stable rebuild 1
sha256 cellar: :any_skip_relocation, arm64_big_sur: "a01ce8767f6ea88eb8e7894347ba64eb29294053a8ee91eed44dfaf0ab5e7ea2"
sha256 cellar: :any_skip_relocation, big_sur: "bdffeff349595ed3c528ed791d568e308b0877246b49e05e867143ba3415a70f"
sha256 cellar: :any_skip_relocation, catalina: "ba0627d70f0ee49c64677f5554881ebd56371f47d45196b6564680089ce69152"
sha256 cellar: :any_skip_relocation, mojave: "0b87901e88bdcf53c55c5138677087b4621c5aaf1fca67b53b730d5a2fd5a40a"
sha256 cellar: :any_skip_relocation, high_sierra: "87e7348b6fb40fd8e4f7597937952469601962189e62d321b8cb4fa421e035ef"
end end
depends_on "python@3.9" depends_on "python@3.9"
resource "Pygments" do resource "Pygments" do
url "https://files.pythonhosted.org/packages/5d/0e/ff13c055b014d634ed17e9e9345a312c28ec6a06448ba6d6ccfa77c3b5e8/Pygments-2.7.2.tar.gz" url "https://files.pythonhosted.org/packages/e1/86/8059180e8217299079d8719c6e23d674aadaba0b1939e25e0cc15dcf075b/Pygments-2.7.4.tar.gz"
sha256 "381985fcc551eb9d37c52088a32914e00517e57f4a21609f48141ba08e193fa0" sha256 "df49d09b498e83c1a73128295860250b0b7edd4c723a32e9bc0d295c7c2ec337"
end end
resource "requests" do resource "requests" do
url "https://files.pythonhosted.org/packages/da/67/672b422d9daf07365259958912ba533a0ecab839d4084c487a5fe9a5405f/requests-2.24.0.tar.gz" url "https://files.pythonhosted.org/packages/6b/47/c14abc08432ab22dc18b9892252efaf005ab44066de871e72a38d6af464b/requests-2.25.1.tar.gz"
sha256 "b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b" sha256 "27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"
end end
resource "requests-toolbelt" do resource "requests-toolbelt" do
@ -36,13 +41,13 @@ class Httpie < Formula
end end
resource "certifi" do resource "certifi" do
url "https://files.pythonhosted.org/packages/40/a7/ded59fa294b85ca206082306bba75469a38ea1c7d44ea7e1d64f5443d67a/certifi-2020.6.20.tar.gz" url "https://files.pythonhosted.org/packages/06/a9/cd1fd8ee13f73a4d4f491ee219deeeae20afefa914dfb4c130cfc9dc397a/certifi-2020.12.5.tar.gz"
sha256 "5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3" sha256 "1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"
end end
resource "urllib3" do resource "urllib3" do
url "https://files.pythonhosted.org/packages/76/d9/bbbafc76b18da706451fa91bc2ebe21c0daf8868ef3c30b869ac7cb7f01d/urllib3-1.25.11.tar.gz" url "https://files.pythonhosted.org/packages/d7/8d/7ee68c6b48e1ec8d41198f694ecdc15f7596356f2ff8e6b1420300cf5db3/urllib3-1.26.3.tar.gz"
sha256 "8d7eaa5a82a1cac232164990f04874c594c9453ec55eef02eab885aa02fc17a2" sha256 "de3eedaad74a2683334e282005cd8d7f22f4d55fa690a2a1020a416cb0a47e73"
end end
resource "idna" do resource "idna" do
@ -51,8 +56,8 @@ class Httpie < Formula
end end
resource "chardet" do resource "chardet" do
url "https://files.pythonhosted.org/packages/fc/bb/a5768c230f9ddb03acc9ef3f0d4a3cf93462473795d18e9535498c8f929d/chardet-3.0.4.tar.gz" url "https://files.pythonhosted.org/packages/ee/2d/9cdc2b527e127b4c9db64b86647d567985940ac3698eeabc7ffaccb4ea61/chardet-4.0.0.tar.gz"
sha256 "84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae" sha256 "0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"
end end
resource "PySocks" do resource "PySocks" do
@ -65,7 +70,7 @@ class Httpie < Formula
end end
test do test do
raw_url = "https://raw.githubusercontent.com/Homebrew/homebrew-core/master/Formula/httpie.rb" raw_url = "https://raw.githubusercontent.com/Homebrew/homebrew-core/HEAD/Formula/httpie.rb"
assert_match "PYTHONPATH", shell_output("#{bin}/http --ignore-stdin #{raw_url}") assert_match "PYTHONPATH", shell_output("#{bin}/http --ignore-stdin #{raw_url}")
end end
end end

View File

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

View File

@ -671,6 +671,16 @@ network.add_argument(
""" """
) )
network.add_argument(
# <https://github.com/httpie/httpie/issues/805>
'--send-term-headers',
'-T',
default=False,
action='store_true',
help="""
"""
)
####################################################################### #######################################################################
# SSL # SSL

View File

@ -34,9 +34,10 @@ def collect_messages(
args: argparse.Namespace, args: argparse.Namespace,
config_dir: Path, config_dir: Path,
request_body_read_callback: Callable[[bytes], None] = None, request_body_read_callback: Callable[[bytes], None] = None,
extra_headers: dict = None,
) -> Iterable[Union[requests.PreparedRequest, requests.Response]]: ) -> Iterable[Union[requests.PreparedRequest, requests.Response]]:
httpie_session = None httpie_session = None
httpie_session_headers = None base_headers = {}
if args.session or args.session_read_only: if args.session or args.session_read_only:
httpie_session = get_httpie_session( httpie_session = get_httpie_session(
config_dir=config_dir, config_dir=config_dir,
@ -44,11 +45,11 @@ def collect_messages(
host=args.headers.get('Host'), host=args.headers.get('Host'),
url=args.url, url=args.url,
) )
httpie_session_headers = httpie_session.headers base_headers = httpie_session.headers
base_headers.update(extra_headers)
request_kwargs = make_request_kwargs( request_kwargs = make_request_kwargs(
args=args, args=args,
base_headers=httpie_session_headers, base_headers=base_headers,
request_body_read_callback=request_body_read_callback request_body_read_callback=request_body_read_callback
) )
send_kwargs = make_send_kwargs(args) send_kwargs = make_send_kwargs(args)

View File

@ -1,6 +1,7 @@
import sys import sys
import os import os
from pathlib import Path from pathlib import Path
from pprint import pprint
from typing import IO, Optional from typing import IO, Optional
@ -37,6 +38,13 @@ class Environment:
stderr_isatty: bool = stderr.isatty() stderr_isatty: bool = stderr.isatty()
colors = 256 colors = 256
program_name: str = 'http' program_name: str = 'http'
# <https://github.com/httpie/httpie/issues/805>
env_lang = os.environ.get('LANG', '').split('_')[0]
env_term = os.environ.get('TERM', '')
env_columns = os.environ.get('COLUMNS', '')
env_lines = os.environ.get('LINES', '')
if not is_windows: if not is_windows:
if curses: if curses:
try: try:
@ -126,3 +134,14 @@ class Environment:
def log_error(self, msg, level='error'): def log_error(self, msg, level='error'):
assert level in ['error', 'warning'] assert level in ['error', 'warning']
self._orig_stderr.write(f'\n{self.program_name}: {level}: {msg}\n\n') self._orig_stderr.write(f'\n{self.program_name}: {level}: {msg}\n\n')
def get_term_info_headers(env:Environment):
# <https://github.com/httpie/httpie/issues/805>
return {
'Accept-Language': env.env_lang,
'X-Terminal': env.env_term,
'X-Terminal-Attached': 'yes' if env.stdin.isatty() else 'no',
'X-Terminal-Columns': env.env_columns,
'X-Terminal-Lines': env.env_lines,
}

View File

@ -11,7 +11,7 @@ from requests import __version__ as requests_version
from httpie import __version__ as httpie_version from httpie import __version__ as httpie_version
from httpie.cli.constants import OUT_REQ_BODY, OUT_REQ_HEAD, OUT_RESP_BODY, OUT_RESP_HEAD from httpie.cli.constants import OUT_REQ_BODY, OUT_REQ_HEAD, OUT_RESP_BODY, OUT_RESP_HEAD
from httpie.client import collect_messages from httpie.client import collect_messages
from httpie.context import Environment from httpie.context import Environment, get_term_info_headers
from httpie.downloads import Downloader from httpie.downloads import Downloader
from httpie.output.writer import write_message, write_stream, MESSAGE_SEPARATOR_BYTES from httpie.output.writer import write_message, write_stream, MESSAGE_SEPARATOR_BYTES
from httpie.plugins.registry import plugin_manager from httpie.plugins.registry import plugin_manager
@ -160,8 +160,12 @@ def program(args: argparse.Namespace, env: Environment) -> ExitStatus:
args.follow = True # --download implies --follow. args.follow = True # --download implies --follow.
downloader = Downloader(output_file=args.output_file, progress_file=env.stderr, resume=args.download_resume) downloader = Downloader(output_file=args.output_file, progress_file=env.stderr, resume=args.download_resume)
downloader.pre_request(args.headers) downloader.pre_request(args.headers)
messages = collect_messages(args=args, config_dir=env.config.directory, messages = collect_messages(
request_body_read_callback=request_body_read_callback) args=args,
config_dir=env.config.directory,
extra_headers=get_term_info_headers(env) if args.send_term_headers else None,
request_body_read_callback=request_body_read_callback,
)
force_separator = False force_separator = False
prev_with_body = False prev_with_body = False

View File

@ -81,7 +81,7 @@ def parse_content_range(content_range: str, resumed_from: int) -> int:
# last-byte-pos value, is invalid. The recipient of an invalid # last-byte-pos value, is invalid. The recipient of an invalid
# byte-content-range- spec MUST ignore it and any content # byte-content-range- spec MUST ignore it and any content
# transferred along with it." # transferred along with it."
if (first_byte_pos >= last_byte_pos if (first_byte_pos > last_byte_pos
or (instance_length is not None or (instance_length is not None
and instance_length <= last_byte_pos)): and instance_length <= last_byte_pos)):
raise ContentRangeError( raise ContentRangeError(

View File

@ -1,3 +1,7 @@
# Please keep all characters in this file in ASCII
# distutils uses system's locale to interpret it and not everybody
# uses UTF-8. See https://github.com/httpie/httpie/issues/1039
# for an example
[wheel] [wheel]
@ -13,7 +17,7 @@ addopts = --tb=native --doctest-modules
exclude = .git,.idea,__pycache__,build,dist,.pytest_cache,*.egg-info exclude = .git,.idea,__pycache__,build,dist,.pytest_cache,*.egg-info
# <http://pycodestyle.pycqa.org/en/latest/intro.html#error-codes> # <http://pycodestyle.pycqa.org/en/latest/intro.html#error-codes>
# E241 - multiple spaces after , # E241 - multiple spaces after ','
# E501 - line too long # E501 - line too long
# W503 - line break before binary operator # W503 - line break before binary operator
ignore = E241,E501,W503 ignore = E241,E501,W503

View File

@ -42,6 +42,7 @@ install_requires = [
'requests[socks]>=2.22.0', 'requests[socks]>=2.22.0',
'Pygments>=2.5.2', 'Pygments>=2.5.2',
'requests-toolbelt>=0.9.1', 'requests-toolbelt>=0.9.1',
'setuptools',
] ]
install_requires_win_only = [ install_requires_win_only = [
'colorama>=0.2.4', 'colorama>=0.2.4',

View File

@ -30,6 +30,9 @@ class TestDownloadUtils:
assert parse('bytes 100-199/200', 100) == 200 assert parse('bytes 100-199/200', 100) == 200
assert parse('bytes 100-199/*', 100) == 200 assert parse('bytes 100-199/*', 100) == 200
# single byte
assert parse('bytes 100-100/*', 100) == 101
# missing # missing
pytest.raises(ContentRangeError, parse, None, 100) pytest.raises(ContentRangeError, parse, None, 100)
@ -45,9 +48,6 @@ class TestDownloadUtils:
# invalid byte-range-resp-spec # invalid byte-range-resp-spec
pytest.raises(ContentRangeError, parse, 'bytes 100-99/199', 100) pytest.raises(ContentRangeError, parse, 'bytes 100-99/199', 100)
# invalid byte-range-resp-spec
pytest.raises(ContentRangeError, parse, 'bytes 100-100/*', 100)
@pytest.mark.parametrize('header, expected_filename', [ @pytest.mark.parametrize('header, expected_filename', [
('attachment; filename=hello-WORLD_123.txt', 'hello-WORLD_123.txt'), ('attachment; filename=hello-WORLD_123.txt', 'hello-WORLD_123.txt'),
('attachment; filename=".hello-WORLD_123.txt"', 'hello-WORLD_123.txt'), ('attachment; filename=".hello-WORLD_123.txt"', 'hello-WORLD_123.txt'),

View File

@ -79,7 +79,7 @@ def test_terminal_request_headers_response_headers(httpbin):
def test_raw_request_headers_response_headers(httpbin): def test_raw_request_headers_response_headers(httpbin):
r = http('--print=Hh', httpbin + '/get') r = http('--print=Hh', httpbin + '/get', env=MockEnvironment(stdout_isatty=False))
assert_output_matches(r, [Expect.REQUEST_HEADERS, Expect.RESPONSE_HEADERS]) assert_output_matches(r, [Expect.REQUEST_HEADERS, Expect.RESPONSE_HEADERS])
@ -97,10 +97,7 @@ def test_raw_headers_and_body():
def test_raw_body(): def test_raw_body():
r = http( r = http('--print=B', '--offline', 'pie.dev', 'AAA=BBB', env=MockEnvironment(stdout_isatty=False))
'--print=B', '--offline', 'pie.dev', 'AAA=BBB',
env=MockEnvironment(stdout_isatty=False),
)
assert_output_matches(r, RAW_BODY) assert_output_matches(r, RAW_BODY)