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)
-------------------------
* Add ``--path-as-is`` to bypass dot segment (``/../`` or ``/./``) URL squashing.
* 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

View File

@ -6,6 +6,7 @@ import zlib
from contextlib import contextmanager
from pathlib import Path
from typing import Iterable, Union
from urllib.parse import urlparse, urlunparse
import requests
from requests.adapters import HTTPAdapter
@ -77,6 +78,11 @@ def collect_messages(
request = requests.Request(**request_kwargs)
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:
compress_body(prepared_request, always=args.compress > 1)
response_count = 0
@ -278,3 +284,30 @@ def make_request_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
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):
r = http('DELETE', httpbin_both + '/delete')
assert HTTP_OK in r