通用 HTTP 客户端(im.ttalk/clj-http):JDK java.net.http 之上的 Ring 式请求 / 响应 map、同步与
CompletableFuture 异步、按行真流式、大小写不敏感的头工具(designs/http-module.md)。零依赖:只用 JDK;
JSON、日志、错误模型都是调用方的事。与 agent 无关,可单独使用;clj-agent 的 provider / mcp / tools 都建在它上面。
(与社区的 dakrone/clj-http 无关:坐标是 im.ttalk/clj-http。)
(require '[im.ttalk.http :as http])
(http/request {:method :post :url "https://api.example.com/v1/x"
:headers {"Authorization" "Bearer …"} :body "{…}" :timeout-ms 30000})
;; => {:status 200 :headers {"content-type" ["application/json"]} :body "…" :url "…"}
(http/request-async req) ;; => CompletableFuture<同形状>
(http/stream-lines (assoc req :on-line f)) ;; 按行真流式 => {:future :cancel}
(http/header resp "Retry-After") ;; 取头:大小写不敏感,单值 / 多值都收
java.net.http.HttpClient 本身(client 造、default-client 取共享的那个);
各处的 :client 选项收的都是它。header / header-values,
不要直接 get——这正是 provider-reliability.md §1.3 那个 Retry-After 从未生效的 bug 的根因。IOException / HttpTimeoutException / IllegalArgumentException …),
异步版异常完成。把它们映射成什么(canonical error、{:status 0 :error …}、工具错误码)由调用方定。头工具移植自 Keelson jvm/modules/clj-net 的 im.ttalk.net.http(5c745869),改动:头名也收关键字;
值也收 java.util.List。
通用 HTTP 客户端(`im.ttalk/clj-http`):JDK `java.net.http` 之上的 Ring 式请求 / 响应 map、同步与
`CompletableFuture` 异步、按行真流式、大小写不敏感的头工具(designs/http-module.md)。零依赖:只用 JDK;
JSON、日志、错误模型都是调用方的事。与 agent 无关,可单独使用;clj-agent 的 provider / mcp / tools 都建在它上面。
(与社区的 dakrone/clj-http 无关:坐标是 im.ttalk/clj-http。)
```clojure
(require '[im.ttalk.http :as http])
(http/request {:method :post :url "https://api.example.com/v1/x"
:headers {"Authorization" "Bearer …"} :body "{…}" :timeout-ms 30000})
;; => {:status 200 :headers {"content-type" ["application/json"]} :body "…" :url "…"}
(http/request-async req) ;; => CompletableFuture<同形状>
(http/stream-lines (assoc req :on-line f)) ;; 按行真流式 => {:future :cancel}
(http/header resp "Retry-After") ;; 取头:大小写不敏感,单值 / 多值都收
```
- **client 就是 `java.net.http.HttpClient` 本身**(`client` 造、`default-client` 取共享的那个);
各处的 `:client` 选项收的都是它。
- **响应头**:小写头名 ⇒ 串向量(去掉 HTTP/2 伪头)。取值一律走 `header` / `header-values`,
不要直接 `get`——这正是 provider-reliability.md §1.3 那个 Retry-After 从未生效的 bug 的根因。
- **失败**:同步版原样抛(`IOException` / `HttpTimeoutException` / `IllegalArgumentException` …),
异步版异常完成。把它们映射成什么(canonical error、`{:status 0 :error …}`、工具错误码)由调用方定。
头工具移植自 Keelson `jvm/modules/clj-net` 的 `im.ttalk.net.http`(5c745869),改动:头名也收关键字;
值也收 `java.util.List`。(assoc-header hs name value)头 map 里 name 换成这一个值(先去掉大小写不同的同名头)。
头 map 里 `name` 换成这一个值(先去掉大小写不同的同名头)。
(build-request {:keys [method url query-params headers body timeout-ms]
:or {method :get}})请求 map ⇒ HttpRequest。键::method(关键字或串,缺省 :get):url(串或 URI):query-params
:headers :body(串 / 字节 / nil):timeout-ms(总时长,nil ⇒ 不设)。
有正文而没给 Content-Type ⇒ 补 application/json(MiniMax 缺它报「缺 messages」;本库的正文几乎都是 JSON)。
请求 map ⇒ `HttpRequest`。键:`:method`(关键字或串,缺省 :get)`:url`(串或 URI)`:query-params` `:headers` `:body`(串 / 字节 / nil)`:timeout-ms`(总时长,nil ⇒ 不设)。 有正文而没给 Content-Type ⇒ 补 `application/json`(MiniMax 缺它报「缺 messages」;本库的正文几乎都是 JSON)。
(charset m)Content-Type 的 charset=(认不得 / 没给 ⇒ UTF-8)。
Content-Type 的 charset=(认不得 / 没给 ⇒ UTF-8)。
(client)(client {:keys [connect-timeout-ms version executor redirect proxy]
:or {connect-timeout-ms 30000
version :http-1.1
executor :virtual
redirect :never}})造一个 java.net.http.HttpClient。选项(都可省):
:connect-timeout-ms 建连上限,缺省 30000;nil ⇒ 不设:version :http-1.1(缺省)| :http-2。缺省固定 1.1:LiteGate 等本地网关在 HTTP/2 握手阶段直接断连:executor :virtual(缺省,虚拟线程)| 任意 java.util.concurrent.Executor:redirect :never(缺省,同 JDK 缺省)| :normal(不从 https 跟到 http)| :always:proxy nil(缺省,直连)| :env(读 http_proxy / https_proxy / all_proxy / no_proxy,大小写两种)|
代理地址串 / URI | im.ttalk.http.proxy 的 map | java.net.ProxySelector造一个 `java.net.http.HttpClient`。选项(都可省):
- `:connect-timeout-ms` 建连上限,缺省 30000;nil ⇒ 不设
- `:version` `:http-1.1`(缺省)| `:http-2`。缺省固定 1.1:LiteGate 等本地网关在 HTTP/2 握手阶段直接断连
- `:executor` `:virtual`(缺省,虚拟线程)| 任意 `java.util.concurrent.Executor`
- `:redirect` `:never`(缺省,同 JDK 缺省)| `:normal`(不从 https 跟到 http)| `:always`
- `:proxy` nil(缺省,直连)| `:env`(读 http_proxy / https_proxy / all_proxy / no_proxy,大小写两种)|
代理地址串 / URI | `im.ttalk.http.proxy` 的 map | `java.net.ProxySelector`(default-client)进程共享的缺省 client(第一次调用时建;(client) 的缺省设置)。连接池因此在各调用方之间复用。
进程共享的缺省 client(第一次调用时建;`(client)` 的缺省设置)。连接池因此在各调用方之间复用。
(dissoc-header hs name)头 map 去掉 name(大小写不敏感)。
头 map 去掉 `name`(大小写不敏感)。
(header-seq hs)头 map ⇒ [名 值] 串对的序列(多值展开)。
头 map ⇒ [名 值] 串对的序列(多值展开)。
(header-values m name)头 name 的全部值(大小写不敏感;没有 ⇒ [])。m 是头 map,或带 :headers 的请求 / 响应。
头值可以是串、串向量或 java.util.List;头名可以是串或关键字。
头 `name` 的全部值(大小写不敏感;没有 ⇒ [])。`m` 是头 map,或带 `:headers` 的请求 / 响应。 头值可以是串、串向量或 `java.util.List`;头名可以是串或关键字。
(media-type m)Content-Type 的媒体类型(小写、不含参数);没有 ⇒ nil。m 同 header-values。
Content-Type 的媒体类型(小写、不含参数);没有 ⇒ nil。`m` 同 `header-values`。
(query-string params)map ⇒ 查询串(键取 name);空 ⇒ nil。
map ⇒ 查询串(键取 name);空 ⇒ nil。
(request req)同步发一条请求,回 {:status :headers :body :url}。请求键见 build-request,另有:
:as :string(缺省)| :bytes | :input-stream(调用方负责关)| :discard:client 缺省 (default-client)
失败原样抛(IOException 一族、HttpTimeoutException、IllegalArgumentException)。同步发一条请求,回 `{:status :headers :body :url}`。请求键见 `build-request`,另有:
- `:as` `:string`(缺省)| `:bytes` | `:input-stream`(调用方负责关)| `:discard`
- `:client` 缺省 `(default-client)`
失败原样抛(IOException 一族、HttpTimeoutException、IllegalArgumentException)。(request-async req)request 的异步版:回 CompletableFuture<同形状响应>,失败(含请求构造失败)异常完成。
`request` 的异步版:回 `CompletableFuture<同形状响应>`,失败(含请求构造失败)异常完成。
(stream-lines {:keys [on-line on-response first-byte-ms idle-ms] :as req})按行真流式:每行到达即回调(BodyHandlers/fromLineSubscriber,真增量、可取消)。请求键同 request,另有:
:on-response (fn [status headers]) 响应头到达时(任何状态码):on-line (fn [line]) 仅 2xx:每行一次,单条流内串行;抛异常 ⇒ 中止流、future 以它异常完成:first-byte-ms 发出请求到第一行的上限(缺省 = :idle-ms):idle-ms 相邻两行之间的上限(空行、SSE 注释心跳也算活动)回 {:future CompletableFuture :cancel (fn [])}:
{:status :headers};:on-line,future 完成为 {:status :headers :body 错误体串};timeout-error? 回 :first-byte / :idle,ex-data 带 :limit-ms);cancel ⇒ 取消上游,future 被取消(CancellationException)。:on-line 里调它也行。
终止只发生一次:完成 / 超时 / 取消 / 失败竞争时先到者为准,之后不再回调 :on-line。按行真流式:每行到达即回调(`BodyHandlers/fromLineSubscriber`,真增量、可取消)。请求键同 `request`,另有:
- `:on-response` (fn [status headers]) 响应头到达时(任何状态码)
- `:on-line` (fn [line]) **仅 2xx**:每行一次,单条流内串行;抛异常 ⇒ 中止流、future 以它异常完成
- `:first-byte-ms` 发出请求到第一行的上限(缺省 = `:idle-ms`)
- `:idle-ms` 相邻两行之间的上限(空行、SSE 注释心跳也算活动)
回 `{:future CompletableFuture :cancel (fn [])}`:
- 2xx 正常结束 ⇒ future 完成为 `{:status :headers}`;
- 非 2xx ⇒ 不调 `:on-line`,future 完成为 `{:status :headers :body 错误体串}`;
- watchdog 超时 ⇒ 取消上游,future 以 ex-info 异常完成(`timeout-error?` 回 :first-byte / :idle,ex-data 带 :limit-ms);
- IO 失败 ⇒ future 以原异常完成;
- `cancel` ⇒ 取消上游,future 被取消(CancellationException)。`:on-line` 里调它也行。
**终止只发生一次**:完成 / 超时 / 取消 / 失败竞争时先到者为准,之后不再回调 `:on-line`。(timeout-error? t)stream-lines 的 watchdog 超时?回 :first-byte / :idle,不是 ⇒ nil。
`stream-lines` 的 watchdog 超时?回 `:first-byte` / `:idle`,不是 ⇒ nil。
(url-encode s)application/x-www-form-urlencoded 编码(空格 ⇒ +)。nil ⇒ nil。
application/x-www-form-urlencoded 编码(空格 ⇒ +)。nil ⇒ nil。
(with-query url params)给 URL 接上查询参数(已有 ? 用 &)。
给 URL 接上查询参数(已有 ? 用 &)。
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |