Liking cljdoc? Tell your friends :D

im.ttalk.http

通用 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")               ;; 取头:大小写不敏感,单值 / 多值都收
  • 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。

通用 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`。
raw docstring

im.ttalk.http.date

HTTP 日期与 Retry-After 解析。移植自 Keelson jvm/modules/clj-net/src/im/ttalk/net/retry.clj(5c745869)的 parse-http-date / retry-after-ms,改动:不判上限(clj-net 是「≥ 上限且 ≥ 退避就忽略」;clj-agent 的口径是 「取 min(值, :max-delay)」,由调用方做,见 designs/http-module.md §1.3);时钟改为毫秒数参数。

HTTP 日期与 Retry-After 解析。移植自 Keelson `jvm/modules/clj-net/src/im/ttalk/net/retry.clj`(5c745869)的
`parse-http-date` / `retry-after-ms`,改动:**不判上限**(clj-net 是「≥ 上限且 ≥ 退避就忽略」;clj-agent 的口径是
「取 min(值, :max-delay)」,由调用方做,见 designs/http-module.md §1.3);时钟改为毫秒数参数。
raw docstring

im.ttalk.http.failure

网络错的分类(与客户端无关):按 java.net / javax.net.ssl 的异常类型与 JDK 固定的系统文案认。 任何客户端底下抛的都是这批异常,拿来分类都行。

移植自 Keelson jvm/modules/clj-net/src/im/ttalk/net/failure.clj(5c745869)的 classify / socket-kind / timeout?,判定逻辑未改(timeout? 修了一处原版 bug,见函数体注释)。只给分类,不给可重试判定:clj-net 的 transient? 不把超时算可重试,而 clj-agent 的 canonical error 一直把 HttpTimeoutException 算 :transient——可重试与否仍由调用方的既有口径决定 (designs/http-module.md §1.3)。

网络错的分类(与客户端无关):按 `java.net` / `javax.net.ssl` 的异常类型与 JDK 固定的系统文案认。
任何客户端底下抛的都是这批异常,拿来分类都行。

移植自 Keelson `jvm/modules/clj-net/src/im/ttalk/net/failure.clj`(5c745869)的 `classify` / `socket-kind` /
`timeout?`,判定逻辑未改(`timeout?` 修了一处原版 bug,见函数体注释)。**只给分类,不给可重试判定**:clj-net 的 `transient?` 不把超时算可重试,而 clj-agent
的 canonical error 一直把 `HttpTimeoutException` 算 `:transient`——可重试与否仍由调用方的既有口径决定
(designs/http-module.md §1.3)。
raw docstring

im.ttalk.http.proxy

代理选择(与客户端无关)。移植自 Keelson jvm/modules/clj-net/src/im/ttalk/net/proxy.clj(5c745869),逻辑未改。

一个「代理」是下面三样之一,select 都收:

  • (from-environment) / (from-env m):环境变量那一档(同 C# HttpClient.DefaultProxy、curl): http_proxy / HTTP_PROXY(CGI 环境下不读大写的,防 httpoxy)、https_proxy / HTTPS_PROXY、all_proxy / ALL_PROXY 兜底; no_proxy / NO_PROXY:逗号分隔,* = 全部绕过,.a.com / a.com 都匹配 a.com 本身与子域。一个都没设 ⇒ nil。
  • (web-proxy "http://h:p" {:bypass [正则…] :bypass-on-local? b}):固定 HTTP 代理 + 绕过表:绕过表的正则 对主机名、scheme://host、scheme://host:port 任一整串匹配(大小写不敏感)即绕过;bypass-on-local? ⇒ 不带点的主机名与回环也绕过。
  • 任何 java.net.ProxySelector。 ->proxy-selector 把前两种变成 ProxySelector。平常不直接用本 ns:(im.ttalk.http/client {:proxy :env}) 即可。
代理选择(与客户端无关)。移植自 Keelson `jvm/modules/clj-net/src/im/ttalk/net/proxy.clj`(5c745869),逻辑未改。

一个「代理」是下面三样之一,`select` 都收:
- `(from-environment)` / `(from-env m)`:环境变量那一档(同 C# HttpClient.DefaultProxy、curl):
  http_proxy / HTTP_PROXY(CGI 环境下不读大写的,防 httpoxy)、https_proxy / HTTPS_PROXY、all_proxy / ALL_PROXY 兜底;
  no_proxy / NO_PROXY:逗号分隔,* = 全部绕过,.a.com / a.com 都匹配 a.com 本身与子域。一个都没设 ⇒ nil。
- `(web-proxy "http://h:p" {:bypass [正则…] :bypass-on-local? b})`:固定 HTTP 代理 + 绕过表:绕过表的正则
  对主机名、scheme://host、scheme://host:port 任一整串匹配(大小写不敏感)即绕过;bypass-on-local? ⇒ 不带点的主机名与回环也绕过。
- 任何 java.net.ProxySelector。
`->proxy-selector` 把前两种变成 ProxySelector。平常不直接用本 ns:`(im.ttalk.http/client {:proxy :env})` 即可。
raw docstring

im.ttalk.http.sse

SSE 分帧核(WHATWG HTML 9.2.6,与客户端无关):字节进、事件出;外加服务端用的拼帧(event-frame / comment-frame / retry-frame,帧是 UTF-8 字节)。移植自 Keelson jvm/modules/clj-net/src/im/ttalk/net/sse.clj (5c745869),逻辑未改;不搬阻塞读者与断线续读循环(designs/http-module.md §1.3:无调用方)。拼帧起初也没搬, clj-agent-server 的 SSE 成了调用方后补上(designs/server-module.md §2)。

  • 事件 = map::event-type(缺省 "message"):data(多行 data 以 \n 连接):last-event-id(到这个事件为止最近一次 id: 的值,跨事件保留):reconnection-ms(这个事件自己带的 retry:,没有 nil)。
  • 行结束收 \n、\r\n、\r(CRLF 被拆在两次 feed 之间也只算一次);按 UTF-8 解码、去 BOM,charset= 被规范忽略; 流在事件中途结束时那半个事件不派发。
(parse-all "event: chat\ndata: a\ndata: b\n\n")   ;; => [{:event-type "chat" :data "a\nb" …}]
(let [p (parser)] (feed! p bytes) (take-event! p))  ;; 增量
SSE 分帧核(WHATWG HTML 9.2.6,与客户端无关):字节进、事件出;外加服务端用的拼帧(`event-frame` /
`comment-frame` / `retry-frame`,帧是 UTF-8 字节)。移植自 Keelson `jvm/modules/clj-net/src/im/ttalk/net/sse.clj`
(5c745869),逻辑未改;**不搬**阻塞读者与断线续读循环(designs/http-module.md §1.3:无调用方)。拼帧起初也没搬,
clj-agent-server 的 SSE 成了调用方后补上(designs/server-module.md §2)。

- 事件 = map:`:event-type`(缺省 "message")`:data`(多行 data 以 \n 连接)`:last-event-id`(到这个事件为止最近一次
  id: 的值,跨事件保留)`:reconnection-ms`(这个事件自己带的 retry:,没有 nil)。
- 行结束收 \n、\r\n、\r(CRLF 被拆在两次 feed 之间也只算一次);按 UTF-8 解码、去 BOM,charset= 被规范忽略;
  流在事件中途结束时那半个事件不派发。

```clojure
(parse-all "event: chat\ndata: a\ndata: b\n\n")   ;; => [{:event-type "chat" :data "a\nb" …}]
(let [p (parser)] (feed! p bytes) (take-event! p))  ;; 增量
```
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close