Если правильно собирать и интерпретировать заголовки, то можно сказать очень многое об устройстве, а возможно и о самом пользователе. В этой статье я расскажу как мы в Wapstart используем сведения, передаваемые в http-заголовках.
Дисклаймер — статья носит обзорный характер. Некоторые вещи могут показаться сообществу слишком "капитанистыми".
Начну с основ:
Как правило, внутри веба взаимодействие производит по протоколу http.
Минимальный валидный http запрос методом GET выглядит так:
GET / HTTP/1.0rn
rn
или так:
GET / HTTP/1.1rn
Host: wapstart.rurn
rn
Заголовок — это пара: наименование поля и его значение, разделенные двоеточием. Подробнее — в rfc.
Как правило, браузеры передают некоторые дополнительные заголовки, которые могут быть описаны в rfc, а могут и не быть. :)
Почти всегда будет передан заголовок User-agent, при работе через прокси так же будут добавлены заголовки via или x-forwarder-for. Строго говоря, rfc не запрещает вам передавать свои заголовки, он просто говорит, что их следует игнорировать.
Например, вот такой запрос все еще валиден:
GET / HTTP/1.1rn
Host: wapstart.rurn
User-agent: dovgrn
x-ololo: trololorn
x-habrauser: dovgrn
rn
В ваше приложение заголовки попадут скорее всего уже в виде, который определется в rfc на cgi протокол. (раздел 4.1)
Грубо говоря, к ним добавится указание протокола (HTTP), они будут переведены в верхний регистр, а минусы (дефисы) будут заменены на знак нижнего подчеркивания: x-habrauser превратится в HTTP_X_HABRAUSER, например. Значения при этом изменений не претерпят.
В реальном мире очень много дополнительных заголовков добавляет opera-mini, а так же стандартный браузер телефонов Nokia.
Вернемся к нашим задачам.
Мы занимаемся рекламой в мобильном вебе, поэтому одна из наших первоочередных задач — это отделение условно «мобильных пользователей» от немобильных.
Само собой, данная задача не может быть решена со 100% эффективностью, т.к. информация формируется на стороне клиента, а как мы знаем, никаким пользовательским данным нельзя доверять.
Помимо определения «мобильности» пользователя, мы хотим знать следующее:
- информация об устройстве пользователя (разрешение экрана, наличие wifi и т.д.);
- операционная система, которая управляет устройством;
- браузер (приложение) из которого сделан запрос.
Эти данные позволяют нам показывать более таргетированную, а значит и интересную пользователю рекламу.
Начнем с первой цели — как понять «мобильность» пользователя. Когда-то давным-давно мы написали такой скрипт.
Я привожу реализацию на php, но используемые тут алгоритмы настолько тривиальны, что скрипт может быть портирован хоть в конфиг nginx. У нас, кстати, была идея делать это на уровне nginx, но до ее реализации руки так и не дошли.
В этой статье код я приводить не буду, он есть на github. Важная ремарка — целиком и полность доверять только одному user-agent нельзя!
Для остальных задач мы придумали базу gdi (Get Device Info), которая в настоящее время умеет доставать информацию об устройстве, os и браузере по совокупности http заголовков.
Для нас интерфейс выглядит следующим образом —
get header:HTTP_ACCEPT_ENCODING=gzip%2C+deflate&HTTP_USER_AGENT=Opera%2F9.80+%28J2ME%2FMIDP%3B+Opera+Mini%2F6.24093%2F27.1324%3B+U%3B+ru%29+Presto%2F2.8.119+Version%2F11.10&HTTP_X_OPERAMINI_FEATURES=advanced%2C+file_system%2C+camera%2C+touch%2C+folding%2C+routing&HTTP_USER_AGENT=LG+%23+KP500&HTTP_USER_AGENT=LG-KP500+Teleca%2FWAP2.0+MIDP-2.0%2FCLDC-1.1
VALUE header:HTTP_ACCEPT_ENCODING=gzip%2C+deflate&HTTP_USER_AGENT=Opera%2F9.80+%28J2ME%2FMIDP%3B+Opera+Mini%2F6.24093%2F27.1324%3B+U%3B+ru%29+Presto%2F2.8.119+Version%2F11.10&HTTP_X_OPERAMINI_FEATURES=advanced%2C+file_system%2C+camera%2C+touch%2C+folding%2C+routing&HTTP_USER_AGENT=LG+%23+KP500&HTTP_USER_AGENT=LG-KP500+Teleca%2FWAP2.0+MIDP-2.0%2FCLDC-1.1 0 234
O:12:"CuttedDevice":6:{s:5:"*id";i:3027;s:7:"*name";s:5:"KP500";s:10:"*deleted";b:0;s:9:"*parent";O:18:"CuttedDeviceParent":2:{s:5:"*id";i:23;s:7:"*name";s:2:"LG";}s:14:"*screenWidth";i:240;s:15:"*screenHeight";i:400;}
//Да, нам удобен протокол memcache ;)
//На самом деле внутри этого чуда совсем не memcache, но это уже отдельная история.
Подробнее о взаимодействии, надеюсь, получится рассказать на devconf вот в этом докладе.
Известные проблемы, которые мы пока не можем решить:
- Apple устройства (Iphone, Ipad, Ipod) передают только сведения о версии операционной системы, но не о модели устройства. Другими словами, имея http запрос из стандартного браузера нельзя сказать с какого Iphone он сделан. С точки зрения передаваемых заголовков 3gs и 4g будут выглядеть одинаково. Да, мы знаем, что это можно решить средствами js.
- Некоторые сборки opera-mini режут (заменяют) всю информацию о телефоне.
Ну и напоследок немного статистики по заголовкам в нашей базе:
gdi=> select count(distinct name) from request;
count
-------
134
(1 row)
gdi=> select count(*) from request;
count
--------
651655
(1 row)
gdi=> select name, count(value) as different_values from request group by name order by different_values desc limit 10;
name | different_values
---------------------------+------------------
HTTP_USER_AGENT | 648494
HTTP_X_WAP_PROFILE | 701
HTTP_X_OPERAMINI_PHONE_UA | 698
HTTP_VIA | 572
HTTP_X_PROXY_ID | 245
HTTP_X_OPERAMINI_FEATURES | 184
HTTP_X_OPERAMINI_PHONE | 109
HTTP_X_MSISDN | 96
HTTP_X_BLUECOAT_VIA | 84
HTTP_X_DEVICE_USER_AGENT | 77
(10 rows)
Автор: