Skip to content

Instantly share code, notes, and snippets.

@dovg
Created March 11, 2012 11:45
Show Gist options
  • Select an option

  • Save dovg/2016156 to your computer and use it in GitHub Desktop.

Select an option

Save dovg/2016156 to your computer and use it in GitHub Desktop.
<img src="http://habrastorage.org/storage2/af2/2ce/a48/af22cea48ee03714dc205fc3e7c8072a.png" align="right" />
Если правильно собирать и интерпретировать заголовки, то можно сказать очень многое о устройстве, а возможно и о самом пользователе. В этой статье я расскажу как мы в Wapstart используем сведения, передаваемые в http-заголовках.
Дисклаймер - статья носит обзорный характер. Некоторые вещи могут показаться сообществу слишком "<a href="http://lurkmore.to/Капитан_Очевидность">капитанистыми</a>".
Начну с основ:
Как правило, внутри веба взаимодействие производит по <a href="http://tools.ietf.org/html/rfc2616">протоколу http</a>.
<habracut />
Минимальный валидный http запрос методом GET выглядит так:
<source>
GET / HTTP/1.0\r\n
\r\n
</source>
или так:
<source>
GET / HTTP/1.1\r\n
Host: wapstart.ru\r\n
\r\n
</source>
Заголовок - это пара: наименование поля и его значение, разделенные двоеточием. Подробнее - в <a href="http://tools.ietf.org/html/rfc2616#section-4.2">rfc</a>.
Как правило, браузеры передают некоторые дополнительные заголовки, которые могут быть описаны в rfc, а могут и не быть. :)
Почти всегда будет передан заголовок User-agent, при работе через прокси так же будут добавлены заголовки via или x-forwarder-for. Строго говоря, rfc <a href="http://tools.ietf.org/html/rfc2068#section-7.1">не запрещает</a> вам передавать свои заголовки, он просто говорит, что их следует игнорировать.
Например, вот такой запрос все еще валиден:
<source>
GET / HTTP/1.1
Host: wapstart.ru
User-agent: dovg
x-ololo: trololo
x-habrauser: dovg
</source>
В ваше приложение заголовки попадут скорее всего уже в виде, который определется в rfc на <a href="http://www.ietf.org/rfc/rfc3875">cgi протокол</a>. (раздел 4.1)
Грубо говоря, к ним добавится указание протокола (HTTP), они будут переведены в верхний регистр, а минусы (дефисы) будут заменены на знак нижнего подчеркивания: x-habrauser превратится в HTTP_X_HABRAUSER, например. Значения при этом изменений не претерпят.
В реальном мире очень много дополнительных заголовков добавляет opera-mini, а так же стандартный браузер телефонов Nokia.
Вернемся к нашим баранам.
Мы занимаемся рекламой в мобильном вебе, поэтому одна из наших первоочередных задач - это отделение условно "мобильных пользователей" от немобильных.
Само собой, данная задача не может быть решена со 100% эффективностью, т.к. информация формируется на стороне клиента, а как мы знаем, никаким пользовательским данным нельзя доверять.
Помимо определения "мобильности" пользователя, мы хотим знать следующее:
<ul>
<li>информация об устройстве пользователя (разрешение экрана, наличие wifi и т.д.);</li>
<li>операционная система, которая управляет устройством;</li>
<li>браузер (приложение) из которого сделан запрос.</li>
</ul>
Эти данные позволяют нам показывать более таргетированную, а значит и интересную рекламу пользователю.
Начнем с первой цели - как понять "мобильность" пользователя. Когда-то давным давно мы написали <a href="https://github.com/Wapstart/stuff/blob/master/WebWapInterfaceChooser.php">такой скрипт</a>.
Я привожу реализацию на php, но используемые тут алгоритмы настолько тривиальны, что скрипт может быть портирован хоть в конфиг nginx. У нас, кстати, была идея делать это на уровне nginx, но до ее реализации руки так и не дошли.
В этой статье код я приводить не буду, он есть на github. Важная ремарка - целиком и полность доверять только одному user-agent нельзя!
Для остальных задач мы придумали базу gdi (Get Device Info), которая в настоящее время умеет доставать информацию об устройстве, os и браузере по совокупности http заголовков.
Для нас интерфейс выглядит следующим образом -
<source>
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, но это уже отдельная история.
</source>
Подробнее о взаимодействии, надеюсь, получится рассказать на devconf вот <a href="http://devconf.ru/offers/6">в этом</a> докладе.
Известные проблемы, которые мы пока не можем решить:<ul>
<li>Apple устройства (Iphone, Ipad, Ipod) передают только сведения о версии операционной системы, но не о модели устройства. Другими словами, имея http запрос из стандартного браузера нельзя сказать с какого Iphone он сделан. С точки зрения передаваемых заголовков 3gs и 4g будут выглядеть одинаково. Да, мы знаем, что это можно решить средствами js.</li>
<li>Некоторые сборки opera-mini режут (заменяют) всю информацию о телефоне.</li>
</ul>
Ну и напоследок немного статистики по заголовкам в нашей базе:
<source>
gdi=> select count(distinct name) from request;
count
-------
134
(1 row)
</source>
<source>
gdi=> select count(*) from request;
count
--------
651655
(1 row)
</source>
<source>
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)
</source>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment