示例#1
0
async def test_concurrent_requests_h2() -> None:
    async with ConnectionPool(http_version="HTTP/2") as http:
        info = await http.get_connection_info()
        assert info == {}

        response_1 = await http.handle_async_request(
            method=b"GET",
            url=(b"http", b"example.org", None, b"/"),
            headers=[],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        status_code_1, headers_1, stream_1, ext_1 = response_1
        info = await http.get_connection_info()
        assert info == {"http://example.org": ["ConnectionState.ACTIVE"]}

        response_2 = await http.handle_async_request(
            method=b"GET",
            url=(b"http", b"example.org", None, b"/"),
            headers=[],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        status_code_2, headers_2, stream_2, ext_2 = response_2
        info = await http.get_connection_info()
        assert info == {"http://example.org": ["ConnectionState.ACTIVE"]}

        await read_body(stream_1)
        info = await http.get_connection_info()
        assert info == {"http://example.org": ["ConnectionState.ACTIVE"]}

        await read_body(stream_2)
        info = await http.get_connection_info()
        assert info == {"http://example.org": ["ConnectionState.IDLE"]}
示例#2
0
async def test_sequential_requests(http_version) -> None:
    async with ConnectionPool(http_version=http_version) as http:
        info = await http.get_connection_info()
        assert info == {}

        response = await http.handle_async_request(
            method=b"GET",
            url=(b"http", b"example.org", None, b"/"),
            headers=[],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        status_code, headers, stream, extensions = response
        info = await http.get_connection_info()
        assert info == {"http://example.org": ["ConnectionState.ACTIVE"]}

        await read_body(stream)
        info = await http.get_connection_info()
        assert info == {"http://example.org": ["ConnectionState.IDLE"]}

        response = await http.handle_async_request(
            method=b"GET",
            url=(b"http", b"example.org", None, b"/"),
            headers=[],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        status_code, headers, stream, extensions = response
        info = await http.get_connection_info()
        assert info == {"http://example.org": ["ConnectionState.ACTIVE"]}

        await read_body(stream)
        info = await http.get_connection_info()
        assert info == {"http://example.org": ["ConnectionState.IDLE"]}
示例#3
0
async def test_retries_exceeded(server: Server) -> None:
    """
    When retries are enabled and connecting failures more than the configured number
    of retries, connect exceptions are raised.
    """
    backend = AsyncMockBackend()
    retries = 1

    async with httpcore.AsyncConnectionPool(
        retries=retries, max_keepalive_connections=0, backend=backend
    ) as http:
        response = await http.handle_async_request(
            method=b"GET",
            url=(b"http", *server.netloc, b"/"),
            headers=[server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        status_code, _, stream, _ = response
        assert status_code == 200
        await read_body(stream)

        # First failure is retried on, second one isn't.
        backend.push(httpcore.ConnectError(), httpcore.ConnectTimeout())
        with pytest.raises(httpcore.ConnectTimeout):
            await http.handle_async_request(
                method=b"GET",
                url=(b"http", *server.netloc, b"/"),
                headers=[server.host_header],
                stream=httpcore.ByteStream(b""),
                extensions={},
            )
示例#4
0
async def test_get_request_with_connection_keepalive() -> None:
    backend = MockBackend(
        http_buffer=[
            b"HTTP/1.1 200 OK\r\n",
            b"Date: Sat, 06 Oct 2049 12:34:56 GMT\r\n",
            b"Server: Apache\r\n",
            b"Content-Length: 13\r\n",
            b"Content-Type: text/plain\r\n",
            b"\r\n",
            b"Hello, world.",
            b"HTTP/1.1 200 OK\r\n",
            b"Date: Sat, 06 Oct 2049 12:34:56 GMT\r\n",
            b"Server: Apache\r\n",
            b"Content-Length: 13\r\n",
            b"Content-Type: text/plain\r\n",
            b"\r\n",
            b"Hello, world.",
        ]
    )

    async with httpcore.AsyncConnectionPool(backend=backend) as http:
        # We're sending a request with a standard keep-alive connection, so
        # it will remain in the pool once we've sent the request.
        response = await http.handle_async_request(
            method=b"GET",
            url=(b"http", b"example.org", None, b"/"),
            headers=[(b"Host", b"example.org")],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        status_code, headers, stream, extensions = response
        body = await stream.aread()
        assert status_code == 200
        assert body == b"Hello, world."
        assert await http.get_connection_info() == {
            "http://example.org": ["HTTP/1.1, IDLE"]
        }

        # This second request will go out over the same connection.
        response = await http.handle_async_request(
            method=b"GET",
            url=(b"http", b"example.org", None, b"/"),
            headers=[(b"Host", b"example.org")],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        status_code, headers, stream, extensions = response
        body = await stream.aread()
        assert status_code == 200
        assert body == b"Hello, world."
        assert await http.get_connection_info() == {
            "http://example.org": ["HTTP/1.1, IDLE"]
        }
示例#5
0
async def test_get_request_with_socket_disconnect_between_requests() -> None:
    backend = MockBackend(
        http_buffer=[
            b"HTTP/1.1 200 OK\r\n",
            b"Date: Sat, 06 Oct 2049 12:34:56 GMT\r\n",
            b"Server: Apache\r\n",
            b"Content-Length: 13\r\n",
            b"Content-Type: text/plain\r\n",
            b"\r\n",
            b"Hello, world.",
        ],
        disconnect=True,
    )

    async with httpcore.AsyncConnectionPool(backend=backend) as http:
        # Send an initial request. We're using a standard keep-alive
        # connection, so the connection remains in the pool after completion.
        response = await http.handle_async_request(
            method=b"GET",
            url=(b"http", b"example.org", None, b"/"),
            headers=[(b"Host", b"example.org")],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        status_code, headers, stream, extensions = response
        body = await stream.aread()
        assert status_code == 200
        assert body == b"Hello, world."
        assert await http.get_connection_info() == {
            "http://example.org": ["HTTP/1.1, IDLE"]
        }

        # On sending this second request, at the point of pool re-acquiry the
        # socket indicates that it has disconnected, and we'll send the request
        # over a new connection.
        response = await http.handle_async_request(
            method=b"GET",
            url=(b"http", b"example.org", None, b"/"),
            headers=[(b"Host", b"example.org")],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        status_code, headers, stream, extensions = response
        body = await stream.aread()
        assert status_code == 200
        assert body == b"Hello, world."
        assert await http.get_connection_info() == {
            "http://example.org": ["HTTP/1.1, IDLE"]
        }
示例#6
0
async def test_broken_socket_detection_many_open_files(backend: str,
                                                       server: Server) -> None:
    """
    Regression test for: https://github.com/encode/httpcore/issues/182
    """
    async with httpcore.AsyncConnectionPool(backend=backend) as http:
        # * First attempt will be successful because it will grab the last
        # available fd before what select() supports on the platform.
        # * Second attempt would have failed without a fix, due to a "filedescriptor
        # out of range in select()" exception.
        for _ in range(2):
            (
                status_code,
                response_headers,
                stream,
                extensions,
            ) = await http.handle_async_request(
                method=b"GET",
                url=(b"http", *server.netloc, b"/"),
                headers=[server.host_header],
                stream=httpcore.ByteStream(b""),
                extensions={},
            )
            await read_body(stream)

            assert status_code == 200
            reason_phrase = b"OK" if server.sends_reason else b""
            assert extensions == {
                "http_version": b"HTTP/1.1",
                "reason_phrase": reason_phrase,
            }
            origin = (b"http", *server.netloc)
            assert len(http._connections[origin]) == 1  # type: ignore
示例#7
0
async def test_proxy_socket_does_not_leak_when_the_connection_hasnt_been_added_to_pool(
    proxy_server: URL,
    server: Server,
    proxy_mode: str,
    protocol: bytes,
    port: int,
):
    with pytest.warns(None) as recorded_warnings:
        async with httpcore.AsyncHTTPProxy(proxy_server,
                                           proxy_mode=proxy_mode) as http:
            for _ in range(100):
                try:
                    _ = await http.handle_async_request(
                        method=b"GET",
                        url=(protocol, b"blockedhost.example.com", port, b"/"),
                        headers=[(b"host", b"blockedhost.example.com")],
                        stream=httpcore.ByteStream(b""),
                        extensions={},
                    )
                except (httpcore.ProxyError, httpcore.RemoteProtocolError):
                    pass

    # have to filter out https://github.com/encode/httpx/issues/825 from other tests
    warnings = [
        *filter(lambda warn: "asyncio" not in warn.filename,
                recorded_warnings.list)
    ]

    assert len(warnings) == 0
示例#8
0
async def test_proxy_https_requests(
    proxy_server: URL,
    proxy_mode: str,
    http2: bool,
    https_server: Server,
) -> None:
    max_connections = 1
    async with httpcore.AsyncHTTPProxy(
            proxy_server,
            proxy_mode=proxy_mode,
            max_connections=max_connections,
            http2=http2,
    ) as http:
        status_code, headers, stream, extensions = await http.handle_async_request(
            method=b"GET",
            url=(b"https", *https_server.netloc, b"/"),
            headers=[https_server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        _ = await read_body(stream)

        assert status_code == 200
        assert extensions["http_version"] == b"HTTP/2" if http2 else b"HTTP/1.1"
        assert extensions.get("reason_phrase", b"") == b"" if http2 else b"OK"
示例#9
0
async def test_max_keepalive_connections_handled_correctly(
        max_keepalive: int, connections_number: int, backend: str,
        server: Server) -> None:
    async with httpcore.AsyncConnectionPool(
            max_keepalive_connections=max_keepalive,
            keepalive_expiry=60,
            backend=backend) as http:
        connections_streams = []
        for _ in range(connections_number):
            _, _, stream, _ = await http.handle_async_request(
                method=b"GET",
                url=(b"http", *server.netloc, b"/"),
                headers=[server.host_header],
                stream=httpcore.ByteStream(b""),
                extensions={},
            )
            connections_streams.append(stream)

        try:
            for i in range(len(connections_streams)):
                await read_body(connections_streams[i])
        finally:
            stats = await http.get_connection_info()

            connections_in_pool = next(iter(stats.values()))
            assert len(connections_in_pool) == min(connections_number,
                                                   max_keepalive)
示例#10
0
async def test_get_request_with_connection_close_header() -> None:
    backend = MockBackend(
        http_buffer=[
            b"HTTP/1.1 200 OK\r\n",
            b"Date: Sat, 06 Oct 2049 12:34:56 GMT\r\n",
            b"Server: Apache\r\n",
            b"Content-Length: 13\r\n",
            b"Content-Type: text/plain\r\n",
            b"\r\n",
            b"Hello, world.",
            b"",  # Terminate the connection.
        ]
    )

    async with httpcore.AsyncConnectionPool(backend=backend) as http:
        # We're sending a request with 'Connection: close', so the connection
        # does not remain in the pool once we've sent the request.
        response = await http.handle_async_request(
            method=b"GET",
            url=(b"http", b"example.org", None, b"/"),
            headers=[(b"Host", b"example.org"), (b"Connection", b"close")],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        status_code, headers, stream, extensions = response
        body = await stream.aread()
        assert status_code == 200
        assert body == b"Hello, world."
        assert await http.get_connection_info() == {}

        # The second request will go out over a new connection.
        response = await http.handle_async_request(
            method=b"GET",
            url=(b"http", b"example.org", None, b"/"),
            headers=[(b"Host", b"example.org"), (b"Connection", b"close")],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        status_code, headers, stream, extensions = response
        body = await stream.aread()
        assert status_code == 200
        assert body == b"Hello, world."
        assert await http.get_connection_info() == {}
示例#11
0
async def test_request_unsupported_protocol(backend: str) -> None:
    async with httpcore.AsyncConnectionPool(backend=backend) as http:
        with pytest.raises(httpcore.UnsupportedProtocol):
            await http.handle_async_request(
                method=b"GET",
                url=(b"ftp", b"example.org", 443, b"/"),
                headers=[(b"host", b"example.org")],
                stream=httpcore.ByteStream(b""),
                extensions={},
            )
示例#12
0
async def test_get_request_with_unclean_close_after_first_request() -> None:
    backend = MockBackend(
        http_buffer=[
            b"HTTP/1.1 200 OK\r\n",
            b"Date: Sat, 06 Oct 2049 12:34:56 GMT\r\n",
            b"Server: Apache\r\n",
            b"Content-Length: 13\r\n",
            b"Content-Type: text/plain\r\n",
            b"\r\n",
            b"Hello, world.",
            b"",  # Terminate the connection.
        ], )

    async with httpcore.AsyncConnectionPool(backend=backend) as http:
        # Send an initial request. We're using a standard keep-alive
        # connection, so the connection remains in the pool after completion.
        response = await http.handle_async_request(
            method=b"GET",
            url=(b"http", b"example.org", None, b"/"),
            headers=[(b"Host", b"example.org")],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        status_code, headers, stream, extensions = response
        body = await stream.aread()
        assert status_code == 200
        assert body == b"Hello, world."
        assert await http.get_connection_info() == {
            "http://example.org": ["HTTP/1.1, IDLE"]
        }

        # At this point we successfully write another request, but the socket
        # read returns `b""`, indicating a premature close.
        with pytest.raises(httpcore.RemoteProtocolError) as excinfo:
            await http.handle_async_request(
                method=b"GET",
                url=(b"http", b"example.org", None, b"/"),
                headers=[(b"Host", b"example.org")],
                stream=httpcore.ByteStream(b""),
                extensions={},
            )
        assert str(
            excinfo.value) == "Server disconnected without sending a response."
示例#13
0
 def request(http: httpcore.SyncHTTPTransport) -> int:
     status_code, headers, stream, extensions = http.handle_request(
         method=b"GET",
         url=(b"http", *server.netloc, b"/"),
         headers=[server.host_header],
         stream=httpcore.ByteStream(b""),
         extensions={},
     )
     read_body(stream)
     return status_code
示例#14
0
async def test_http_request_cannot_reuse_dropped_connection(
        backend: str, server: Server) -> None:
    async with httpcore.AsyncConnectionPool(backend=backend) as http:
        status_code, headers, stream, extensions = await http.handle_async_request(
            method=b"GET",
            url=(b"http", *server.netloc, b"/"),
            headers=[server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        await read_body(stream)

        assert status_code == 200
        reason_phrase = b"OK" if server.sends_reason else b""
        assert extensions == {
            "http_version": b"HTTP/1.1",
            "reason_phrase": reason_phrase,
        }
        origin = (b"http", *server.netloc)
        assert len(http._connections[origin]) == 1  # type: ignore

        # Mock the connection as having been dropped.
        connection = list(http._connections[origin])[0]  # type: ignore
        connection.is_socket_readable = lambda: True  # type: ignore

        status_code, headers, stream, extensions = await http.handle_async_request(
            method=b"GET",
            url=(b"http", *server.netloc, b"/"),
            headers=[server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        await read_body(stream)

        assert status_code == 200
        reason_phrase = b"OK" if server.sends_reason else b""
        assert extensions == {
            "http_version": b"HTTP/1.1",
            "reason_phrase": reason_phrase,
        }
        origin = (b"http", *server.netloc)
        assert len(http._connections[origin]) == 1  # type: ignore
示例#15
0
async def test_no_retries(server: Server) -> None:
    """
    By default, connection failures are not retried on.
    """
    backend = AsyncMockBackend()

    async with httpcore.AsyncConnectionPool(
        max_keepalive_connections=0, backend=backend
    ) as http:
        response = await http.handle_async_request(
            method=b"GET",
            url=(b"http", *server.netloc, b"/"),
            headers=[server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        status_code, _, stream, _ = response
        assert status_code == 200
        await read_body(stream)

        backend.push(httpcore.ConnectTimeout(), httpcore.ConnectError())

        with pytest.raises(httpcore.ConnectTimeout):
            await http.handle_async_request(
                method=b"GET",
                url=(b"http", *server.netloc, b"/"),
                headers=[server.host_header],
                stream=httpcore.ByteStream(b""),
                extensions={},
            )

        with pytest.raises(httpcore.ConnectError):
            await http.handle_async_request(
                method=b"GET",
                url=(b"http", *server.netloc, b"/"),
                headers=[server.host_header],
                stream=httpcore.ByteStream(b""),
                extensions={},
            )
示例#16
0
async def test_cannot_connect_tcp(backend: str, url) -> None:
    """
    A properly wrapped error is raised when connecting to the server fails.
    """
    async with httpcore.AsyncConnectionPool(backend=backend) as http:
        with pytest.raises(httpcore.ConnectError):
            await http.handle_async_request(
                method=b"GET",
                url=url,
                headers=[],
                stream=httpcore.ByteStream(b""),
                extensions={},
            )
示例#17
0
async def test_request_with_missing_host_header() -> None:
    backend = MockBackend(http_buffer=[])

    async with httpcore.AsyncConnectionPool(backend=backend) as http:
        with pytest.raises(httpcore.LocalProtocolError) as excinfo:
            await http.handle_async_request(
                method=b"GET",
                url=(b"http", b"example.org", None, b"/"),
                headers=[],
                stream=httpcore.ByteStream(b""),
                extensions={},
            )
        assert str(excinfo.value) == "Missing mandatory Host: header"
示例#18
0
async def test_https_request_reuse_connection(backend: str,
                                              https_server: Server) -> None:
    async with httpcore.AsyncConnectionPool(backend=backend) as http:
        status_code, headers, stream, extensions = await http.handle_async_request(
            method=b"GET",
            url=(b"https", *https_server.netloc, b"/"),
            headers=[https_server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        await read_body(stream)

        assert status_code == 200
        reason_phrase = b"OK" if https_server.sends_reason else b""
        assert extensions == {
            "http_version": b"HTTP/1.1",
            "reason_phrase": reason_phrase,
        }
        origin = (b"https", *https_server.netloc)
        assert len(http._connections[origin]) == 1  # type: ignore

        status_code, headers, stream, extensions = await http.handle_async_request(
            method=b"GET",
            url=(b"https", *https_server.netloc, b"/"),
            headers=[https_server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        await read_body(stream)

        assert status_code == 200
        reason_phrase = b"OK" if https_server.sends_reason else b""
        assert extensions == {
            "http_version": b"HTTP/1.1",
            "reason_phrase": reason_phrase,
        }
        origin = (b"https", *https_server.netloc)
        assert len(http._connections[origin]) == 1  # type: ignore
示例#19
0
async def test_connection_pool_get_connection_info(
    http2: bool,
    keepalive_expiry: float,
    expected_during_active: dict,
    expected_during_idle: dict,
    backend: str,
    https_server: Server,
) -> None:
    async with httpcore.AsyncConnectionPool(http2=http2,
                                            keepalive_expiry=keepalive_expiry,
                                            backend=backend) as http:
        _, _, stream_1, _ = await http.handle_async_request(
            method=b"GET",
            url=(b"https", *https_server.netloc, b"/"),
            headers=[https_server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        _, _, stream_2, _ = await http.handle_async_request(
            method=b"GET",
            url=(b"https", *https_server.netloc, b"/"),
            headers=[https_server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )

        try:
            stats = await http.get_connection_info()
            assert stats == expected_during_active
        finally:
            await read_body(stream_1)
            await read_body(stream_2)

        stats = await http.get_connection_info()
        assert stats == expected_during_idle

    stats = await http.get_connection_info()
    assert stats == {}
示例#20
0
async def test_cannot_connect_uds(backend: str) -> None:
    """
    A properly wrapped error is raised when connecting to the UDS server fails.
    """
    uds = "/tmp/doesnotexist.sock"
    async with httpcore.AsyncConnectionPool(backend=backend, uds=uds) as http:
        with pytest.raises(httpcore.ConnectError):
            await http.handle_async_request(
                method=b"GET",
                url=(b"http", b"localhost", None, b"/"),
                headers=[],
                stream=httpcore.ByteStream(b""),
                extensions={},
            )
示例#21
0
def test_http2_request(backend: str, https_server: Server) -> None:
    with httpcore.SyncConnectionPool(backend=backend, http2=True) as http:
        status_code, headers, stream, extensions = http.handle_request(
            method=b"GET",
            url=(b"https", *https_server.netloc, b"/"),
            headers=[https_server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        read_body(stream)

        assert status_code == 200
        assert extensions == {"http_version": b"HTTP/2"}
        origin = (b"https", *https_server.netloc)
        assert len(http._connections[origin]) == 1  # type: ignore
示例#22
0
def test_request_with_missing_host_header() -> None:
    backend = MockBackend(http_buffer=[])

    server_config = h2.config.H2Configuration(client_side=False)
    server_conn = h2.connection.H2Connection(config=server_config)
    server_conn.initiate_connection()
    backend = MockBackend(http_buffer=[server_conn.data_to_send()])

    with httpcore.SyncConnectionPool(backend=backend) as http:
        with pytest.raises(httpcore.LocalProtocolError) as excinfo:
            http.handle_request(
                method=b"GET",
                url=(b"http", b"example.org", None, b"/"),
                headers=[],
                stream=httpcore.ByteStream(b""),
                extensions={},
            )
        assert str(excinfo.value) == "Missing mandatory Host: header"
示例#23
0
def stream(
    data, files, boundary: bytes
) -> Union[httpcore.AsyncByteStream, httpcore.SyncByteStream]:
    if files:
        # TODO Get rid of this internal import
        # import is performed at runtime when needed to reduce impact of internal changes in httpx
        from httpx._multipart import MultipartStream

        return MultipartStream(data=data or {}, files=files, boundary=boundary)

    if isinstance(data, str):
        data = data.encode("utf-8")
    elif data is None:
        data = b""

    if isinstance(data, bytes):
        return httpcore.ByteStream(data)

    return IteratorStream(data)
示例#24
0
def test_explicit_backend_name(server: Server) -> None:
    with httpcore.SyncConnectionPool(backend=lookup_sync_backend()) as http:
        status_code, headers, stream, extensions = http.handle_request(
            method=b"GET",
            url=(b"http", *server.netloc, b"/"),
            headers=[server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        read_body(stream)

        assert status_code == 200
        reason_phrase = b"OK" if server.sends_reason else b""
        assert extensions == {
            "http_version": b"HTTP/1.1",
            "reason_phrase": reason_phrase,
        }
        origin = (b"http", *server.netloc)
        assert len(http._connections[origin]) == 1  # type: ignore
示例#25
0
async def test_http_request_unix_domain_socket(uds_server: Server,
                                               backend: str) -> None:
    uds = uds_server.uds
    async with httpcore.AsyncConnectionPool(uds=uds, backend=backend) as http:
        status_code, headers, stream, extensions = await http.handle_async_request(
            method=b"GET",
            url=(b"http", b"localhost", None, b"/"),
            headers=[(b"host", b"localhost")],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        assert status_code == 200
        reason_phrase = b"OK" if uds_server.sends_reason else b""
        assert extensions == {
            "http_version": b"HTTP/1.1",
            "reason_phrase": reason_phrase,
        }
        body = await read_body(stream)
        assert body == b"Hello, world!"
示例#26
0
async def test_closing_http_request(backend: str, server: Server) -> None:
    async with httpcore.AsyncConnectionPool(backend=backend) as http:
        status_code, headers, stream, extensions = await http.handle_async_request(
            method=b"GET",
            url=(b"http", *server.netloc, b"/"),
            headers=[server.host_header, (b"connection", b"close")],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        await read_body(stream)

        assert status_code == 200
        reason_phrase = b"OK" if server.sends_reason else b""
        assert extensions == {
            "http_version": b"HTTP/1.1",
            "reason_phrase": reason_phrase,
        }
        origin = (b"http", *server.netloc)
        assert origin not in http._connections  # type: ignore
示例#27
0
async def test_post_request() -> None:
    bytes_generator = HTTP2BytesGenerator()
    bytes_to_send = bytes_generator.get_server_bytes(
        request_headers=[
            (b":method", b"POST"),
            (b":authority", b"www.example.com"),
            (b":scheme", b"https"),
            (b":path", "/"),
            (b"content-length", b"13"),
        ],
        request_data=b"Hello, world.",
        response_headers=[
            (b":status", b"200"),
            (b"date", b"Sat, 06 Oct 2049 12:34:56 GMT"),
            (b"server", b"Apache"),
            (b"content-length", b"13"),
            (b"content-type", b"text/plain"),
        ],
        response_data=b"Hello, world.",
    )
    backend = MockBackend(http_buffer=[bytes_to_send])

    async with httpcore.AsyncConnectionPool(http2=True,
                                            backend=backend) as http:
        # We're sending a request with a standard keep-alive connection, so
        # it will remain in the pool once we've sent the request.
        response = await http.handle_async_request(
            method=b"POST",
            url=(b"https", b"example.org", None, b"/"),
            headers=[(b"Host", b"example.org"), (b"Content-length", b"13")],
            stream=httpcore.ByteStream(b"Hello, world."),
            extensions={},
        )
        status_code, headers, stream, extensions = response
        body = await stream.aread()
        assert status_code == 200
        assert body == b"Hello, world."
        assert await http.get_connection_info() == {
            "https://example.org": ["HTTP/2, IDLE, 0 streams"]
        }
示例#28
0
def test_http_request_local_address(backend: str, server: Server) -> None:
    if backend == "sync" and lookup_sync_backend() == "trio":
        pytest.skip("The trio backend does not support local_address")

    with httpcore.SyncConnectionPool(backend=backend,
                                     local_address="0.0.0.0") as http:
        status_code, headers, stream, extensions = http.handle_request(
            method=b"GET",
            url=(b"http", *server.netloc, b"/"),
            headers=[server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        read_body(stream)

        assert status_code == 200
        reason_phrase = b"OK" if server.sends_reason else b""
        assert extensions == {
            "http_version": b"HTTP/1.1",
            "reason_phrase": reason_phrase,
        }
        origin = (b"http", *server.netloc)
        assert len(http._connections[origin]) == 1  # type: ignore
示例#29
0
async def test_http_proxy(proxy_server: URL, proxy_mode: str, backend: str,
                          server: Server) -> None:
    max_connections = 1
    async with httpcore.AsyncHTTPProxy(
            proxy_server,
            proxy_mode=proxy_mode,
            max_connections=max_connections,
            backend=backend,
    ) as http:
        status_code, headers, stream, extensions = await http.handle_async_request(
            method=b"GET",
            url=(b"http", *server.netloc, b"/"),
            headers=[server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        await read_body(stream)

        assert status_code == 200
        reason_phrase = b"OK" if server.sends_reason else b""
        assert extensions == {
            "http_version": b"HTTP/1.1",
            "reason_phrase": reason_phrase,
        }
示例#30
0
async def test_retries_enabled(server: Server) -> None:
    """
    When retries are enabled, connection failures are retried on with
    a fixed exponential backoff.
    """
    backend = AsyncMockBackend()
    retries = 10  # Large enough to not run out of retries within this test.

    async with httpcore.AsyncConnectionPool(
        retries=retries, max_keepalive_connections=0, backend=backend
    ) as http:
        # Standard case, no failures.
        response = await http.handle_async_request(
            method=b"GET",
            url=(b"http", *server.netloc, b"/"),
            headers=[server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        assert backend.pop_open_tcp_stream_intervals() == []
        status_code, _, stream, _ = response
        assert status_code == 200
        await read_body(stream)

        # One failure, then success.
        backend.push(httpcore.ConnectError(), None)
        response = await http.handle_async_request(
            method=b"GET",
            url=(b"http", *server.netloc, b"/"),
            headers=[server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        assert backend.pop_open_tcp_stream_intervals() == [
            pytest.approx(0, abs=5e-3),  # Retry immediately.
        ]
        status_code, _, stream, _ = response
        assert status_code == 200
        await read_body(stream)

        # Three failures, then success.
        backend.push(
            httpcore.ConnectError(),
            httpcore.ConnectTimeout(),
            httpcore.ConnectTimeout(),
            None,
        )
        response = await http.handle_async_request(
            method=b"GET",
            url=(b"http", *server.netloc, b"/"),
            headers=[server.host_header],
            stream=httpcore.ByteStream(b""),
            extensions={},
        )
        assert backend.pop_open_tcp_stream_intervals() == [
            pytest.approx(0, abs=5e-3),  # Retry immediately.
            pytest.approx(0.5, rel=0.1),  # First backoff.
            pytest.approx(1.0, rel=0.1),  # Second (increased) backoff.
        ]
        status_code, _, stream, _ = response
        assert status_code == 200
        await read_body(stream)

        # Non-connect exceptions are not retried on.
        backend.push(httpcore.ReadTimeout(), httpcore.NetworkError())
        with pytest.raises(httpcore.ReadTimeout):
            await http.handle_async_request(
                method=b"GET",
                url=(b"http", *server.netloc, b"/"),
                headers=[server.host_header],
                stream=httpcore.ByteStream(b""),
                extensions={},
            )
        with pytest.raises(httpcore.NetworkError):
            await http.handle_async_request(
                method=b"GET",
                url=(b"http", *server.netloc, b"/"),
                headers=[server.host_header],
                stream=httpcore.ByteStream(b""),
                extensions={},
            )