nginxとRackとX-Accel-Mappingと。

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

タイトルとURLをコピーしました