Обнаружение устройств
Обнаружение устройств подразумевает определение типа контента, который следует предоставить клиенту, основываясь на строке User-Agent, предоставленной в запросе.
Примеры использования включают отправку файлов меньшего размера мобильным клиентам с маленькими экранами и медленными сетями или предоставление кодека потокового видео, который понимает клиент.
Существует несколько типичных стратегий для таких сценариев: 1) Переадресация на другой URL. 2) Использование другого бэкэнда для специального клиента. 3) Изменение запроса бэкэнда, чтобы бэкэнд отправлял настраиваемый контент.
Для упрощения понимания стратегий, в данном контексте мы предполагаем, что заголовок req.http.X-UA-Device присутствует и уникален для каждого класса клиентов.
Установка этого заголовка может быть такой же простой, как:
sub vcl_recv {
if (req.http.User-Agent ~ "(?i)iphone" {
set req.http.X-UA-Device = "mobile-iphone";
}
}
Существует множество коммерческих и бесплатных решений для группировки и идентификации клиентов более подробно. Для базового и основанного на сообществе набора регулярных выражений см. https://github.com/varnishcache/varnish-devicedetect/.
Предоставление разного контента по одному URL
Включаемые приемы: 1. Обнаружение клиента (довольно просто, просто включите devicedetect.vcl и вызовите его). 2. Определение способа передачи информации о классе клиента бэкэнду. Это включает, например, установку заголовка, изменение заголовка или даже изменение URL-адреса запроса бэкэнда. 3. Изменение любого ответа от бэкэнда для добавления недостающих заголовков «Vary», чтобы внутренняя обработка Varnish заработала. 4. Изменение данных, отправляемых клиенту, чтобы внешние кэши не предоставляли неверный контент.
Все это необходимо делать, гарантируя, что мы получаем только один кэшированный объект на URL для каждого класса устройств.
Пример 1: Отправка HTTP-заголовка бэкэнду
В базовом случае Varnish добавляет HTTP-заголовок «X-UA-Device» к запросам бэкэнда, а бэкэнд указывает в заголовке ответа «Vary», что контент зависит от этого заголовка.
Все работает в автоматическом режиме со стороны Varnish.
VCL:
sub vcl_recv {
# call some detection engine that set req.http.X-UA-Device
}
# req.http.X-UA-Device is copied by Varnish into bereq.http.X-UA-Device
# so, this is a bit counterintuitive. The backend creates content based on
# the normalized User-Agent, but we use Vary on X-UA-Device so Varnish will
# use the same cached object for all U-As that map to the same X-UA-Device.
#
# If the backend does not mention in Vary that it has crafted special
# content based on the User-Agent (==X-UA-Device), add it.
# If your backend does set Vary: User-Agent, you may have to remove that here.
sub vcl_backend_response {
if (bereq.http.X-UA-Device) {
if (!beresp.http.Vary) { # no Vary at all
set beresp.http.Vary = "X-UA-Device";
} elseif (beresp.http.Vary !~ "X-UA-Device") { # add to existing Vary
set beresp.http.Vary = beresp.http.Vary + ", X-UA-Device";
}
}
# comment this out if you don't want the client to know your
# classification
set beresp.http.X-UA-Device = bereq.http.X-UA-Device;
}
# to keep any caches in the wild from serving wrong content to client #2
# behind them, we need to transform the Vary on the way out.
sub vcl_deliver {
if ((req.http.X-UA-Device) && (resp.http.Vary)) {
set resp.http.Vary = regsub(resp.http.Vary, "X-UA-Device", "User-Agent");
}
}
Пример 2: Нормализация строки User-Agent
Другой способ обозначения типа устройства — переопределение или нормализация заголовка «User-Agent», отправляемого бэкэнду.
Например:
User-Agent: Mozilla/5.0 (Linux; U; Android 2.2; nb-no; HTC Desire Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1
превращается в:
User-Agent: mobile-android
при получении бэкэндом.
Это работает, если вам не нужен исходный заголовок для чего-либо на бэкэнде. Возможным применением этого является использование CGI-скриптов, где для скрипта по умолчанию доступен только небольшой набор предопределенных заголовков.
VCL:
sub vcl_recv {
# call some detection engine that set req.http.X-UA-Device
}
# override the header before it is sent to the backend
sub vcl_miss { if (req.http.X-UA-Device) { set req.http.User-Agent = req.http.X-UA-Device; } }
sub vcl_pass { if (req.http.X-UA-Device) { set req.http.User-Agent = req.http.X-UA-Device; } }
# standard Vary handling code from previous examples.
sub vcl_backend_response {
if (bereq.http.X-UA-Device) {
if (!beresp.http.Vary) { # no Vary at all
set beresp.http.Vary = "X-UA-Device";
} elseif (beresp.http.Vary !~ "X-UA-Device") { # add to existing Vary
set beresp.http.Vary = beresp.http.Vary + ", X-UA-Device";
}
}
set beresp.http.X-UA-Device = bereq.http.X-UA-Device;
}
sub vcl_deliver {
if ((req.http.X-UA-Device) && (resp.http.Vary)) {
set resp.http.Vary = regsub(resp.http.Vary, "X-UA-Device", "User-Agent");
}
}
Пример 3: Добавление класса устройства в качестве параметра GET
Если все остальное не работает, вы можете добавить тип устройства в качестве аргумента GET.
http://example.com/article/1234.html –> http://example.com/article/1234.html?devicetype=mobile-iphone
Сам клиент не видит эту классификацию, изменяется только запрос бэкэнда.
VCL:
sub vcl_recv {
# call some detection engine that set req.http.X-UA-Device
}
sub append_ua {
if ((req.http.X-UA-Device) && (req.method == "GET")) {
# if there are existing GET arguments;
if (req.url ~ "\?") {
set req.http.X-get-devicetype = "&devicetype=" + req.http.X-UA-Device;
} else {
set req.http.X-get-devicetype = "?devicetype=" + req.http.X-UA-Device;
}
set req.url = req.url + req.http.X-get-devicetype;
unset req.http.X-get-devicetype;
}
}
# do this after vcl_hash, so all Vary-ants can be purged in one go. (avoid ban()ing)
sub vcl_miss { call append_ua; }
sub vcl_pass { call append_ua; }
# Handle redirects, otherwise standard Vary handling code from previous
# examples.
sub vcl_backend_response {
if (bereq.http.X-UA-Device) {
if (!beresp.http.Vary) { # no Vary at all
set beresp.http.Vary = "X-UA-Device";
} elseif (beresp.http.Vary !~ "X-UA-Device") { # add to existing Vary
set beresp.http.Vary = beresp.http.Vary + ", X-UA-Device";
}
# if the backend returns a redirect (think missing trailing slash),
# we will potentially show the extra address to the client. we
# don't want that. if the backend reorders the get parameters, you
# may need to be smarter here. (? and & ordering)
if (beresp.status == 301 || beresp.status == 302 || beresp.status == 303) {
set beresp.http.location = regsub(beresp.http.location, "[?&]devicetype=.*$", "");
}
}
set beresp.http.X-UA-Device = bereq.http.X-UA-Device;
}
sub vcl_deliver {
if ((req.http.X-UA-Device) && (resp.http.Vary)) {
set resp.http.Vary = regsub(resp.http.Vary, "X-UA-Device", "User-Agent");
}
}
Разный бэкэнд для мобильных клиентов
Если у вас есть другой бэкэнд, который обслуживает страницы для мобильных клиентов, или есть особые потребности в VCL, вы можете использовать заголовок «X-UA-Device» следующим образом:
backend mobile {
.host = "10.0.0.1";
.port = "80";
}
sub vcl_recv {
# call some detection engine
if (req.http.X-UA-Device ~ "^mobile" || req.http.X-UA-device ~ "^tablet") {
set req.backend_hint = mobile;
}
}
sub vcl_hash {
if (req.http.X-UA-Device) {
hash_data(req.http.X-UA-Device);
}
}
Перенаправление мобильных клиентов
Если вы хотите перенаправить мобильных клиентов, вы можете использовать следующий фрагмент.
VCL:
sub vcl_recv {
# call some detection engine
if (req.http.X-UA-Device ~ "^mobile" || req.http.X-UA-device ~ "^tablet") {
return(synth(750, "Moved Temporarily"));
}
}
sub vcl_synth {
if (obj.status == 750) {
set obj.http.Location = "http://m.example.com" + req.url;
set obj.status = 302;
return(deliver);
}
}
Copyright © 2006 Verdens Gang AS
Copyright © 2006–2020 Varnish Software AS
Licensed under the BSD-2-Clause License.
https://varnish-cache.org/docs/7.4/users-guide/devicedetection.html