Closed8

Node.js と ブラウザでのfetchの違いについて

nissy-devnissy-dev

まず Deno からまとめると以下の通り

  • cookie jar がない → レスポンスに含まれる set-cookie ヘッダは無視される
  • SOPの概念がない → SOPに関連するスペックをサポートしない
  • fetchのredirectオプションにmanualを渡した時のレスポンスは、opaqueredirect レスポンスではなく basic レスポンスになる

cookie jar がないということは、リクエストでcookieを保存しないということ。cookie の用途といえば、ブラウザから fetch するときにユーザーを判別してコンテンツを出し分けたりするところだと思うので、確かに deno 環境からの fetch には不要そう。そもそも set-cookie に指定されるデータはブラウザに保存される前提だったので、納得感ある。

SOPの概念がないことについて

SOPは、ブラウザからのリソース取得のときに同一オリジンからしか原則許可しないルールのこと。なので、SOPの概念がないのは理解できる。Denoで言及されているスペックは以下の通り。

  • Section 3.1. 'Origin' header
  • Section 3.2. CORS protocol
  • Section 3.5. CORB
  • Section 3.6. 'Cross-Origin-Resource-Policy' header

fetchのredirectオプションの詳細について

ブラウザでは、fetchのredirectオプションにmanualを渡した時のレスポンスは、opaqueredirect レスポンスになる。MDNによると、ヘッダーにもボディにも何も情報が含まれない status 0 を返すレスポンスらしい。

https://developer.mozilla.org/en-US/docs/Web/API/Response/type

manual の redirect なので次のリダイレクト先のリンクの情報は必要になるが、opaqueredirect レスポンスにはそれらの情報は含まない。そもそも、fetchのredirectオプションのmanualってなんのためにある...?

https://github.com/whatwg/fetch/issues/763

nissy-devnissy-dev

同じことを質問している人がいた
https://stackoverflow.com/questions/42716082/fetch-api-whats-the-use-of-redirect-manual

service worker で使われる感じはわかったけど、どういう感じで使われるのかは全くわからなかった... これは聞いておこう

PS. この答えは Fetch のSpecにあった

Retrieves an opaque-redirect filtered response when a request is met with a redirect, to allow a service worker to replay the redirect offline. The response is otherwise indistinguishable from a network error, to not violate atomic HTTP redirect handling.

service worker がオフラインでもリダイレクトできるように、リクエストがリダイレクトに遭遇したときにopaque-redirect filtered response を取得する。このレスポンスは、atomic HTTP redirect handling に反しないように定義されたレスポンスである。

Opaque Response とは?

azu さんの資料にまとまっている

https://azu.github.io/slide/pixelgrid/serviceworker-cors.html

  • fetch で mode: "no-cors" の場合のレスポンス
  • typeがopaqueとなり、statusが0、bodyがnullとフィルターされる
    • same-originを無視して取得したデータをそのまま見れるのはまずい
    • エラーレスポンスと区別がつかないようなデータ
  • キャッシュとしては使える (内部のみ)
    • これができないとService Worker は SOP を超えたキャッシュができなくなってしまう...

まとめ

SOPを超えたリダイレクトのキャッシュをSWがしたいがために、fetchのredirectオプションのmanualやOpaque Responseなどはあると理解した。SWはブラウザの話かつ Opaque にしている理由も XSS を防ぐためなので、無視して良さそうな感じはする。

nissy-devnissy-dev

atomic HTTP redirect handling

https://fetch.spec.whatwg.org/#atomic-http-redirect-handling

アトミックなHTTPリダイレクト処理とは、リダイレクトに関するレスポンス (status が 300系?) をAPIに公開しないこと。XSS攻撃を受ける可能性がある。opaqueredirect レスポンスが何も情報を持たないのは、この仕様によるもの。

XSS自体は、確かにブラウザでのリクエストに関する脆弱性なので、対処しなくてよさそうなのはなんとなくわかる。

nissy-devnissy-dev

最後にNodeの方も見ておく

Expect

Expect のヘッダーフィールドをサポートしない。すぐに リクエスト body は送られる。100 Continue のレスポンスは無視される。

Expect ヘッダーは、これから Content-Length で指定されているサイズでbody をPOSTしても問題ないかをサーバー側に問い合わせるために使われる。100 Continue が帰ってきたら、ブラウザはbodyをPOSTする。

ネットワーク帯域の節約のために使われる技術らしいので、確かにサーバー側でサポートしないのはわかる感じがする。

Pipelining

  • pipelining factor が 1 より大きい時のみ pipeline を使う
    • 基本は stream を使うってことなのかな
  • connection が必ず確立されているということを前提にリクエストを送信する
    • pipeline 化されてなかったときの fallback には対応しない
    • ブラウザでは connection を確認するはず
  • 接続が失敗するとすぐにパイプラインを再開しようとするが、失敗したリクエストについて retry せずエラーにする
    • ブラウザだと対応するリクエストがすべて取得できるようにretryする
      • 失敗したリクエストも再度取得しようと試みる
  • リクエスト失敗後は、対応するコールバック/プロミス/ストリームをエラーにする
    • ブラウザでは、対応するレスポンスがすべて取得できるようにリトライする

undici は 基本的にはコネクションが確立されていることを前提としてリクエストを送る。またコネクションが失敗した時の retry 機構を持つが、一度失敗したリクエストはエラーを返す。

GC

ブラウザの Fetch は接続リソースの解放をブラウザのGCに任せているが、undici の場合はGCに任せていない。なので、メモリなどを適切に管理する必要がある場合もあるかもしれない。Node.jsのGCに任せない理由としては、NodeのGCが積極的なものではなく、非決定的なものであるため。

nissy-devnissy-dev

Node.js では、CookieStore API の実装が始まっているらしい

https://developer.mozilla.org/en-US/docs/Web/API/CookieStore

Cookie をParseして、リクエストにセットする方法が欲しいという感じ。
ユースケースとしては、認証のあるページのスクレイピングなどが挙げられる。Cookieをセットできれば node.js からでもfetchできるが、現状は cookie を扱えないので jsdom or puppeteer などの heavy なライブラリを使うしかないとのこと。

a node.js environment isn’t good for simulating a browser, maybe you want to use something like jsdom or puppeteer

It’s a common use case to want to request data from an API that requires authentication, where that authentication check takes the form of checking for a cookie. It’s not only done when you’re trying to simulate a browser.

https://github.com/whatwg/fetch/issues/1384

https://github.com/nodejs/node/pull/41749#issuecomment-1025311795

このスクラップは2022/03/27にクローズされました