Содержание
FastCGI в Nginx
Как использовать Nginx для работы с любым языком программирования? Использовать универсальный протокол FastCGI!
Установка fcgiwrap
Для работы Nginx с FastCGI необходимо установить пакет fcgiwrap
:
sudo apt-get install fcgiwrap
Настройка Nginx
Конфиг Nginx, в примере порт 8300:
- fcgi.conf
server { server_name _; listen 8300; error_log /var/log/nginx/8300-error.log error; access_log /var/log/nginx/8300.log combined; proxy_connect_timeout 600; proxy_send_timeout 600; proxy_read_timeout 600; send_timeout 600; # Для данного location будем использовать только FastCGI location ^~ /cgi/ { gzip off; root /var/www; fastcgi_pass unix:/var/run/fcgiwrap.socket; # Эту часть можно вынести в отдельный файл и делать include fastcgi_params; fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; # According to RFC3875 (https://tools.ietf.org/html/rfc3875#section-4.1.14) in SERVER_NAME # we should put actual hostname user came to. For nginx it is in $host # This will allow to run multihost instances fastcgi_param SERVER_NAME $host; } }
Пример FastCGI на bash
Bash скрипт, который формирует ответ:
- /var/www/cgi/index.sh
#!/bin/bash NAME=`"cpuinfo"` echo "Content-type:text/html" echo echo "<html><head>" echo "<title>$NAME</title>" echo '<meta name="description" content="'$NAME'">' echo '<meta name="keywords" content="'$NAME'">' echo '<meta http-equiv="Content-type" content="text/html;charset=UTF-8">' echo '<meta name="ROBOTS" content="noindex">' echo "</head><body><pre>" date echo -e "\nuname -a" uname -a echo -e "\ncpuinfo" cat /proc/cpuinfo echo "</pre></body></html>"
chmod +x файл-скрипта
), а сам скрипт будет выполнен под пользователем, от которого работает Nginx.
Теперь по адресу http://localhost:8300/cgi/index.sh будет выполняться Bash-скрипт:
2018/09/17 16:32:09 [error] 1254#1254: *187 FastCGI sent in stderr: "Cannot get script name, are DOCUMENT_ROOT and SCRIPT_NAME (or SCRIPT_FILENAME) set and is the script executable?" while reading response header from upstream, client: 127.0.0.1, server: _, request: "GET /cgi/index.sh HTTP/1.1", upstream: "fastcgi://unix:/var/run/fcgiwrap.socket:", host: "localhost:8300"
Такая ошибка указывает на то, что забыли выставить +x
для скрипта.
Пример FastCGI на NodeJS
NodeJS в данном случае запускается каждый раз, и НЕ работает как отдельный веб-сервер.
- /var/www/cgi/index.js
#!/usr/bin/node console.log("Content-type:text/html"); console.log(""); console.log("<html><head>"); console.log("<meta http-equiv=\"Content-type\" content=\"text/html;charset=UTF-8\">"); console.log("</head><body>"); console.log("Текущее время: ", new Date()); console.log("</body></html>");
Пример FastCGI на C
- fastcgi.c
#include <stdio.h> int main(void){ printf("Content-Type: text/html\r\n"); printf("Connection: close\r\n"); printf("\r\n \r\n"); printf("<html><head></head>\r\n"); printf("<body>\r\n"); printf("Hello world.\r\n"); printf("<br />\r\n"); printf("Bye Bye\r\n"); printf("</body></html>\r\n"); return 0; }
Далее скомпилируем:
gcc -o fastcgi fastcgi.c
При переходе по http://localhost:8300/cgi/fastcgi увидим
Hello world. Bye Bye
Пример FastCGI на PHP
PHP можно использовать через различные интерфейсы SAPI.
Чтобы понять все различия, я написал этот скрипт:
- fast.php
#!/usr/bin/php <?php if('cli' === php_sapi_name()) { /* Nginx с помощью fcgiwrap запускает скрипт через cli-интерфейс, поэтому отправляем заголовок "вручную", иначе получим "502 Bad Gateway" */ echo "Content-type: text/plain\n\n"; } else { /* В других случаях используем штатный способ, и удаляем "#!/usr/bin/php" (PHP буферизирует вывод, и такая небольшая строка как раз умещается) */ ob_end_clean(); ob_start(); header("Content-type: text/plain"); /* Откроем stdout и stderr (в cli они уже открыты и доступны через константы STDOUT и STDERR) */ define('STDOUT', fopen('php://stdout', 'w')); define('STDERR', fopen('php://stderr', 'w')); } echo "php_sapi_name: " . php_sapi_name() . "\n"; echo "stdout: "; fwrite(STDOUT, "STDOUT"); echo "\n"; /* При SAPI !== 'cli' вызов `ob_end_clean();` очистит предыдущий вывод */ // ob_end_clean(); /* - Если используется веб-сервер, он "отрежет" вывод ошибок, вне зависимости от SAPI - Если веб-сервера нет, и запуск через командную строку, ошибка будет выведена в stderr */ echo "stderr: "; fwrite(STDERR, "STDERR"); echo "\n";
FastCGI, работающий через fcgiwrap
, который запускает cli
:
php_sapi_name: cli stdout: STDOUT stderr:
fpm-fcgi
:
php_sapi_name: fpm-fcgi stdout: stderr:
cgi-fcgi
:
php_sapi_name: cgi-fcgi stdout: stderr:
php fast.php
Content-type: text/plain php_sapi_name: cli stdout: STDOUT stderr: STDERR
Таким образом, запись в stdout
с выводом на экран работает только для Cli-SAPI, в остальных случаях - молчание.