2019-09-03 17:14:39 +02:00
|
|
|
import argparse
|
|
|
|
import errno
|
2020-09-25 13:44:28 +02:00
|
|
|
from typing import IO, TextIO, Tuple, Type, Union
|
2019-09-03 17:14:39 +02:00
|
|
|
|
2021-12-08 16:49:12 +01:00
|
|
|
from ..cli.dicts import HTTPHeadersDict
|
2021-05-05 14:13:39 +02:00
|
|
|
from ..context import Environment
|
2021-11-25 00:45:39 +01:00
|
|
|
from ..models import (
|
|
|
|
HTTPRequest,
|
|
|
|
HTTPResponse,
|
|
|
|
HTTPMessage,
|
|
|
|
RequestsMessage,
|
|
|
|
RequestsMessageKind,
|
2021-12-23 21:13:25 +01:00
|
|
|
OutputOptions
|
2021-11-25 00:45:39 +01:00
|
|
|
)
|
2021-05-05 14:13:39 +02:00
|
|
|
from .processing import Conversion, Formatting
|
|
|
|
from .streams import (
|
2020-09-25 13:44:28 +02:00
|
|
|
BaseStream, BufferedPrettyStream, EncodedStream, PrettyStream, RawStream,
|
2019-09-03 17:14:39 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2021-01-30 22:14:57 +01:00
|
|
|
MESSAGE_SEPARATOR = '\n\n'
|
|
|
|
MESSAGE_SEPARATOR_BYTES = MESSAGE_SEPARATOR.encode()
|
|
|
|
|
|
|
|
|
2019-09-03 17:14:39 +02:00
|
|
|
def write_message(
|
2021-11-25 00:45:39 +01:00
|
|
|
requests_message: RequestsMessage,
|
2019-09-03 17:14:39 +02:00
|
|
|
env: Environment,
|
|
|
|
args: argparse.Namespace,
|
2021-12-23 21:13:25 +01:00
|
|
|
output_options: OutputOptions,
|
2019-09-03 17:14:39 +02:00
|
|
|
):
|
2021-12-23 21:13:25 +01:00
|
|
|
if not output_options.any():
|
2019-09-03 17:14:39 +02:00
|
|
|
return
|
|
|
|
write_stream_kwargs = {
|
|
|
|
'stream': build_output_stream_for_message(
|
|
|
|
args=args,
|
|
|
|
env=env,
|
|
|
|
requests_message=requests_message,
|
2021-12-23 21:13:25 +01:00
|
|
|
output_options=output_options,
|
2019-09-03 17:14:39 +02:00
|
|
|
),
|
|
|
|
# NOTE: `env.stdout` will in fact be `stderr` with `--download`
|
|
|
|
'outfile': env.stdout,
|
|
|
|
'flush': env.stdout_isatty or args.stream
|
|
|
|
}
|
|
|
|
try:
|
|
|
|
if env.is_windows and 'colors' in args.prettify:
|
2021-07-26 23:56:38 +02:00
|
|
|
write_stream_with_colors_win(**write_stream_kwargs)
|
2019-09-03 17:14:39 +02:00
|
|
|
else:
|
|
|
|
write_stream(**write_stream_kwargs)
|
2021-05-31 10:10:41 +02:00
|
|
|
except OSError as e:
|
2019-09-03 17:14:39 +02:00
|
|
|
show_traceback = args.debug or args.traceback
|
|
|
|
if not show_traceback and e.errno == errno.EPIPE:
|
|
|
|
# Ignore broken pipes unless --traceback.
|
|
|
|
env.stderr.write('\n')
|
|
|
|
else:
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
|
|
def write_stream(
|
|
|
|
stream: BaseStream,
|
|
|
|
outfile: Union[IO, TextIO],
|
|
|
|
flush: bool
|
|
|
|
):
|
|
|
|
"""Write the output stream."""
|
|
|
|
try:
|
2021-05-27 13:05:41 +02:00
|
|
|
# Writing bytes so we use the buffer interface.
|
2019-09-03 17:14:39 +02:00
|
|
|
buf = outfile.buffer
|
|
|
|
except AttributeError:
|
|
|
|
buf = outfile
|
|
|
|
|
|
|
|
for chunk in stream:
|
|
|
|
buf.write(chunk)
|
|
|
|
if flush:
|
|
|
|
outfile.flush()
|
|
|
|
|
|
|
|
|
2021-07-26 23:56:38 +02:00
|
|
|
def write_stream_with_colors_win(
|
2019-09-03 17:14:39 +02:00
|
|
|
stream: 'BaseStream',
|
|
|
|
outfile: TextIO,
|
|
|
|
flush: bool
|
|
|
|
):
|
|
|
|
"""Like `write`, but colorized chunks are written as text
|
|
|
|
directly to `outfile` to ensure it gets processed by colorama.
|
2021-05-27 13:05:41 +02:00
|
|
|
Applies only to Windows and colorized terminal output.
|
2019-09-03 17:14:39 +02:00
|
|
|
|
|
|
|
"""
|
|
|
|
color = b'\x1b['
|
|
|
|
encoding = outfile.encoding
|
|
|
|
for chunk in stream:
|
|
|
|
if color in chunk:
|
|
|
|
outfile.write(chunk.decode(encoding))
|
|
|
|
else:
|
|
|
|
outfile.buffer.write(chunk)
|
|
|
|
if flush:
|
|
|
|
outfile.flush()
|
|
|
|
|
|
|
|
|
|
|
|
def build_output_stream_for_message(
|
|
|
|
args: argparse.Namespace,
|
|
|
|
env: Environment,
|
2021-11-25 00:45:39 +01:00
|
|
|
requests_message: RequestsMessage,
|
2021-12-23 21:13:25 +01:00
|
|
|
output_options: OutputOptions,
|
2019-09-03 17:14:39 +02:00
|
|
|
):
|
2021-10-06 17:27:07 +02:00
|
|
|
message_type = {
|
2021-11-25 00:45:39 +01:00
|
|
|
RequestsMessageKind.REQUEST: HTTPRequest,
|
|
|
|
RequestsMessageKind.RESPONSE: HTTPResponse,
|
2021-12-23 21:13:25 +01:00
|
|
|
}[output_options.kind]
|
2019-09-03 17:14:39 +02:00
|
|
|
stream_class, stream_kwargs = get_stream_type_and_kwargs(
|
|
|
|
env=env,
|
|
|
|
args=args,
|
2021-10-06 17:27:07 +02:00
|
|
|
message_type=message_type,
|
2021-12-08 16:49:12 +01:00
|
|
|
headers=requests_message.headers
|
2019-09-03 17:14:39 +02:00
|
|
|
)
|
|
|
|
yield from stream_class(
|
2021-10-06 17:27:07 +02:00
|
|
|
msg=message_type(requests_message),
|
2021-12-23 21:13:25 +01:00
|
|
|
output_options=output_options,
|
2019-09-03 17:14:39 +02:00
|
|
|
**stream_kwargs,
|
|
|
|
)
|
2022-01-13 13:04:44 +01:00
|
|
|
if (env.stdout_isatty and output_options.body and not output_options.meta
|
2020-09-28 12:16:57 +02:00
|
|
|
and not getattr(requests_message, 'is_body_upload_chunk', False)):
|
2019-09-03 17:14:39 +02:00
|
|
|
# Ensure a blank line after the response body.
|
|
|
|
# For terminal output only.
|
2021-01-30 22:14:57 +01:00
|
|
|
yield MESSAGE_SEPARATOR_BYTES
|
2019-09-03 17:14:39 +02:00
|
|
|
|
|
|
|
|
|
|
|
def get_stream_type_and_kwargs(
|
|
|
|
env: Environment,
|
2021-10-06 17:27:07 +02:00
|
|
|
args: argparse.Namespace,
|
|
|
|
message_type: Type[HTTPMessage],
|
2021-12-08 16:49:12 +01:00
|
|
|
headers: HTTPHeadersDict,
|
2019-09-03 17:14:39 +02:00
|
|
|
) -> Tuple[Type['BaseStream'], dict]:
|
|
|
|
"""Pick the right stream type and kwargs for it based on `env` and `args`.
|
|
|
|
|
|
|
|
"""
|
2021-12-08 16:49:12 +01:00
|
|
|
is_stream = args.stream
|
|
|
|
if not is_stream and message_type is HTTPResponse:
|
|
|
|
# If this is a response, then check the headers for determining
|
|
|
|
# auto-streaming.
|
|
|
|
is_stream = headers.get('Content-Type') == 'text/event-stream'
|
|
|
|
|
2019-09-03 17:14:39 +02:00
|
|
|
if not env.stdout_isatty and not args.prettify:
|
|
|
|
stream_class = RawStream
|
|
|
|
stream_kwargs = {
|
|
|
|
'chunk_size': (
|
|
|
|
RawStream.CHUNK_SIZE_BY_LINE
|
2021-12-08 16:49:12 +01:00
|
|
|
if is_stream
|
2019-09-03 17:14:39 +02:00
|
|
|
else RawStream.CHUNK_SIZE
|
|
|
|
)
|
|
|
|
}
|
|
|
|
else:
|
|
|
|
stream_class = EncodedStream
|
|
|
|
stream_kwargs = {
|
2021-10-06 17:27:07 +02:00
|
|
|
'env': env,
|
2019-09-03 17:14:39 +02:00
|
|
|
}
|
2021-10-06 17:27:07 +02:00
|
|
|
if message_type is HTTPResponse:
|
|
|
|
stream_kwargs.update({
|
|
|
|
'mime_overwrite': args.response_mime,
|
|
|
|
'encoding_overwrite': args.response_charset,
|
|
|
|
})
|
|
|
|
if args.prettify:
|
2021-12-08 16:49:12 +01:00
|
|
|
stream_class = PrettyStream if is_stream else BufferedPrettyStream
|
2021-10-06 17:27:07 +02:00
|
|
|
stream_kwargs.update({
|
|
|
|
'conversion': Conversion(),
|
|
|
|
'formatting': Formatting(
|
|
|
|
env=env,
|
|
|
|
groups=args.prettify,
|
|
|
|
color_scheme=args.style,
|
|
|
|
explicit_json=args.json,
|
|
|
|
format_options=args.format_options,
|
|
|
|
)
|
|
|
|
})
|
2019-09-03 17:14:39 +02:00
|
|
|
|
|
|
|
return stream_class, stream_kwargs
|