Created
March 11, 2012 11:45
-
-
Save dovg/2016156 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <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