From 17a4ebef15d883a75b23b904a77a80ef645fe2b8 Mon Sep 17 00:00:00 2001 From: Marcelo Trylesinski Date: Sun, 31 May 2026 19:49:49 +0200 Subject: [PATCH] Integrate httpcore2 documentation into the httpx2 docs site Move the httpcore2 docs into the main mkdocs build under an httpcore2 nav section, rewriting httpcore references to httpcore2 and repointing encode URLs at pydantic/httpx2. Drop the standalone httpcore2 mkdocs config, which was unbuilt and still pointed at the old upstream. --- .../docs => docs/httpcore2}/async.md | 40 ++++----- .../httpcore2}/connection-pools.md | 24 +++--- .../docs => docs/httpcore2}/connections.md | 12 +-- docs/httpcore2/exceptions.md | 18 ++++ .../docs => docs/httpcore2}/extensions.md | 42 +++++----- .../docs => docs/httpcore2}/http2.md | 34 ++++---- .../docs => docs/httpcore2}/index.md | 14 ++-- docs/httpcore2/logging.md | 41 ++++++++++ .../httpcore2}/network-backends.md | 82 +++++++++---------- .../docs => docs/httpcore2}/proxies.md | 54 ++++++------ .../docs => docs/httpcore2}/quickstart.md | 48 +++++------ .../httpcore2}/requests-responses-urls.md | 22 ++--- mkdocs.yml | 13 +++ src/httpcore2/docs/exceptions.md | 18 ---- src/httpcore2/docs/logging.md | 41 ---------- src/httpcore2/docs/table-of-contents.md | 49 ----------- src/httpcore2/mkdocs.yml | 36 -------- 17 files changed, 258 insertions(+), 330 deletions(-) rename {src/httpcore2/docs => docs/httpcore2}/async.md (87%) rename {src/httpcore2/docs => docs/httpcore2}/connection-pools.md (90%) rename {src/httpcore2/docs => docs/httpcore2}/connections.md (55%) create mode 100644 docs/httpcore2/exceptions.md rename {src/httpcore2/docs => docs/httpcore2}/extensions.md (86%) rename {src/httpcore2/docs => docs/httpcore2}/http2.md (74%) rename {src/httpcore2/docs => docs/httpcore2}/index.md (78%) create mode 100644 docs/httpcore2/logging.md rename {src/httpcore2/docs => docs/httpcore2}/network-backends.md (76%) rename {src/httpcore2/docs => docs/httpcore2}/proxies.md (57%) rename {src/httpcore2/docs => docs/httpcore2}/quickstart.md (68%) rename {src/httpcore2/docs => docs/httpcore2}/requests-responses-urls.md (61%) delete mode 100644 src/httpcore2/docs/exceptions.md delete mode 100644 src/httpcore2/docs/logging.md delete mode 100644 src/httpcore2/docs/table-of-contents.md delete mode 100644 src/httpcore2/mkdocs.yml diff --git a/src/httpcore2/docs/async.md b/docs/httpcore2/async.md similarity index 87% rename from src/httpcore2/docs/async.md rename to docs/httpcore2/async.md index cd7866df..9926dc1f 100644 --- a/src/httpcore2/docs/async.md +++ b/docs/httpcore2/async.md @@ -13,13 +13,13 @@ Launching concurrent async tasks is far more resource efficient than spawning mu If you're using async with [Python's stdlib `asyncio` support](https://docs.python.org/3/library/asyncio.html), install the optional dependencies using: ```shell -pip install 'httpcore[asyncio]' +pip install 'httpcore2[asyncio]' ``` Alternatively, if you're working with [the Python `trio` package](https://trio.readthedocs.io/en/stable/): ```shell -pip install 'httpcore[trio]' +pip install 'httpcore2[trio]' ``` We highly recommend `trio` for async support. The `trio` project [pioneered the principles of structured concurrency](https://en.wikipedia.org/wiki/Structured_concurrency), and has a more carefully constrained API against which to work from. @@ -29,21 +29,21 @@ We highly recommend `trio` for async support. The `trio` project [pioneered the When using async support, you need make sure to use an async connection pool class: ```python -# The async variation of `httpcore.ConnectionPool` -async with httpcore.AsyncConnectionPool() as http: +# The async variation of `httpcore2.ConnectionPool` +async with httpcore2.AsyncConnectionPool() as http: ... ``` ### Sending requests -Sending requests with the async version of `httpcore` requires the `await` keyword: +Sending requests with the async version of `httpcore2` requires the `await` keyword: ```python import asyncio -import httpcore +import httpcore2 async def main(): - async with httpcore.AsyncConnectionPool() as http: + async with httpcore2.AsyncConnectionPool() as http: response = await http.request("GET", "https://www.example.com/") @@ -65,11 +65,11 @@ For example: ```python import asyncio -import httpcore +import httpcore2 async def main(): - async with httpcore.AsyncConnectionPool() as http: + async with httpcore2.AsyncConnectionPool() as http: async with http.stream("GET", "https://www.example.com/") as response: async for chunk in response.aiter_stream(): print(f"Downloaded: {chunk}") @@ -80,10 +80,10 @@ asyncio.run(main()) ### Pool lifespans -When using `httpcore` in an async environment it is strongly recommended that you instantiate and use connection pools using the context managed style: +When using `httpcore2` in an async environment it is strongly recommended that you instantiate and use connection pools using the context managed style: ```python -async with httpcore.AsyncConnectionPool() as http: +async with httpcore2.AsyncConnectionPool() as http: ... ``` @@ -93,7 +93,7 @@ If you do want to use a connection pool without this style then you'll need to e ```python try: - http = httpcore.AsyncConnectionPool() + http = httpcore2.AsyncConnectionPool() ... finally: await http.aclose() @@ -117,7 +117,7 @@ Let's take a look at sending several outgoing HTTP requests concurrently, using ```python import asyncio -import httpcore +import httpcore2 import time @@ -126,7 +126,7 @@ async def download(http, year): async def main(): - async with httpcore.AsyncConnectionPool() as http: + async with httpcore2.AsyncConnectionPool() as http: started = time.time() # Here we use `asyncio.gather()` in order to run several tasks concurrently... tasks = [download(http, year) for year in range(2000, 2020)] @@ -146,7 +146,7 @@ asyncio.run(main()) Trio is [an alternative async library](https://trio.readthedocs.io/en/stable/), designed around the [the principles of structured concurrency](https://en.wikipedia.org/wiki/Structured_concurrency). ```python -import httpcore +import httpcore2 import trio import time @@ -156,7 +156,7 @@ async def download(http, year): async def main(): - async with httpcore.AsyncConnectionPool() as http: + async with httpcore2.AsyncConnectionPool() as http: started = time.time() async with trio.open_nursery() as nursery: for year in range(2000, 2020): @@ -178,7 +178,7 @@ AnyIO is an [asynchronous networking and concurrency library](https://anyio.read The `anyio` library is designed around the [the principles of structured concurrency](https://en.wikipedia.org/wiki/Structured_concurrency), and brings many of the same correctness and usability benefits that Trio provides, while interoperating with existing `asyncio` libraries. ```python -import httpcore +import httpcore2 import anyio import time @@ -188,7 +188,7 @@ async def download(http, year): async def main(): - async with httpcore.AsyncConnectionPool() as http: + async with httpcore2.AsyncConnectionPool() as http: started = time.time() async with anyio.create_task_group() as task_group: for year in range(2000, 2020): @@ -207,9 +207,9 @@ anyio.run(main) # Reference -## `httpcore.AsyncConnectionPool` +## `httpcore2.AsyncConnectionPool` -::: httpcore.AsyncConnectionPool +::: httpcore2.AsyncConnectionPool handler: python rendering: show_source: False diff --git a/src/httpcore2/docs/connection-pools.md b/docs/httpcore2/connection-pools.md similarity index 90% rename from src/httpcore2/docs/connection-pools.md rename to docs/httpcore2/connection-pools.md index cb461a98..1203aabe 100644 --- a/src/httpcore2/docs/connection-pools.md +++ b/docs/httpcore2/connection-pools.md @@ -1,15 +1,15 @@ # Connection Pools -While the top-level API provides convenience functions for working with `httpcore`, +While the top-level API provides convenience functions for working with `httpcore2`, in practice you'll almost always want to take advantage of the connection pooling functionality that it provides. To do so, instantiate a pool instance, and use it to send requests: ```python -import httpcore +import httpcore2 -http = httpcore.ConnectionPool() +http = httpcore2.ConnectionPool() r = http.request("GET", "https://www.example.com/") print(r) @@ -21,11 +21,11 @@ Connection pools support the same `.request()` and `.stream()` APIs [as describe We can observe the benefits of connection pooling with a simple script like so: ```python -import httpcore +import httpcore2 import time -http = httpcore.ConnectionPool() +http = httpcore2.ConnectionPool() for counter in range(5): started = time.time() response = http.request("GET", "https://www.example.com/") @@ -52,7 +52,7 @@ The connection pool instance is also the main point of configuration. Let's take ### SSL configuration * `ssl_context`: An SSL context to use for verifying connections. - If not specified, the default `httpcore.default_ssl_context()` + If not specified, the default `httpcore2.default_ssl_context()` will be used. ### Pooling configuration @@ -93,20 +93,20 @@ Working with a single global instance isn't a bad idea for many use case, since # This is perfectly fine for most purposes. # The connection pool will automatically be closed when it is garbage collected, # or when the Python interpreter exits. -http = httpcore.ConnectionPool() +http = httpcore2.ConnectionPool() ``` However, to be more explicit around the resource usage, we can use the connection pool within a context manager: ```python -with httpcore.ConnectionPool() as http: +with httpcore2.ConnectionPool() as http: ... ``` Or else close the pool explicitly: ```python -http = httpcore.ConnectionPool() +http = httpcore2.ConnectionPool() try: ... finally: @@ -115,7 +115,7 @@ finally: ## Thread and task safety -Connection pools are designed to be thread-safe. Similarly, when using `httpcore` in an async context connection pools are task-safe. +Connection pools are designed to be thread-safe. Similarly, when using `httpcore2` in an async context connection pools are task-safe. This means that you can have a single connection pool instance shared by multiple threads. @@ -123,9 +123,9 @@ This means that you can have a single connection pool instance shared by multipl # Reference -## `httpcore.ConnectionPool` +## `httpcore2.ConnectionPool` -::: httpcore.ConnectionPool +::: httpcore2.ConnectionPool handler: python rendering: show_source: False diff --git a/src/httpcore2/docs/connections.md b/docs/httpcore2/connections.md similarity index 55% rename from src/httpcore2/docs/connections.md rename to docs/httpcore2/connections.md index 0ca21556..8ad87b39 100644 --- a/src/httpcore2/docs/connections.md +++ b/docs/httpcore2/connections.md @@ -6,23 +6,23 @@ TODO # Reference -## `httpcore.HTTPConnection` +## `httpcore2.HTTPConnection` -::: httpcore.HTTPConnection +::: httpcore2.HTTPConnection handler: python rendering: show_source: False -## `httpcore.HTTP11Connection` +## `httpcore2.HTTP11Connection` -::: httpcore.HTTP11Connection +::: httpcore2.HTTP11Connection handler: python rendering: show_source: False -## `httpcore.HTTP2Connection` +## `httpcore2.HTTP2Connection` -::: httpcore.HTTP2Connection +::: httpcore2.HTTP2Connection handler: python rendering: show_source: False diff --git a/docs/httpcore2/exceptions.md b/docs/httpcore2/exceptions.md new file mode 100644 index 00000000..fa85fd1b --- /dev/null +++ b/docs/httpcore2/exceptions.md @@ -0,0 +1,18 @@ +# Exceptions + +The following exceptions may be raised when sending a request: + +* `httpcore2.TimeoutException` + * `httpcore2.PoolTimeout` + * `httpcore2.ConnectTimeout` + * `httpcore2.ReadTimeout` + * `httpcore2.WriteTimeout` +* `httpcore2.NetworkError` + * `httpcore2.ConnectError` + * `httpcore2.ReadError` + * `httpcore2.WriteError` +* `httpcore2.ProtocolError` + * `httpcore2.RemoteProtocolError` + * `httpcore2.LocalProtocolError` +* `httpcore2.ProxyError` +* `httpcore2.UnsupportedProtocol` diff --git a/src/httpcore2/docs/extensions.md b/docs/httpcore2/extensions.md similarity index 86% rename from src/httpcore2/docs/extensions.md rename to docs/httpcore2/extensions.md index 7a24a418..0dd4b584 100644 --- a/src/httpcore2/docs/extensions.md +++ b/docs/httpcore2/extensions.md @@ -1,6 +1,6 @@ # Extensions -The request/response API used by `httpcore` is kept deliberately simple and explicit. +The request/response API used by `httpcore2` is kept deliberately simple and explicit. The `Request` and `Response` models are pretty slim wrappers around this core API: @@ -24,7 +24,7 @@ Well... almost. There is a maxim in Computer Science that *"All non-trivial abstractions, to some degree, are leaky"*. When an expression is leaky, it's important that it ought to at least leak only in well-defined places. -In order to handle cases that don't otherwise fit inside this core abstraction, `httpcore` requests and responses have 'extensions'. These are a dictionary of optional additional information. +In order to handle cases that don't otherwise fit inside this core abstraction, `httpcore2` requests and responses have 'extensions'. These are a dictionary of optional additional information. Let's expand on our request/response abstraction... @@ -49,7 +49,7 @@ Let's expand on our request/response abstraction... Several extensions are supported both on the request: ```python -r = httpcore.request( +r = httpcore2.request( "GET", "https://www.example.com", extensions={"timeout": {"connect": 5.0}} @@ -59,7 +59,7 @@ r = httpcore.request( And on the response: ```python -r = httpcore.request("GET", "https://www.example.com") +r = httpcore2.request("GET", "https://www.example.com") print(r.extensions["http_version"]) # When using HTTP/1.1 on the client side, the server HTTP response @@ -79,7 +79,7 @@ For example: ```python # Timeout if a connection takes more than 5 seconds to established, or if # we are blocked waiting on the connection pool for more than 10 seconds. -r = httpcore.request( +r = httpcore2.request( "GET", "https://www.example.com", extensions={"timeout": {"connect": 5.0, "pool": 10.0}} @@ -89,19 +89,19 @@ r = httpcore.request( ### `"trace"` The trace extension allows a callback handler to be installed to monitor the internal -flow of events within `httpcore`. The simplest way to explain this is with an example: +flow of events within `httpcore2`. The simplest way to explain this is with an example: ```python -import httpcore +import httpcore2 def log(event_name, info): print(event_name, info) -r = httpcore.request("GET", "https://www.example.com/", extensions={"trace": log}) +r = httpcore2.request("GET", "https://www.example.com/", extensions={"trace": log}) # connection.connect_tcp.started {'host': 'www.example.com', 'port': 443, 'local_address': None, 'timeout': None} -# connection.connect_tcp.complete {'return_value': } +# connection.connect_tcp.complete {'return_value': } # connection.start_tls.started {'ssl_context': , 'server_hostname': b'www.example.com', 'timeout': None} -# connection.start_tls.complete {'return_value': } +# connection.start_tls.complete {'return_value': } # http11.send_request_headers.started {'request': } # http11.send_request_headers.complete {'return_value': None} # http11.send_request_body.started {'request': } @@ -120,7 +120,7 @@ The `event_name` and `info` arguments here will be one of the following: * `{event_type}.{event_name}.complete`, `{"return_value": <...>}` * `{event_type}.{event_name}.failed`, `{"exception": <...>}` -Note that when using the async variant of `httpcore` the handler function passed to `"trace"` must be an `async def ...` function. +Note that when using the async variant of `httpcore2` the handler function passed to `"trace"` must be an `async def ...` function. The following event types are currently exposed... @@ -147,7 +147,7 @@ The following event types are currently exposed... * `"http2.receive_response_body"` * `"http2.response_closed"` -The exact set of trace events may be subject to change across different versions of `httpcore`. If you need to rely on a particular set of events it is recommended that you pin installation of the package to a fixed version. +The exact set of trace events may be subject to change across different versions of `httpcore2`. If you need to rely on a particular set of events it is recommended that you pin installation of the package to a fixed version. ### `"sni_hostname"` @@ -158,7 +158,7 @@ For example: ``` python headers = {"Host": "www.encode.io"} extensions = {"sni_hostname": "www.encode.io"} -response = httpcore.request( +response = httpcore2.request( "GET", "https://185.199.108.153", headers=headers, @@ -180,7 +180,7 @@ For example: ```python extensions = {"target": b"www.encode.io:443"} -response = httpcore.request( +response = httpcore2.request( "CONNECT", "http://your-tunnel-proxy.com", headers=headers, @@ -238,18 +238,18 @@ A proxy CONNECT request using the network stream: # CONNECT http://www.example.com HTTP/1.1 url = "http://127.0.0.1:8080" extensions = {"target: "http://www.example.com"} -with httpcore.stream("CONNECT", url, extensions=extensions) as response: +with httpcore2.stream("CONNECT", url, extensions=extensions) as response: network_stream = response.extensions["network_stream"] # Upgrade to an SSL stream... network_stream = network_stream.start_tls( - ssl_context=httpcore.default_ssl_context(), + ssl_context=httpcore2.default_ssl_context(), hostname=b"www.example.com", ) # Manually send an HTTP request over the network stream, and read the response... # - # For a more complete example see the httpcore `TunnelHTTPConnection` implementation. + # For a more complete example see the httpcore2 `TunnelHTTPConnection` implementation. network_stream.write(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") data = network_stream.read() print(data) @@ -260,7 +260,7 @@ with httpcore.stream("CONNECT", url, extensions=extensions) as response: Using the `wsproto` package to handle a websockets session: ```python -import httpcore +import httpcore2 import wsproto import os import base64 @@ -273,7 +273,7 @@ headers = { b"Sec-WebSocket-Key": base64.b64encode(os.urandom(16)), b"Sec-WebSocket-Version": b"13" } -with httpcore.stream("GET", url, headers=headers) as response: +with httpcore2.stream("GET", url, headers=headers) as response: if response.status != 101: raise Exception("Failed to upgrade to websockets", response) @@ -304,7 +304,7 @@ with httpcore.stream("GET", url, headers=headers) as response: The network stream abstraction also allows access to various low-level information that may be exposed by the underlying socket: ```python -response = httpcore.request("GET", "https://www.example.com") +response = httpcore2.request("GET", "https://www.example.com") network_stream = response.extensions["network_stream"] client_addr = network_stream.get_extra_info("client_addr") @@ -316,7 +316,7 @@ print("Server address", server_addr) The socket SSL information is also available through this interface, although you need to ensure that the underlying connection is still open, in order to access it... ```python -with httpcore.stream("GET", "https://www.example.com") as response: +with httpcore2.stream("GET", "https://www.example.com") as response: network_stream = response.extensions["network_stream"] ssl_object = network_stream.get_extra_info("ssl_object") diff --git a/src/httpcore2/docs/http2.md b/docs/httpcore2/http2.md similarity index 74% rename from src/httpcore2/docs/http2.md rename to docs/httpcore2/http2.md index f136a95d..6ebb2767 100644 --- a/src/httpcore2/docs/http2.md +++ b/docs/httpcore2/http2.md @@ -10,20 +10,20 @@ For a comprehensive guide to HTTP/2 you may want to check out "[HTTP2 Explained] ## Enabling HTTP/2 -When using the `httpcore` client, HTTP/2 support is not enabled by default, because HTTP/1.1 is a mature, battle-hardened transport layer, and our HTTP/1.1 implementation may be considered the more robust option at this point in time. It is possible that a future version of `httpcore` may enable HTTP/2 support by default. +When using the `httpcore2` client, HTTP/2 support is not enabled by default, because HTTP/1.1 is a mature, battle-hardened transport layer, and our HTTP/1.1 implementation may be considered the more robust option at this point in time. It is possible that a future version of `httpcore2` may enable HTTP/2 support by default. If you're issuing highly concurrent requests you might want to consider trying out our HTTP/2 support. You can do so by first making sure to install the optional HTTP/2 dependencies... ```shell -pip install 'httpcore[http2]' +pip install 'httpcore2[http2]' ``` And then instantiating a connection pool with HTTP/2 support enabled: ```python -import httpcore +import httpcore2 -pool = httpcore.ConnectionPool(http2=True) +pool = httpcore2.ConnectionPool(http2=True) ``` We can take a look at the difference in behaviour by issuing several outgoing requests in parallel. @@ -31,7 +31,7 @@ We can take a look at the difference in behaviour by issuing several outgoing re Start out by using a standard HTTP/1.1 connection pool: ```python -import httpcore +import httpcore2 import concurrent.futures import time @@ -41,7 +41,7 @@ def download(http, year): def main(): - with httpcore.ConnectionPool() as http: + with httpcore2.ConnectionPool() as http: started = time.time() with concurrent.futures.ThreadPoolExecutor(max_workers=10) as threads: for year in range(2000, 2020): @@ -75,7 +75,7 @@ We can see that the connection pool required a number of connections in order to If we now upgrade our connection pool to support HTTP/2: ```python -with httpcore.ConnectionPool(http2=True) as http: +with httpcore2.ConnectionPool(http2=True) as http: ... ``` @@ -97,9 +97,9 @@ Enabling HTTP/2 support on the client does not *necessarily* mean that your requ You can determine which version of the HTTP protocol was used by examining the `"http_version"` response extension. ```python -import httpcore +import httpcore2 -pool = httpcore.ConnectionPool(http2=True) +pool = httpcore2.ConnectionPool(http2=True) response = pool.request("GET", "https://www.example.com/") # Should be one of b"HTTP/2", b"HTTP/1.1", b"HTTP/1.0", or b"HTTP/0.9". @@ -116,13 +116,13 @@ Robust servers need to support both HTTP/2 and HTTP/1.1 capable clients, and so Generally the method used is for the server to advertise if it has HTTP/2 support during the part of the SSL connection handshake. This is known as ALPN - "Application Layer Protocol Negotiation". -Most browsers only provide HTTP/2 support over HTTPS connections, and this is also the default behaviour that `httpcore` provides. If you enable HTTP/2 support you should still expect to see HTTP/1.1 connections for any `http://` URLs. +Most browsers only provide HTTP/2 support over HTTPS connections, and this is also the default behaviour that `httpcore2` provides. If you enable HTTP/2 support you should still expect to see HTTP/1.1 connections for any `http://` URLs. ### HTTP/2 over HTTP Servers can optionally also support HTTP/2 over HTTP by supporting the `Upgrade: h2c` header. -This mechanism is not supported by `httpcore`. It requires an additional round-trip between the client and server, and also requires any request body to be sent twice. +This mechanism is not supported by `httpcore2`. It requires an additional round-trip between the client and server, and also requires any request body to be sent twice. ### Prior Knowledge @@ -131,14 +131,14 @@ If you know in advance that the server you are communicating with will support H This is managed by disabling HTTP/1.1 support on the connection pool: ```python -pool = httpcore.ConnectionPool(http1=False, http2=True) +pool = httpcore2.ConnectionPool(http1=False, http2=True) ``` ## Request & response headers Because HTTP/2 frames the requests and responses somewhat differently to HTTP/1.1, there is a difference in some of the headers that are used. -In order for the `httpcore` library to support both HTTP/1.1 and HTTP/2 transparently, the HTTP/1.1 style is always used throughout the API. Any differences in header styles are only mapped onto HTTP/2 at the internal network layer. +In order for the `httpcore2` library to support both HTTP/1.1 and HTTP/2 transparently, the HTTP/1.1 style is always used throughout the API. Any differences in header styles are only mapped onto HTTP/2 at the internal network layer. ## Request headers @@ -146,16 +146,16 @@ The following pseudo-headers are used by HTTP/2 in the request: * `:method` - The request method. * `:path` - Taken from the URL of the request. -* `:authority` - Equivalent to the `Host` header in HTTP/1.1. In `httpcore` this is represented using the request `Host` header, which is automatically populated from the request URL if no `Host` header is explicitly included. +* `:authority` - Equivalent to the `Host` header in HTTP/1.1. In `httpcore2` this is represented using the request `Host` header, which is automatically populated from the request URL if no `Host` header is explicitly included. * `:scheme` - Taken from the URL of the request. -These pseudo-headers are included in `httpcore` as part of the `request.method` and `request.url` attributes, and through the `request.headers["Host"]` header. *They are not exposed directly by their psuedo-header names.* +These pseudo-headers are included in `httpcore2` as part of the `request.method` and `request.url` attributes, and through the `request.headers["Host"]` header. *They are not exposed directly by their psuedo-header names.* The one other difference to be aware of is the `Transfer-Encoding: chunked` header. In HTTP/2 this header is never used, since streaming data is framed using a different mechanism. -In `httpcore` the `Transfer-Encoding: chunked` header is always used to represent the presence of a streaming body on the request, and is automatically populated if required. However the header is only sent if the underlying connection ends up being HTTP/1.1, and is omitted if the underlying connection ends up being HTTP/2. +In `httpcore2` the `Transfer-Encoding: chunked` header is always used to represent the presence of a streaming body on the request, and is automatically populated if required. However the header is only sent if the underlying connection ends up being HTTP/1.1, and is omitted if the underlying connection ends up being HTTP/2. ## Response headers @@ -163,4 +163,4 @@ The following pseudo-header is used by HTTP/2 in the response: * `:status` - The response status code. -In `httpcore` this *is represented by the `response.status` attribute, rather than being exposed as a psuedo-header*. +In `httpcore2` this *is represented by the `response.status` attribute, rather than being exposed as a psuedo-header*. diff --git a/src/httpcore2/docs/index.md b/docs/httpcore2/index.md similarity index 78% rename from src/httpcore2/docs/index.md rename to docs/httpcore2/index.md index 6505febe..96af14c9 100644 --- a/src/httpcore2/docs/index.md +++ b/docs/httpcore2/index.md @@ -1,7 +1,7 @@ # HTTPCore -[![Test Suite](https://github.com/encode/httpcore/workflows/Test%20Suite/badge.svg)](https://github.com/encode/httpcore/actions) -[![Package version](https://badge.fury.io/py/httpcore.svg)](https://pypi.org/project/httpcore/) +[![Test Suite](https://github.com/pydantic/httpx2/workflows/Test%20Suite/badge.svg)](https://github.com/pydantic/httpx2/actions) +[![Package version](https://badge.fury.io/py/httpcore2.svg)](https://pypi.org/project/httpcore2/) > *Do one thing, and do it well.* @@ -28,19 +28,19 @@ Some things HTTP Core does do: For HTTP/1.1 only support, install with: ```shell -pip install httpcore +pip install httpcore2 ``` For HTTP/1.1 and HTTP/2 support, install with: ```shell -pip install httpcore[http2] +pip install httpcore2[http2] ``` For SOCKS proxy support, install with: ```shell -pip install httpcore[socks] +pip install httpcore2[socks] ``` ## Example @@ -48,9 +48,9 @@ pip install httpcore[socks] Let's check we're able to send HTTP requests: ```python -import httpcore +import httpcore2 -response = httpcore.request("GET", "https://www.example.com/") +response = httpcore2.request("GET", "https://www.example.com/") print(response) # diff --git a/docs/httpcore2/logging.md b/docs/httpcore2/logging.md new file mode 100644 index 00000000..122e9b47 --- /dev/null +++ b/docs/httpcore2/logging.md @@ -0,0 +1,41 @@ +# Logging + +If you need to inspect the internal behaviour of `httpcore2`, you can use Python's standard logging to output debug level information. + +For example, the following configuration... + +```python +import logging +import httpcore2 + +logging.basicConfig( + format="%(levelname)s [%(asctime)s] %(name)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + level=logging.DEBUG +) + +httpcore2.request('GET', 'https://www.example.com') +``` + +Will send debug level output to the console, or wherever `stdout` is directed too... + +``` +DEBUG [2023-01-09 14:44:00] httpcore2.connection - connect_tcp.started host='www.example.com' port=443 local_address=None timeout=None +DEBUG [2023-01-09 14:44:00] httpcore2.connection - connect_tcp.complete return_value= +DEBUG [2023-01-09 14:44:00] httpcore2.connection - start_tls.started ssl_context= server_hostname='www.example.com' timeout=None +DEBUG [2023-01-09 14:44:00] httpcore2.connection - start_tls.complete return_value= +DEBUG [2023-01-09 14:44:00] httpcore2.http11 - send_request_headers.started request= +DEBUG [2023-01-09 14:44:00] httpcore2.http11 - send_request_headers.complete +DEBUG [2023-01-09 14:44:00] httpcore2.http11 - send_request_body.started request= +DEBUG [2023-01-09 14:44:00] httpcore2.http11 - send_request_body.complete +DEBUG [2023-01-09 14:44:00] httpcore2.http11 - receive_response_headers.started request= +DEBUG [2023-01-09 14:44:00] httpcore2.http11 - receive_response_headers.complete return_value=(b'HTTP/1.1', 200, b'OK', [(b'Age', b'572646'), (b'Cache-Control', b'max-age=604800'), (b'Content-Type', b'text/html; charset=UTF-8'), (b'Date', b'Mon, 09 Jan 2023 14:44:00 GMT'), (b'Etag', b'"3147526947+ident"'), (b'Expires', b'Mon, 16 Jan 2023 14:44:00 GMT'), (b'Last-Modified', b'Thu, 17 Oct 2019 07:18:26 GMT'), (b'Server', b'ECS (nyb/1D18)'), (b'Vary', b'Accept-Encoding'), (b'X-Cache', b'HIT'), (b'Content-Length', b'1256')]) +DEBUG [2023-01-09 14:44:00] httpcore2.http11 - receive_response_body.started request= +DEBUG [2023-01-09 14:44:00] httpcore2.http11 - receive_response_body.complete +DEBUG [2023-01-09 14:44:00] httpcore2.http11 - response_closed.started +DEBUG [2023-01-09 14:44:00] httpcore2.http11 - response_closed.complete +DEBUG [2023-01-09 14:44:00] httpcore2.connection - close.started +DEBUG [2023-01-09 14:44:00] httpcore2.connection - close.complete +``` + +The exact formatting of the debug logging may be subject to change across different versions of `httpcore2`. If you need to rely on a particular format it is recommended that you pin installation of the package to a fixed version. diff --git a/src/httpcore2/docs/network-backends.md b/docs/httpcore2/network-backends.md similarity index 76% rename from src/httpcore2/docs/network-backends.md rename to docs/httpcore2/network-backends.md index fbb6bfdb..169b5364 100644 --- a/src/httpcore2/docs/network-backends.md +++ b/docs/httpcore2/network-backends.md @@ -1,6 +1,6 @@ # Network Backends -The API layer at which `httpcore` interacts with the network is described as the network backend. Various backend implementations are provided, allowing `httpcore` to handle networking in different runtime contexts. +The API layer at which `httpcore2` interacts with the network is described as the network backend. Various backend implementations are provided, allowing `httpcore2` to handle networking in different runtime contexts. ## Working with network backends @@ -11,9 +11,9 @@ Typically you won't need to specify a network backend, as a default will automat First we're making a standard HTTP request, using a connection pool: ```python -import httpcore +import httpcore2 -with httpcore.ConnectionPool() as http: +with httpcore2.ConnectionPool() as http: response = http.request('GET', 'https://www.example.com') print(response) ``` @@ -21,23 +21,23 @@ with httpcore.ConnectionPool() as http: We can also have the same behavior, but be explicit with our selection of the network backend: ```python -import httpcore +import httpcore2 -network_backend = httpcore.SyncBackend() -with httpcore.ConnectionPool(network_backend=network_backend) as http: +network_backend = httpcore2.SyncBackend() +with httpcore2.ConnectionPool(network_backend=network_backend) as http: response = http.request('GET', 'https://www.example.com') print(response) ``` -The `httpcore.SyncBackend()` implementation handles the opening of TCP connections, and operations on the socket stream, such as reading, writing, and closing the connection. +The `httpcore2.SyncBackend()` implementation handles the opening of TCP connections, and operations on the socket stream, such as reading, writing, and closing the connection. We can get a better understanding of this by using a network backend to send a basic HTTP/1.1 request directly: ```python -import httpcore +import httpcore2 # Create an SSL context using 'certifi' for the certificates. -ssl_context = httpcore.default_ssl_context() +ssl_context = httpcore2.default_ssl_context() # A basic HTTP/1.1 request as a plain bytestring. request = b'\r\n'.join([ @@ -49,7 +49,7 @@ request = b'\r\n'.join([ ]) # Open a TCP stream and upgrade it to SSL. -network_backend = httpcore.SyncBackend() +network_backend = httpcore2.SyncBackend() network_stream = network_backend.connect_tcp("www.example.com", 443) network_stream = network_stream.start_tls(ssl_context, server_hostname="www.example.com") @@ -73,32 +73,32 @@ while True: If we're working with an `async` codebase, then we need to select a different backend. -The `httpcore.AnyIOBackend` is suitable for usage if you're running under `asyncio`. This is a networking backend implemented using [the `anyio` package](https://anyio.readthedocs.io/en/3.x/). +The `httpcore2.AnyIOBackend` is suitable for usage if you're running under `asyncio`. This is a networking backend implemented using [the `anyio` package](https://anyio.readthedocs.io/en/3.x/). ```python -import httpcore +import httpcore2 import asyncio async def main(): - network_backend = httpcore.AnyIOBackend() - async with httpcore.AsyncConnectionPool(network_backend=network_backend) as http: + network_backend = httpcore2.AnyIOBackend() + async with httpcore2.AsyncConnectionPool(network_backend=network_backend) as http: response = await http.request('GET', 'https://www.example.com') print(response) asyncio.run(main()) ``` -The `AnyIOBackend` will work when running under either `asyncio` or `trio`. However, if you're working with async using the [`trio` framework](https://trio.readthedocs.io/en/stable/), then we recommend using the `httpcore.TrioBackend`. +The `AnyIOBackend` will work when running under either `asyncio` or `trio`. However, if you're working with async using the [`trio` framework](https://trio.readthedocs.io/en/stable/), then we recommend using the `httpcore2.TrioBackend`. This will give you the same kind of networking behavior you'd have using `AnyIOBackend`, but there will be a little less indirection so it will be marginally more efficient and will present cleaner tracebacks in error cases. ```python -import httpcore +import httpcore2 import trio async def main(): - network_backend = httpcore.TrioBackend() - async with httpcore.AsyncConnectionPool(network_backend=network_backend) as http: + network_backend = httpcore2.TrioBackend() + async with httpcore2.AsyncConnectionPool(network_backend=network_backend) as http: response = await http.request('GET', 'https://www.example.com') print(response) @@ -113,16 +113,16 @@ These backends accept a list of bytes, and return network stream interfaces that Here's an example of mocking a simple HTTP/1.1 response... ```python -import httpcore +import httpcore2 -network_backend = httpcore.MockBackend([ +network_backend = httpcore2.MockBackend([ b"HTTP/1.1 200 OK\r\n", b"Content-Type: plain/text\r\n", b"Content-Length: 13\r\n", b"\r\n", b"Hello, world!", ]) -with httpcore.ConnectionPool(network_backend=network_backend) as http: +with httpcore2.ConnectionPool(network_backend=network_backend) as http: response = http.request("GET", "https://example.com/") print(response.extensions['http_version']) print(response.status) @@ -134,7 +134,7 @@ Mocking a HTTP/2 response is more complex, since it uses a binary format... ```python import hpack import hyperframe.frame -import httpcore +import httpcore2 content = [ hyperframe.frame.SettingsFrame().serialize(), @@ -155,8 +155,8 @@ content = [ # Note that we instantiate the mock backend with an `http2=True` argument. # This ensures that the mock network stream acts as if the `h2` ALPN flag has been set, # and causes the connection pool to interact with the connection using HTTP/2. -network_backend = httpcore.MockBackend(content, http2=True) -with httpcore.ConnectionPool(network_backend=network_backend) as http: +network_backend = httpcore2.MockBackend(content, http2=True) +with httpcore2.ConnectionPool(network_backend=network_backend) as http: response = http.request("GET", "https://example.com/") print(response.extensions['http_version']) print(response.status) @@ -176,10 +176,10 @@ You can use this to provide advanced networking functionality such as: Here's an example that records the network response to a file on disk: ```python -import httpcore +import httpcore2 -class RecordingNetworkStream(httpcore.NetworkStream): +class RecordingNetworkStream(httpcore2.NetworkStream): def __init__(self, record_file, stream): self.record_file = record_file self.stream = stream @@ -210,13 +210,13 @@ class RecordingNetworkStream(httpcore.NetworkStream): return self.stream.get_extra_info(info) -class RecordingNetworkBackend(httpcore.NetworkBackend): +class RecordingNetworkBackend(httpcore2.NetworkBackend): """ A custom network backend that records network responses. """ def __init__(self, record_file): self.record_file = record_file - self.backend = httpcore.SyncBackend() + self.backend = httpcore2.SyncBackend() def connect_tcp( self, @@ -243,13 +243,13 @@ class RecordingNetworkBackend(httpcore.NetworkBackend): # Once you make the request, the raw HTTP/1.1 response will be available -# in the 'network-recording' file. +# in the 'network-recording' file. # # Try switching to `http2=True` to see the difference when recording HTTP/2 binary network traffic, # or add `headers={'Accept-Encoding': 'gzip'}` to see HTTP content compression. with open("network-recording", "wb") as record_file: network_backend = RecordingNetworkBackend(record_file) - with httpcore.ConnectionPool(network_backend=network_backend) as http: + with httpcore2.ConnectionPool(network_backend=network_backend) as http: response = http.request("GET", "https://www.example.com/") print(response) ``` @@ -260,20 +260,20 @@ with open("network-recording", "wb") as record_file: ### Networking Backends -* `httpcore.SyncBackend` -* `httpcore.AnyIOBackend` -* `httpcore.TrioBackend` +* `httpcore2.SyncBackend` +* `httpcore2.AnyIOBackend` +* `httpcore2.TrioBackend` ### Mock Backends -* `httpcore.MockBackend` -* `httpcore.MockStream` -* `httpcore.AsyncMockBackend` -* `httpcore.AsyncMockStream` +* `httpcore2.MockBackend` +* `httpcore2.MockStream` +* `httpcore2.AsyncMockBackend` +* `httpcore2.AsyncMockStream` ### Base Interface -* `httpcore.NetworkBackend` -* `httpcore.NetworkStream` -* `httpcore.AsyncNetworkBackend` -* `httpcore.AsyncNetworkStream` +* `httpcore2.NetworkBackend` +* `httpcore2.NetworkStream` +* `httpcore2.AsyncNetworkBackend` +* `httpcore2.AsyncNetworkStream` diff --git a/src/httpcore2/docs/proxies.md b/docs/httpcore2/proxies.md similarity index 57% rename from src/httpcore2/docs/proxies.md rename to docs/httpcore2/proxies.md index 492ea86f..75198dc3 100644 --- a/src/httpcore2/docs/proxies.md +++ b/docs/httpcore2/proxies.md @@ -1,21 +1,21 @@ # Proxies -The `httpcore` package provides support for HTTP proxies, using either "HTTP Forwarding" or "HTTP Tunnelling". Forwarding is a proxy mechanism for sending requests to `http` URLs via an intermediate proxy. Tunnelling is a proxy mechanism for sending requests to `https` URLs via an intermediate proxy. +The `httpcore2` package provides support for HTTP proxies, using either "HTTP Forwarding" or "HTTP Tunnelling". Forwarding is a proxy mechanism for sending requests to `http` URLs via an intermediate proxy. Tunnelling is a proxy mechanism for sending requests to `https` URLs via an intermediate proxy. Sending requests via a proxy is very similar to sending requests using a standard connection pool: ```python -import httpcore +import httpcore2 -proxy = httpcore.Proxy("http://127.0.0.1:8080/") -pool = httpcore.ConnectionPool(proxy=proxy) +proxy = httpcore2.Proxy("http://127.0.0.1:8080/") +pool = httpcore2.ConnectionPool(proxy=proxy) r = proxy.request("GET", "https://www.example.com/") print(r) # ``` -You can test the `httpcore` proxy support, using the Python [`proxy.py`](https://pypi.org/project/proxy.py/) tool: +You can test the `httpcore2` proxy support, using the Python [`proxy.py`](https://pypi.org/project/proxy.py/) tool: ```shell pip install proxy.py @@ -29,88 +29,88 @@ Requests will automatically use either forwarding or tunnelling, depending on if Proxy authentication can be included in the initial configuration: ```python -import httpcore +import httpcore2 # A `Proxy-Authorization` header will be included on the initial proxy connection. -proxy = httpcore.Proxy( +proxy = httpcore2.Proxy( url="http://127.0.0.1:8080/", auth=("", "") ) -pool = httpcore.ConnectionPool(proxy=proxy) +pool = httpcore2.ConnectionPool(proxy=proxy) ``` Custom headers can also be included: ```python -import httpcore +import httpcore2 import base64 # Construct and include a `Proxy-Authorization` header. auth = base64.b64encode(b":") -proxy = httpcore.Proxy( +proxy = httpcore2.Proxy( url="http://127.0.0.1:8080/", headers={"Proxy-Authorization": b"Basic " + auth} ) -pool = httpcore.ConnectionPool(proxy=proxy) +pool = httpcore2.ConnectionPool(proxy=proxy) ``` ## Proxy SSL -The `httpcore` package also supports HTTPS proxies for http and https destinations. +The `httpcore2` package also supports HTTPS proxies for http and https destinations. HTTPS proxies can be used in the same way that HTTP proxies are. ```python -proxy = httpcore.Proxy(url="https://127.0.0.1:8080/") +proxy = httpcore2.Proxy(url="https://127.0.0.1:8080/") ``` Also, when using HTTPS proxies, you may need to configure the SSL context, which you can do with the `ssl_context` argument. ```python import ssl -import httpcore +import httpcore2 proxy_ssl_context = ssl.create_default_context() proxy_ssl_context.check_hostname = False -proxy = httpcore.Proxy( +proxy = httpcore2.Proxy( url='https://127.0.0.1:8080/', ssl_context=proxy_ssl_context ) -pool = httpcore.ConnectionPool(proxy=proxy) +pool = httpcore2.ConnectionPool(proxy=proxy) ``` ## HTTP Versions -If you use proxies, keep in mind that the `httpcore` package only supports proxies to HTTP/1.1 servers. +If you use proxies, keep in mind that the `httpcore2` package only supports proxies to HTTP/1.1 servers. ## SOCKS proxy support -The `httpcore` package also supports proxies using the SOCKS5 protocol. +The `httpcore2` package also supports proxies using the SOCKS5 protocol. -Make sure to install the optional dependency using `pip install 'httpcore[socks]'`. +Make sure to install the optional dependency using `pip install 'httpcore2[socks]'`. The `SOCKSProxy` class should be using instead of a standard connection pool: ```python -import httpcore +import httpcore2 # Note that the SOCKS port is 1080. -proxy = httpcore.Proxy(url="socks5://127.0.0.1:1080/") -pool = httpcore.ConnectionPool(proxy=proxy) +proxy = httpcore2.Proxy(url="socks5://127.0.0.1:1080/") +pool = httpcore2.ConnectionPool(proxy=proxy) r = pool.request("GET", "https://www.example.com/") ``` Authentication via SOCKS is also supported: ```python -import httpcore +import httpcore2 -proxy = httpcore.Proxy( +proxy = httpcore2.Proxy( url="socks5://127.0.0.1:1080/", auth=("", ""), ) -pool = httpcore.ConnectionPool(proxy=proxy) +pool = httpcore2.ConnectionPool(proxy=proxy) r = pool.request("GET", "https://www.example.com/") ``` @@ -118,9 +118,9 @@ r = pool.request("GET", "https://www.example.com/") # Reference -## `httpcore.Proxy` +## `httpcore2.Proxy` -::: httpcore.Proxy +::: httpcore2.Proxy handler: python rendering: show_source: False diff --git a/src/httpcore2/docs/quickstart.md b/docs/httpcore2/quickstart.md similarity index 68% rename from src/httpcore2/docs/quickstart.md rename to docs/httpcore2/quickstart.md index e43b0596..c63a1d19 100644 --- a/src/httpcore2/docs/quickstart.md +++ b/docs/httpcore2/quickstart.md @@ -1,15 +1,15 @@ # Quickstart -For convenience, the `httpcore` package provides a couple of top-level functions that you can use for sending HTTP requests. You probably don't want to integrate against functions if you're writing a library that uses `httpcore`, but you might find them useful for testing `httpcore` from the command-line, or if you're writing a simple script that doesn't require any of the connection pooling or advanced configuration that `httpcore` offers. +For convenience, the `httpcore2` package provides a couple of top-level functions that you can use for sending HTTP requests. You probably don't want to integrate against functions if you're writing a library that uses `httpcore2`, but you might find them useful for testing `httpcore2` from the command-line, or if you're writing a simple script that doesn't require any of the connection pooling or advanced configuration that `httpcore2` offers. ## Sending a request We'll start off by sending a request... ```python -import httpcore +import httpcore2 -response = httpcore.request("GET", "https://www.example.com/") +response = httpcore2.request("GET", "https://www.example.com/") print(response) # @@ -26,17 +26,17 @@ print(response.content) Request headers may be included either in a dictionary style, or as a list of two-tuples. ```python -import httpcore +import httpcore2 import json -headers = {'User-Agent': 'httpcore'} -r = httpcore.request('GET', 'https://httpbin.org/headers', headers=headers) +headers = {'User-Agent': 'httpcore2'} +r = httpcore2.request('GET', 'https://httpbin.org/headers', headers=headers) print(json.loads(r.content)) # { # 'headers': { # 'Host': 'httpbin.org', -# 'User-Agent': 'httpcore', +# 'User-Agent': 'httpcore2', # 'X-Amzn-Trace-Id': 'Root=1-616ff5de-5ea1b7e12766f1cf3b8e3a33' # } # } @@ -53,10 +53,10 @@ The `Host` header will always be automatically included in any outgoing request, A request body can be included either as bytes... ```python -import httpcore +import httpcore2 import json -r = httpcore.request('POST', 'https://httpbin.org/post', content=b'Hello, world') +r = httpcore2.request('POST', 'https://httpbin.org/post', content=b'Hello, world') print(json.loads(r.content)) # { @@ -78,11 +78,11 @@ print(json.loads(r.content)) Or as an iterable that returns bytes... ```python -import httpcore +import httpcore2 import json with open("hello-world.txt", "rb") as input_file: - r = httpcore.request('POST', 'https://httpbin.org/post', content=input_file) + r = httpcore2.request('POST', 'https://httpbin.org/post', content=input_file) print(json.loads(r.content)) # { @@ -109,14 +109,14 @@ The `Transfer-Encoding: chunked` header is the mechanism that HTTP/1.1 uses for ## Streaming responses -When using the `httpcore.request()` function, the response body will automatically be read to completion, and made available in the `response.content` attribute. +When using the `httpcore2.request()` function, the response body will automatically be read to completion, and made available in the `response.content` attribute. -Sometimes you may be dealing with large responses and not want to read the entire response into memory. The `httpcore.stream()` function provides a mechanism for sending a request and dealing with a streaming response: +Sometimes you may be dealing with large responses and not want to read the entire response into memory. The `httpcore2.stream()` function provides a mechanism for sending a request and dealing with a streaming response: ```python -import httpcore +import httpcore2 -with httpcore.stream('GET', 'https://example.com') as response: +with httpcore2.stream('GET', 'https://example.com') as response: for chunk in response.iter_stream(): print(f"Downloaded: {chunk}") ``` @@ -124,20 +124,20 @@ with httpcore.stream('GET', 'https://example.com') as response: Here's a more complete example that demonstrates downloading a response: ```python -import httpcore +import httpcore2 -with httpcore.stream('GET', 'https://speed.hetzner.de/100MB.bin') as response: +with httpcore2.stream('GET', 'https://speed.hetzner.de/100MB.bin') as response: with open("download.bin", "wb") as output_file: for chunk in response.iter_stream(): output_file.write(chunk) ``` -The `httpcore.stream()` API also allows you to *conditionally* read the response... +The `httpcore2.stream()` API also allows you to *conditionally* read the response... ```python -import httpcore +import httpcore2 -with httpcore.stream('GET', 'https://example.com') as response: +with httpcore2.stream('GET', 'https://example.com') as response: content_length = [int(v) for k, v in response.headers if k.lower() == b'content-length'][0] if content_length > 100_000_000: raise Exception("Response too large.") @@ -148,16 +148,16 @@ with httpcore.stream('GET', 'https://example.com') as response: # Reference -## `httpcore.request()` +## `httpcore2.request()` -::: httpcore.request +::: httpcore2.request handler: python rendering: show_source: False -## `httpcore.stream()` +## `httpcore2.stream()` -::: httpcore.stream +::: httpcore2.stream handler: python rendering: show_source: False diff --git a/src/httpcore2/docs/requests-responses-urls.md b/docs/httpcore2/requests-responses-urls.md similarity index 61% rename from src/httpcore2/docs/requests-responses-urls.md rename to docs/httpcore2/requests-responses-urls.md index 7919a54c..f4e8fb74 100644 --- a/src/httpcore2/docs/requests-responses-urls.md +++ b/docs/httpcore2/requests-responses-urls.md @@ -4,27 +4,27 @@ TODO ## Requests -Request instances in `httpcore` are deliberately simple, and only include the essential information required to represent an HTTP request. +Request instances in `httpcore2` are deliberately simple, and only include the essential information required to represent an HTTP request. Properties on the request are plain byte-wise representations. ```python ->>> request = httpcore.Request("GET", "https://www.example.com/") +>>> request = httpcore2.Request("GET", "https://www.example.com/") >>> request.method b"GET" >>> request.url -httpcore.URL(scheme=b"https", host=b"www.example.com", port=None, target=b"/") +httpcore2.URL(scheme=b"https", host=b"www.example.com", port=None, target=b"/") >>> request.headers [(b'Host', b'www.example.com')] >>> request.stream - + ``` The interface is liberal in the types that it accepts, but specific in the properties that it uses to represent them. For example, headers may be specified as a dictionary of strings, but internally are represented as a list of `(byte, byte)` tuples. ```python >>> headers = {"User-Agent": "custom"} ->>> request = httpcore.Request("GET", "https://www.example.com/", headers=headers) +>>> request = httpcore2.Request("GET", "https://www.example.com/", headers=headers) >>> request.headers [(b'Host', b'www.example.com'), (b"User-Agent", b"custom")] @@ -40,23 +40,23 @@ The interface is liberal in the types that it accepts, but specific in the prope # Reference -## `httpcore.Request` +## `httpcore2.Request` -::: httpcore.Request +::: httpcore2.Request handler: python rendering: show_source: False -## `httpcore.Response` +## `httpcore2.Response` -::: httpcore.Response +::: httpcore2.Response handler: python rendering: show_source: False -## `httpcore.URL` +## `httpcore2.URL` -::: httpcore.URL +::: httpcore2.URL handler: python rendering: show_source: False diff --git a/mkdocs.yml b/mkdocs.yml index 2e1654a3..9829a244 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -48,6 +48,19 @@ nav: - Third Party Packages: 'third_party_packages.md' - Contributing: 'contributing.md' - Code of Conduct: 'code_of_conduct.md' + - httpcore2: + - Introduction: 'httpcore2/index.md' + - Quickstart: 'httpcore2/quickstart.md' + - Requests, Responses, and URLs: 'httpcore2/requests-responses-urls.md' + - Connection Pools: 'httpcore2/connection-pools.md' + - Connections: 'httpcore2/connections.md' + - Proxies: 'httpcore2/proxies.md' + - HTTP/2: 'httpcore2/http2.md' + - Async Support: 'httpcore2/async.md' + - Network Backends: 'httpcore2/network-backends.md' + - Extensions: 'httpcore2/extensions.md' + - Logging: 'httpcore2/logging.md' + - Exceptions: 'httpcore2/exceptions.md' plugins: - mkdocstrings: diff --git a/src/httpcore2/docs/exceptions.md b/src/httpcore2/docs/exceptions.md deleted file mode 100644 index 63ef3f28..00000000 --- a/src/httpcore2/docs/exceptions.md +++ /dev/null @@ -1,18 +0,0 @@ -# Exceptions - -The following exceptions may be raised when sending a request: - -* `httpcore.TimeoutException` - * `httpcore.PoolTimeout` - * `httpcore.ConnectTimeout` - * `httpcore.ReadTimeout` - * `httpcore.WriteTimeout` -* `httpcore.NetworkError` - * `httpcore.ConnectError` - * `httpcore.ReadError` - * `httpcore.WriteError` -* `httpcore.ProtocolError` - * `httpcore.RemoteProtocolError` - * `httpcore.LocalProtocolError` -* `httpcore.ProxyError` -* `httpcore.UnsupportedProtocol` diff --git a/src/httpcore2/docs/logging.md b/src/httpcore2/docs/logging.md deleted file mode 100644 index 8db85875..00000000 --- a/src/httpcore2/docs/logging.md +++ /dev/null @@ -1,41 +0,0 @@ -# Logging - -If you need to inspect the internal behaviour of `httpcore`, you can use Python's standard logging to output debug level information. - -For example, the following configuration... - -```python -import logging -import httpcore - -logging.basicConfig( - format="%(levelname)s [%(asctime)s] %(name)s - %(message)s", - datefmt="%Y-%m-%d %H:%M:%S", - level=logging.DEBUG -) - -httpcore.request('GET', 'https://www.example.com') -``` - -Will send debug level output to the console, or wherever `stdout` is directed too... - -``` -DEBUG [2023-01-09 14:44:00] httpcore.connection - connect_tcp.started host='www.example.com' port=443 local_address=None timeout=None -DEBUG [2023-01-09 14:44:00] httpcore.connection - connect_tcp.complete return_value= -DEBUG [2023-01-09 14:44:00] httpcore.connection - start_tls.started ssl_context= server_hostname='www.example.com' timeout=None -DEBUG [2023-01-09 14:44:00] httpcore.connection - start_tls.complete return_value= -DEBUG [2023-01-09 14:44:00] httpcore.http11 - send_request_headers.started request= -DEBUG [2023-01-09 14:44:00] httpcore.http11 - send_request_headers.complete -DEBUG [2023-01-09 14:44:00] httpcore.http11 - send_request_body.started request= -DEBUG [2023-01-09 14:44:00] httpcore.http11 - send_request_body.complete -DEBUG [2023-01-09 14:44:00] httpcore.http11 - receive_response_headers.started request= -DEBUG [2023-01-09 14:44:00] httpcore.http11 - receive_response_headers.complete return_value=(b'HTTP/1.1', 200, b'OK', [(b'Age', b'572646'), (b'Cache-Control', b'max-age=604800'), (b'Content-Type', b'text/html; charset=UTF-8'), (b'Date', b'Mon, 09 Jan 2023 14:44:00 GMT'), (b'Etag', b'"3147526947+ident"'), (b'Expires', b'Mon, 16 Jan 2023 14:44:00 GMT'), (b'Last-Modified', b'Thu, 17 Oct 2019 07:18:26 GMT'), (b'Server', b'ECS (nyb/1D18)'), (b'Vary', b'Accept-Encoding'), (b'X-Cache', b'HIT'), (b'Content-Length', b'1256')]) -DEBUG [2023-01-09 14:44:00] httpcore.http11 - receive_response_body.started request= -DEBUG [2023-01-09 14:44:00] httpcore.http11 - receive_response_body.complete -DEBUG [2023-01-09 14:44:00] httpcore.http11 - response_closed.started -DEBUG [2023-01-09 14:44:00] httpcore.http11 - response_closed.complete -DEBUG [2023-01-09 14:44:00] httpcore.connection - close.started -DEBUG [2023-01-09 14:44:00] httpcore.connection - close.complete -``` - -The exact formatting of the debug logging may be subject to change across different versions of `httpcore`. If you need to rely on a particular format it is recommended that you pin installation of the package to a fixed version. \ No newline at end of file diff --git a/src/httpcore2/docs/table-of-contents.md b/src/httpcore2/docs/table-of-contents.md deleted file mode 100644 index 5dc9a10b..00000000 --- a/src/httpcore2/docs/table-of-contents.md +++ /dev/null @@ -1,49 +0,0 @@ -# API Reference - -* Quickstart - * `httpcore.request()` - * `httpcore.stream()` -* Requests, Responses, and URLs - * `httpcore.Request` - * `httpcore.Response` - * `httpcore.URL` -* Connection Pools - * `httpcore.ConnectionPool` -* Proxies - * `httpcore.Proxy` -* Connections - * `httpcore.HTTPConnection` - * `httpcore.HTTP11Connection` - * `httpcore.HTTP2Connection` -* Async Support - * `httpcore.AsyncConnectionPool` - * `httpcore.AsyncHTTPConnection` - * `httpcore.AsyncHTTP11Connection` - * `httpcore.AsyncHTTP2Connection` -* Network Backends - * Sync - * `httpcore.backends.sync.SyncBackend` - * `httpcore.backends.mock.MockBackend` - * Async - * `httpcore.backends.auto.AutoBackend` - * `httpcore.backends.asyncio.AsyncioBackend` - * `httpcore.backends.trio.TrioBackend` - * `httpcore.backends.mock.AsyncMockBackend` - * Base interfaces - * `httpcore.backends.base.NetworkBackend` - * `httpcore.backends.base.AsyncNetworkBackend` -* Exceptions - * `httpcore.TimeoutException` - * `httpcore.PoolTimeout` - * `httpcore.ConnectTimeout` - * `httpcore.ReadTimeout` - * `httpcore.WriteTimeout` - * `httpcore.NetworkError` - * `httpcore.ConnectError` - * `httpcore.ReadError` - * `httpcore.WriteError` - * `httpcore.ProtocolError` - * `httpcore.RemoteProtocolError` - * `httpcore.LocalProtocolError` - * `httpcore.ProxyError` - * `httpcore.UnsupportedProtocol` diff --git a/src/httpcore2/mkdocs.yml b/src/httpcore2/mkdocs.yml deleted file mode 100644 index 9f4200ba..00000000 --- a/src/httpcore2/mkdocs.yml +++ /dev/null @@ -1,36 +0,0 @@ -site_name: HTTPCore -site_description: A minimal HTTP client for Python. -site_url: https://www.encode.io/httpcore/ - -repo_name: encode/httpcore -repo_url: https://github.com/encode/httpcore/ - -nav: - - Introduction: 'index.md' - - Quickstart: 'quickstart.md' - - Connection Pools: 'connection-pools.md' - - Proxies: 'proxies.md' - - HTTP/2: 'http2.md' - - Async Support: 'async.md' - - Network Backends: 'network-backends.md' - - Extensions: 'extensions.md' - - Logging: 'logging.md' - - Exceptions: 'exceptions.md' - -theme: - name: "material" - -plugins: - - search - - mkdocstrings: - default_handler: python - watch: - - httpcore - handlers: - python: - members_order: - - "source" - -markdown_extensions: - - codehilite: - css_class: highlight