Запуск Perl в режиме fastcgi оказался нетривиальной задачей. Главные причины того, что я использую такую настройку, состоят в том, что мне необходимо было использовать nginx в качестве сервера и сократить количество процессов perl.
Настройка nginx для отработки скриптов через fastcgi проблем не составляет:
Nginx.conf
location ~ ^/cgi-bin/.*\.cgi$
{
fastcgi_pass unix:/var/run/nginx-fcgi.sock;
fastcgi_read_timeout 5m;
fastcgi_index index.cgi;
#
# You may copy and paste the lines under or use include directive
# include /etc/nginx/nginx-fcgi.conf;
# In this example all is in one file
#
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
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 GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx;
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 REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;
}
(http://www.nginx.eu/nginx-fcgi.html)
Самым узким местом является запуск perl-wrapper в режиме fastcgi. Скрипт для запуска nginx-fcgi.pl, представленный в документации, неверно отрабатывает POST запросы.
# processing any STDIN input from WebServer (for CGI-POST actions)
$stdin_passthrough = ”;
$req_len = 0 + $req_params{‘CONTENT_LENGTH’};
if (($req_params{‘REQUEST_METHOD’} eq ‘POST’) && ($req_len != 0) ){
while ($req_len) {
$stdin_passthrough .= getc(STDIN);
$req_len–;
}
}
Я встречал несколько модификаций скрипта, одна из которых предлагала такое решение:
#processing any STDIN input from WebServer (for CGI-POST actions)
$stdin_passthrough =”;
$req_len = 0 + $req_params{‘CONTENT_LENGTH’};
if (($req_params{‘REQUEST_METHOD’} eq ‘POST’) && ($req_len != 0) ){
my $bytes_read = 0;
while ($bytes_read < $req_len) {
my $data = '';
my $bytes = read(STDIN, $data, ($req_len - $bytes_read));
last if ($bytes == 0 || !defined($bytes));
$stdin_passthrough .= $data;
$bytes_read += $bytes;
}
}
(http://www.ruby-forum.com/topic/145858)
В качестве хостинга у меня был nichost.ru, поэтому накладывалось множество ограничений, связанных с отсутствием syscall и ограничениями cpan. Поэтому я решил пойти иным путем и вручную конвертировать POST-запрос в GET путем добавления к QUERY_STRING тела POST-запроса. Также нужно не забыть присвоить REQUEST_METHOD значение GET.
# little hack translate all POST data to GET data:
$input_get = $ENV{‘QUERY_STRING’};
$input_post = $stdin_passthrough;
if (($input_get ne ”) and ($input_post ne ”)){
$input = $input_get.’&’.$input_post;
} else {
$input = $input_get.$input_post;
}
$ENV{‘QUERY_STRING’} = $input;
$ENV{‘REQUEST_METHOD’} = ‘GET’;
Также исходные скрипт при любом исходе пишет в заголовки информацию об ошибке. Это связано с неверной обработкой и исправить это можно следующим образом:
open $cgi_app, ‘-|’, $req_params{SCRIPT_FILENAME}, $stdin_passthrough or scripterror($log_file, $req_params);
sub script_error {
my ($log_file, $req_params) = @_;
print(“Content-type: text/plain\r\n\r\n”);
print “Error: CGI app returned no output – Executing $req_params{SCRIPT_FILENAME} failed !\n”;
addlog($logfile, “Error: CGI app returned no output – Executing $req_params{SCRIPT_FILENAME} failed !”);
}
Конечно же это не самое лучшее решение, но работать будет. Стоит отметить, что multipart/form-data при таком подходе работать не будет.
0 comments ↓
There are no comments yet...Kick things off by filling out the form below.
Leave a Comment