From ee9a6579916f557620435ad24e4e22cd7c06b44d Mon Sep 17 00:00:00 2001 From: adam Date: Tue, 13 Oct 2026 09:51:45 +0800 Subject: [PATCH] =?UTF-8?q?docs(api):=20builder=20=E5=AE=B6=E6=97=8F?= =?UTF-8?q?=E8=BF=9B=E5=85=A5=20spec/API=20=E5=8F=82=E8=80=83=20+=20?= =?UTF-8?q?=E6=8B=86=E6=8E=89=20server.h=20=E9=87=8C=E7=9A=84=20request=20?= =?UTF-8?q?=E5=A3=B0=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v2.10 roadmap d8(API 参考与示例同步)。对账发现两份文档对 uvhttp_server.h 的 45 个公共函数都有实质缺口,且 server.h 里还挂着声明在 别处的 request 函数。 ## 1. builder 家族原本两份文档都没有条目 `uvhttp_server_create` / `uvhttp_get..uvhttp_any` / `uvhttp_set_max_connections` / `uvhttp_server_run` / `stop_simple` / `simple_free` / `uvhttp_serve` / `uvhttp_server_new_with_loop` —— 一个条目 都没有,而 `uvhttp_serve` 与 builder 链是 README 快速开始之外最常被抄的入口。 d5 刚改了这条路径的契约,文档还是空白。 补齐内容(spec 给完整契约、API 参考给精简版): - **builder 是视图不是所有者** —— server/router/config 由 server_free 释放, 拆卸只有 simple_free 一条路,没有 per-field teardown 的选项 - **错误码透传**(d5 新增):NULL host 是 INVALID_PARAM,端口占用是 SERVER_LISTEN,不再一律塌缩成 SERVER_LISTEN - **out-param 契约**:任何失败下 *server 为 NULL,调用方无回滚可写 - uvhttp_get..uvhttp_any 的失败**只记日志不返回** —— 链式调用无法从返回值 发现注册失败,此前无任何文档说明 - uvhttp_set_max_connections 是**唯一有效**的 builder 配置旋钮(另两个假 旋钮已在 v2.10 删除,指向 config-api.md 的归属表) - uvhttp_serve 的 host=NULL 是错误而非默认(发警告后继续走 listen,然后失败) ## 2. uvhttp_request 的"内部函数"块从 server.h 拆出 server.h 尾部挂着 `/* internalFunction */` 的 uvhttp_request_init / uvhttp_request_cleanup —— request API 出现在 server.h 里,正是本轮审计 O1「归属错误」的同款(#470 已删三个纯别名,这两个是漏网的)。cleanup 在 request.h 本就有一份声明(两份并存),init 则只在 server.h。 处理:init 移到 request.h(紧邻其余 request API),两块都从 server.h 移除。 uvhttp.h 同时包含两个头文件,标准入口不受影响;只 include server.h 的调用方 需自行补上 request.h —— 破坏性,但与 v2.10 "一次改干净"的口径一致。 ## 3. 记录的文档债(本 PR 不补) - WebSocket 13 个 ws_* 函数、限流 7 个 rate_limit_* 函数在两份文档仍无条目 —— 分别属 docs/guide/websocket.md 与 RATE_LIMIT_API.md 的领域,建议各自 单独 PR 补齐,不塞进本次 - port 范围校验在 listen / listen_routes / create 三条路径都缺(#470 评审 已记),待 d6 一并处理 ## 验证 ctest 120/120;test-validity 门禁 PASS;check-doc-sync(31 文件 0 过时 0 缺 中译);check-brain OK;核心 API 对账双向零缺口;门禁口径内文件 clang-format 合规。 --- docs/api/API_REFERENCE.md | 155 ++++++++++++++++++++++++++++++++++++++ docs/spec/server-api.md | 64 ++++++++++++++++ include/uvhttp_request.h | 1 + include/uvhttp_server.h | 4 - 4 files changed, 220 insertions(+), 4 deletions(-) diff --git a/docs/api/API_REFERENCE.md b/docs/api/API_REFERENCE.md index 368c6cd9..3a0f5601 100644 --- a/docs/api/API_REFERENCE.md +++ b/docs/api/API_REFERENCE.md @@ -183,6 +183,161 @@ uv_run(loop, UV_RUN_DEFAULT); uvhttp_server_free(server); ``` +## Server API (reference) + +### uvhttp_server_new_with_loop + +```c +uvhttp_error_t uvhttp_server_new_with_loop(uvhttp_server_t** server); +``` + +Creates a server that **owns** its event loop (released by `uvhttp_server_free`). +Prefer `uvhttp_server_new(loop, &server)` unless the server must outlive the +caller's loop. + +**Return Value**: `UVHTTP_OK`, `UVHTTP_ERROR_INVALID_PARAM` (NULL `server`), or +`UVHTTP_ERROR_OUT_OF_MEMORY`. + +### uvhttp_server_enable_tls / uvhttp_server_disable_tls + +```c +uvhttp_error_t uvhttp_server_enable_tls(uvhttp_server_t* server, + uvhttp_tls_context_t* tls_ctx); +uvhttp_error_t uvhttp_server_disable_tls(uvhttp_server_t* server); +``` + +Hands a TLS context to the server (**ownership transfers** - the server frees it, +like `uvhttp_server_take_router`). Requires `BUILD_WITH_HTTPS`. + +### uvhttp_server_set_handler + +```c +uvhttp_error_t uvhttp_server_set_handler(uvhttp_server_t* server, + uvhttp_request_handler_t handler); +``` + +Sets a single catch-all handler for every request. Mutually exclusive with a +router: when a router is installed it wins. + +### uvhttp_server_take_context + +```c +uvhttp_error_t uvhttp_server_take_context(uvhttp_server_t* server, + struct uvhttp_context* context); +``` + +Hands a context to the server (**ownership transfers**, no detach, one context +per server). See `uvhttp_server_take_router` for the same rules. + +### uvhttp_server_enable_health_check + +```c +uvhttp_error_t uvhttp_server_enable_health_check(uvhttp_server_t* server, + const char* path); +``` + +Registers a `{"status":"ok"}` handler at `path` on the server's router, +creating a router first if the server has none. + +## Builder API + +The builder entry point (`uvhttp_server_create`) creates server + router + +config, starts listening, and returns a `uvhttp_server_builder_t*` for +chaining. It has the same all-or-nothing contract as +`uvhttp_server_listen_routes`: on any failure the out pointer is NULL and +nothing is allocated. + +The builder is a **view, not an owner**: `server`, `router` and `config` +handles are exposed for convenience, but `uvhttp_server_free` releases all +three. Tear down with `uvhttp_server_simple_free()` (server + builder) - never +per-field. + +### uvhttp_server_create + +```c +uvhttp_error_t uvhttp_server_create(uv_loop_t* loop, const char* host, + int port, + uvhttp_server_builder_t** server); +``` + +**Parameters**: +- `loop`: Initialized event loop +- `host`: Listen address (e.g. "0.0.0.0") +- `port`: Listen port +- `server`: Out pointer; NULL on failure + +**Return Value** (v2.10: the real cause is propagated, not collapsed into +`SERVER_LISTEN`): +- `UVHTTP_OK`: Success - fully listening builder +- `UVHTTP_ERROR_INVALID_PARAM`: NULL `loop`/`host`/`server` +- `UVHTTP_ERROR_OUT_OF_MEMORY`: any allocation failed +- `UVHTTP_ERROR_SERVER_LISTEN`: bind or listen syscall failed + +**Example**: +```c +uvhttp_server_builder_t* builder = NULL; +uvhttp_error_t result = + uvhttp_server_create(loop, "0.0.0.0", 8080, &builder); +if (result != UVHTTP_OK) { + /* nothing was allocated; nothing to clean up */ + return 1; +} + +/* Chaining: routes plus the one config knob that has an effect. */ +uvhttp_get(builder, "/", hello_handler); +uvhttp_get(builder, "/health", health_handler); +uvhttp_set_max_connections(builder, 1000); + +uvhttp_server_run(builder); +uvhttp_server_simple_free(builder); +``` + +### uvhttp_get / uvhttp_post / uvhttp_put / uvhttp_delete / uvhttp_any + +```c +uvhttp_server_builder_t* uvhttp_get(uvhttp_server_builder_t* server, + const char* path, + uvhttp_request_handler_t handler); +``` + +Registers a route on the builder's router; the four siblings differ only in +method. Returns the builder for chaining (NULL in, NULL out). + +Registration failures are **logged, not returned** - the chain continues, so +verify the path/handler you passed rather than the return value. + +### uvhttp_set_max_connections + +```c +uvhttp_server_builder_t* uvhttp_set_max_connections( + uvhttp_server_builder_t* server, int max_conn); +``` + +The connection cap, and the only builder config knob with an effect. Silent +no-op on NULL or on a builder without a config. + +### uvhttp_server_run / uvhttp_server_stop_simple / uvhttp_server_simple_free + +```c +int uvhttp_server_run(uvhttp_server_builder_t* server); +void uvhttp_server_stop_simple(uvhttp_server_builder_t* server); +void uvhttp_server_simple_free(uvhttp_server_builder_t* server); +``` + +Run the loop until stopped; stop accepting connections; release the builder +(and, through it, the server, router and config). All three are NULL-tolerant. + +### uvhttp_serve + +```c +int uvhttp_serve(uv_loop_t* loop, const char* host, int port); +``` + +One-key start for scripts and demos: `uvhttp_server_create` + a `/` default +route + run + free. Validates `port` in 1..65535; a NULL `host` is treated as +0.0.0.0 with a warning (the listen call then fails, so a NULL host is an +error, not a default). + ## Router API ### uvhttp_router_new diff --git a/docs/spec/server-api.md b/docs/spec/server-api.md index 417c5743..b94362cc 100644 --- a/docs/spec/server-api.md +++ b/docs/spec/server-api.md @@ -89,6 +89,70 @@ connection management. - `UVHTTP_ERROR_SERVER_LISTEN`: bind or listen syscall failed - **Thread safety**: Not thread-safe. Must be called from the event loop thread. +### uvhttp_server_new_with_loop +- **Signature**: `uvhttp_error_t uvhttp_server_new_with_loop(uvhttp_server_t** server)` +- **Purpose**: Create a server that **owns** its event loop (`uv_loop_new` internally; released by `uvhttp_server_free`). +- **Preconditions**: `server` must be a non-NULL out pointer. +- **Postconditions**: `*server` is a valid, non-listening server with `config == NULL` and `context == NULL` (no context is created). +- **Error conditions**: + - `UVHTTP_ERROR_INVALID_PARAM`: `server` is NULL + - `UVHTTP_ERROR_OUT_OF_MEMORY`: server or loop allocation failed +- **Thread safety**: Not thread-safe. +- **Note**: Prefer `uvhttp_server_new(loop, &server)` - the caller-owns-the-loop shape is what most embedding code already does. This entry exists for the rare case where the server should outlive the caller's loop. + +### uvhttp_server_create +- **Signature**: `uvhttp_error_t uvhttp_server_create(uv_loop_t* loop, const char* host, int port, uvhttp_server_builder_t** server)` +- **Purpose**: Builder construction: create server + router + config, start listening, and return a **builder** for chaining routes and the one tunable config knob. +- **Preconditions**: `loop`, `host`, and `server` must be non-NULL. +- **Postconditions**: On success, `*server` is a fully listening builder. On **any** failure, `*server` is NULL and nothing is allocated - the caller has no rollback to write (v2.10: the out-param is established once at function entry instead of per branch). +- **Ownership**: the builder is a **view, not an owner**. `server`/`router`/`config` handles are exposed for convenience; `uvhttp_server_free` releases all three. Dropping a builder is therefore always `uvhttp_server_simple_free()` (server + builder), never a per-field teardown. +- **Error conditions** (v2.10: propagated, not collapsed - a caller mistake and a runtime failure need different remediation): + - `UVHTTP_ERROR_INVALID_PARAM`: `loop`, `host`, or `server` is NULL + - `UVHTTP_ERROR_OUT_OF_MEMORY`: any allocation failed + - `UVHTTP_ERROR_SERVER_LISTEN`: bind or listen syscall failed (occupied port) +- **Thread safety**: Not thread-safe. Must be called from the event loop thread. + +### uvhttp_server_run / uvhttp_server_stop_simple / uvhttp_server_simple_free +- **Signatures**: `int uvhttp_server_run(uvhttp_server_builder_t* server)` / `void uvhttp_server_stop_simple(uvhttp_server_builder_t* server)` / `void uvhttp_server_simple_free(uvhttp_server_builder_t* server)` +- **Purpose**: Run the loop until stopped; stop accepting connections; release the builder (and, through it, the server, router and config). +- **Preconditions**: `server` must be a builder from `uvhttp_server_create` (NULL-tolerant throughout: each function no-ops on NULL). +- **Postconditions**: `simple_free` leaves nothing allocated; the builder owns nothing, so there is no double-release hazard when the caller also holds handles from the builder struct. +- **Thread safety**: Not thread-safe. `run` blocks the calling thread. + +### uvhttp_get / uvhttp_post / uvhttp_put / uvhttp_delete / uvhttp_any +- **Signature**: `uvhttp_server_builder_t* uvhttp_(uvhttp_server_builder_t* server, const char* path, uvhttp_request_handler_t handler)` +- **Purpose**: Register a route on the builder's router and return the builder (chaining). +- **Preconditions**: `server`, `path`, and `handler` must be non-NULL; `path` must match the router's rules (no query string, `:param` supported, length < `MAX_ROUTE_PATH_LEN`). +- **Postconditions**: `server->router` contains the route. The builder pointer is returned unchanged for chaining, so a NULL argument silently returns NULL. +- **Error conditions**: registration failure is **logged, not returned** - the chain continues, so the caller must verify the path/handler it passed rather than the return value. +- **Thread safety**: Not thread-safe. + +### uvhttp_set_max_connections +- **Signature**: `uvhttp_server_builder_t* uvhttp_set_max_connections(uvhttp_server_builder_t* server, int max_conn)` +- **Purpose**: Set the connection cap - **the only builder config knob that has an effect**. It writes `config->max_connections`, which the connection path reads. +- **Preconditions**: `server` must be a builder from `uvhttp_server_create` (it must have a config). +- **Postconditions**: `config->max_conn == max_conn`. Returns the builder for chaining; on NULL it returns NULL. +- **Error conditions**: none reported - a builder without a config (e.g. from `uvhttp_server_new`) makes this a silent no-op, as does NULL. +- **History**: `uvhttp_set_timeout()` and `uvhttp_set_max_body_size()` were removed in v2.10 (they wrote config fields no execution path read); see docs/spec/config-api.md. +- **Thread safety**: Not thread-safe. + +### uvhttp_serve +- **Signature**: `int uvhttp_serve(uv_loop_t* loop, const char* host, int port)` +- **Purpose**: One-key start: `uvhttp_server_create` + a `/` default route + run + free. For scripts and demos. +- **Preconditions**: `loop` non-NULL, `port` in 1..65535. `host` may be NULL (treated as 0.0.0.0 with a warning). +- **Postconditions**: returns when the loop stops; the server is freed before returning. +- **Error conditions**: `UVHTTP_ERROR_INVALID_PARAM` (NULL loop, out-of-range port, or NULL host - which makes the listen call fail) or whatever `uvhttp_server_create` propagated; 0 on a clean loop exit. +- **Thread safety**: Not thread-safe. Blocks the calling thread. + +### uvhttp_server_enable_health_check +- **Signature**: `uvhttp_error_t uvhttp_server_enable_health_check(uvhttp_server_t* server, const char* path)` +- **Purpose**: Register a `{"status":"ok"}` handler at `path` for load-balancer probes. +- **Preconditions**: `server` and `path` must be non-NULL. A router is created if the server has none. +- **Postconditions**: `GET path` returns 200 `application/json` `{"status":"ok"}`. +- **Error conditions**: + - `UVHTTP_ERROR_INVALID_PARAM`: `server` or `path` is NULL, or route registration failed +- **Thread safety**: Not thread-safe. + ### uvhttp_server_enable_tls / uvhttp_server_disable_tls - **Signature**: `uvhttp_error_t uvhttp_server_enable_tls(uvhttp_server_t* server, uvhttp_tls_context_t* tls_ctx)` / `uvhttp_error_t uvhttp_server_disable_tls(uvhttp_server_t* server)` - **Purpose**: Enable or disable TLS on the server diff --git a/include/uvhttp_request.h b/include/uvhttp_request.h index 9e14f685..4b36397b 100644 --- a/include/uvhttp_request.h +++ b/include/uvhttp_request.h @@ -88,6 +88,7 @@ UVHTTP_STATIC_ASSERT(offsetof(uvhttp_request_t, headers) >= 64, "headers array should be after first 64 bytes"); /* API functions */ +uvhttp_error_t uvhttp_request_init(uvhttp_request_t* request, uv_tcp_t* client); const char* uvhttp_request_get_method(uvhttp_request_t* request); const char* uvhttp_request_get_url(uvhttp_request_t* request); void uvhttp_request_free(uvhttp_request_t* request); diff --git a/include/uvhttp_server.h b/include/uvhttp_server.h index e94a856e..73ab16bc 100644 --- a/include/uvhttp_server.h +++ b/include/uvhttp_server.h @@ -607,10 +607,6 @@ void uvhttp_server_ws_update_activity(uvhttp_server_t* server, uvhttp_ws_connection_t* ws_conn); #endif -/* internalFunction */ -uvhttp_error_t uvhttp_request_init(uvhttp_request_t* request, uv_tcp_t* client); -void uvhttp_request_cleanup(uvhttp_request_t* request); - /* TLSFunction (whenDisable) */ /* uvhttp_error_t uvhttp_tls_init(void); */ /* void uvhttp_tls_context_free(uvhttp_tls_context_t* ctx); */