Rails3/4のリクエストヘッダー実装の違いを調べてみた

* Rails3と4のrequest.headers周りを調べることがあったのでメモっておく
    * コードリーディングとして楽しかったｗ

## 比較
### Rails3
```rb:actionpack-3.2.22/lib/action_dispatch/http/headers.rb
      1 module ActionDispatch
      2   module Http
      3     class Headers < ::Hash
      4       @@env_cache = Hash.new { |h,k| h[k] = "HTTP_#{k.upcase.gsub(/-/, '_')}" }
      5 
      6       def initialize(*args)
      7 
      8         if args.size == 1 && args[0].is_a?(Hash)
      9           super()
     10           update(args[0])
     11         else
     12           super
     13         end
     14       end
     15 
     16       def [](header_name)
     17         if include?(header_name)
     18           super
     19         else
     20           super(env_name(header_name))
     21         end
     22       end
     23 
     24       private
     25         # Converts a HTTP header name to an environment variable name.
     26         def env_name(header_name)
     27           @@env_cache[header_name]
     28         end
     29     end
     30   end
     31 end
```

* リクエストヘッダを@envに格納し、\[]()で呼び出される際に2種類のキーで関連付けられた値を探します
    * 探す順番
        1. 元のキーの値
        2. HTTP_xxxに変換した値
    * 例えばrequest.headers['Hogehoge']を実行すると'Hogehoge','HTTP_HOGEHOGE'の順に@envから探され先にヒットしたほうが返ります
* []=が定義されておらず、親クラスのHash#[]=が実行されます
    * request.headers['Hogehoge']='Hogehoge' としても request.headers['HTTP_HOGEHOGE'] はnilとなります



### Rails4

```rb:actionpack-4.1.11/lib/action_dispatch/http/headers.rb
      1 module ActionDispatch
      2   module Http
      3     class Headers
      4       CGI_VARIABLES = %w(
      5         CONTENT_TYPE CONTENT_LENGTH
      6         HTTPS AUTH_TYPE GATEWAY_INTERFACE
      7         PATH_INFO PATH_TRANSLATED QUERY_STRING
      8         REMOTE_ADDR REMOTE_HOST REMOTE_IDENT REMOTE_USER
      9         REQUEST_METHOD SCRIPT_NAME
     10         SERVER_NAME SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE
     11       )
     12       HTTP_HEADER = /\A[A-Za-z0-9-]+\z/
     ...
     17       def initialize(env = {})
     18         @env = env
     19       end
     ...
     21       def [](key)
     22         @env[env_name(key)]
     23       end
     24 
     25       def []=(key, value)
     26         @env[env_name(key)] = value
     27       end
     ...
     54       private
     55       def env_name(key)
     56         key = key.to_s
     57         if key =~ HTTP_HEADER
     58           key = key.upcase.tr('-', '_')
     59           key = "HTTP_" + key unless CGI_VARIABLES.include?(key)
     60         end
     61         key
     62       end
     63     end
     64   end
     65 end
```

* @envにリクエストヘッダが格納されるのはRails3と同じですが\[]()の実装が違っています
    * 常にHTTP_xxxの方で関連付けられた値を求めるようになっています
    * request.headers['Hoge']とrequest.headers['HTTP_HOGE']は常に同じ結果となります
* []=が定義されています
    * Rails4ではkeyを"HTTP_*"形式に変換します
    * request.headers['Hoge']='hoge' で設定した値が request.headers['HTTP_HOGE'] で取得できます

## まとめ
* 原則的にはRails3,4で同じ使い方が出来ますが以下の様な場合に差が出てきます
    * request.headers['Hoge']='hoge';request.headeres['HTTP_HOGE'] の結果が違ってきます
        * 上記の通り
        * Rails3はnilとなります
    * テストなどでhedears中に'Hoge','HTTP_HOGE'が両方が設定された場合
        * Rails3ではrequest.headers['Hoge'], request.headers['HTTP_HOGE'] でそれぞれの値が取得できます
        * Rails4では常に 'HTTP_HOGE' で設定した値が取得されます
        * テストなどで狙って書かない限りはこのようにはならないはずです、もしテストでリクエストヘッダーを弄る場合は"HTTP_*"形式で書いておくと不要なトラブルを避けられそうです

### 感想
* ActionDispatchctionDispatch::Http::Headers#[]=が定義されたのが良いですね！
* Rails3で許されていた曖昧な書き方がなくなり、Rails4の方がミスりにくくなっているように思います