一角獣は夜に啼く

ただの日記です。

思ってることとか考えたこととか適当に書きます。 主にソフトウェア開発の話題を扱う 「ひだまりソケットは壊れない」 というブログもやってます。

Android アプリや iOS アプリなどの内蔵 WebView に UA 文字列を設定することについて

Android アプリや iOS アプリの内蔵ブラウザ (WebView) に User Agent 文字列を設定したいことありますか。 ありませんか。

普通はないかもしれませんね。 でもまあ設定したいことがあるとしましょう。

例えばアプリ名が 「すごいアプリ」 だったとして、バージョンが 1.0 だったら、UA 文字列を 『SugoiApp/1.0』 とかにしちゃいますか? しちゃうかもしれませんね。 それで問題ないこともありますが問題があることもあります。

その WebView で表示しようとしたページが UA 文字列を見て何か挙動を変えるかもしれません。 一般的な UA 文字列ならばユーザーにとって良いように挙動が変えられる場合でも、『SugoiApp/1.0』 という特殊な UA 文字列に対しては変な挙動を示すかもしれません。 ええ、ええ、言いたいことはわかります。 そういう web ページを作るな、と。 まあまあそういうこともありますよ。 落ち着きましょう。 もしかしたら JS が UA を見て挙動を変えるかもしれませんね。 よくあることです。

さて、では我々はどうすべきなのでしょうか。 UA 文字列を変更しない? まあそれも一つです。 しかし、なんとしてでも UA 文字列の中にアプリの情報を入れたいとしましょう。

そういうときは、もともとの UA 文字列の末尾 (RFC 的には先頭の方がいいかも?) にアプリを表す製品トークンを追加すれば良さそうです。

以下詳細です。

RFC 2616 を見てみよう

そもそも UA 文字列って何? って話に戻りまして。 いま私が UA 文字列って言ってるのは HTTP リクエストヘッダの User-Agent フィールドに設定される値のことです。 HTTP のことなら RFC 2616 を見てみましょう。

User-Agent リクエストヘッダフィールドについては次のように書かれています。

The User-Agent request-header field contains information about the user agent originating the request. This is for statistical purposes, the tracing of protocol violations, and automated recognition of user agents for the sake of tailoring responses to avoid particular user agent limitations. User agents SHOULD include this field with requests. The field can contain multiple product tokens (section 3.8) and comments identifying the agent and any subproducts which form a significant part of the user agent. By convention, the product tokens are listed in order of their significance for identifying the application.

User-Agent     = "User-Agent" ":" 1*( product | comment )

Example:

User-Agent: CERN-LineMode/2.15 libwww/2.17b3
RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1

このフィールドは、統計に使用したり、プロトコル違反を追跡したり、UA の自動認識 (?) をしたりするといった目的のために使われるようです。

フィールドの値として複数の製品トークンを並べることができます。 で、『By convention, the product tokens are listed in order of their significance for identifying the application.』 とのことですよ。 上の例を見てもわかるように、慣習的にはアプリケーション固有の値を前に持ってくるみたいです。 (CERN-LineMode というアプリケーションの中で libwww ライブラリを使ってる、という風に読めます。)

Product Tokens

ちなみに製品トークンというのは何かというと、3.8 節に書かれています。

Product tokens are used to allow communicating applications to identify themselves by software name and version. Most fields using product tokens also allow sub-products which form a significant part of the application to be listed, separated by white space. By convention, the products are listed in order of their significance for identifying the application.

product         = token ["/" product-version]
product-version = token

Examples:

User-Agent: CERN-LineMode/2.15 libwww/2.17b3
Server: Apache/0.8.4
RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1

単に token ですね。 スラッシュに続けてバージョンを表す token を付けることもできます。 良く見る 「Mozilla/5.0」 というようなやつが 1 つの製品トークンです。

ちなみに token とは何かというと以下のような感じです。 英数字 + α、って感じの認識でいいんじゃないでしょうか。

token          = 1*<any CHAR except CTLs or separators>
CHAR           = <any US-ASCII character (octets 0 - 127)>
CTL            = <any US-ASCII control character
                 (octets 0 - 31) and DEL (127)>
separators     = "(" | ")" | "<" | ">" | "@"
               | "," | ";" | ":" | "\" | <">
               | "/" | "[" | "]" | "?" | "="
               | "{" | "}" | SP | HT
SP             = <US-ASCII SP, space (32)>
HT             = <US-ASCII HT, horizontal-tab (9)>
RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1

Comment について

User Agent フィールドの値には product だけでなく comment も含められます。

comment        = "(" *( ctext | quoted-pair | comment ) ")"
ctext          = <any TEXT excluding "(" and ")">
quoted-pair    = "\" CHAR
RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1

詳しく踏み込みはしませんが、User Agent 文字列の中によく出てくる括弧で囲まれた部分は comment ってことです。 「(compatible; MSIE 10.0; Windows NT 6.2; Trident/6.0)」 みたいなやつです。

というわけで、アプリの情報を UA 文字列の中に入れるなら

ここまで、

  • User Agent 文字列の値は複数の製品トークンからなり、
  • UA 文字列を見て挙動を変える web ページや JS などがある、

ということを見てきました。 これらのことから、アプリの情報を UA 文字列に入れるのであれば、WebView にもともと設定されている UA 文字列の製品トークンの中にアプリを表す製品トークンを追加する、というのが自然な発想だと思います。

さて、問題はどこに追加するかですが、もともとの UA 文字列の先頭か末尾に追加するのが良いでしょう。 間に入れるよりもはるかに楽ですからね。 先頭か末尾のどちらに入れるのがいいのか、というのも問題として残っていますね。 上で説明したように、慣習的にはアプリケーションを特定するために重要な製品トークンを前に置くようです。

ですが、多くの web ブラウザの UA 文字列を見ると、大抵 「Mozilla」 という文字列で始まっています。 これを崩すことはしない方がいいかもしれません。 (わかりません。)

また、Gecko の UA 文字列のリファレンス を見ると、「Mozilla」 「Gecko」 「Firefox」 という風に、アプリケーションを特定するための製品トークンが後ろの方に置かれています。 他のブラウザでもその傾向があるような気がします。

そんなわけなので、RFC で言う 「慣習」 とは違っていますが、広く使われている web ブラウザに倣って、WebView にもともと設定されている UA 文字列の末尾に、アプリを表すための製品トークンを追加するのが良さそうに思った次第です。

完。