ことの経緯としては、
- とあるHTTP関連のOSSにPRを出そうとしていた
- コード書いてテストを実行すると、どうやらPRを出す前からコケてるケースがあった
- 原因は、`new Response(null, { status: 101 })`が`RangeError`で落ちてたから
さて、これはなんでエラーになるんや?WebSocketとかで使うんではなかったか?(うろ覚え)
というところから、なんとなく気になったのであれこれ調べてみたメモです。
エラーと環境
RangeError: init["status"] must be in the range of 200 to 599, inclusive.
というエラーが`throw`されてた。
確認できた環境としては、
どれも同様なエラーになって、おそらくバージョンは関係なさそう。
ちなみに、このOSSのテストでは`isomorphic-fetch`が使用されていて、`isomorphic-fetch`(が内部的に依存している`node-fetch`)の実装では、`new Response(null, { status: 101 })`はエラーにならない。
ただ、このポリフィルは`global.fetch`が存在する場合には何もしないので、バージョン18以降のNode.jsを使っていると、本体の`undici`にある`Response`で処理されて、そこでエラーになってた模様。
そもそもSpecでは
`Response`オブジェクトといえば`fetch`の同期ということで、Fetch Standardを見てみた。
すると、どうやら仕様としては「エラーになって然るべし」らしいことがわかった。
To initialize a response, given a Response object response, ResponseInit init, and an optional body with type body, run these steps:
1. If init["status"] is not in the range 200 to 599, inclusive, then throw a RangeError.
https://fetch.spec.whatwg.org/#initialize-a-response
`throw`するってめっちゃ書いてある!
そういうわけで、モダンブラウザと`undici`は仕様に準拠した実装になってると言えそう。
`isomorphic-fetch`でも、仕様に合わせようっていうIssue/PRが立ってるようだった。
というわけで、エラーになるのが仕様通りで、仕様からするとテストコードにバグがあるという話だった。
〜終〜
と言いたいとこやけど・・。
エラーにならない環境もある
これが違和感の正体で、ここまで調べようと思った動機であり。
この`new Response(null, { status: 101 })`、Cloudflare Workersでは動くんですよね・・・。
なんならDocsにものってるし。
ということはもしや?って思って、エッジWorkerたちの実装を調べてみると、軒並みエラーにならなかった・・・!
- workerd
- bun
- deno
どれも`101`だけは例外的に許可するって実装になってた。
WinterCGのSpecでは
どうやらサーバーサイドJSをやっていくために、このWGではFetch Standardをforkしてるらしく。
もしかそこでは許可されるように変更された?って思ったけど、今の時点では別にそうなってなかった。
これからなるかもしれないし、ならないかもしれない。
まとめ
というわけで、`new Response(null, { status: 101 })`は、
- 環境によって、エラーになったりならなかったりする
- 仕様が実装に必ずしも反映されるとは限らない
- まぁWebの常って感じではあるけど
- `101`なレスポンスを返したいサーバーサイドの実装としては、困るかもしれない
- ただ、Node.jsでWebSocketサーバーを`fetch.Response`を使って実装したい人なんかいない気がする
- 既存の枯れた実装はいっぱいあるし、`node:net`なり使えばいいし
- エッジ系ランタイムでは、仕様に明記されてないものの、動作する
そもそもFetch StandardはHTTPクライアントの出自であり、サーバー用途で使われる日がくるとは!って感じかも?知らんけど。