HTTP1.1のLocationヘッダは、絶対URLでないとRFC違反

さきゅばす2のバグ対処で知ったお話です。とりあえず、HTTPの基礎知識はあることを前提にします。

さきゅばす2では、公式動画のDLがどうしても出来ませんでした。こんなエラーが出ちゃうんです。

[E][    PyBridgeImpl] Python says:
(略)
HTTPError: HTTP Error 302: Found - Redirection to url '/watch/1339405721' is not allowed

どうやらリダイレクションの段階で問題が発生してるみたいですねぇ。

HTTPでのリダイレクション

とあるページにアクセスした時に他のページにジャンプしなおさせる「リダイレクション」ですが、このリダレクションはHTTPはレスポンスヘッダで301,302,303,307のレスポンスコードを返した上で、Locationヘッダを設定することで実現しています。

どこかのページのレスポンスヘッダで、

Location: http://www.google.com/

こういうふうなヘッダを返すと、そのページにアクセスした人がGoogleに無条件で飛ばされるわけですね。

で、ニコニコ動画のリダイレクションでは、

Location: /watch/1339405721

となっていて、http://www.nicovideo.jp/watch/1339405721 にジャンプするようになっていました。うーん、特に問題なさそう。

Locationヘッダは絶対パスしか認められない。

…ところがどっこい、実はLocationヘッダのURLは絶対パスしか認められないのです。RFC見ましょうか。

Location       = "Location" ":" absoluteURI

で、このabsoluteURIはRFC2396: Uniform Resource Identifiers (URI): Generic Syntaxで定義されておりまして、

absoluteURI   = scheme ":" ( hier_part | opaque_part )

ということで、URIスキーム(httpとかftpとか)から始まる絶対URIであることがわかります。

とはいえ、色々調べてみると、携帯とか一部のブラウザを除くと大体相対パスでも動くそうで、絶対URIを作成時に予測するのが難しい、他人に配布するスクリプトなどでは相対パスで指定していることが多いそうです。

 

ニコニコ動画の場合は絶対URIでも良いと思うのですが今回は相対パスで指定されていて、ふつうのブラウザなどではこれで意図通りに動くのですが、さきゅばす2のPythonでは最近のバージョンまで相対パスに対応していなかったため問題になったようです。で、最新のバージョンでは相対パスに対応しているので、配布バイナリを最新バージョンに差し替えることにしました。

で、ほかのヘッダではどうなってるの?

HTTPのヘッダには、ほかにもいくつかパスを指定できるものがありましたよね。いくつか調べてみました。

Request-URI

リクエストURIは、GET ****** HTTP/1.1みたいな形式で指定されるときの、あのURIです。

Request-URI    = "*" | absoluteURI | abs_path | authority

なんと…。実は絶対URI指定でも良いのですね。たまに絶対URIでのリクエストがこのサーバに飛んでくるのですが、てっきり不正なリクエストなんだと思ってました。…とはいえ、ほとんどのクライアントは絶対パスで取得するのがふつうなので、ちょっと警戒すべきだとは思いますけど…。

 

Content-Location:

Locationと似てますが別物です。リクエストされたコンテンツが別のところからも取得可能な時に使われる…そうですが、実際使われてるのを見たことがありません…。だれか教えて(ぇ

Content-Location = "Content-Location" ":"
( absoluteURI | relativeURI )

こっちは相対URIも可能です。同じサーバであることが自然だからなのかなあ。

Referer

リファラです。ほかのページのリンクなどをたどって来たことを示すわけですが…

Referer        = "Referer" ":" ( absoluteURI | relativeURI )

なんと!これも相対URIはOKでした。とはいえ、世の中のクライアントは大体絶対URIじゃないでしょうか?

以上です。たまにはRFC読んでみると意外な発見がいろいろありますね。

追記:RFC7321では相対URLが認められている

その後2012に出たRFC7231では相対URLもOKになりました。以下、引用です:

7.1.2. Location

   The "Location" header field is used in some responses to refer to a
   specific resource in relation to the response.  The type of
   relationship is defined by the combination of request method and
   status code semantics.

     Location = URI-reference

   The field value consists of a single URI-reference.  When it has the
   form of a relative reference ([RFC3986], Section 4.2), the final
   value is computed by resolving it against the effective request URI
   ([RFC3986], Section 5).

   For 201 (Created) responses, the Location value refers to the primary
   resource created by the request.  For 3xx (Redirection) responses,
   the Location value refers to the preferred target resource for
   automatically redirecting the request.

   If the Location value provided in a 3xx (Redirection) response does
   not have a fragment component, a user agent MUST process the
   redirection as if the value inherits the fragment component of the
   URI reference used to generate the request target (i.e., the
   redirection inherits the original reference's fragment, if any).

   For example, a GET request generated for the URI reference
   "http://www.example.org/~tim" might result in a 303 (See Other)
   response containing the header field:

     Location: /People.html#tim

   which suggests that the user agent redirect to
   "http://www.example.org/People.html#tim"