mirror of
https://github.com/httpie/cli.git
synced 2025-06-21 01:57:42 +02:00
Automatically enable --stream on server sent events (#1226)
* Automatically enable --stream when used chunked encoding * try fix 3.6 mock issue * Only enable on text/event-stream Co-authored-by: Jakub Roztocil <jakub@roztocil.co>
This commit is contained in:
parent
62e43abc86
commit
207b970d94
@ -13,8 +13,10 @@ This project adheres to [Semantic Versioning](https://semver.org/).
|
|||||||
- Added support for _sending_ multiple HTTP header lines with the same name. ([#130](https://github.com/httpie/httpie/issues/130))
|
- Added support for _sending_ multiple HTTP header lines with the same name. ([#130](https://github.com/httpie/httpie/issues/130))
|
||||||
- Added support for _receiving_ multiple HTTP headers lines with the same name. ([#1207](https://github.com/httpie/httpie/issues/1207))
|
- Added support for _receiving_ multiple HTTP headers lines with the same name. ([#1207](https://github.com/httpie/httpie/issues/1207))
|
||||||
- Added support for basic JSON types on `--form`/`--multipart` when using JSON only operators (`:=`/`:=@`). ([#1212](https://github.com/httpie/httpie/issues/1212))
|
- Added support for basic JSON types on `--form`/`--multipart` when using JSON only operators (`:=`/`:=@`). ([#1212](https://github.com/httpie/httpie/issues/1212))
|
||||||
|
- Added support for automatically enabling `--stream` when `Content-Type` is `text/event-stream`. ([#376](https://github.com/httpie/httpie/issues/376))
|
||||||
- Broken plugins will no longer crash the whole application. ([#1204](https://github.com/httpie/httpie/issues/1204))
|
- Broken plugins will no longer crash the whole application. ([#1204](https://github.com/httpie/httpie/issues/1204))
|
||||||
|
|
||||||
|
|
||||||
## [2.6.0](https://github.com/httpie/httpie/compare/2.5.0...2.6.0) (2021-10-14)
|
## [2.6.0](https://github.com/httpie/httpie/compare/2.5.0...2.6.0) (2021-10-14)
|
||||||
|
|
||||||
[What’s new in HTTPie 2.6.0 →](https://httpie.io/blog/httpie-2.6.0)
|
[What’s new in HTTPie 2.6.0 →](https://httpie.io/blog/httpie-2.6.0)
|
||||||
|
@ -2,6 +2,7 @@ import argparse
|
|||||||
import errno
|
import errno
|
||||||
from typing import IO, TextIO, Tuple, Type, Union
|
from typing import IO, TextIO, Tuple, Type, Union
|
||||||
|
|
||||||
|
from ..cli.dicts import HTTPHeadersDict
|
||||||
from ..context import Environment
|
from ..context import Environment
|
||||||
from ..models import (
|
from ..models import (
|
||||||
HTTPRequest,
|
HTTPRequest,
|
||||||
@ -110,6 +111,7 @@ def build_output_stream_for_message(
|
|||||||
env=env,
|
env=env,
|
||||||
args=args,
|
args=args,
|
||||||
message_type=message_type,
|
message_type=message_type,
|
||||||
|
headers=requests_message.headers
|
||||||
)
|
)
|
||||||
yield from stream_class(
|
yield from stream_class(
|
||||||
msg=message_type(requests_message),
|
msg=message_type(requests_message),
|
||||||
@ -128,16 +130,23 @@ def get_stream_type_and_kwargs(
|
|||||||
env: Environment,
|
env: Environment,
|
||||||
args: argparse.Namespace,
|
args: argparse.Namespace,
|
||||||
message_type: Type[HTTPMessage],
|
message_type: Type[HTTPMessage],
|
||||||
|
headers: HTTPHeadersDict,
|
||||||
) -> Tuple[Type['BaseStream'], dict]:
|
) -> Tuple[Type['BaseStream'], dict]:
|
||||||
"""Pick the right stream type and kwargs for it based on `env` and `args`.
|
"""Pick the right stream type and kwargs for it based on `env` and `args`.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
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'
|
||||||
|
|
||||||
if not env.stdout_isatty and not args.prettify:
|
if not env.stdout_isatty and not args.prettify:
|
||||||
stream_class = RawStream
|
stream_class = RawStream
|
||||||
stream_kwargs = {
|
stream_kwargs = {
|
||||||
'chunk_size': (
|
'chunk_size': (
|
||||||
RawStream.CHUNK_SIZE_BY_LINE
|
RawStream.CHUNK_SIZE_BY_LINE
|
||||||
if args.stream
|
if is_stream
|
||||||
else RawStream.CHUNK_SIZE
|
else RawStream.CHUNK_SIZE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -152,7 +161,7 @@ def get_stream_type_and_kwargs(
|
|||||||
'encoding_overwrite': args.response_charset,
|
'encoding_overwrite': args.response_charset,
|
||||||
})
|
})
|
||||||
if args.prettify:
|
if args.prettify:
|
||||||
stream_class = PrettyStream if args.stream else BufferedPrettyStream
|
stream_class = PrettyStream if is_stream else BufferedPrettyStream
|
||||||
stream_kwargs.update({
|
stream_kwargs.update({
|
||||||
'conversion': Conversion(),
|
'conversion': Conversion(),
|
||||||
'formatting': Formatting(
|
'formatting': Formatting(
|
||||||
|
@ -2,6 +2,7 @@ import json
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import responses
|
import responses
|
||||||
|
from unittest.mock import Mock
|
||||||
|
|
||||||
from httpie.compat import is_windows
|
from httpie.compat import is_windows
|
||||||
from httpie.cli.constants import PRETTY_MAP
|
from httpie.cli.constants import PRETTY_MAP
|
||||||
@ -107,3 +108,28 @@ def test_redirected_stream(httpbin):
|
|||||||
r = http('--pretty=none', '--stream', '--verbose', 'GET',
|
r = http('--pretty=none', '--stream', '--verbose', 'GET',
|
||||||
httpbin.url + '/get', env=env)
|
httpbin.url + '/get', env=env)
|
||||||
assert BIN_FILE_CONTENT in r
|
assert BIN_FILE_CONTENT in r
|
||||||
|
|
||||||
|
|
||||||
|
# /drip endpoint produces 3 individual lines,
|
||||||
|
# if we set text/event-stream HTTPie should stream
|
||||||
|
# it by default. Otherwise, it will buffer and then
|
||||||
|
# print.
|
||||||
|
@pytest.mark.parametrize('extras, expected', [
|
||||||
|
(
|
||||||
|
['Accept:text/event-stream'],
|
||||||
|
3
|
||||||
|
),
|
||||||
|
(
|
||||||
|
['Accept:text/plain'],
|
||||||
|
1
|
||||||
|
)
|
||||||
|
])
|
||||||
|
def test_auto_streaming(http_server, extras, expected):
|
||||||
|
env = MockEnvironment()
|
||||||
|
env.stdout.write = Mock()
|
||||||
|
http(http_server + '/drip', *extras, env=env)
|
||||||
|
assert len([
|
||||||
|
call_arg
|
||||||
|
for call_arg in env.stdout.write.call_args_list
|
||||||
|
if b'test' in call_arg[0][0]
|
||||||
|
]) == expected
|
||||||
|
@ -36,6 +36,22 @@ def get_headers(handler):
|
|||||||
handler.end_headers()
|
handler.end_headers()
|
||||||
|
|
||||||
|
|
||||||
|
@TestHandler.handler('GET', '/drip')
|
||||||
|
def chunked_drip(handler):
|
||||||
|
handler.send_response(200)
|
||||||
|
accept = handler.headers.get('Accept')
|
||||||
|
if accept is not None:
|
||||||
|
handler.send_header('Content-Type', accept)
|
||||||
|
handler.send_header('Transfer-Encoding', 'chunked')
|
||||||
|
handler.end_headers()
|
||||||
|
|
||||||
|
for _ in range(3):
|
||||||
|
body = 'test\n'
|
||||||
|
handler.wfile.write(f'{len(body):X}\r\n{body}\r\n'.encode('utf-8'))
|
||||||
|
|
||||||
|
handler.wfile.write('0\r\n\r\n'.encode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="function")
|
@pytest.fixture(scope="function")
|
||||||
def http_server():
|
def http_server():
|
||||||
"""A custom HTTP server implementation for our tests, that is
|
"""A custom HTTP server implementation for our tests, that is
|
||||||
|
Loading…
x
Reference in New Issue
Block a user