Rack
https://github.com/rack/rack/blob/master/lib/rack/sendfile.rb#L149
まず、課題となっている場所はここで、いまの実装はこんな感じ。
def map_accel_path(env, path)
if mapping = @mappings.find { |internal,_| internal =~ path }
path.sub(*mapping)
elsif mapping = env['HTTP_X_ACCEL_MAPPING']
internal, external = mapping.split('=', 2).map(&:strip)
path.sub(/^#{internal}/i, external)
end
end
nginx
設定はこう。「X-Accel-Mapping」を複数定義している。
server {
location ~ /foo/(.+) {
internal;
alias /path/to/real/foo/$1;
}
location ~ /bar/(.+) {
internal;
alias /path/to/real/bar/$1;
}
location / {
proxy_set_header X-Sendfile-Type X-Accel-Redirect;
proxy_set_header X-Accel-Mapping '/path/to/releases/\d{14}/abc/=/foo/';
proxy_set_header X-Accel-Mapping '/path/to/releases/\d{14}/def/=/bar/';
}
}
説明
複数の「X-Accel-Mapping」が定義されていると、Rackの「env[‘HTTP_X_ACCEL_MAPPING’]」に渡ってくる時は「, 」(カンマとスペース)で区切られた状態になる。
/path/to/releases/\d{14}/abc/=/foo/, /path/to/releases/\d{14}/def/=/bar/
こうなると、いまのRackの実装ではいきなり「=」でsplitしている(ひとつしか定義されていない前提)ため、結果として「internal」と「external 」はこんな感じになって、正しいパスを返せなくなる。
internal #=> '/path/to/releases/\d{14}/abc/'
external #=> '/foo/, /path/to/releases/\d{14}/def/=/bar/'
対策
そこで、複数の「X-Accel-Mapping」が定義されることも考慮して、この様にしてみた。
def map_accel_path(env, path)
if mapping = @mappings.find { |internal,_| internal =~ path }
path.sub(*mapping)
elsif mapping = env['HTTP_X_ACCEL_MAPPING']
mapping.split(',').map(&:strip).each do |m|
internal, external = m.split('=', 2).map(&:strip)
new_path = path.sub(/^#{internal}/i, external)
return new_path unless path == new_path
end
path
end
end
これで「X-Accel-Mapping」の数に関わらず、定義したパスを返せるようになった。
ので、PR出してみた。どうかな。
https://github.com/rack/rack/pull/1187