Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions httpie/uploads.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,39 @@ def _prepare_file_for_upload(
return file


def _normalize_content_length_header(
value: Optional[Union[int, bytes, str]]
) -> Optional[int]:
if value is None:
return None

if isinstance(value, int):
if value < 0:
raise ValueError('Invalid Content-Length header value')
return value

if isinstance(value, bytes):
try:
value = value.decode('ascii')
except UnicodeDecodeError as exc:
raise ValueError('Invalid Content-Length header value') from exc

if isinstance(value, str):
value = value.strip()
if value == '':
raise ValueError('Invalid Content-Length header value')

try:
normalized = int(value)
except (TypeError, ValueError) as exc:
raise ValueError('Invalid Content-Length header value') from exc

if normalized < 0:
raise ValueError('Invalid Content-Length header value')

return normalized


def prepare_request_body(
env: Environment,
raw_body: Union[str, bytes, IO, 'MultipartEncoder', RequestDataDict],
Expand All @@ -210,6 +243,10 @@ def prepare_request_body(
else:
return body

content_length_header_value = _normalize_content_length_header(
content_length_header_value
)

if is_file_like:
return _prepare_file_for_upload(
env,
Expand Down
29 changes: 29 additions & 0 deletions tests/test_uploads.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from httpie.client import FORM_CONTENT_TYPE
from httpie.compat import is_windows
from httpie.status import ExitStatus
from httpie.uploads import prepare_request_body
from .utils import (
MockEnvironment, StdinBytesIO, http,
HTTP_OK,
Expand Down Expand Up @@ -91,6 +92,34 @@ def test_chunked_raw(httpbin_with_chunked_support):
assert 'Transfer-Encoding: chunked' in r


def test_content_length_header_normalization(monkeypatch):
env = MockEnvironment(stdin_isatty=False)
captured = {}

def fake_prepare(env_arg, file, callback, chunked=False, content_length_header_value=None):
captured['value'] = content_length_header_value
return b''

monkeypatch.setattr('httpie.uploads._prepare_file_for_upload', fake_prepare)

prepare_request_body(
env,
StdinBytesIO(b'data'),
lambda chunk: chunk,
content_length_header_value=b'4',
)
assert captured['value'] == 4

with pytest.raises(ValueError):
prepare_request_body(
env,
StdinBytesIO(b'data'),
lambda chunk: chunk,
content_length_header_value=b'invalid',
)
assert captured['value'] == 4


@contextlib.contextmanager
def stdin_processes(httpbin, *args, warn_threshold=0.1):
process_1 = subprocess.Popen(
Expand Down
Loading