Add --path-as-is

Close #895
This commit is contained in:
Jakub Roztocil 2020-04-13 20:18:01 +02:00
parent 5754e33a75
commit 684a4708d7
4 changed files with 62 additions and 0 deletions

View File

@ -8,6 +8,7 @@ This project adheres to `Semantic Versioning <https://semver.org/>`_.
`2.1.0-dev`_ (unreleased) `2.1.0-dev`_ (unreleased)
------------------------- -------------------------
* Add ``--path-as-is`` to bypass dot segment (``/../`` or ``/./``) URL squashing.
* Fixed ``--form`` file upload mixed with redirected ``stdin`` error handling. * Fixed ``--form`` file upload mixed with redirected ``stdin`` error handling.

View File

@ -552,6 +552,15 @@ network.add_argument(
""" """
) )
network.add_argument(
'--path-as-is',
default=False,
action='store_true',
help="""
Bypass dot segment (/../ or /./) URL squashing.
"""
)
####################################################################### #######################################################################
# SSL # SSL

View File

@ -6,6 +6,7 @@ import zlib
from contextlib import contextmanager from contextlib import contextmanager
from pathlib import Path from pathlib import Path
from typing import Iterable, Union from typing import Iterable, Union
from urllib.parse import urlparse, urlunparse
import requests import requests
from requests.adapters import HTTPAdapter from requests.adapters import HTTPAdapter
@ -77,6 +78,11 @@ def collect_messages(
request = requests.Request(**request_kwargs) request = requests.Request(**request_kwargs)
prepared_request = requests_session.prepare_request(request) prepared_request = requests_session.prepare_request(request)
if args.path_as_is:
prepared_request.url = ensure_path_as_is(
orig_url=args.url,
prepped_url=prepared_request.url,
)
if args.compress and prepared_request.body: if args.compress and prepared_request.body:
compress_body(prepared_request, always=args.compress > 1) compress_body(prepared_request, always=args.compress > 1)
response_count = 0 response_count = 0
@ -278,3 +284,30 @@ def make_request_kwargs(
} }
return kwargs return kwargs
def ensure_path_as_is(orig_url: str, prepped_url: str) -> str:
"""
Handle `--path-as-is` by replacing the path component of the prepared
URL with the path component from the original URL. Other parts stay
untouched because other (welcome) processing on the URL might have
taken place.
<https://github.com/jakubroztocil/httpie/issues/895>
<https://ec.haxx.se/http/http-basics#path-as-is>
<https://curl.haxx.se/libcurl/c/CURLOPT_PATH_AS_IS.html>
>>> ensure_path_as_is('http://foo/../', 'http://foo/?foo=bar')
'http://foo/../?foo=bar'
"""
parsed_orig, parsed_prepped = urlparse(orig_url), urlparse(prepped_url)
final_dict = {
# noinspection PyProtectedMember
**parsed_prepped._asdict(),
'path': parsed_orig.path,
}
final_url = urlunparse(tuple(final_dict.values()))
return final_url

View File

@ -55,6 +55,25 @@ def test_GET(httpbin_both):
assert HTTP_OK in r assert HTTP_OK in r
def test_path_dot_normalization():
r = http(
'--offline',
'example.org/../../etc/password',
'param==value'
)
assert 'GET /etc/password?param=value' in r
def test_path_as_is():
r = http(
'--offline',
'--path-as-is',
'example.org/../../etc/password',
'param==value'
)
assert 'GET /../../etc/password?param=value' in r
def test_DELETE(httpbin_both): def test_DELETE(httpbin_both):
r = http('DELETE', httpbin_both + '/delete') r = http('DELETE', httpbin_both + '/delete')
assert HTTP_OK in r assert HTTP_OK in r