Nginx

nginx + fcgiwrap:fastcgi_param 的順序如何重要?

  • May 30, 2013

我正在執行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指令的順序,Web 伺服器將返回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參數。對於結果並不重要,您也可以放棄它並收到一個錯誤,即 no SCRIPT_NAMEis given 之類的。

引用自:https://serverfault.com/questions/512028