def test_iter_raw(): stream = IteratorStream(iterator=streaming_body()) response = httpx.Response(200, stream=stream, request=REQUEST) raw = b"" for part in response.iter_raw(): raw += part assert raw == b"Hello, world!"
def test_cannot_read_after_stream_consumed(): stream = IteratorStream(iterator=streaming_body()) response = httpx.Response(200, stream=stream, request=REQUEST) content = b"" for part in response.iter_bytes(): content += part with pytest.raises(httpx.StreamConsumed): response.read()
def test_sync_streaming_response(): stream = IteratorStream(iterator=streaming_body()) response = httpx.Response(200, stream=stream, request=REQUEST) assert response.status_code == 200 assert not response.is_closed content = response.read() assert content == b"Hello, world!" assert response.content == b"Hello, world!" assert response.is_closed
def test_iter_raw_increments_updates_counter(): stream = IteratorStream(iterator=streaming_body()) response = httpx.Response( 200, stream=stream, ) num_downloaded = response.num_bytes_downloaded for part in response.iter_raw(): assert len(part) == (response.num_bytes_downloaded - num_downloaded) num_downloaded = response.num_bytes_downloaded
def test_cannot_read_after_response_closed(): is_closed = False def close_func(): nonlocal is_closed is_closed = True stream = IteratorStream(iterator=streaming_body(), close_func=close_func) response = httpx.Response(200, stream=stream, request=REQUEST) response.close() assert is_closed with pytest.raises(httpx.ResponseClosed): response.read()
def _request( self, method: bytes, url: typing.Tuple[bytes, bytes, int, bytes], headers: typing.List[typing.Tuple[bytes, bytes]], stream: ContentStream, timeout: typing.Mapping[str, typing.Optional[float]] = None, ) -> typing.Tuple[bytes, int, bytes, typing.List[typing.Tuple[ bytes, bytes]], ContentStream]: scheme, host, port, path = url if scheme not in (b"http", b"https"): raise httpcore.UnsupportedProtocol( f"Scheme {scheme!r} not supported.") path, _, query = path.partition(b"?") if path == b"/no_redirect": return b"HTTP/1.1", httpx.codes.OK, b"OK", [], ByteStream(b"") elif path == b"/redirect_301": def body(): yield b"<a href='https://example.org/'>here</a>" status_code = httpx.codes.MOVED_PERMANENTLY headers = [(b"location", b"https://example.org/")] stream = IteratorStream(iterator=body()) return b"HTTP/1.1", status_code, b"Moved Permanently", headers, stream elif path == b"/redirect_302": status_code = httpx.codes.FOUND headers = [(b"location", b"https://example.org/")] return b"HTTP/1.1", status_code, b"Found", headers, ByteStream(b"") elif path == b"/redirect_303": status_code = httpx.codes.SEE_OTHER headers = [(b"location", b"https://example.org/")] return b"HTTP/1.1", status_code, b"See Other", headers, ByteStream( b"") elif path == b"/relative_redirect": status_code = httpx.codes.SEE_OTHER headers = [(b"location", b"/")] return b"HTTP/1.1", status_code, b"See Other", headers, ByteStream( b"") elif path == b"/malformed_redirect": status_code = httpx.codes.SEE_OTHER headers = [(b"location", b"https://:443/")] return b"HTTP/1.1", status_code, b"See Other", headers, ByteStream( b"") elif path == b"/invalid_redirect": status_code = httpx.codes.SEE_OTHER headers = [(b"location", "https://😇/".encode("utf-8"))] return b"HTTP/1.1", status_code, b"See Other", headers, ByteStream( b"") elif path == b"/no_scheme_redirect": status_code = httpx.codes.SEE_OTHER headers = [(b"location", b"//example.org/")] return b"HTTP/1.1", status_code, b"See Other", headers, ByteStream( b"") elif path == b"/multiple_redirects": params = parse_qs(query.decode("ascii")) count = int(params.get("count", "0")[0]) redirect_count = count - 1 code = httpx.codes.SEE_OTHER if count else httpx.codes.OK phrase = b"See Other" if count else b"OK" location = b"/multiple_redirects" if redirect_count: location += b"?count=" + str(redirect_count).encode("ascii") headers = [(b"location", location)] if count else [] return b"HTTP/1.1", code, phrase, headers, ByteStream(b"") if path == b"/redirect_loop": code = httpx.codes.SEE_OTHER headers = [(b"location", b"/redirect_loop")] return b"HTTP/1.1", code, b"See Other", headers, ByteStream(b"") elif path == b"/cross_domain": code = httpx.codes.SEE_OTHER headers = [(b"location", b"https://example.org/cross_domain_target")] return b"HTTP/1.1", code, b"See Other", headers, ByteStream(b"") elif path == b"/cross_domain_target": headers_dict = { key.decode("ascii"): value.decode("ascii") for key, value in headers } stream = ByteStream(json.dumps({"headers": headers_dict}).encode()) return b"HTTP/1.1", 200, b"OK", [], stream elif path == b"/redirect_body": code = httpx.codes.PERMANENT_REDIRECT headers = [(b"location", b"/redirect_body_target")] return b"HTTP/1.1", code, b"Permanent Redirect", headers, ByteStream( b"") elif path == b"/redirect_no_body": code = httpx.codes.SEE_OTHER headers = [(b"location", b"/redirect_body_target")] return b"HTTP/1.1", code, b"See Other", headers, ByteStream(b"") elif path == b"/redirect_body_target": content = b"".join(stream) headers_dict = { key.decode("ascii"): value.decode("ascii") for key, value in headers } stream = ByteStream( json.dumps({ "body": content.decode(), "headers": headers_dict }).encode()) return b"HTTP/1.1", 200, b"OK", [], stream elif path == b"/cross_subdomain": host = get_header_value(headers, "host") if host != "www.example.org": headers = [(b"location", b"https://www.example.org/cross_subdomain")] return ( b"HTTP/1.1", httpx.codes.PERMANENT_REDIRECT, b"Permanent Redirect", headers, ByteStream(b""), ) else: return b"HTTP/1.1", 200, b"OK", [], ByteStream( b"Hello, world!") elif path == b"/redirect_custom_scheme": status_code = httpx.codes.MOVED_PERMANENTLY headers = [(b"location", b"market://details?id=42")] return ( b"HTTP/1.1", status_code, b"Moved Permanently", headers, ByteStream(b""), ) stream = ByteStream( b"Hello, world!") if method != b"HEAD" else ByteStream(b"") return b"HTTP/1.1", 200, b"OK", [], stream