Typed parsers and formatters for common HTTP header values
メンテナーはふだん 1 日以内に返信
評価
この issue はまだ評価されていません。
説明
Problem
effect/http treats header values as plain strings. Each place that needs to understand a value parses it on the spot, and the copies disagree.
Retry-After is parsed in eight places with six different rules. RFC 9110 §10.2.3 allows whole seconds or an HTTP date. For the invalid value -5:
HttpClient(http/HttpClient.ts:1461) falls through toDate.parse("-5"), which V8 reads as a date in 2001, so it retries after 1 ms.- The OTLP exporter and the Anthropic, OpenAI, OpenRouter and OpenAI-compatible clients return a delay of −5 seconds. These four AI clients also ignore HTTP dates and accept
1e3andInfinity. - GraphQL and Cloudflare return
undefined, and the typesafe client returns 0.
Other examples on main:
- Media types are matched by substring (
HttpServerRequest.ts:274,GraphQLProtocol.ts:260,HttpStaticServer.ts:438). The static server treatsAccept: text/html;q=0, which means "never send HTML", as asking for HTML. - There are three separate
Accept-style parsers: compression,McpServerand HttpApi. - HTTP dates go through
Date.parse, which reads the asctime format in local time. WithTZ=America/New_York,Sun Nov 6 08:49:37 1994comes out five hours off. Etagcan format a tag but not parse or compare one. The static server splitsIf-None-Matchon raw commas (HttpStaticServer.ts:448), but a comma is valid inside a tag.- There is nothing for
Cache-Control,Range,Link,WWW-Authenticateor Structured Fields.
Two related bugs are filed separately: basicAuth encoding (#9678) and multipart filenames (#9679).
Proposal
Add one small module per header family to effect/http. Each one provides:
- an immutable value;
parse, which returns aResultand is forgiving where the RFC allows;format, which always produces the canonical form;- an
Equivalence; - a Schema codec from string, so the value can be used directly in HttpApi
headersschemas and inWithHeaders.
| Module | Standard |
|---|---|
MediaType |
RFC 9110 §8.3.1 |
Accept, AcceptEncoding, AcceptLanguage, with negotiation |
RFC 9110 §12.5, RFC 4647 |
ContentDisposition |
RFC 6266, RFC 8187 |
HttpDate, RetryAfter |
RFC 9110 §5.6.7, §10.2.3 |
Etag lists and comparison, conditional requests |
RFC 9110 §8.8.3, §13 |
Range, ContentRange |
RFC 9110 §14 |
CacheControl |
RFC 9111 §5.2 |
Link |
RFC 8288 |
Authorization, WWW-Authenticate (Basic, Bearer) |
RFC 9110 §11, RFC 7617, RFC 6750 |
StructuredField |
RFC 9651 |
// names illustrative
RetryAfter.parse("Wed, 21 Oct 2015 07:28:00 GMT") // Result<RetryAfter, HeaderValueError>
RetryAfter.delay(value, now) // Duration, never negative
Etag.parseList(`"a,b", W/"c"`) // two tags
HttpApiEndpoint.get("file", "/file", {
headers: { "if-none-match": Schema.optional(Etag.ListFromString) }
})
Then switch Effect's own call sites over to these modules.
Why in Effect
- Effect's own code needs these parsers, and today every bug has to be fixed in every copy.
- Header values are wire data. Schema codecs give HttpApi users typed headers, decode errors and OpenAPI output without writing their own parsers.
- Other ecosystems provide them too. Go has
mime.ParseMediaType,http.ParseTime, and precondition and range handling inhttp.ServeContent. Rust has theheadersandsfvcrates. Node has jshttp'scontent-type,negotiator,range-parserandfresh.
Scope
In scope:
- The modules above, and switching Effect's own call sites over to them.
Out of scope:
- An HTTP cache.
- Cookies and trace context, which already have modules.
Forwarded(#9681) and Problem Details (#9686), which have their own issues.- Accept-based response selection in HttpApi.
Open questions
- Should conditional-request evaluation (
If-Match,If-None-Match,If-Modified-Since,If-Range) be a public helper that any handler can call? The static server would then use it too. - One issue with a checklist, or one PR per module?
- 主要言語
- TypeScript
- スター
- 16.7k
- フォーク
- 808
- 平均マージ
- 10時間 7分
- マージ済み PR(30日)
- 808
環境構築
このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
Effect-TS/effect のほかの issue
-
Support {self: this} for fnUntraced対応中かも @ArjunCodess が 32 日前に担当しました。 オープンenhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
Effect-TS/effect#8101 · コメント 1 件 ·
メンテナーはふだん 1 日以内に返信
-
難易度 3/5 1〜2日 初心者へのやさしさ 72/100
メンテナーはふだん 1 日以内に返信
-
v4 Cache.get: a caller that joins an abandoned lookup fails with an interruption it did not ask forオープン
難易度 4/5 3〜5日 初心者へのやさしさ 42/100
メンテナーはふだん 1 日以内に返信
-
Typed unix socket and TLS options for `BunHttpClient` and `DenoHttpClient`対応中かも @effect-bot が今日担当しました。 オープンdocker-made-me-do-it enhancement platform
Effect-TS/effect#9687 · 担当者 1 名 ·
メンテナーはふだん 1 日以内に返信
-
RFC 9457 Problem Details for HttpApi errors対応中かも @effect-bot が今日担当しました。 オープンdocker-made-me-do-it enhancement
難易度 5/5 1週間以上 初心者へのやさしさ 20/100
メンテナーはふだん 1 日以内に返信
Effect-TS/effect の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
paperclipai/paperclip#17151 ·
メンテナーはふだん 1 日以内に返信
-
needs-triage
難易度 2/5 1〜3時間 初心者へのやさしさ 83/100
AgoraIO-Extensions/agent-infra#3159 ·
メンテナーはふだん 1 日以内に返信
-
blocklist addition
難易度 1/5 1時間未満 初心者へのやさしさ 66/100
MetaMask/eth-phishing-detect#304596 ·
メンテナーはふだん 1 日以内に返信
-
friction
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
kentcdodds/kody#4142 ·
-
[Bug] Statistics | Task statistics are always empty on MikroORM with MySQL or SQLite対応中かも @NdekoCode が今日担当しました。 オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 76/100
ever-co/ever-gauzy#11214 ·
メンテナーはふだん 1 日以内に返信