Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 47 additions & 52 deletions docs/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,83 +250,78 @@ async with httpx.Client(proxies=proxy) as client:
has not been implemented yet. To use proxies you must pass the proxy
information at `Client` initialization.

## Timeout fine-tuning
## Timeout Configuration

HTTPX offers various request timeout management options. Three types of timeouts
are available: **connect** timeouts, **write** timeouts and **read** timeouts.
HTTPX is careful to enforce timeouts everywhere by default.

* The **connect timeout** specifies the maximum amount of time to wait until
a connection to the requested host is established. If HTTPX is unable to connect
within this time frame, a `ConnectTimeout` exception is raised.
* The **write timeout** specifies the maximum duration to wait for a chunk of
data to be sent (for example, a chunk of the request body). If HTTPX is unable
to send data within this time frame, a `WriteTimeout` exception is raised.
* The **read timeout** specifies the maximum duration to wait for a chunk of
data to be received (for example, a chunk of the response body). If HTTPX is
unable to receive data within this time frame, a `ReadTimeout` exception is raised.

### Setting timeouts
The default behavior is to raise a `TimeoutException` after 5 seconds of
network inactivity.

You can set timeouts on two levels:
### Setting and disabling timeouts

- For a given request:
You can set timeouts for an individual request:

```python
# Using top-level API
await httpx.get('http://example.com/api/v1/example', timeout=5)
# Using the top-level API:
await httpx.get('http://example.com/api/v1/example', timeout=10.0)

# Or, with a client:
# Using a client instance:
async with httpx.Client() as client:
await client.get("http://example.com/api/v1/example", timeout=5)
await client.get("http://example.com/api/v1/example", timeout=10.0)
```

- On a client instance, which results in the given `timeout` being used as a default for requests made with this client:
Or disable timeouts for an individual request:

```python
async with httpx.Client(timeout=5) as client:
await client.get('http://example.com/api/v1/example')
# Using the top-level API:
await httpx.get('http://example.com/api/v1/example', timeout=None)

# Using a client instance:
async with httpx.Client() as client:
await client.get("http://example.com/api/v1/example", timeout=None)
```

Besides, you can pass timeouts in two forms:
### Setting a default timeout on a client

- A number, which sets the read, write and connect timeouts to the same value, as in the examples above.
- A `Timeout` instance, which allows to define the read, write and connect timeouts independently:
You can set a timeout on a client instance, which results in the given
`timeout` being used as the default for requests made with this client:

```python
timeout = httpx.Timeout(
connect_timeout=5,
read_timeout=10,
write_timeout=15
)

resp = await httpx.get('http://example.com/api/v1/example', timeout=timeout)
client = httpx.Client() # Use a default 5s timeout everywhere.
client = httpx.Client(timeout=10.0) # Use a default 10s timeout everywhere.
client = httpx.Client(timeout=None) # Disable all timeouts by default.
```

### Default timeouts

By default all types of timeouts are set to 5 second.
### Fine tuning the configuration

### Disabling timeouts
HTTPX also allows you to specify the timeout behavior in more fine grained detail.

To disable timeouts, you can pass `None` as a timeout parameter.
Note that currently this is not supported by the top-level API.

```python
url = "http://example.com/api/v1/delay/10"

await httpx.get(url, timeout=None) # Times out after 5s
There are four different types of timeouts that may occur. These are **connect**,
**read**, **write**, and **pool** timeouts.

* The **connect timeout** specifies the maximum amount of time to wait until
a connection to the requested host is established. If HTTPX is unable to connect
within this time frame, a `ConnectTimeout` exception is raised.
* The **read timeout** specifies the maximum duration to wait for a chunk of
data to be received (for example, a chunk of the response body). If HTTPX is
unable to receive data within this time frame, a `ReadTimeout` exception is raised.
* The **write timeout** specifies the maximum duration to wait for a chunk of
data to be sent (for example, a chunk of the request body). If HTTPX is unable
to send data within this time frame, a `WriteTimeout` exception is raised.
* The **pool timeout** specifies the maximum duration to wait for acquiring
a connection from the connection pool. If HTTPX is unable to acquire a connection
within this time frame, a `PoolTimeout` exception is raised. A related
configuration here is the maximum number of allowable connections in the
connection pool, which is configured by the `pool_limits`.

async with httpx.Client(timeout=None) as client:
await client.get(url) # Does not timeout, returns after 10s
You can configure the timeout behavior for any of these values...

```python
# A client with a 60s timeout for connecting, and a 10s timeout elsewhere.
timeout = httpx.Timeout(10.0, connect_timeout=60.0)
client = httpx.Client(timeout=timeout)

timeout = httpx.Timeout(
connect_timeout=5,
read_timeout=None,
write_timeout=5
)
await httpx.get(url, timeout=timeout) # Does not timeout, returns after 10s
response = await client.get('http://example.com/')
```

## Multipart file encoding
Expand Down
6 changes: 6 additions & 0 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,12 @@ value to be more or less strict:
>>> await httpx.get('https://github.com/', timeout=0.001)
```

You can also disable the timeout behavior completely...

```python
>>> await httpx.get('https://github.com/', timeout=None)
```

For advanced timeout management, see [Timeout fine-tuning](https://www.encode.io/httpx/advanced/#timeout-fine-tuning).

## Authentication
Expand Down
4 changes: 2 additions & 2 deletions httpx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@
ReadTimeout,
RedirectBodyUnavailable,
RedirectLoop,
RequestTimeout,
ResponseClosed,
ResponseNotRead,
StreamConsumed,
TimeoutException,
TooManyRedirects,
WriteTimeout,
)
Expand Down Expand Up @@ -127,7 +127,7 @@
"QueryParamTypes",
"Request",
"RequestData",
"RequestTimeout",
"TimeoutException",
"Response",
"ResponseContent",
"RequestFiles",
Expand Down
9 changes: 2 additions & 7 deletions httpx/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
DEFAULT_MAX_REDIRECTS,
DEFAULT_POOL_LIMITS,
DEFAULT_TIMEOUT_CONFIG,
UNSET,
CertTypes,
PoolLimits,
Timeout,
TimeoutTypes,
UnsetType,
VerifyTypes,
)
from .dispatch.asgi import ASGIDispatch
Expand Down Expand Up @@ -50,13 +52,6 @@
logger = get_logger(__name__)


class UnsetType:
pass # pragma: nocover


UNSET = UnsetType()


class Client:
"""
An HTTP client, with connection pooling, HTTP/2, redirects, cookie persistence, etc.
Expand Down
81 changes: 51 additions & 30 deletions httpx/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@
logger = get_logger(__name__)


class UnsetType:
pass # pragma: nocover


UNSET = UnsetType()


class SSLConfig:
"""
SSL Configuration.
Expand Down Expand Up @@ -203,44 +210,58 @@ def _load_client_certs(self, ssl_context: ssl.SSLContext) -> None:

class Timeout:
"""
Timeout values.
Timeout configuration.

**Usage**:

Timeout() # No timeout.
Timeout(5.0) # 5s timeout on all operations.
Timeout(connect_timeout=5.0) # 5s timeout on connect, no other timeouts.
Timeout(5.0, connect_timeout=10.0) # 10s timeout on connect. 5s timeout elsewhere.
Timeout(5.0, pool_timeout=None) # No timeout on acquiring connection from pool.
# 5s timeout elsewhere.
"""

def __init__(
self,
timeout: TimeoutTypes = None,
*,
connect_timeout: float = None,
read_timeout: float = None,
write_timeout: float = None,
pool_timeout: float = None,
connect_timeout: typing.Union[None, float, UnsetType] = UNSET,
read_timeout: typing.Union[None, float, UnsetType] = UNSET,
write_timeout: typing.Union[None, float, UnsetType] = UNSET,
pool_timeout: typing.Union[None, float, UnsetType] = UNSET,
):
if timeout is None:
self.connect_timeout = connect_timeout
self.read_timeout = read_timeout
self.write_timeout = write_timeout
self.pool_timeout = pool_timeout
if isinstance(timeout, Timeout):
# Passed as a single explicit Timeout.
assert connect_timeout is UNSET
assert read_timeout is UNSET
assert write_timeout is UNSET
assert pool_timeout is UNSET
self.connect_timeout = (
timeout.connect_timeout
) # type: typing.Optional[float]
self.read_timeout = timeout.read_timeout # type: typing.Optional[float]
self.write_timeout = timeout.write_timeout # type: typing.Optional[float]
self.pool_timeout = timeout.pool_timeout # type: typing.Optional[float]
elif isinstance(timeout, tuple):
# Passed as a tuple.
self.connect_timeout = timeout[0]
self.read_timeout = timeout[1]
self.write_timeout = None if len(timeout) < 3 else timeout[2]
self.pool_timeout = None if len(timeout) < 4 else timeout[3]
else:
# Specified as a single timeout value
assert connect_timeout is None
assert read_timeout is None
assert write_timeout is None
assert pool_timeout is None
if isinstance(timeout, Timeout):
self.connect_timeout = timeout.connect_timeout
self.read_timeout = timeout.read_timeout
self.write_timeout = timeout.write_timeout
self.pool_timeout = timeout.pool_timeout
elif isinstance(timeout, tuple):
self.connect_timeout = timeout[0]
self.read_timeout = timeout[1]
self.write_timeout = None if len(timeout) < 3 else timeout[2]
self.pool_timeout = None if len(timeout) < 4 else timeout[3]
else:
self.connect_timeout = timeout
self.read_timeout = timeout
self.write_timeout = timeout
self.pool_timeout = timeout
self.connect_timeout = (
timeout if isinstance(connect_timeout, UnsetType) else connect_timeout
)
self.read_timeout = (
timeout if isinstance(read_timeout, UnsetType) else read_timeout
)
self.write_timeout = (
timeout if isinstance(write_timeout, UnsetType) else write_timeout
)
self.pool_timeout = (
timeout if isinstance(pool_timeout, UnsetType) else pool_timeout
)

def __eq__(self, other: typing.Any) -> bool:
return (
Expand Down
10 changes: 5 additions & 5 deletions httpx/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,31 @@ def __init__(
# Timeout exceptions...


class RequestTimeout(HTTPError):
class TimeoutException(HTTPError):
"""
A base class for all timeouts.
"""


class ConnectTimeout(RequestTimeout):
class ConnectTimeout(TimeoutException):
"""
Timeout while establishing a connection.
"""


class ReadTimeout(RequestTimeout):
class ReadTimeout(TimeoutException):
"""
Timeout while reading response data.
"""


class WriteTimeout(RequestTimeout):
class WriteTimeout(TimeoutException):
"""
Timeout while writing request data.
"""


class PoolTimeout(RequestTimeout):
class PoolTimeout(TimeoutException):
"""
Timeout while waiting to acquire a connection from the pool.
"""
Expand Down
10 changes: 10 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@ def test_timeout_from_one_none_value():
assert timeout == httpx.Timeout()


def test_timeout_from_one_value():
timeout = httpx.Timeout(read_timeout=5.0)
assert timeout == httpx.Timeout(timeout=(None, 5.0, None, None))


def test_timeout_from_one_value_and_default():
timeout = httpx.Timeout(5.0, pool_timeout=60.0)
assert timeout == httpx.Timeout(timeout=(5.0, 5.0, 5.0, 60.0))


def test_timeout_from_tuple():
timeout = httpx.Timeout(timeout=(5.0, 5.0, 5.0, 5.0))
assert timeout == httpx.Timeout(timeout=5.0)
Expand Down