<commit_before># Cross Origin でカスタムヘッダを送受信する
<a id="top"></a>

Cross Origin でカスタムヘッダを送信する方法をまとめる。


###### CONTENTS

1. [sinatra でのサンプル](#sample-on-sinatra)
1. [preflight レスポンスヘッダ](#headers-for-options)
1. [コンテンツレスポンスヘッダ](#headers-for-contents)
1. [まとめ](#postscript)
1. [参考資料](#reference)


###### ENVIRONMENTS

- ruby : 2.6.1
- sinatra : 2.0.5


[TOP](#top)
<a id="sample-on-sinatra"></a>
### sinatra でのサンプル

まず sinatra でのサンプルを示す。

```ruby
require "sinatra"

options "*" do
  headers(
    "Access-Control-Allow-Origin" => "https://example.com",
    "Access-Control-Allow-Methods" => "GET,POST,PUT,DELETE,OPTIONS,HEAD",
    "Access-Control-Allow-Headers" => "Authorization",
  )
end

post "/upload" do
  halt 401 unless authorize(env["HTTP_AUTHORIZATION"]) # authorize by token

  halt 400 unless file = (params[:file] && params[:file][:tempfile])

  upload_id = upload file # process uploaded file...

  headers(
    "Access-Control-Allow-Origin" => "https://example.com",
    "Access-Control-Expose-Headers" => "X-Upload-ID",
    "X-Upload-ID" => upload_id.to_s,
  )

  content_type "application/json"
  { message: :ok }.to_json
end
```

ここでは以下の流れを想定している。

1. `https://example.com` で html を配信して、そこから ajax リクエストを受ける
1. Authorization ヘッダでトークンを受け取り、認証
1. ファイルアップロードの処理を行い、 ID を生成
1. 生成された ID を X-Upload-ID ヘッダで返す

これは「シンプルなリクエスト」ではないので、ブラウザは preflight リクエストを行う。


[TOP](#top)
<a id="headers-for-options"></a>
### preflight レスポンスヘッダ

「シンプルなリクエスト」以外のリクエストをする場合、ブラウザは preflight リクエストを行う。
preflight リクエストは対象のリソースに対して `OPTIONS` メソッドでリクエストの可否を問い合わせる。

この時、サーバーは以下のヘッダを含めて応答する。

- Access-Control-Allow-Origin : アクセスを許可する Origin
- Access-Control-Allow-Methods : アクセスを許可する Method
- Access-Control-Allow-Headers : 含めて良い Header

`Access-Control-Allow-Headers` に関してはを「シンプルなリクエストヘッダ」は常に許可されるので列挙する必要はない。

ブラウザは、このレスポンスヘッダで許可されたリクエストのみサーバーに発行する。


#### シンプルなリクエスト

「シンプルなリクエスト」は以下を満たす。

- 以下のメソッドのうち、いずれかである
  - HEAD
  - GET
  - POST
- 以下のヘッダ以外を含まない
  - Accept
  - Accept-Language
  - Content-Language
  - Content-Type
  - Last-Event-ID
- 以下の Content-Type のうち、いずれかである
  - application/x-www-form-urlencoded
  - multipart/form-data
  - text/plain


#### シンプルなリクエストヘッダ

- Accept
- Accept-Language
- Content-Language
- Content-Type は以下のうち、いずれかである
  - application/x-www-form-urlencoded
  - multipart/form-data
  - text/plain


[TOP](#top)
<a id="headers-for-contents"></a>
### コンテンツレスポンスヘッダ

コンテンツとして「シンプルなレスポンスヘッダ」以外を返したい場合、 `Access-Control-Expose-Headers` を含める必要がある。
これを含めないと、 `xhr.getResponseHeader` や `xhr.getAllResponseHeaders` でレスポンスヘッダを受け取ることができない。

ちなみにヘッダは case-insensitive なので、この例では `xhr.getResponseHeader("x-upload-id")` で取得できる。
<commit_msg>fix: cors-custom-header<commit_after># Cross Origin でカスタムヘッダを送受信する
<a id="top"></a>

Cross Origin でカスタムヘッダを送信する方法をまとめる。


###### CONTENTS

1. [sinatra でのサンプル](#sample-on-sinatra)
1. [preflight レスポンスヘッダ](#headers-for-options)
1. [レスポンスヘッダ](#headers-for-contents)
1. [まとめ](#postscript)
1. [参考資料](#reference)


###### ENVIRONMENTS

- ruby : 2.6.1
- sinatra : 2.0.5


[TOP](#top)
<a id="sample-on-sinatra"></a>
### sinatra でのサンプル

まず sinatra でのサンプルを示す。

```ruby
require "sinatra"

options "*" do
  headers(
    "Access-Control-Allow-Origin" => "https://example.com",
    "Access-Control-Allow-Methods" => "GET,POST,PUT,DELETE,OPTIONS,HEAD",
    "Access-Control-Allow-Headers" => "Authorization",
  )
end

post "/upload" do
  halt 401 unless authorize(env["HTTP_AUTHORIZATION"]) # authorize by token

  halt 400 unless file = (params[:file] && params[:file][:tempfile])

  upload_id = upload file # process uploaded file...

  headers(
    "Access-Control-Allow-Origin" => "https://example.com",
    "Access-Control-Expose-Headers" => "X-Upload-ID",
    "X-Upload-ID" => upload_id.to_s,
  )

  content_type "application/json"
  { message: :ok }.to_json
end
```

ここでは以下の流れを想定している。

1. `https://example.com` で html を配信して、そこから ajax リクエストを受ける
1. Authorization ヘッダでトークンを受け取り、認証
1. ファイルアップロードの処理を行い、 ID を生成
1. 生成された ID を X-Upload-ID ヘッダで返す

これは「シンプルなリクエスト」ではないので、ブラウザは preflight リクエストを行う。


#### シンプルなリクエスト

「シンプルなリクエスト」は以下を満たす。

- 以下のメソッドのうち、いずれかである
  - HEAD
  - GET
  - POST
- 以下のヘッダ以外を含まない
  - Accept
  - Accept-Language
  - Content-Language
  - Content-Type
  - Last-Event-ID
- 以下の Content-Type のうち、いずれかである
  - application/x-www-form-urlencoded
  - multipart/form-data
  - text/plain


[TOP](#top)
<a id="headers-for-options"></a>
### preflight レスポンスヘッダ

「シンプルなリクエスト」以外のリクエストをする場合、ブラウザは preflight リクエストを行う。
preflight リクエストは対象のリソースに対して `OPTIONS` メソッドでリクエストの可否を問い合わせる。

この時、サーバーは以下のヘッダを含めて応答する。

- Access-Control-Allow-Origin : アクセスを許可する Origin
- Access-Control-Allow-Methods : アクセスを許可する Method
- Access-Control-Allow-Headers : 含めて良い Header

`Access-Control-Allow-Headers` に関してはを「シンプルなリクエストヘッダ」は常に許可されるので列挙する必要はない。

ブラウザは、このレスポンスヘッダで許可されたリクエストのみサーバーに発行する。


#### シンプルなリクエストヘッダ

- Accept
- Accept-Language
- Content-Language
- Content-Type は以下のうち、いずれかである
  - application/x-www-form-urlencoded
  - multipart/form-data
  - text/plain


[TOP](#top)
<a id="headers-for-contents"></a>
### レスポンスヘッダ

「シンプルなレスポンスヘッダ」以外を返したい場合、 `Access-Control-Expose-Headers` を含める必要がある。
これを含めないと、 `xhr.getResponseHeader` や `xhr.getAllResponseHeaders` でレスポンスヘッダを受け取ることができない。

ちなみにヘッダは case-insensitive なので、この例では `xhr.getResponseHeader("x-upload-id")` で取得できる。

{'fraction_non_alphanumeric': 0.14897260273972604, 'fraction_numerical': 0.007990867579908675, 'mean_word_length': 4.522058823529412, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 31, 'https://': 6, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7375621', 'n_tokens_mistral': 2844, 'n_tokens_neox': 2759, 'n_words': 353}