# HG changeset patch # User Maxim Dounin # Date 1239808931 -14400 # Node ID 28af4b0b32c118e4a7003d567ab15bf8186e5b39 # Parent 181a487581b75ef12478a0f5c1bd7cc50a9c5c77 Keepalive: experimental fastcgi support. Works only with patched nginx and must be explicitly activated by define (./configure --with-cc-opt="-D NGX_UPSTREAM_KEEPALIVE_PATCHED"). diff --git a/ngx_http_upstream_keepalive_module.c b/ngx_http_upstream_keepalive_module.c --- a/ngx_http_upstream_keepalive_module.c +++ b/ngx_http_upstream_keepalive_module.c @@ -313,8 +313,12 @@ ngx_http_upstream_free_keepalive_peer(ng if (!kp->failed && pc->connection != NULL +#if (NGX_UPSTREAM_KEEPALIVE_PATCHED) + && u->keepalive) +#else && (status == NGX_HTTP_NOT_FOUND || (status == NGX_HTTP_OK && u->header_sent && u->length == 0))) +#endif { c = pc->connection; diff --git a/t/fastcgi-keepalive.t b/t/fastcgi-keepalive.t new file mode 100644 --- /dev/null +++ b/t/fastcgi-keepalive.t @@ -0,0 +1,174 @@ +#!/usr/bin/perl + +# (C) Maxim Dounin + +# Test for fastcgi backend with keepalive. + +############################################################################### + +use warnings; +use strict; + +use Test::More; +use Test::Nginx; + +############################################################################### + +select STDERR; $| = 1; +select STDOUT; $| = 1; + +my $t = Test::Nginx->new()->plan(6) + ->write_file_expand('nginx.conf', <<'EOF'); + +master_process off; +daemon off; + +events { + worker_connections 1024; +} + +http { + access_log off; + + client_body_temp_path %%TESTDIR%%/client_body_temp; + fastcgi_temp_path %%TESTDIR%%/fastcgi_temp; + proxy_temp_path %%TESTDIR%%/proxy_temp; + + upstream backend { + server 127.0.0.1:8081; + keepalive 1; + } + + server { + listen localhost:8080; + server_name localhost; + + location / { + fastcgi_pass backend; + } + } +} + +EOF + +$t->run_daemon(\&fastcgi_test_daemon); +$t->run(); + +############################################################################### + +like(http_get('/'), qr/SEE-THIS/, 'fastcgi request'); +like(http_get('/redir'), qr/302/, 'fastcgi redirect'); +like(http_get('/'), qr/^request: 3$/m, 'fastcgi third request'); + +{ +local $TODO = 'needs experimental patches'; + +like(http_get('/single'), qr/^connection: 1$/m, 'single connection used'); + +} + +# New connection to fastcgi application should be established after HEAD +# requests since nginx doesn't read whole response (as it doesn't need +# body). + +unlike(http_head('/head'), qr/SEE-THIS/, 'no data in HEAD'); + +{ +local $TODO = 'needs experimental patches'; + +like(http_get('/after'), qr/^connection: 2$/m, 'new connection after HEAD'); +} + +############################################################################### + +# Simple FastCGI responder implementation. Unlike FCGI and FCGI::Async it's +# able to count connections. + +# http://www.fastcgi.com/devkit/doc/fcgi-spec.html + +sub fastcgi_read_record($) { + my ($socket) = @_; + + my ($n, $h, $header); + + $n = $socket->read($header, 8); + return undef if !defined $n or $n != 8; + + @{$h}{qw/ version type id clen plen /} = unpack("CCnnC", $header); + + $n = $socket->read($h->{content}, $h->{clen}); + return undef if $n != $h->{clen}; + + $n = $socket->read($h->{padding}, $h->{plen}); + return undef if $n != $h->{plen}; + + $h->{socket} = $socket; + return $h; +} + +sub fastcgi_respond($$) { + my ($h, $body) = @_; + + # stdout + $h->{socket}->write(pack("CCnnCx", $h->{version}, 6, $h->{id}, + length($body), 0)); + $h->{socket}->write($body); + + select(undef, undef, undef, 0.1); + + # close stdout + $h->{socket}->write(pack("CCnnCx", $h->{version}, 6, $h->{id}, 0, 0)); + + select(undef, undef, undef, 0.1); + + # end request + $h->{socket}->write(pack("CCnnCx", $h->{version}, 3, $h->{id}, 8, 0)); + $h->{socket}->write(pack("NCxxx", 0, 0)); +} + +sub fastcgi_test_daemon { + my $server = IO::Socket::INET->new( + Proto => 'tcp', + LocalAddr => '127.0.0.1:8081', + Listen => 5, + Reuse => 1 + ) + or die "Can't create listening socket: $!\n"; + + local $SIG{PIPE} = 'IGNORE'; + + my $ccount = 0; + my $rcount = 0; + + while (my $client = $server->accept()) { + $client->autoflush(1); + Test::Nginx::log_core('||', "fastcgi connection"); + + $ccount++; + + while (my $h = fastcgi_read_record($client)) { + Test::Nginx::log_core('||', "fastcgi record: " + . " $h->{version}, $h->{type}, $h->{id}, " + . "'$h->{content}'"); + + # skip everything unless stdin, then respond + next if $h->{type} != 5; + + $rcount++; + + # respond + fastcgi_respond($h, <