Как передать данные (заголовок?) в Varnish 4.0

Я использую devicedetect.vcl для отправки заголовка X-UA-Device моему приложению, поэтому оно знает, какой макет отображать. Возможные значения, которые задает для этого заголовка лак: mobile или desktop.

На выходе этот заголовок преобразуется в Vary: User-Agent.

Теперь, как отдельный изолированный проект, мне нужно установить другой заголовок для объекта resp (который отправляется нашему прокси-серверу Golang до того, как он будет отправлен клиенту). Этот заголовок будет называться X-Analytics-Device и будет иметь возможные значения bot, mobile, tablet или desktop.

Бэкенд-серверу не нужно что-либо делать с X-Analytics-Device. Только наш прокси-сервер Go будет анализировать, а затем удалять этот заголовок перед отправкой клиенту.

Проблема в том, что мне нужно установить заголовок X-Analytics-Device на основе результатов подпрограммы call devicedetect;, которая находится в vcl_recv. В конечном итоге мне нужно установить его на resp, который находится в vcl_deliver, и мне нужно знать, как лучше всего передать данные.

Единственный реальный способ, которым я могу думать, что это может работать (основываясь на моем ограниченном понимании Varnish), заключается в том, что мне нужно установить какой-то другой заголовок и получить к нему доступ позже.

Возможно, что-то вроде этого (я пока не указал bot):

if (req.http.X-UA-Device ~ "^mobile") {
  set req.http.X-UA-Device        = "mobile";
  set req.http.X-Analytics-Device = "mobile";

} elseif (req.http.X-UA-Device ~ "^tablet") {
  set req.http.X-UA-Device        = "desktop";
  set req.http.X-Analytics-Device = "tablet";

} else {
  set req.http.X-UA-Device        = "desktop";
  set req.http.X-Analytics-Device = "desktop";
}

После этого... я не знаю. Мне нужно установить это так в vcl_deliver?

set resp.http.X-Analytics-Device = req.http.X-Analytics-Device;

Как он передается от resp к req? Что произойдет, если это попадание или промах? Это имеет значение? Будет ли он пытаться кэшировать этот заголовок в лаке (чего, очевидно, не должно быть)?

Мой главный страх, когда я делаю это таким образом, заключается в том, что есть так много движущихся частей, что я просто не знаю, как лучше всего.

Конечным результатом является то, что... КАЖДЫЙ запрос должен проверять устройство, и на выходе ему нужно установить заголовок, без кэширования этого значения вместе с данными в лаке, и при этом не помешает отправить его в бэкенд, он не нужен.

Вот мой полный VCL, прежде чем я добавил строки псевдокода выше.

vcl 4.0;

backend default {
  .host = "127.0.0.1";
  .port = "8080";
}

import std;

include "purge.vcl";
include "devicedetect.vcl";

acl purge {
  "localhost";
  "127.0.0.1";
  "10.0.0.0"/8;
}

sub vcl_recv {
  call devicedetect;
  if (req.http.X-UA-Device ~ "^mobile") {
    set req.http.X-UA-Device = "mobile";
  } else {
    set req.http.X-UA-Device = "desktop";
  }

  if (req.restarts == 0) {
    if (req.http.X-Forwarded-For) {
      set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
    } else {
      set req.http.X-Forwarded-For = client.ip;
    }
  }

  if (req.method !~ "^(GET|HEAD|PUT|POST|OPTIONS|DELETE)$") {
    return (synth(405));
  }

  # never cache anything except GET/HEAD
  if (req.method != "GET" && req.method != "HEAD") {
    return (pass);
  }

  # don't cache images or assets
  if (req.url ~ "\.(js|css|jpg|jpeg|png|gif|ico|tiff|tif|bmp|svg)$") {
    return (pass);
  }

  # fix up the request
  unset req.http.cookie;
  return (hash);
}

sub vcl_backend_response {
  set beresp.do_stream = false;

  # device detect
  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";
    }
  }

  # bypass cache for files > 5 MB
  if (std.integer(beresp.http.Content-Length, 0) > 5242880) {
    set beresp.uncacheable = true;
    set beresp.ttl = 120s;
    return (deliver);
  }

  # catch obvious reasons we can't cache
  if (beresp.http.Set-Cookie) {
    set beresp.ttl = 0s;
  }

  # avoid caching error responses (1m grace period)
  if (beresp.status >= 500) {
    set beresp.ttl = 1m;
    return (deliver);
  }

  # set times
  set beresp.ttl = 24h;
  set beresp.grace = 4h;
  return (deliver);
}

sub vcl_deliver {
  # device detect
  if ((req.http.X-UA-Device) && (resp.http.Vary)) {
    set resp.http.Vary = regsub(resp.http.Vary, "X-UA-Device", "User-Agent");
  }

  # remove junk headers
  unset resp.http.Server;
  unset resp.http.Via;
  unset resp.http.X-Powered-By;
  unset resp.http.X-Runtime;
  unset resp.http.X-Varnish;

  if (obj.hits > 0) {
    set resp.http.X-Cache = "HIT";
  } else {
    set resp.http.X-Cache = "MISS";
  }
}

person Tallboy    schedule 07.09.2015    source источник


Ответы (2)


Эта ссылка на самом деле прекрасно разъясняет и отвечает на все, что я не смог сформулировать... https://info.varnish-software.com/blog/adding-headers-gain-insight-vcl

Ответ заключается в том, чтобы собрать все необходимые биты данных в заголовки req в vcl_recv, а затем скопировать их в ответ в vcl_deliver.

Он заявляет следующее о том, почему он не будет кэширован:

Поскольку объект req не доставляется клиенту, нам нужно скопировать данные из объекта req в соотв. Мы делаем это при доставке. Если вы сделаете это в vcl_backend_response, заголовки будут храниться в кеше, и это может быть не то, что вам нужно.

person Tallboy    schedule 07.09.2015

Ответ от @Tallboy спас мой день. Подводя итог, что вы хотите сделать, это:

# set the value in vcl_recv
sub vcl_recv {
   set req.http.X-NAVO-AY = "AYAYAYAYAYAYYYYY";
}
# copy the value from req to resp (because it is not done automatically)
sub vcl_deliver {
    set resp.http.X-NAVO-AY = req.http.X-NAVO-AY;
}
person Hafenkranich    schedule 07.06.2016