В один прекрасный момент приложение на нашем production-сервере (написанное на Ruby On Rails), начало докучать нам не страшными, но противными Exception’ами. Выглядело это так:
CollectionsController# (ActionView::MissingTemplate) “Missing template collections/show, application/show with {:handlers=>[:erb, :builder, :haml], :formats=>[\"*/*;q=0.9\"], :locale=>[:en, :en]}.
В ходе расследования было выяснено, что такие запросы слал бот со следующим User-agent:
DoCoMo/2.0 N905i(c100;TB;W24H16) (compatible; Googlebot-Mobile/2.1; +http://www.google.com/bot.html)
и HTTP-заголовком “Accept */*;q=0.9″. Как известно в заголовок Accept можно выставлять несколько mime-types, добавляя к ним параметр q (quality), который указывает веб-серверу “предпочтительность” одного типа перед другими. Впрочем, об этом лучше почитать в соотвествующем RFC. Поэтому вызвало недоумение наличие параметра q вместе с только одним mime-type. Хоть этот параметр и бессмысленен, но RFC 2616 ничего не говорит о таком случае. Оставим на совести google-бота такое непонятное поведение. Так или иначе, с этим нужно было что-то делать.
В недрах Rails был найден следующий код, отвечающий за разбор заголовка Accept в actionpack/lib/action_dispatch/http/mime_type.rb (актуально для ветки 3.1):
-
def parse(accept_header)
-
if accept_header !~ /,/
-
if accept_header =~ TRAILING_STAR_REGEXP
-
parse_data_with_trailing_star($1)
-
else
-
[Mime::Type.lookup(accept_header)]
-
end
-
else
-
# keep track of creation order to keep the subsequent sort stable
-
list, index = [], 0
-
accept_header.split(/,/).each do |header|
-
params, q = header.split(/;\s*q=/)
-
…
Как видно split для header’a делается только в случае если майм-тайпов было несколько (accept_header !~ /,/), в противном выполнялось слудующее: Mime::Type.lookup(“*/*;q=0.9″). В этом-то и проблема. Не похоже, чтобы эта проблема сильно докучала общественность, но тем был найден тикет на Rails lighthouse более чем годичной давности с патчем, но его как видно не приняли. Было решено написать pull-request, впрочем тоже проигнорированный на данный момент(его можно посмотреть и проголосовать здесь). Нужено было искать какой-нибудь workaround, в ходе которого родился следующий middleware: https://github.com/uzzz/rails-accept-header-fix.
P.S. Воспроизвести можно с помощью:
curl -H "Accept:*/*;q=0.9" -I "http://example.com"
Макс Жилинский