# HTTP 客户端 `http/*`(该命名空间的客户端部分)通过 mbedTLS 提供非阻塞、 由 epoll 驱动的 HTTP 请求以支持 TLS。每个请求返回一个 Future; 调用方通过 `await` 获取响应映射。 ## `http/*` 客户端标准库函数 | 函数 | 返回值 | 用途 | |---|---|---| | `[http/get $url ?opts]` | Future | GET 请求 | | `[http/post $url $body ?opts]` | Future | POST 请求 | | `[http/put $url $body ?opts]` | Future | PUT 请求 | | `[http/patch $url $body ?opts]` | Future | PATCH 请求 | | `[http/delete $url ?opts]` | Future | DELETE 请求 | | `[http/head $url ?opts]` | Future | HEAD 请求 | | `[http/request :METHOD $url $opts]` | Future | 完全控制 | | `[http/client]` | 客户端资源 | 创建可复用客户端(连接池) | | `[http/close $client]` | nil | 关闭客户端句柄 | | `[http/do $client :METHOD $url ?opts]` | Future | 通过客户端发起请求 | | `[http/set-timeout $client ms]` | nil | 设置请求超时 | ## 基本用法
set resp [await [http/get "https://api.github.com/repos/ziglang/zig"]]
echo $resp~status                              # => 200
echo $resp~body                                # => JSON string

set body [json/parse $resp~body]
echo $body~stargazers_count
`$resp~status` 是 HTTP 状态码;`$resp~body` 是作为字符串的响应体; `$resp~headers` 是响应头的小写映射。 ## 方法
set r1 [await [http/get    "https://example.com"]]
set r2 [await [http/post   "https://api.example.com" $payload]]
set r3 [await [http/put    "https://api.example.com/users/1" $payload]]
set r4 [await [http/patch  "https://api.example.com/users/1" $payload]]
set r5 [await [http/delete "https://api.example.com/users/1"]]
set r6 [await [http/head   "https://example.com/large-file"]]
## 自定义请求头 传入带有 `:headers` 条目的选项映射:
set r [await [http/get "https://api.example.com/me" %{
    :headers %{
        :Authorization "Bearer $token"
        :Accept "application/json"
    |
    }
}]]
请求头名称可以是字符串或关键字;运行时会将其规范化。 ## 请求体 `http/post`、`http/put`、`http/patch` 接受一个请求体参数。原始请求体 传入字符串;JSON 请自行序列化:
def payload [json/stringify %{
    :title "Hello"
    :body  "World"
}]

set r [await [http/post "https://api.example.com/posts" $payload %{
    :headers %{ :Content-Type "application/json" }
}]]
## 完全控制请求 `[http/request :METHOD $url $opts]` 暴露所有选项:
set r [await [http/request :GET $url %{
    :headers %{ :Authorization "Bearer $token" }
    :body $payload
    :timeout 5000
}]]
## 持久化客户端 每次一次性调用 `http/get` 等都会使用共享的默认客户端。若要在对同一 主机的众多请求之间复用连接池,请创建显式客户端:
def c [http/client]
http/set-timeout $c 5000

set r1 [await [http/do $c :GET "https://api.example.com/users"]]
set r2 [await [http/do $c :GET "https://api.example.com/users/1"]]

http/close $c
当服务器支持 keep-alive 时,池化客户端会在多个请求之间复用 TCP 连接(以及 TLS 会话)。 ## 并发请求 使用 `deftask` 并发地扇出请求(其函数体默认在新的操作系统线程上 运行)。每个任务体必须自包含(约束参见 并发 —— 捕获的局部变量不会进入任务的 运行时):
from "deft/stdext/async" import await-all

def f1 [deftask { [await [http/get "https://api.example.com/a"]] }]
def f2 [deftask { [await [http/get "https://api.example.com/b"]] }]
def f3 [deftask { [await [http/get "https://api.example.com/c"]] }]
def results [await-all @{$f1 $f2 $f3}]
完整的异步机制参见 并发。 ## 错误处理 错误返回 `%Err{ :message ... }`。使用 `try` 捕获,或用 `err?` 检查:
set r [http/get $url]

try {
    set resp [await $r]
    echo $resp~status
} catch e {
    echo "request failed: $e~message"
}

# Or use err? on the awaited result
set resp [await $r]
if [err? $resp] {
    echo "failed: $resp~message"
} {
    echo $resp~body
}
网络错误(DNS 失败、连接被拒绝、TLS 握手失败)都会以 `%Err` 形式 出现。非 2xx 的 HTTP 状态码不是错误 —— 它们会作为设置了 `$resp~status` 的响应返回。 ## 超时 `[http/set-timeout $client ms]` 为客户端上的请求设置超时。传入 `nil` 则在共享的默认客户端上设置:
http/set-timeout nil 5000                      # default for one-shot requests
超时会中止请求,并以 `%Err` 拒绝该 Future。 ## JSON-GET 惯用法 一种常见模式:
def fetch-json {|url|
    @doc "Fetch a URL and parse the body as JSON."
    set resp [await [http/get $url]]
    if [err? $resp] { return $resp }
    [json/parse $resp~body]
}

set data [fetch-json "https://api.github.com/repos/ziglang/zig"]
echo $data~stargazers_count
## 快速参考 | 形式 | 返回值 | 用途 | |---|---|---| | `[http/get $url ?opts]` | Future | GET | | `[http/post $url $body ?opts]` | Future | POST | | `[http/put $url $body ?opts]` | Future | PUT | | `[http/patch $url $body ?opts]` | Future | PATCH | | `[http/delete $url ?opts]` | Future | DELETE | | `[http/head $url ?opts]` | Future | HEAD | | `[http/request :METHOD $url $opts]` | Future | 完全控制 | | `[http/client]` | client | 池化客户端 | | `[http/do $client :METHOD $url ?opts]` | Future | 通过池化客户端 | | `[http/set-timeout $client ms]` | nil | 超时 | | `[await $future]` | response map | 阻塞等待响应 |