Распаковка сжатых URL на сервере
Недавно я рассказывал, как использовать API браузеров для сжатия адресов страниц. В продолжение опишу, как эти сжатые адреса распаковыать на сервере на примере того же сервиса генерации картинок с формулами.
Текущая версия конфига nginx для обработки и старых несжатых URL, и новых сжатых получилась такой:
location ~ ^(?s)/(?<ext>svg|png)(?<is_base64>b?)/(?<formula>.*)$ {
gunzip on;
gzip_static always;
gzip_vary on;
gzip_proxied expired no-cache no-store private auth;
expires 1d;
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
set $compress_error 0;
set_by_lua_block $file_path {
local ext = ngx.var.ext
local is_base64 = ngx.var.is_base64
local formula = ngx.var.formula
if is_base64 == "b" then
local base64 = require "ngx.base64"
local zlib = require "zlib"
local compressed, err = base64.decode_base64url(formula)
if not compressed then
ngx.log(ngx.ERR, "base64 decode error: ", err)
ngx.var.compress_error = 1
return ""
end
local inflator = zlib.inflate(-15)
local ok, decoded_formula = pcall(inflator, compressed)
if not ok then
ngx.log(ngx.ERR, "deflate decompress error: ", decoded_formula)
ngx.var.compress_error = 1
return ""
end
formula = decoded_formula
end
formula = formula:gsub("^%s*(.-)%s*$", "%1")
local md5 = ngx.md5(formula)
return md5:sub(1, 2) .. "/" .. md5:sub(3, 4) .. "/" .. md5:sub(5) .. "." .. ext
}
if ($compress_error) {
return 400;
}
if (-f $document_root/_error/$file_path) {
return 400;
}
rewrite ^ /_cache/$file_path break;
error_page 404 = @s2_latex_renderer;
log_not_found off;
}
location @s2_latex_renderer {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
include /etc/nginx/fastcgi.conf;
fastcgi_pass php-tex-sock;
fastcgi_param SCRIPT_FILENAME $document_root/render.php;
fastcgi_param SCRIPT_NAME /render.php;
fastcgi_cache_key "$request_method$uri"; # В $uri УРЛ после rewrite, напр. "/_cache/4d/81/658b25df7544f9e2d0cb7f4dc402.svg"
fastcgi_cache i_upmath;
fastcgi_cache_valid 200 10m;
fastcgi_cache_methods GET HEAD;
fastcgi_cache_lock on;
fastcgi_cache_lock_age 9s;
fastcgi_cache_lock_timeout 9s;
fastcgi_buffers 8 16k;
fastcgi_buffer_size 32k;
fastcgi_connect_timeout 90;
fastcgi_send_timeout 90;
fastcgi_read_timeout 300;
}
Чтобы встраивать set_by_lua_block
, в Debian достаточно установить пакет nginx-extras
. Для распаковки сжатого текста в этом скрипте через функции zlib также требуется установить пакет lua-zlib
.
Напомню алгоритм обработки адресов картинок. Исходник формулы, например, x^2
, извлекается из адреса и декодируется. Вычисляется rewrite ^ /_cache/$file_path break;
), то nginx отдает его содержимое напрямую. Если файла нет, то запрос передается в error_page 404 = @s2_latex_renderer;
).
Раньше вместо rewrite
и error_page
я использовал более современную и подходящую директиву try_files
. Но она перестает работать после активации модуля gunzip. Этот модуль позволяет держать в файловом кеше только сжатые версии файлов с расширением .gz
, экономя место на диске. Причем для обработки большинства запросов от нормальных браузеров, поддерживающих rewrite
корректно работает с модулем gunzip, а try_files
— нет, не очень понятно. Но что есть, то есть.
В конфиге есть интересный момент, связанный с кешем внутри nginx (инструкции fastcgi_cache*
). Он предотвращает race condition при одновременном запросе формулы, которой нет в файловом кеше. В противном случае nginx будет передавать в
Для распаковки полученного фрагмента URL в PHP подойдет следующий код:
public static function decodeCompressedFormula(string $compressed): string
{
$base64 = strtr($compressed, '-_', '+/'); // URL-safe base64 to standard
$compressed = base64_decode($base64);
$result = @gzinflate($compressed);
if ($result === false) {
throw new \RuntimeException('Failed to decompress formula.');
}
return $result;
}
В PHP весь алгоритм уместился в три строки — как минимум в несколько раз короче, чем в JS и nginx/lua. Такими моментами PHP радует меня до сих пор.
Оставьте свой комментарий