This commit is contained in:
Jakub Roztocil 2020-09-25 13:44:28 +02:00
parent c431ed7728
commit d12af4a569
8 changed files with 140 additions and 47 deletions

View File

@ -15,12 +15,10 @@ from httpie.cli.argtypes import (
parse_format_options,
)
from httpie.cli.constants import (
DEFAULT_FORMAT_OPTIONS, HTTP_GET, HTTP_POST, OUTPUT_OPTIONS,
OUTPUT_OPTIONS_DEFAULT,
OUTPUT_OPTIONS_DEFAULT_STDOUT_REDIRECTED, OUT_RESP_BODY, PRETTY_MAP,
PRETTY_STDOUT_TTY_ONLY, SEPARATOR_CREDENTIALS, SEPARATOR_GROUP_ALL_ITEMS,
SEPARATOR_GROUP_DATA_ITEMS, URL_SCHEME_RE,
OUTPUT_OPTIONS_DEFAULT_OFFLINE,
HTTP_GET, HTTP_POST, OUTPUT_OPTIONS, OUTPUT_OPTIONS_DEFAULT,
OUTPUT_OPTIONS_DEFAULT_OFFLINE, OUTPUT_OPTIONS_DEFAULT_STDOUT_REDIRECTED,
OUT_RESP_BODY, PRETTY_MAP, PRETTY_STDOUT_TTY_ONLY, SEPARATOR_CREDENTIALS,
SEPARATOR_GROUP_ALL_ITEMS, SEPARATOR_GROUP_DATA_ITEMS, URL_SCHEME_RE,
)
from httpie.cli.exceptions import ParseError
from httpie.cli.requestitems import RequestItems
@ -165,7 +163,8 @@ class HTTPieArgumentParser(argparse.ArgumentParser):
if self.args.quiet:
self.env.stderr = self.env.devnull
if not (self.args.output_file_specified and not self.args.download):
if not (
self.args.output_file_specified and not self.args.download):
self.env.stdout = self.env.devnull
def _process_auth(self):
@ -193,8 +192,8 @@ class HTTPieArgumentParser(argparse.ArgumentParser):
plugin = plugin_manager.get_auth_plugin(self.args.auth_type)()
if (not self.args.ignore_netrc
and self.args.auth is None
and plugin.netrc_parse):
and self.args.auth is None
and plugin.netrc_parse):
# Only host needed, so its OK URL not finalized.
netrc_credentials = get_netrc_auth(self.args.url)
if netrc_credentials:
@ -222,7 +221,7 @@ class HTTPieArgumentParser(argparse.ArgumentParser):
credentials = parse_auth(self.args.auth)
if (not credentials.has_password()
and plugin.prompt_password):
and plugin.prompt_password):
if self.args.ignore_stdin:
# Non-tty stdin read by now
self.error(
@ -276,7 +275,13 @@ class HTTPieArgumentParser(argparse.ArgumentParser):
'data (key=value) cannot be mixed. Pass '
'--ignore-stdin to let key/value take priority. '
'See https://httpie.org/doc#scripting for details.')
self.args.data = getattr(fd, 'buffer', fd).read()
buffer = getattr(fd, 'buffer', fd)
# if fd is self.env.stdin and not self.args.chunked:
# self.args.data = buffer.read()
# else:
# self.args.data = buffer
# print(type(fd))
self.args.data = buffer
def _guess_method(self):
"""Set `args.method` if not specified to either POST or GET
@ -312,8 +317,8 @@ class HTTPieArgumentParser(argparse.ArgumentParser):
has_data = (
self.has_stdin_data
or any(
item.sep in SEPARATOR_GROUP_DATA_ITEMS
for item in self.args.request_items)
item.sep in SEPARATOR_GROUP_DATA_ITEMS
for item in self.args.request_items)
)
self.args.method = HTTP_POST if has_data else HTTP_GET
@ -416,11 +421,12 @@ class HTTPieArgumentParser(argparse.ArgumentParser):
if self.args.download_resume:
self.error('--continue only works with --download')
if self.args.download_resume and not (
self.args.download and self.args.output_file):
self.args.download and self.args.output_file):
self.error('--continue requires --output to be specified')
def _process_format_options(self):
parsed_options = PARSED_DEFAULT_FORMAT_OPTIONS
for options_group in self.args.format_options or []:
parsed_options = parse_format_options(options_group, defaults=parsed_options)
parsed_options = parse_format_options(options_group,
defaults=parsed_options)
self.args.format_options = parsed_options

View File

@ -657,6 +657,15 @@ network.add_argument(
'''
)
network.add_argument(
'--chunked',
default=False,
action='store_true',
help="""
"""
)
#######################################################################
# SSL
#######################################################################

View File

@ -5,7 +5,7 @@ import sys
import zlib
from contextlib import contextmanager
from pathlib import Path
from typing import Iterable, Union
from typing import Callable, Iterable, Union
from urllib.parse import urlparse, urlunparse
import requests
@ -16,7 +16,10 @@ from httpie.cli.dicts import RequestHeadersDict
from httpie.plugins.registry import plugin_manager
from httpie.sessions import get_httpie_session
from httpie.ssl import AVAILABLE_SSL_VERSION_ARG_MAPPING, HTTPieHTTPSAdapter
from httpie.uploads import get_multipart_data_and_content_type
from httpie.uploads import (
wrap_request_data,
get_multipart_data_and_content_type,
)
from httpie.utils import get_expired_cookies, repr_dict
@ -31,6 +34,7 @@ DEFAULT_UA = f'HTTPie/{__version__}'
def collect_messages(
args: argparse.Namespace,
config_dir: Path,
body_chunk_sent_callback: Callable[[bytes], None]=None,
) -> Iterable[Union[requests.PreparedRequest, requests.Response]]:
httpie_session = None
httpie_session_headers = None
@ -46,6 +50,7 @@ def collect_messages(
request_kwargs = make_request_kwargs(
args=args,
base_headers=httpie_session_headers,
callback=body_chunk_sent_callback
)
send_kwargs = make_send_kwargs(args)
send_kwargs_mergeable_from_env = make_send_kwargs_mergeable_from_env(args)
@ -251,7 +256,8 @@ def make_send_kwargs_mergeable_from_env(args: argparse.Namespace) -> dict:
def make_request_kwargs(
args: argparse.Namespace,
base_headers: RequestHeadersDict = None
base_headers: RequestHeadersDict = None,
callback=lambda chunk: chunk
) -> dict:
"""
Translate our `args` into `requests.Request` keyword arguments.
@ -289,7 +295,7 @@ def make_request_kwargs(
'method': args.method.lower(),
'url': args.url,
'headers': headers,
'data': data,
'data': wrap_request_data(data, callback=callback),
'auth': args.auth,
'params': args.params,
'files': files,

View File

@ -9,6 +9,10 @@ from pygments import __version__ as pygments_version
from requests import __version__ as requests_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.client import collect_messages
from httpie.context import Environment
from httpie.downloads import Downloader
@ -111,6 +115,22 @@ def main(
return exit_status
def get_output_options(
args: argparse.Namespace,
message: Union[requests.PreparedRequest, requests.Response]
) -> dict:
return {
requests.PreparedRequest: {
'with_headers': OUT_REQ_HEAD in args.output_options,
'with_body': OUT_REQ_BODY in args.output_options,
},
requests.Response: {
'with_headers': OUT_RESP_HEAD in args.output_options,
'with_body': OUT_RESP_BODY in args.output_options,
},
}[type(message)]
def program(
args: argparse.Namespace,
env: Environment,
@ -135,15 +155,36 @@ def program(
initial_request = None
final_response = None
for message in collect_messages(args, env.config.directory):
write_message(
requests_message=message,
env=env,
args=args,
)
if isinstance(message, requests.PreparedRequest):
def upload_callback(chunk):
print('GOT', chunk)
messages = collect_messages(
args=args,
config_dir=env.config.directory,
body_chunk_sent_callback=upload_callback
)
for message in messages:
is_request = isinstance(message, requests.PreparedRequest)
output_options = get_output_options(args=args, message=message)
if not is_request or not output_options['with_body']:
write_message(
requests_message=message,
env=env,
args=args,
**output_options,
)
if is_request:
if not initial_request:
initial_request = message
if 0and not args.offline:
output_options['with_body'] = False
write_message(
requests_message=message,
env=env,
args=args,
**output_options,
)
else:
final_response = message
if args.check_status or downloader:

View File

@ -23,6 +23,13 @@ LARGE_UPLOAD_SUPPRESSED_NOTICE = (
)
def might_suppress(chunk):
if isinstance(chunk, MultipartEncoder):
raise LargeUploadSuppressedError()
# if isinstance(io.IOBase):
# pass
class DataSuppressedError(Exception):
message = None

View File

@ -1,6 +1,6 @@
import argparse
import errno
from typing import Union, IO, TextIO, Tuple, Type
from typing import IO, TextIO, Tuple, Type, Union
import requests
@ -8,12 +8,7 @@ from httpie.context import Environment
from httpie.models import HTTPRequest, HTTPResponse
from httpie.output.processing import Conversion, Formatting
from httpie.output.streams import (
RawStream, PrettyStream,
BufferedPrettyStream, EncodedStream,
BaseStream,
)
from httpie.cli.constants import (
OUT_REQ_BODY, OUT_REQ_HEAD, OUT_RESP_BODY, OUT_RESP_HEAD,
BaseStream, BufferedPrettyStream, EncodedStream, PrettyStream, RawStream,
)
@ -21,26 +16,18 @@ def write_message(
requests_message: Union[requests.PreparedRequest, requests.Response],
env: Environment,
args: argparse.Namespace,
with_headers=False,
with_body=False,
):
output_options_by_message_type = {
requests.PreparedRequest: {
'with_headers': OUT_REQ_HEAD in args.output_options,
'with_body': OUT_REQ_BODY in args.output_options,
},
requests.Response: {
'with_headers': OUT_RESP_HEAD in args.output_options,
'with_body': OUT_RESP_BODY in args.output_options,
},
}
output_options = output_options_by_message_type[type(requests_message)]
if not any(output_options.values()):
if not (with_body or with_headers):
return
write_stream_kwargs = {
'stream': build_output_stream_for_message(
args=args,
env=env,
requests_message=requests_message,
**output_options,
with_body=with_body,
with_headers=with_headers,
),
# NOTE: `env.stdout` will in fact be `stderr` with `--download`
'outfile': env.stdout,

View File

@ -1,5 +1,6 @@
from typing import Tuple, Union
from requests.utils import super_len
from requests_toolbelt import MultipartEncoder
from httpie.cli.dicts import RequestDataDict, RequestFilesDict
@ -28,5 +29,40 @@ def get_multipart_data_and_content_type(
else:
content_type = encoder.content_type
data = encoder.to_string() if encoder.len < UPLOAD_BUFFER else encoder
data = encoder.to_string() if 0 and encoder.len < UPLOAD_BUFFER else encoder
return data, content_type
class Stdin:
def __init__(self, stdin, callback):
self.callback = callback
self.stdin = stdin
def __iter__(self):
for chunk in self.stdin:
print("__iter__() =>", chunk)
self.callback(chunk)
yield chunk
@classmethod
def is_stdin(cls, obj):
return super_len(obj) == 0
def wrap_request_data(data, callback=lambda chunk: print('chunk', chunk)):
if hasattr(data, 'read'):
if Stdin.is_stdin(data):
data = Stdin(data, callback=callback)
else:
orig_read = data.read
def new_read(*args):
val = orig_read(*args)
print('read() =>', val)
callback(callback)
return val
data.read = new_read
return data

View File

@ -1,5 +1,6 @@
"""High-level tests."""
import io
import sys
from unittest import mock
import pytest