Назад | Перейти на главную страницу

nginx + fcgiwrap: почему порядок fastcgi_param имеет значение?

я бегу Debian 6.0.3 (squeeze), nginx-0.7.67, fcgiwrap-1.0-1+squeeze1. Вот тестовый сценарий:

#!/usr/bin/perl
use 5.010;
use warnings;
use strict;

use Data::Dumper;

print "Content-Type: text/html\n\n";
say Dumper {map {$_ => $ENV{$_}} 'SCRIPT_NAME', 'DOCUMENT_ROOT', 'WHATEVER'};
say "$<, $>, $(, $)";

А вот nginx конфигурация:

server {
    server_name   domain.com;
    root   /home/yuri/6;
    access_log   /var/log/nginx/domain.com-access.log;
    error_log   /var/log/nginx/domain.com-error.log;

    location /cgi-bin/ {
        fastcgi_pass   unix:/var/run/fcgiwrap.socket;
        fastcgi_param   DOCUMENT_ROOT   $document_root;
        fastcgi_param   DOCUMENT_ROOT   /home/yuri/7/;
        fastcgi_param   SCRIPT_NAME   $fastcgi_script_name;
        fastcgi_param   WHATEVER   1;
        fastcgi_param   WHATEVER   2;
    }

    location /1.php {
        fastcgi_pass   unix:/var/run/php5-fpm.sock;
        fastcgi_param   PHP_ADMIN_VALUE   cgi.fix_pathinfo=1;
        fastcgi_param   REQUEST_METHOD    $request_method;
        fastcgi_param   SCRIPT_FILENAME   whatever;
        fastcgi_param   SCRIPT_FILENAME   $document_root$fastcgi_script_name;
    }
}

Вот что я получаю в браузере, если перехожу на URL http://domain.com/cgi-bin/1.pl:

$VAR1 = { 'SCRIPT_NAME' => '/cgi-bin/1.pl', 'DOCUMENT_ROOT' => '/home/yuri/7/', 'WHATEVER' => '2' };

Кажется, что fcgiwrap использует первый DOCUMENT_ROOT для поиска скрипта, но скрипт получает последние значения параметров. Если вы измените порядок DOCUMENT_ROOT директивы, веб-сервер возвращает 403. Вопрос в том ... как получилось?

php хотя работает как положено: второй SCRIPT_FILENAME отменяет первый.

Библиотека fastcgi просто передает любые параметры, которые ей были заданы, в environ указатель. getenv() как используется fcgiwrap использует любую переменную среды, которая была первой (оптимизация?). Вероятно, что PHP FPM обрабатывает эту среду так же, как тип данных массива, любой последний ключ перезаписывает первый.

Не стоит полагаться на порядок и следить за тем, чтобы ключ был только один. Я не смотрел спецификацию FastCGI, независимо от того, задокументировано ли правильное поведение или нет.

Что касается Зачем вы получите 403, я думаю, что нет /home/yuri/7/1.pl файл. (помните, что первый параметр соответствует fcgiwrap). Поскольку это не исполняемый файл, fcgiwrap возвращает 403.

Тестирование

Приведенный ниже патч печатает среду, указанную FCGI_Accept():

diff --git a/fcgiwrap.c b/fcgiwrap.c
index e86ff9d..0dff2e6 100644
--- a/fcgiwrap.c
+++ b/fcgiwrap.c
@@ -470,6 +470,11 @@ static void inherit_environment(void)
    char * const * p;
    char *q;

+   for (p = environ; *p; p++)
+       fprintf(stderr, "ENV: %s\n", *p);
+
+   fprintf(stderr, "ENV[FOO] = %s\n", getenv("FOO"));
+
    for (p = inherited_environ; *p; p++) {
        q = strchr(*p, '=');
        if (!q) {

Конфигурация nginx, использованная для теста:

events {
    worker_connections 768;
}

pid pid;
error_log error_log;
http {
    server {
        access_log off;
        listen 5555;
        location / {
            fastcgi_param FOO BAR;
            fastcgi_param FOO BARRED;
            fastcgi_pass unix:/tmp/sock;
        }
    }
}

Команды для запуска серверов (при условии nginx.conf в текущем каталоге):

$ nginx -p . -c nginx.conf
$ env -i ./fcgiwrap -p /dev/null -s unix:/tmp/sock

Теперь беги curl http://localhost:5555/ и stderr напечатает:

ENV: FCGI_ROLE=RESPONDER
ENV: FOO=BAR
ENV: FOO=BARRED
ENV: HTTP_USER_AGENT=curl/7.30.0
ENV: HTTP_HOST=localhost:5555
ENV: HTTP_ACCEPT=*/*
ENV[FOO] = BAR
Cannot get script name, are DOCUMENT_ROOT and SCRIPT_NAME (or SCRIPT_FILENAME) set and is the script executable?

Кроме того, это версия в разработке. Текущая стабильная версия (1.1.0) не содержит -p параметр выше. Для результатов это не имеет особого значения, вы могли бы также уронить его и получить ошибку, которая не SCRIPT_NAME дано что ли.