changeset 77:ff5f4f298532

Tests: merge.
author Maxim Dounin <mdounin@mdounin.ru>
date Sat, 28 Mar 2009 01:18:06 +0300
parents f81c6be872e5 (diff) c7e29639f915 (current diff)
children c893908c1a44
files lib/Test/Nginx.pm
diffstat 22 files changed, 1058 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/README
+++ b/README
@@ -10,7 +10,8 @@ nginx binary available as ../nginx/objs/
 Note: some tests may fail since they are for bugs not fixed in public code.
 
 Note: tests run nginx (and backend daemons if needed) listening on localhost.
-Currently this includes following ports: 8025, 8026, 8080, 8081.
+Currently this includes following ports: 8025, 8026, 8080, 8081, 8110, 8111,
+8143, 8144.
 
 Tests for memcached required memcached itself and Cache::Memcached to be
 installed.
new file mode 100644
--- /dev/null
+++ b/expect-100-continue.t
@@ -0,0 +1,85 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for Expect: 100-continue support.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->plan(2);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    access_log    off;
+    root          %%TESTDIR%%;
+
+    client_body_temp_path  %%TESTDIR%%/client_body_temp;
+    fastcgi_temp_path      %%TESTDIR%%/fastcgi_temp;
+    proxy_temp_path        %%TESTDIR%%/proxy_temp;
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+        location / {
+            proxy_pass http://localhost:8080/local;
+        }
+        location /local {
+        }
+    }
+}
+
+EOF
+
+$t->run();
+
+###############################################################################
+
+like(http_100_request('/', '1.1'), qr/100/, 'expect 100 continue');
+
+# From RFC 2616, 8.2.3 Use of the 100 (Continue) Status:
+#
+#      - An origin server SHOULD NOT send a 100 (Continue) response if
+#        the request message does not include an Expect request-header
+#        field with the "100-continue" expectation, and MUST NOT send a
+#        100 (Continue) response if such a request comes from an HTTP/1.0
+#        (or earlier) client.
+
+unlike(http_100_request('/', '1.0'), qr/100/, 'no 100 continue via http 1.0');
+
+###############################################################################
+
+sub http_100_request {
+	my ($url, $version) = @_;
+	my $r = http(<<EOF);
+POST $url HTTP/$version
+Host: localhost
+Expect: 100-continue
+Content-Length: 0
+Connection: close
+
+EOF
+}
+
+###############################################################################
--- a/fastcgi.t
+++ b/fastcgi.t
@@ -31,7 +31,6 @@ master_process off;
 daemon         off;
 
 events {
-    worker_connections  1024;
 }
 
 http {
new file mode 100644
--- /dev/null
+++ b/imap.t
@@ -0,0 +1,122 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for nginx mail imap module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+use IO::Socket;
+use MIME::Base64;
+use Socket qw/ CRLF /;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::IMAP;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()
+	->has('mail')->plan(8)
+	->run_daemon(\&Test::Nginx::IMAP::imap_test_daemon)
+	->write_file_expand('nginx.conf', <<'EOF')->run();
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+mail {
+    proxy_pass_error_message  on;
+    auth_http  http://127.0.0.1:8080/mail/auth;
+
+    server {
+        listen     127.0.0.1:8143;
+        protocol   imap;
+    }
+}
+
+http {
+    access_log    off;
+
+    client_body_temp_path  %%TESTDIR%%/client_body_temp;
+    fastcgi_temp_path      %%TESTDIR%%/fastcgi_temp;
+    proxy_temp_path        %%TESTDIR%%/proxy_temp;
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location = /mail/auth {
+            set $reply ERROR;
+
+            if ($http_auth_smtp_to ~ example.com) {
+                set $reply OK;
+            }
+
+            set $userpass "$http_auth_user:$http_auth_pass";
+            if ($userpass ~ '^test@example.com:secret$') {
+                set $reply OK;
+            }
+
+            add_header Auth-Status $reply;
+            add_header Auth-Server 127.0.0.1;
+            add_header Auth-Port 8144;
+            add_header Auth-Wait 1;
+            return 204;
+        }
+    }
+}
+
+EOF
+
+###############################################################################
+
+my $s = Test::Nginx::IMAP->new();
+$s->ok('greeting');
+
+# auth plain
+
+$s->send('1 AUTHENTICATE PLAIN ' . encode_base64("\0test\@example.com\0bad", ''));
+$s->check(qr/^\S+ NO/, 'auth plain with bad password');
+
+$s->send('1 AUTHENTICATE PLAIN ' . encode_base64("\0test\@example.com\0secret", ''));
+$s->ok('auth plain');
+
+# auth login simple
+
+$s = Test::Nginx::IMAP->new();
+$s->read();
+
+$s->send('1 AUTHENTICATE LOGIN');
+$s->check(qr/\+ VXNlcm5hbWU6/, 'auth login username challenge');
+
+$s->send(encode_base64('test@example.com', ''));
+$s->check(qr/\+ UGFzc3dvcmQ6/, 'auth login password challenge');
+
+$s->send(encode_base64('secret', ''));
+$s->ok('auth login simple');
+
+# auth login with username
+
+$s = Test::Nginx::IMAP->new();
+$s->read();
+
+$s->send('1 AUTHENTICATE LOGIN ' . encode_base64('test@example.com', ''));
+$s->check(qr/\+ UGFzc3dvcmQ6/, 'auth login with username password challenge');
+
+$s->send(encode_base64('secret', ''));
+$s->ok('auth login with username');
+
+###############################################################################
--- a/lib/Test/Nginx.pm
+++ b/lib/Test/Nginx.pm
@@ -42,6 +42,9 @@ sub new {
 sub DESTROY {
 	my ($self) = @_;
 	$self->stop();
+	if ($ENV{TEST_NGINX_CATLOG}) {
+		system("cat $self->{_testdir}/error.log");
+	}
 }
 
 sub has {
@@ -98,17 +101,45 @@ sub run(;$) {
 
 	# wait for nginx to start
 
-	for (1 .. 30) {
-		select undef, undef, undef, 0.05;
-		last if -e "$self->{_testdir}/nginx.pid";
-	}
-
-	die "Can't start nginx" unless -e "$self->{_testdir}/nginx.pid";
+	$self->waitforfile("$testdir/nginx.pid")
+		or die "Can't start nginx";
 
 	$self->{_started} = 1;
 	return $self;
 }
 
+sub waitforfile($) {
+	my ($self, $file) = @_;
+
+	# wait for file to appear
+
+	for (1 .. 30) {
+		return 1 if -e $file;
+		select undef, undef, undef, 0.1;
+	}
+
+	return undef;
+}
+
+sub waitforsocket($) {
+	my ($self, $peer) = @_;
+
+	# wait for socket to accept connections
+
+	for (1 .. 30) {
+		my $s = IO::Socket::INET->new(
+			Proto => 'tcp',
+			PeerAddr => $peer
+		);
+
+		return 1 if defined $s;
+
+		select undef, undef, undef, 0.1;
+	}
+
+	return undef;
+}
+
 sub stop() {
 	my ($self) = @_;
 
@@ -175,19 +206,24 @@ sub testdir() {
 
 ###############################################################################
 
-sub log_out {
-	my ($msg) = @_;
-	$msg =~ s/^/# >> /gm;
+sub log_core {
+	return unless $ENV{TEST_NGINX_VERBOSE};
+	my ($prefix, $msg) = @_;
+	($prefix, $msg) = ('', $prefix) unless defined $msg;
+	$prefix .= ' ' if length($prefix) > 0;
+ 
+	$msg =~ s/^/# $prefix/gm;
+	$msg =~ s/([^\x20-\x7e])/sprintf('\\x%02x', ord($1)) . (($1 eq "\n") ? "\n" : '')/gmxe;
 	$msg .= "\n" unless $msg =~ /\n\Z/;
 	print $msg;
 }
 
+sub log_out {
+	log_core('>>', @_);
+}
+
 sub log_in {
-	my ($msg) = @_;
-	$msg =~ s/^/# << /gm;
-	$msg =~ s/([^\x20-\x7e])/sprintf('\\x%02x', ord($1)) . (($1 eq "\n") ? "\n" : '')/gmxe;
-	$msg .= "\n" unless $msg =~ /\n\Z/;
-	print $msg;
+	log_core('<<', @_);
 }
 
 ###############################################################################
new file mode 100644
--- /dev/null
+++ b/lib/Test/Nginx/IMAP.pm
@@ -0,0 +1,109 @@
+package Test::Nginx::IMAP;
+
+# (C) Maxim Dounin
+
+# Module for nginx imap tests.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More qw//;
+use IO::Socket;
+use Socket qw/ CRLF /;
+
+use Test::Nginx;
+
+use base qw/ IO::Socket::INET /;
+
+sub new {
+	my $class = shift;
+
+	my $self = return $class->SUPER::new(
+		Proto => "tcp",
+		PeerAddr => "127.0.0.1:8143",
+		@_
+	)
+		or die "Can't connect to nginx: $!\n";
+
+	$self->autoflush(1);
+
+	return $self;
+}
+
+sub send {
+	my ($self, $cmd) = @_;
+	log_out($cmd);
+	$self->print($cmd . CRLF);
+}
+
+sub read {
+	my ($self) = @_;
+	eval {
+		alarm(2);
+		local $SIG{ALRM} = sub { die "alarm\n" };
+		while (<$self>) {
+			log_in($_);
+			# XXX
+			next if m/^\d\d\d-/;
+			last;
+		}
+		alarm(0);
+	};
+	alarm(0);
+	if ($@) {
+		return undef;
+	}
+	return $_;
+}
+
+sub check {
+	my ($self, $regex, $name) = @_;
+	Test::More->builder->like($self->read(), $regex, $name);
+}
+
+sub ok {
+	my $self = shift; 
+	Test::More->builder->like($self->read(), qr/^\S+ OK/, @_);
+}
+
+###############################################################################
+
+sub imap_test_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalAddr => '127.0.0.1:8144',
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+		print $client "* OK fake imap server ready" . CRLF;
+
+		while (<$client>) {
+			my $tag = '';
+
+			$tag = $1 if m/^(\S+)/;
+			s/^(\S+)\s+//;
+
+			if (/^logout/i) {
+				print $client $tag . ' OK logout ok' . CRLF;
+			} elsif (/^login /i) {
+				print $client $tag . ' OK login ok' . CRLF;
+			} else {
+				print $client $tag . ' ERR unknown command' . CRLF;
+			}
+                }
+
+		close $client;
+	}
+}
+
+###############################################################################
+
+1;
+
+###############################################################################
new file mode 100644
--- /dev/null
+++ b/lib/Test/Nginx/POP3.pm
@@ -0,0 +1,106 @@
+package Test::Nginx::POP3;
+
+# (C) Maxim Dounin
+
+# Module for nginx pop3 tests.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More qw//;
+use IO::Socket;
+use Socket qw/ CRLF /;
+
+use Test::Nginx;
+
+use base qw/ IO::Socket::INET /;
+
+sub new {
+	my $class = shift;
+
+	my $self = return $class->SUPER::new(
+		Proto => "tcp",
+		PeerAddr => "127.0.0.1:8110",
+		@_
+	)
+		or die "Can't connect to nginx: $!\n";
+
+	$self->autoflush(1);
+
+	return $self;
+}
+
+sub send {
+	my ($self, $cmd) = @_;
+	log_out($cmd);
+	$self->print($cmd . CRLF);
+}
+
+sub read {
+	my ($self) = @_;
+	eval {
+		alarm(2);
+		local $SIG{ALRM} = sub { die "alarm\n" };
+		while (<$self>) {
+			log_in($_);
+			# XXX
+			next if m/^\d\d\d-/;
+			last;
+		}
+		alarm(0);
+	};
+	alarm(0);
+	if ($@) {
+		return undef;
+	}
+	return $_;
+}
+
+sub check {
+	my ($self, $regex, $name) = @_;
+	Test::More->builder->like($self->read(), $regex, $name);
+}
+
+sub ok {
+	my $self = shift; 
+	Test::More->builder->like($self->read(), qr/^\+OK/, @_);
+}
+
+###############################################################################
+
+sub pop3_test_daemon {
+	my $server = IO::Socket::INET->new(
+		Proto => 'tcp',
+		LocalAddr => '127.0.0.1:8111',
+		Listen => 5,
+		Reuse => 1
+	)
+		or die "Can't create listening socket: $!\n";
+
+	while (my $client = $server->accept()) {
+		$client->autoflush(1);
+		print $client "+OK fake pop3 server ready" . CRLF;
+
+		while (<$client>) {
+			if (/^quit/i) {
+				print $client '+OK quit ok' . CRLF;
+			} elsif (/^user test\@example.com/i) {
+				print $client '+OK user ok' . CRLF;
+			} elsif (/^pass secret/i) {
+				print $client '+OK pass ok' . CRLF;
+			} else {
+				print $client "-ERR unknown command" . CRLF;
+			}
+                }
+
+		close $client;
+	}
+}
+
+###############################################################################
+
+1;
+
+###############################################################################
--- a/lib/Test/Nginx/SMTP.pm
+++ b/lib/Test/Nginx/SMTP.pm
@@ -59,12 +59,17 @@ sub read {
 
 sub check {
 	my ($self, $regex, $name) = @_;
-	Test::More::like($self->read(), $regex, $name);
+	Test::More->builder->like($self->read(), $regex, $name);
 }
 
 sub ok {
 	my $self = shift; 
-	$self->check(qr/^2\d\d /, @_);
+	Test::More->builder->like($self->read(), qr/^2\d\d /, @_);
+}
+
+sub authok {
+	my $self = shift; 
+	Test::More->builder->like($self->read(), qr/^235 /, @_);
 }
 
 ###############################################################################
@@ -83,6 +88,8 @@ sub smtp_test_daemon {
 		print $client "220 fake esmtp server ready" . CRLF;
 
 		while (<$client>) {
+			Test::Nginx::log_core('||', $_);
+
 			if (/^quit/i) {
 				print $client '221 quit ok' . CRLF;
 			} elsif (/^(ehlo|helo)/i) {
@@ -97,6 +104,8 @@ sub smtp_test_daemon {
 				print $client '500 rcpt to error' . CRLF;
 			} elsif (/^rcpt to:/i) {
 				print $client '250 rcpt to ok' . CRLF;
+			} elsif (/^xclient/i) {
+				print $client '220 xclient ok' . CRLF;
 			} else {
 				print $client "500 unknown command" . CRLF;
 			}
--- a/memcached.t
+++ b/memcached.t
@@ -22,7 +22,7 @@ select STDERR; $| = 1;
 select STDOUT; $| = 1;
 
 eval { require Cache::Memcached; };
-plain(skip_all => 'Cache::Memcached not installed') if $@;
+plan(skip_all => 'Cache::Memcached not installed') if $@;
 
 my $t = Test::Nginx->new()->has('rewrite')->has_daemon('memcached')->plan(4)
 	->write_file_expand('nginx.conf', <<'EOF');
@@ -31,7 +31,6 @@ master_process off;
 daemon         off;
 
 events {
-    worker_connections  1024;
 }
 
 http {
@@ -63,13 +62,18 @@ EOF
 $t->run_daemon('memcached', '-l', '127.0.0.1', '-p', '8081');
 $t->run();
 
+$t->waitforsocket('127.0.0.1:8081')
+	or die "Can't start memcached";
+
 ###############################################################################
 
 my $memd = Cache::Memcached->new(servers => [ '127.0.0.1:8081' ]);
 $memd->set('/', 'SEE-THIS');
 
 like(http_get('/'), qr/SEE-THIS/, 'memcached request');
+
 like(http_get('/notfound'), qr/404/, 'memcached not found');
+
 like(http_get('/next'), qr/404/, 'not found with memcached_next_upstream');
 
 unlike(http_head('/'), qr/SEE-THIS/, 'memcached no data in HEAD');
new file mode 100644
--- /dev/null
+++ b/pop3.t
@@ -0,0 +1,122 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for nginx mail pop3 module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+use IO::Socket;
+use MIME::Base64;
+use Socket qw/ CRLF /;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::POP3;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()
+	->has('mail')->plan(8)
+	->run_daemon(\&Test::Nginx::POP3::pop3_test_daemon)
+	->write_file_expand('nginx.conf', <<'EOF')->run();
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+mail {
+    proxy_pass_error_message  on;
+    auth_http  http://127.0.0.1:8080/mail/auth;
+
+    server {
+        listen     127.0.0.1:8110;
+        protocol   pop3;
+    }
+}
+
+http {
+    access_log    off;
+
+    client_body_temp_path  %%TESTDIR%%/client_body_temp;
+    fastcgi_temp_path      %%TESTDIR%%/fastcgi_temp;
+    proxy_temp_path        %%TESTDIR%%/proxy_temp;
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location = /mail/auth {
+            set $reply ERROR;
+
+            if ($http_auth_smtp_to ~ example.com) {
+                set $reply OK;
+            }
+
+            set $userpass "$http_auth_user:$http_auth_pass";
+            if ($userpass ~ '^test@example.com:secret$') {
+                set $reply OK;
+            }
+
+            add_header Auth-Status $reply;
+            add_header Auth-Server 127.0.0.1;
+            add_header Auth-Port 8111;
+            add_header Auth-Wait 1;
+            return 204;
+        }
+    }
+}
+
+EOF
+
+###############################################################################
+
+my $s = Test::Nginx::POP3->new();
+$s->ok('greeting');
+
+# auth plain
+
+$s->send('AUTH PLAIN ' . encode_base64("\0test\@example.com\0bad", ''));
+$s->check(qr/^-ERR/, 'auth plain with bad password');
+
+$s->send('AUTH PLAIN ' . encode_base64("\0test\@example.com\0secret", ''));
+$s->ok('auth plain');
+
+# auth login simple
+
+$s = Test::Nginx::POP3->new();
+$s->read();
+
+$s->send('AUTH LOGIN');
+$s->check(qr/\+ VXNlcm5hbWU6/, 'auth login username challenge');
+
+$s->send(encode_base64('test@example.com', ''));
+$s->check(qr/\+ UGFzc3dvcmQ6/, 'auth login password challenge');
+
+$s->send(encode_base64('secret', ''));
+$s->ok('auth login simple');
+
+# auth login with username
+
+$s = Test::Nginx::POP3->new();
+$s->read();
+
+$s->send('AUTH LOGIN ' . encode_base64('test@example.com', ''));
+$s->check(qr/\+ UGFzc3dvcmQ6/, 'auth login with username password challenge');
+
+$s->send(encode_base64('secret', ''));
+$s->ok('auth login with username');
+
+###############################################################################
new file mode 100644
--- /dev/null
+++ b/proxy-cache.t
@@ -0,0 +1,85 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for http proxy cache.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More tests => 2;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new();
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    access_log    off;
+    root          %%TESTDIR%%;
+
+    client_body_temp_path  %%TESTDIR%%/client_body_temp;
+    fastcgi_temp_path      %%TESTDIR%%/fastcgi_temp;
+    proxy_temp_path        %%TESTDIR%%/proxy_temp;
+
+    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2
+                       keys_zone=NAME:10m
+                       inactive=5m     clean_time=2h00m;
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            proxy_pass    http://127.0.0.1:8081;
+
+            proxy_cache   NAME;
+
+            proxy_cache_valid   200 302  1h;
+            proxy_cache_valid   301      1d;
+            proxy_cache_valid   any      1m;
+
+            proxy_cache_min_uses  1;
+
+            proxy_cache_use_stale   error  timeout invalid_header http_500;
+        }
+    }
+    server {
+        listen       127.0.0.1:8081;
+        server_name  localhost;
+
+        location / {
+        }
+    }
+}
+
+EOF
+
+$t->write_file('t.html', 'SEE-THIS');
+$t->run();
+
+###############################################################################
+
+like(http_get('/t.html'), qr/SEE-THIS/, 'proxy request');
+
+$t->write_file('t.html', 'NOOP');
+like(http_get('/t.html'), qr/SEE-THIS/, 'proxy request cached');
+
+###############################################################################
--- a/proxy-noclose.t
+++ b/proxy-noclose.t
@@ -40,7 +40,6 @@ master_process off;
 daemon         off;
 
 events {
-    worker_connections  1024;
 }
 
 http {
@@ -80,6 +79,7 @@ EOF
 
 TODO: {
 local $TODO = 'not fixed yet, patches under review';
+local $SIG{__WARN__} = sub {};
 
 like(http_get('/'), qr/SEE-THIS/, 'request to bad backend');
 like(http_get('/multi'), qr/AND-THIS/, 'bad backend - multiple packets');
--- a/proxy-store.t
+++ b/proxy-store.t
@@ -29,7 +29,6 @@ master_process off;
 daemon         off;
 
 events {
-    worker_connections  1024;
 }
 
 http {
--- a/proxy.t
+++ b/proxy.t
@@ -29,7 +29,6 @@ master_process off;
 daemon         off;
 
 events {
-    worker_connections  1024;
 }
 
 http {
--- a/range-flv.t
+++ b/range-flv.t
@@ -29,7 +29,6 @@ master_process off;
 daemon         off;
 
 events {
-    worker_connections  1024;
 }
 
 http {
--- a/range.t
+++ b/range.t
@@ -29,7 +29,6 @@ master_process off;
 daemon         off;
 
 events {
-    worker_connections  1024;
 }
 
 http {
new file mode 100644
--- /dev/null
+++ b/rewrite.t
@@ -0,0 +1,76 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for rewrite module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has('rewrite')->plan(5)
+	->write_file_expand('nginx.conf', <<'EOF');
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    access_log    off;
+
+    client_body_temp_path  %%TESTDIR%%/client_body_temp;
+    fastcgi_temp_path      %%TESTDIR%%/fastcgi_temp;
+    proxy_temp_path        %%TESTDIR%%/proxy_temp;
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location / {
+            rewrite ^ http://example.com/ redirect;
+        }
+
+        location /add {
+            rewrite ^ http://example.com/?c=d redirect;
+        }
+
+        location /no {
+            rewrite ^ http://example.com/?c=d? redirect;
+        }
+    }
+}
+
+EOF
+
+$t->run();
+
+###############################################################################
+
+like(http_get('/'), qr!^Location: http://example.com/\x0d?$!ms, 'simple');
+like(http_get('/?a=b'), qr!^Location: http://example.com/\?a=b\x0d?$!ms,
+	'simple with args');
+like(http_get('/add'), qr!^Location: http://example.com/\?c=d\x0d?$!ms,
+	'add args');
+
+like(http_get('/add?a=b'), qr!^Location: http://example.com/\?c=d&a=b\x0d?$!ms,
+	'add args with args');
+
+like(http_get('/no?a=b'), qr!^Location: http://example.com/\?c=d\x0d?$!ms,
+	'no args with args');
+
+###############################################################################
--- a/smtp-greeting-delay.t
+++ b/smtp-greeting-delay.t
@@ -27,7 +27,6 @@ master_process off;
 daemon         off;
 
 events {
-    worker_connections  1024;
 }
 
 mail {
@@ -42,6 +41,10 @@ mail {
     }
 }
 
+http {
+    # stub to avoid SIGSEGV when perl module compiled in, <= 0.7.30
+}
+
 EOF
 
 ###############################################################################
@@ -52,6 +55,12 @@ EOF
 my $s = Test::Nginx::SMTP->new();
 $s->send('HELO example.com');
 $s->check(qr/^5.. /, "command before greeting - session must be rejected");
+
+TODO: {
+local $TODO = 'not in official nginx yet';
+
 ok($s->eof(), "session have to be closed");
 
+}
+
 ###############################################################################
new file mode 100644
--- /dev/null
+++ b/smtp-xclient.t
@@ -0,0 +1,141 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+use MIME::Base64;
+use Socket qw/ CRLF /;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+use Test::Nginx::SMTP;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has('mail')->plan(6)
+	->run_daemon(\&Test::Nginx::SMTP::smtp_test_daemon)
+	->write_file_expand('nginx.conf', <<'EOF')->run();
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+mail {
+    proxy_pass_error_message  on;
+    auth_http  http://127.0.0.1:8080/mail/auth;
+    xclient    on;
+
+    server {
+        listen     127.0.0.1:8025;
+        protocol   smtp;
+        smtp_auth  login plain none;
+    }
+}
+
+http {
+    access_log    off;
+
+    client_body_temp_path  %%TESTDIR%%/client_body_temp;
+    fastcgi_temp_path      %%TESTDIR%%/fastcgi_temp;
+    proxy_temp_path        %%TESTDIR%%/proxy_temp;
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+
+        location = /mail/auth {
+            add_header Auth-Status OK;
+            add_header Auth-Server 127.0.0.1;
+            add_header Auth-Port   8026;
+            add_header Auth-Wait   1;
+            return 204;
+        }
+    }
+}
+
+EOF
+
+###############################################################################
+
+# When XCLIENT's HELO= argument isn't used, the  following combinations may be
+# send to backend with xclient on:
+#
+# xclient
+# xclient, helo
+# xclient, ehlo
+# xclient, from, rcpt
+# xclient, helo, from, rcpt
+# xclient, ehlo, from, rcpt
+#
+# Test them in order.
+
+# xclient
+
+my $s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('AUTH PLAIN ' . encode_base64("\0test\@example.com\0secret", ''));
+$s->authok('xclient');
+
+# xclient, helo
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('HELO example.com');
+$s->read();
+$s->send('AUTH PLAIN ' . encode_base64("\0test\@example.com\0secret", ''));
+$s->authok('xclient, helo');
+
+# xclient, ehlo
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('EHLO example.com');
+$s->read();
+$s->send('AUTH PLAIN ' . encode_base64("\0test\@example.com\0secret", ''));
+$s->authok('xclient, ehlo');
+
+# xclient, from, rcpt
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('MAIL FROM:<test@example.com>');
+$s->read();
+$s->send('RCPT TO:<test@example.com>');
+$s->ok('xclient, from');
+
+# xclient, helo, from, rcpt
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('HELO example.com');
+$s->read();
+$s->send('MAIL FROM:<test@example.com>');
+$s->read();
+$s->send('RCPT TO:<test@example.com>');
+$s->ok('xclient, helo, from');
+
+# xclient, ehlo, from, rcpt
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('EHLO example.com');
+$s->read();
+$s->send('MAIL FROM:<test@example.com>');
+$s->read();
+$s->send('RCPT TO:<test@example.com>');
+$s->ok('xclient, ehlo, from');
+
+###############################################################################
--- a/smtp.t
+++ b/smtp.t
@@ -26,7 +26,7 @@ select STDERR; $| = 1;
 select STDOUT; $| = 1;
 
 my $t = Test::Nginx->new()
-	->has('mail')->plan(26)
+	->has('mail')->plan(25)
 	->run_daemon(\&Test::Nginx::SMTP::smtp_test_daemon)
 	->write_file_expand('nginx.conf', <<'EOF')->run();
 
@@ -34,7 +34,6 @@ master_process off;
 daemon         off;
 
 events {
-    worker_connections  1024;
 }
 
 mail {
@@ -66,7 +65,9 @@ http {
             if ($http_auth_smtp_to ~ example.com) {
                 set $reply OK;
             }
-            if ($http_auth_pass ~ secret) {
+
+            set $userpass "$http_auth_user:$http_auth_pass";
+            if ($userpass ~ '^test@example.com:secret$') {
                 set $reply OK;
             }
 
@@ -89,11 +90,11 @@ my $s = Test::Nginx::SMTP->new();
 $s->send('EHLO example.com');
 $s->check(qr/^250 /, "ehlo");
 
-$s->send('AUTH PLAIN ' . encode_base64("test\@example.com\0\0bad", ''));
+$s->send('AUTH PLAIN ' . encode_base64("\0test\@example.com\0bad", ''));
 $s->check(qr/^5.. /, 'auth plain with bad password');
 
-$s->send('AUTH PLAIN ' . encode_base64("test\@example.com\0\0secret", ''));
-$s->ok('auth plain');
+$s->send('AUTH PLAIN ' . encode_base64("\0test\@example.com\0secret", ''));
+$s->authok('auth plain');
 
 # We are talking to backend from this point
 
@@ -109,13 +110,45 @@ my $s = Test::Nginx::SMTP->new();
 $s->send('QUIT');
 $s->ok("quit");
 
-# Try auth plain with pipelining
+# Try auth login in simple form
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
+$s->send('EHLO example.com');
+$s->read();
+
+$s->send('AUTH LOGIN');
+$s->check(qr/^334 VXNlcm5hbWU6/, 'auth login simple username challenge');
+$s->send(encode_base64('test@example.com', ''));
+$s->check(qr/^334 UGFzc3dvcmQ6/, 'auth login simple password challenge');
+$s->send(encode_base64('secret', ''));
+$s->authok('auth login simple');
+
+# Try auth plain with username.  Details:
+#
+# [MS-XLOGIN]: SMTP Protocol AUTH LOGIN Extension Specification
+# http://download.microsoft.com/download/5/D/D/5DD33FDF-91F5-496D-9884-0A0B0EE698BB/%5BMS-XLOGIN%5D.pdf
 
 $s = Test::Nginx::SMTP->new();
-$s->check(qr/^220 /, "greeting");
+$s->read();
+$s->send('EHLO example.com');
+$s->read();
+
+$s->send('AUTH LOGIN ' . encode_base64('test@example.com', ''));
+$s->check(qr/^334 UGFzc3dvcmQ6/, 'auth login with username password challenge');
+$s->send(encode_base64('secret', ''));
+$s->authok('auth login with username');
 
+# Try auth plain with pipelining
+
+TODO: {
+local $TODO = 'pipelining not in official nginx';
+local $SIG{__WARN__} = sub {};
+
+$s = Test::Nginx::SMTP->new();
+$s->read();
 $s->send('EHLO example.com');
-$s->check(qr/^250 /, "ehlo");
+$s->read();
 
 $s->send('INVALID COMMAND WITH ARGUMENTS' . CRLF
 	. 'RSET');
@@ -123,24 +156,25 @@ my $s = Test::Nginx::SMTP->new();
 $s->ok('pipelined rset after invalid command');
 
 $s->send('AUTH PLAIN '
-	. encode_base64("test\@example.com\0\0bad", '') . CRLF
+	. encode_base64("\0test\@example.com\0bad", '') . CRLF
 	. 'MAIL FROM:<test@example.com> SIZE=100');
 $s->read();
 $s->ok('mail from after failed pipelined auth');
 
 $s->send('AUTH PLAIN '
-	. encode_base64("test\@example.com\0\0secret", '') . CRLF
+	. encode_base64("\0test\@example.com\0secret", '') . CRLF
 	. 'MAIL FROM:<test@example.com> SIZE=100');
 $s->read();
 $s->ok('mail from after pipelined auth');
 
+}
+
 # Try auth none
 
 $s = Test::Nginx::SMTP->new();
-$s->check(qr/^220 /, "greeting");
-
+$s->read();
 $s->send('EHLO example.com');
-$s->check(qr/^250 /, "ehlo");
+$s->read();
 
 $s->send('MAIL FROM:<test@example.com> SIZE=100');
 $s->ok('auth none - mail from');
@@ -154,10 +188,9 @@ my $s = Test::Nginx::SMTP->new();
 # Auth none with pipelining
 
 $s = Test::Nginx::SMTP->new();
-$s->check(qr/^220 /, "greeting");
-
+$s->read();
 $s->send('EHLO example.com');
-$s->check(qr/^250 /, "ehlo");
+$s->read();
 
 $s->send('MAIL FROM:<test@example.com> SIZE=100' . CRLF
 	. 'RCPT TO:<test@example.com>' . CRLF
@@ -165,16 +198,21 @@ my $s = Test::Nginx::SMTP->new();
 
 $s->ok('pipelined mail from');
 
+TODO: {
+local $TODO = 'pipelining not in official nginx';
+local $SIG{__WARN__} = sub {};
+
 $s->ok('pipelined rcpt to');
 $s->ok('pipelined rset');
 
+}
+
 # Connection must stay even if error returned to rcpt to command
 
 $s = Test::Nginx::SMTP->new();
-$s->read(); # skip greeting
-
+$s->read();
 $s->send('EHLO example.com');
-$s->read(); # skip ehlo reply
+$s->read();
 
 $s->send('MAIL FROM:<test@example.com> SIZE=100');
 $s->read(); # skip mail from reply
--- a/ssi-include-big.t
+++ b/ssi-include-big.t
@@ -29,7 +29,6 @@ master_process off;
 daemon         off;
 
 events {
-    worker_connections  1024;
 }
 
 http {
@@ -83,15 +82,10 @@ my $t2 = http_gzip_request('/test2.html'
 ok(defined $t2, 'small included file (equal to output_buffers)');
 http_gzip_like($t2, qr/^X{1024}\Z/, 'small included file content');
 
-TODO: {
-local $TODO = 'not fixed yet, patch under review';
-
 my $t3 = http_gzip_request('/test3.html');
 ok(defined $t3, 'big included file (more than output_buffers)');
 http_gzip_like($t3, qr/^X{1025}\Z/, 'big included file content');
 
-}
-
 my $t4 = http_gzip_request('/test4.html');
 ok(defined $t4, 'big ssi main file');
 http_gzip_like($t4, qr/^X{1025}\Z/, 'big ssi main file content');
new file mode 100644
--- /dev/null
+++ b/ssi.t
@@ -0,0 +1,74 @@
+#!/usr/bin/perl
+
+# (C) Maxim Dounin
+
+# Tests for nginx ssi module.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->plan(9);
+
+$t->write_file_expand('nginx.conf', <<'EOF');
+
+master_process off;
+daemon         off;
+
+events {
+}
+
+http {
+    access_log    off;
+    root          %%TESTDIR%%;
+
+    client_body_temp_path  %%TESTDIR%%/client_body_temp;
+    fastcgi_temp_path      %%TESTDIR%%/fastcgi_temp;
+    proxy_temp_path        %%TESTDIR%%/proxy_temp;
+
+    server {
+        listen       127.0.0.1:8080;
+        server_name  localhost;
+        ssi on;
+    }
+}
+
+EOF
+
+$t->write_file('test1.html', 'X<!--#echo var="arg_test" -->X');
+$t->write_file('test2.html',
+	'X<!--#include virtual="/test1.html?test=test" -->X');
+$t->write_file('test3.html',
+	'X<!--#set var="blah" value="test" --><!--#echo var="blah" -->X');
+
+$t->run();
+
+###############################################################################
+
+like(http_get('/test1.html'), qr/^X\(none\)X$/m, 'echo no argument');
+like(http_get('/test1.html?test='), qr/^XX$/m, 'empty argument');
+like(http_get('/test1.html?test=test'), qr/^XtestX$/m, 'argument');
+like(http_get('/test1.html?test=test&a=b'), qr/^XtestX$/m, 'argument 2');
+like(http_get('/test1.html?a=b&test=test'), qr/^XtestX$/m, 'argument 3');
+like(http_get('/test1.html?a=b&test=test&d=c'), qr/^XtestX$/m, 'argument 4');
+like(http_get('/test1.html?atest=a&testb=b&ctestc=c&test=test'), qr/^XtestX$/m,
+	'argument 5');
+
+like(http_get('/test2.html'), qr/^XXtestXX$/m, 'argument via include');
+
+like(http_get('/test3.html'), qr/^XtestX$/m, 'set');
+
+###############################################################################