HTTP Routing | Heroku Dev Center
職場でHerokuをプロダクション環境で使ってるので一通り目を通してみました。
以下は2015年10月12日ごろの上記ページの内容のオレオレ翻訳メモです。
内容の正確性は全く保証しませんw (訳も〜ですます調と〜だ調が混在してますし)
もし正しくない箇所があったらご指摘いただければと思います。
Cedarスタック上のすべてのweb dynoに対するHTTPリクエストのエントリーポイントはheroku.com
である。
Routing
- heroku routerの役割はweb dynoの場所を特定してその中の一つにリクエストを転送すること
- HTTP 1.1対応、HTTP 1.0も互換性も維持している
Request distribution
- routerは無作為アルゴリズムでweb dyno間のHTTPリクエストを振り分けしている
Request queueing
- routerごとに各アプリに対するアクティブリクエストカウンターを保持しており、dynoあたり50n(n=web dynoの数)を上限としている。
- 上限を超えるとルーターはH11のレスポンスを返す
Dyno connection behavior
- web dynoに対するコネクション確立が拒否もしくは5秒以内に終わらない場合は、routerはそのdynoを隔離し5秒間別のコネクションを確立させようとしない。
- 別のrouterは確立させに行くかもしれない(それぞれ独立しているので)
- コネクション拒否もしくはタイムアウトした場合最大10回(web dynoの数が10以下ならweb dynoの数)再施行する。それでもダメならH19やH21のエラーを返す
- 全部のdynoが隔離された場合、75秒後(この期間は増加していく)にリトライし、ダメだったらH99をエラーを返す
Timeouts
- HTTPコネクション確立後、30秒以内に最初のウィンドウ(TCPウィンドウのこと?)が返却されないとH12エラーをログに吐く
- 最初のレスポンスを受け取ったあとは55秒以内にウインドウを返却しないとH15やH28エラーをログに吐き、コネクションが切断される
Simultaneous connections
herokuapp.com
のルーティングスタックはweb dynoに多くのコネクションを確立するのを許可している- なのでwebserverは応答性を最大化するため複数コネクションに対応したものを選ぶべき
Request buffering
- routerは1MBのレスポンスバッファを維持している
- これは1MB以下のレスポンスであればクライアントの受信レートによらず返すことが可能であることを意味する(翻訳あやしい)
- 1MBを超えるレスポンスをの転送レートはクライアントの受信がどれだけ早いかによって制限される
Heroku Headers
略
Heroku router log format
略
Caching
WebScoekts
サポートしてる
Gzipped responses
- Cedarアプリへのリクエストは(nginxのようなHTTPプロキシサーバを通している訳ではなく)直接アプリケーションサーバに送られるため、レスポンス圧縮はアプリケーション側で行う必要がある。
Supported HTTP Methods
- すべてのHTTP methodをサポートしている、RFCで定義されてないCONNECTメソッドもサポートしている。
Expect: 100-continue
http-end-to-end-continue
をonにすることでExpect: 100-continue
ヘッダーをサーバに通し、100 continue
レスポンスを透過的に扱ってくれる
$ heroku labs:enable http-end-to-end-continue
Corner cases
- RFC 2068では
100 Continue
というのレスポンス仕様が定義されている、それ以降のRFCにExpect: 100-continue
が含まれており100 continue
もそのメカニズムに含まれている- なので後方互換性を考えたら
Expect
ヘッダがなくても極力100 Continue
を返した方が良いが、そうしないことを推奨している
- なので後方互換性を考えたら
- クライアントはサーバが即座にbodyデータを受け取って処理した場合、
100 Continue
を返さない場合があることを認識しておく Expect
ヘッダーは複数の値を保つ場合があるので、パースをするときは気をつける- コネクションを管理する複数のヘッダによる予期しない相互作用がある
- websocketの
Connection: Upgrade
とExpect: 100-continue
が同時に来たらどうする? 100 Contirnue
とConnection: close
が同時に来たらどうする?受信時に切断する?
- websocketの
上記のcorner caseに対してHeroku routerは一貫性をもった振る舞いを提供しなければならない。
Proxy requirements
Expect
ヘッダーは本来はclientとserver間のE2Eメカニズムだけど間のproxyの協調も必要になるのでRFCで定義されてる
- ヘッダーは現在のままサーバに渡す
- プロキシが対象サーバがHTTP 1.0以下のバージョンであることを知っている場合、
417 Expectation Failed
を返す - HTTP 1.0以下のClientが
Expect: 100-Continue
をつけずにリクエストを送り、サーバがそれでも100 Continue
レスポンスを返した場合、プロキシはそのレスポンスを取り去り最終的なステータスが来るのを待つ
Heroku router 100-continue support
- HTTP 1.0(より古い)クライアントの
100 Continue
レスポンスはExpect: 100-continue
がリクエストに入ってない場合、剥ぎ取られる(「返却されない」の意?)。 - routerはリクエストボディを送るとき、(サーバに?)
100 Continue
レスポンスを要求しない、が、(ボディを送るまでの?)待ち時間はクライアントに委ねる Expect
ヘッダーに100 Continue
(大文字小文字区別なし)以外の値が含まれていた場合routerは自動的に417 Expectation Failed
レスポンスを返してdynoとのコネクションをクローズする- WebSocket upgradeが要求されたとき、(routerは?)dynoにありのままを送信し、どんなレスポンスが来ても引き受ける。
100 Continue
ステータスがWebSocket upgradeを無視して何かしらのコードを返すかもしれない。また101 Switching Protocol
はExpect
ヘッダーの振る舞いを無視する。HTTPbis Draft(HTTP1.1の仕様?)を尊重するための注意、100 Continue
がサーバから受け取ったあと、routerはそれでも101 Switching Protocol
を待ち受けます。 - routerは
100 Continue
の時のConnection: close
を無視して最終的なレスポンスが届いた時のcloseだけを期待します。RFCの仕様として、コネクションは"現在のrequest/responseが完了した後"closeされるべきで、100
は終端ステータスではありません。ただし注意、Connection: close
はhop-by-hopな仕組みなので、routerは必ずしもclientへのconnectionをcloseする必要はなく、またcloseを転送しないかもしれない。 - routeは
100 Continue
レスポンスからのすべてのヘッダーを引き剥がします(「転送しない」の意?)、それはRFCで規定されており実装をよりシンプルにします。 - routeは5xxのエラーコードをサーバが最初の
100 Continue
の後で100 Continue
を返した場合に返します。routerは無限の1xxストリームにまだ対応していません。 - routerは
100 Continue
が先行しているかいないかに関係なく終端ステータスが来た後にサーバとのコネクションをcloseします。dynoへのコネクションはkeep-aliveしません。 - routerは
100 Continue
が先行していない終端ステータスが来た後にクライアントへのコネクションをクローズします。これは次のリクエストを処理することができるサーバを保持する前に、とにかくリクエストボディを送信する必要があるクライアントを持つことを避けます。(意味分かってない、サーバ側が100 Continue
のリクエストボディを受ける前に次のリクエストを処理しないようにするということ?)
他のメカニズムもそのプロトコルをそのまま尊重するべきであり、routerはRFCによる仕様としてのリクエストを転送するべきである。
Http versions supported
Heroku routerはHTTP/1.0とHTTP/1.1だけをサポートしており、HTTP/0.9以下はもうサポートしていません。SPDYとHTTP/2.0は現時点ではサポートしていない。
routerの振る舞いはHTTP/1.1の仕様に出来るだけ従っているが、特例はHTTP/1.0のためになされなければならない。
- routerはたとえクライアントがHTTP/1.0が使っていようがいまいが、自身はHTTP/1.1を使うと示す。
- routerは細切れのレスポンスから通常のHTTPレスポンスへの必要な変換を取り扱う。(ギガバイト級のデータを扱うことを除いて)そうするために、クライアントへのレスポンスはコネクションの終了によって境界づけられる。(参照 (Point 4.4.5](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4))
- routerはクライアントがリクエストごとにコネクションをクローズしたいと決めつける(keep-aliveはない)
- HTTP/1.0クライアントは明示的に
connection:keep-alive
ヘッダを送ってくるかもしれない。keep-aliveメカニズムは1.0にバックポートされていないにもかかわらず、routerは、要求する振る舞いが現時点でのHTTP/1.1の振る舞いに似ていると仮定します。
HTTP validation and restrictions
Request validation:
- chunked encodingとcontent-lengthが両方存在する場合、chunked encodingを行う
- 複数のcontent-lengthヘッダーが存在し、同じ値の場合は一つにマージする
- content-lengthが複数の値を持つ、もしくは複数のcontent-lengthヘッダが存在する場合、400statusを返す
- ヘッダーは8192byte/行に制限されている(ヘッダー名は1000byteまで)
- Hop-by-hop ヘッダは混乱を避けるために剥ぎ取られる
- 最大1000ヘッダまで
- HTTPリクエストのRequest Line(リクエスト行?)は8192byteまで
- Request Lineはスペースで動詞(メソッド?),パス, HTTPバージョンを分割していることが期待されている
Response Validation:
- Hop-by-hop ヘッダは混乱を避けるために剥ぎ取られる
- ヘッダは512kb/行に制限されている
- Cookiesははっきりと8192byteに制限されている。これは大きなcookieをほとんど受け入れないような共通の制限(たとえばCDNが課すような)から保護するため。そういうケースでは開発者はふとしたことから大きなcookieをセットする、そしてそれはユーザーに返される、そしてその時になってユーザーはリクエストが拒否されたことを知る。
- ステータス行(
HTTP/1.1 200 OK
)は8192byteの長さに制限される
これらの制限を破ったアプリケーションは502 Bad Gatewayのレスポンスを伴って失敗となる、そしてH25 errorがログストリームに記述される。リクエスト制限を破ったクライアントは400 Bad Requestレスポンスを伴って失敗となる。
加えて、HTTP/1.1のリクエストとレスポンスがデフォルトでkeep-alive
を期待されているうちは、初期リクエストがはっきりとルーターからdynoに対してのconnection: close
ヘッダを持っていたら、dynoはcontent-encodingを指定していない、もしくははっきりとcontent-lengthを指定していないようなコネクション切断によって境界づけられたレスポンスを送ることが出来る。
Protocol upgrades
以前のHeroku RouterはHTTPプロトコルアップグレードをWebSocketのみに制限していた一方で、新しいrouterはすべてのアップグレートに寛大である。
特徴は実装に関係している
- どんなHTTP動詞(メソッド?)もアップグレード可能なコネクションを伴って使うことが出来る。
- HTTP
HEAD
はたいてい、すべての行(たとえばcontent-lengthに関して)を送るようなちゃんとしたレスポンスを要求しないにもかかわらず、HEAD
リクエストは101 Swithcing Protocols
を伴うレスポンスと連携するのに適している。アップグレードを求めないdynoは異なったステータスコードを送るべき、そしてコネクションはアップグレードしないだろう。
Not supported
- SPDY
- HTTP/2.x
100-continue
以外のコンテンツを伴ったExpect
ヘッダ(417が発生)- 上の方の100-continueの項を参照
- WEBDAVのようなHTTP拡張
- content-lengthやchunked encodingを伴ったプロキシされてないHEAD,1xx, 204, 304レスポンスは決してこないBodyを中継しようとする。
- CRLF(
\r\n
)以外のヘッダ - HTTPコンテンツのキャッシュ
- dyno上で動いているサーバのHTTPバージョンのキャッシュ
- 長時間事前割当されたアイドルコネクション。その上限はアイドルコネクションがクローズされる1分前に設定されます。
Host
ヘッダを伴わないHTTP/1.0リクエスト、それはfull URLがリクエスト行に送信されている時でさえも対応しません。