Skip to content

Commit 1826471

Browse files
committed
docs: clarify that client_timeout only limits header-reading, not handler execution
The `client_timeout` parameter maps to actix-web's `client_request_timeout`, which only controls how long the server waits for the client to transmit the complete request headers. It does NOT limit handler execution time or overall request duration. Updated docstrings, type stubs, Rust doc comments, and both English and Chinese documentation to make this clear and prevent user confusion. Made-with: Cursor
1 parent 15ee1d5 commit 1826471

6 files changed

Lines changed: 64 additions & 17 deletions

File tree

docs_src/src/pages/documentation/en/api_reference/timeout_configuration.mdx

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ async def hello(request):
2424
app.start(
2525
host="0.0.0.0",
2626
port=8080,
27-
client_timeout=30, # Client connection timeout (seconds)
27+
client_timeout=30, # Max seconds to read request headers (seconds)
2828
keep_alive_timeout=20 # Keep-alive timeout (seconds)
2929
)
3030
```
@@ -46,8 +46,8 @@ python app.py
4646

4747
| Parameter | Default | Description | Environment Variable |
4848
|-----------|---------|-------------|---------------------|
49-
| `client_timeout` | 30 | Maximum time (seconds) for client request processing | `ROBYN_CLIENT_TIMEOUT` |
50-
| `keep_alive_timeout` | 20 | Time (seconds) to keep idle connections alive | `ROBYN_KEEP_ALIVE_TIMEOUT` |
49+
| `client_timeout` | 30 | Maximum time (seconds) to wait for the client to transmit the complete request headers. This does **not** limit handler execution time or overall request duration — it only guards against slow or stalled clients during the initial header-reading phase. Maps to actix-web's `client_request_timeout`. | `ROBYN_CLIENT_TIMEOUT` |
50+
| `keep_alive_timeout` | 20 | Time (seconds) to keep idle connections alive before closing them | `ROBYN_KEEP_ALIVE_TIMEOUT` |
5151

5252
## Usage Examples
5353

@@ -65,7 +65,7 @@ app.start(client_timeout=30)
6565
app.start(
6666
host="0.0.0.0",
6767
port=8080,
68-
client_timeout=60, # Allow longer processing time
68+
client_timeout=60, # More time for clients on slow networks to send headers
6969
keep_alive_timeout=15 # Shorter keep-alive for faster turnover
7070
)
7171
```
@@ -75,7 +75,7 @@ app.start(
7575
```python
7676
# Development-friendly settings
7777
app.start(
78-
client_timeout=300, # Long timeout for debugging
78+
client_timeout=300, # Generous header-read timeout for debugging
7979
keep_alive_timeout=60 # Longer keep-alive for testing
8080
)
8181
```
@@ -85,7 +85,7 @@ app.start(
8585
```python
8686
# Optimized for load testing with tools like wrk
8787
app.start(
88-
client_timeout=10, # Quick timeouts
88+
client_timeout=10, # Quick header-read timeout
8989
keep_alive_timeout=5 # Fast connection turnover
9090
)
9191
```
@@ -130,10 +130,12 @@ If you encounter file descriptor exhaustion:
130130
- Lower `keep_alive_timeout` (5-15s)
131131
- Moderate `client_timeout` (15-30s)
132132

133-
**For long-running operations:**
134-
- Higher `client_timeout` (60-300s)
133+
**For clients on slow networks:**
134+
- Higher `client_timeout` (60-120s) so slow clients have time to send headers
135135
- Standard `keep_alive_timeout` (20-30s)
136136

137+
> **Note:** `client_timeout` only controls how long the server waits for request headers — it does not cap handler execution time. If you need to limit how long a handler can run, implement that logic within the handler itself (e.g. with `asyncio.wait_for`).
138+
137139

138140
## Best Practices
139141

docs_src/src/pages/documentation/zh/api_reference/timeout_configuration.mdx

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ async def hello(request):
2424
app.start(
2525
host="0.0.0.0",
2626
port=8080,
27-
client_timeout=30, # 客户端连接超时(秒)
27+
client_timeout=30, # 读取请求头的最大等待时间(秒)
2828
keep_alive_timeout=20, # 保持连接超时(秒)
2929
)
3030
```
@@ -46,7 +46,7 @@ python app.py
4646

4747
| 参数 | 默认值 | 描述 | 环境变量 |
4848
|------|-------|------|---------|
49-
| `client_timeout` | 30 | 客户端请求处理的最大时间(秒) | `ROBYN_CLIENT_TIMEOUT` |
49+
| `client_timeout` | 30 | 等待客户端发送完整请求头的最大时间(秒)。此参数****限制处理函数执行时间或整个请求的持续时间——它仅防止客户端在发送头信息阶段过慢或停滞。对应 actix-web 的 `client_request_timeout` | `ROBYN_CLIENT_TIMEOUT` |
5050
| `keep_alive_timeout` | 20 | 保持空闲连接的时间(秒) | `ROBYN_KEEP_ALIVE_TIMEOUT` |
5151

5252
## 使用示例
@@ -65,7 +65,7 @@ app.start(client_timeout=30)
6565
app.start(
6666
host="0.0.0.0",
6767
port=8080,
68-
client_timeout=60, # 允许更长的处理时间
68+
client_timeout=60, # 为慢速网络客户端留更多时间发送请求头
6969
keep_alive_timeout=15, # 更短的保持连接以加快周转
7070
)
7171
```
@@ -75,7 +75,7 @@ app.start(
7575
```python
7676
# 开发友好设置
7777
app.start(
78-
client_timeout=300, # 调试的长超时
78+
client_timeout=300, # 调试时宽裕的请求头读取超时
7979
keep_alive_timeout=60, # 测试的长保持连接
8080
)
8181
```
@@ -85,7 +85,7 @@ app.start(
8585
```python
8686
# 针对 wrk 等工具的负载测试优化
8787
app.start(
88-
client_timeout=10, # 快速超时
88+
client_timeout=10, # 快速请求头读取超时
8989
keep_alive_timeout=5, # 快速连接周转
9090
)
9191
```
@@ -130,10 +130,12 @@ app.start(client_timeout=30)
130130
- 较低的 `keep_alive_timeout` (5-15秒)
131131
- 中等的 `client_timeout` (15-30秒)
132132

133-
**对于长时间运行的操作**
134-
- 较高的 `client_timeout` (60-300秒)
133+
**对于慢速网络上的客户端**
134+
- 较高的 `client_timeout` (60-120秒),为慢速客户端留出发送请求头的时间
135135
- 标准的 `keep_alive_timeout` (20-30秒)
136136

137+
> **注意:** `client_timeout` 仅控制服务器等待请求头的时间——它不会限制处理函数的执行时间。如需限制处理函数运行时长,请在处理函数内部实现该逻辑(例如使用 `asyncio.wait_for`)。
138+
137139
## 与其他框架的比较
138140

139141
| 框架 | 默认客户端超时 | 默认保持连接 | 配置方式 |

robyn/__init__.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -633,8 +633,15 @@ def start(self, host: str = "127.0.0.1", port: int = 8080, _check_port: bool = T
633633
:param host str: represents the host at which the server is listening
634634
:param port int: represents the port number at which the server is listening
635635
:param _check_port bool: represents if the port should be checked if it is already in use
636-
:param client_timeout int: timeout for client connections in seconds (default: 30)
637-
:param keep_alive_timeout int: timeout for keep-alive connections in seconds (default: 20)
636+
:param client_timeout int: maximum time in seconds to wait for the client to send
637+
the complete request headers (default: 30). This does **not** limit handler
638+
execution time or the overall request duration — it only guards against slow
639+
or stalled clients during the initial header-reading phase. Under the hood
640+
this maps to actix-web's ``client_request_timeout``.
641+
Can be overridden with the ``ROBYN_CLIENT_TIMEOUT`` environment variable.
642+
:param keep_alive_timeout int: time in seconds to keep an idle connection open
643+
before closing it (default: 20). Can be overridden with the
644+
``ROBYN_KEEP_ALIVE_TIMEOUT`` environment variable.
638645
"""
639646

640647
host = os.getenv("ROBYN_HOST", host)

robyn/processpool.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ def run_processes(
3131
client_timeout: int = 30,
3232
keep_alive_timeout: int = 20,
3333
) -> List[Process]:
34+
"""
35+
Spawn server processes.
36+
37+
:param client_timeout: max seconds to wait for the client to send the
38+
complete request headers (maps to actix-web's
39+
``client_request_timeout``). This does **not** limit handler execution
40+
time or overall request duration.
41+
:param keep_alive_timeout: seconds to keep idle connections open.
42+
"""
3443
socket = SocketHeld(url, port)
3544

3645
process_pool = init_processpool(
@@ -175,6 +184,11 @@ def spawn_process(
175184
:param socket SocketHeld: This is the main tcp socket, which is being shared across multiple processes.
176185
:param process_name string: This is the name given to the process to identify the process
177186
:param workers int: This is the name given to the process to identify the process
187+
:param client_timeout int: max seconds to wait for the client to send the
188+
complete request headers (maps to actix-web's
189+
``client_request_timeout``). This does **not** limit handler execution
190+
time or overall request duration.
191+
:param keep_alive_timeout int: seconds to keep idle connections open.
178192
"""
179193

180194
loop = initialize_event_loop()

robyn/robyn.pyi

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,19 @@ class Server:
515515
) -> None:
516516
pass
517517
def start(self, socket: SocketHeld, workers: int, client_timeout: int, keep_alive_timeout: int) -> None:
518+
"""
519+
Start the Robyn server.
520+
521+
:param socket: The shared socket to listen on.
522+
:param workers: Number of worker threads.
523+
:param client_timeout: Maximum time in seconds to wait for the client
524+
to transmit the complete request headers. This does **not** limit
525+
handler execution time or overall request duration — it only guards
526+
against slow or stalled clients during the initial header-reading
527+
phase (maps to actix-web's ``client_request_timeout``).
528+
:param keep_alive_timeout: Time in seconds to keep an idle connection
529+
open before closing it.
530+
"""
518531
pass
519532

520533
class WebSocketConnector:

src/server.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,15 @@ impl Server {
8181
}
8282
}
8383

84+
/// Start the Robyn server.
85+
///
86+
/// * `client_timeout` – maximum seconds to wait for the client to transmit
87+
/// the complete request headers. This does **not** limit handler execution
88+
/// time or overall request duration; it only guards against slow or
89+
/// stalled clients during the initial header-reading phase. Mapped to
90+
/// actix-web's `client_request_timeout`.
91+
/// * `keep_alive_timeout` – seconds to keep an idle connection open before
92+
/// closing it. Mapped to actix-web's `KeepAlive::Timeout`.
8493
pub fn start(
8594
&mut self,
8695
_py: Python,

0 commit comments

Comments
 (0)