diff --git a/CHANGELOG.md b/CHANGELOG.md index 0497ac3508..64ce41c733 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ This document records all notable changes to [HTTPie](https://httpie.io). This project adheres to [Semantic Versioning](https://semver.org/). +## Unreleased + +- Added short `-k` alias for `--verify` to skip SSL verification (equivalent to `--verify=no`). ([#1547](https://github.com/httpie/cli/issues/1547)) + + ## [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)) diff --git a/httpie/cli/argtypes.py b/httpie/cli/argtypes.py index 8f19c3c51e..6d330980a7 100644 --- a/httpie/cli/argtypes.py +++ b/httpie/cli/argtypes.py @@ -1,6 +1,7 @@ import argparse import getpass import os +import shlex import sys from copy import deepcopy from typing import List, Optional, Union @@ -70,43 +71,28 @@ def __call__(self, s: str) -> KeyValueArg: 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) + # Tokenize respecting quotes so values with spaces are preserved, + # e.g. field_name="Foo Bar" -> ["field_name=Foo Bar"] + tokens = shlex.split(s) for i, token in enumerate(tokens): + for sep in self.separators: + if sep in token: + key, value = token.split(sep, 1) - 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())] + # Any preceding tokens are part of the key. + key = ''.join(tokens[:i]) + key - key, value = token.split(sep, 1) + # Any following tokens are part of the value. + # Preserve spaces that were part of the original value. + if i + 1 < len(tokens): + value += ' ' + ' '.join(tokens[i + 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 + return self.key_value_class(key=key, value=value, sep=sep, orig=s) 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 diff --git a/httpie/cli/definition.py b/httpie/cli/definition.py index 843b29c9cf..0ca90bbfc3 100644 --- a/httpie/cli/definition.py +++ b/httpie/cli/definition.py @@ -820,6 +820,14 @@ def format_auth_help(auth_plugins_mapping, *, isolation_mode: bool = False): variable instead.) """, ) +ssl.add_argument( + '-k', + action='store_const', + const='no', + dest='verify', + short_help='Shortcut for --verify=no (skip SSL verification).', + help=Qualifiers.SUPPRESS, +) ssl.add_argument( '--ssl', dest='ssl_version', diff --git a/tests/test_ssl.py b/tests/test_ssl.py index 6fb983785a..df459130ed 100644 --- a/tests/test_ssl.py +++ b/tests/test_ssl.py @@ -113,6 +113,12 @@ def test_verify_no_OK(self, httpbin_secure): r = http(httpbin_secure.url + '/get', '--verify=no') assert HTTP_OK in r + def test_verify_short_flag_k_OK(self, httpbin_secure): + # short alias -k should behave like --verify=no + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + r = http(httpbin_secure.url + '/get', '-k') + assert HTTP_OK in r + @pytest.mark.parametrize('verify_value', ['false', 'fALse']) def test_verify_false_OK(self, httpbin_secure, verify_value): urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)