rack-cors (1.0.2)のコードリーディングをしました。

その名の通りRackミドルウェアになっていてRack::Corsを明示的にミドルウェアとして設定する必要があります。

ということでRack::Corsの中身を見ていきます。lib/rack/cors.rbの1ファイルで全て構成されているので分割して説明します。

まず初期化時にblockを渡すとRack::Corsのコンテキストでinstance_evalされます。

arityなしの場合、allowを呼び出すとResourcesオブジェクトのコンテキストでinstance_evalします。

Resources#originsは以下のように定義されています。例えば example.comを定義した場合は@originsに /^[a-z][a-z0-9.+-]*:\/\/example\.com$/ がセットされます。

#resourceはResourceのオブジェクトを@resourcesに追加しています。設定は以上になります。

次にHTTPリクエスト時のミドルウェアの動きを追っていきます。Originヘッダの中身はenv[HTTP_ORIGIN]にセットされます。リクエストメソッドがOPTIONSでAccess-Control-Request-Methodヘッダが設定されている場合(=preflight request)、process_preflightでレスポンスヘッダを生成してステータスコード200で返します。

#process_preflightは#match_resourceでResourceのインスタンスを取得し、Resource#process_preflightを呼び出します。

Resource#match_resouceは設定されているResourcesのインスタンスに対してResources#allow_origin?を呼び出し、trueであればさらにResources#match_resourceを呼び出します。

originに*が設定されている場合はpublic_resources? = trueになるので、戻り値としてtrueを返します。そうでなければoriginsの各値とHTTP_ORIGINの値を===で比較します。ドメインを指定した場合は、ドメインに対応する正規表現との比較になります。

Resources#match_resourceは各リソースに対してResource#match?を呼び出します。

patternはResourceのpathから生成された正規表現になります。例えば /hogeのパスを指定した場合は/^\/hoge$/が正規表現として設定されます。よって正規表現のマッチングによってCORSに対応したパスかどうかを判定します。

CORに対応したオリジン、パスの場合はさらにResource#process_preflightが呼ばれ、preflightリクエストのレスポンスヘッダを生成します。

methods.include?でメソッドが対応しているかや、Access-Control-Request-Headersのヘッダが許可されているかを判定します。どちらも対応している場合は#to_preflight_headersがレスポンスヘッダに追加されて返されます。

#to_preflight_headersは#to_headersを呼び出しています。以下のようなハッシュを返しており、これがpreflight requestのレスポンスヘッダになります

preflight requestでないリクエストの場合はRack::Cors#process_corsが呼ばれます。#match_resouceでResourceを検索し、マッチした場合はResource#to_headersによってレスポンスヘッダが追加されます。