Nginx.pm 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. package Test::Nginx;
  2. # (C) Maxim Dounin
  3. # Generic module for nginx tests.
  4. ###############################################################################
  5. use warnings;
  6. use strict;
  7. use base qw/ Exporter /;
  8. our @EXPORT = qw/ log_in log_out http http_get http_head /;
  9. our @EXPORT_OK = qw/ http_gzip_request http_gzip_like /;
  10. our %EXPORT_TAGS = (
  11. gzip => [ qw/ http_gzip_request http_gzip_like / ]
  12. );
  13. ###############################################################################
  14. use File::Temp qw/ tempdir /;
  15. use IO::Socket;
  16. use Socket qw/ CRLF /;
  17. use Test::More qw//;
  18. ###############################################################################
  19. our $NGINX = defined $ENV{TEST_NGINX_BINARY} ? $ENV{TEST_NGINX_BINARY}
  20. : '../nginx/objs/nginx';
  21. sub new {
  22. my $self = {};
  23. bless $self;
  24. $self->{_testdir} = tempdir(
  25. 'nginx-test-XXXXXXXXXX',
  26. TMPDIR => 1,
  27. CLEANUP => not $ENV{TEST_NGINX_LEAVE}
  28. )
  29. or die "Can't create temp directory: $!\n";
  30. return $self;
  31. }
  32. sub DESTROY {
  33. my ($self) = @_;
  34. $self->stop();
  35. $self->stop_daemons();
  36. if ($ENV{TEST_NGINX_CATLOG}) {
  37. system("cat $self->{_testdir}/error.log");
  38. }
  39. }
  40. sub has($;) {
  41. my ($self, @features) = @_;
  42. foreach my $feature (@features) {
  43. Test::More::plan(skip_all => "$feature not compiled in")
  44. unless $self->has_module($feature);
  45. }
  46. return $self;
  47. }
  48. sub has_module($) {
  49. my ($self, $feature) = @_;
  50. my %regex = (
  51. mail => '--with-mail(?!\S)',
  52. flv => '--with-http_flv_module',
  53. perl => '--with-http_perl_module',
  54. charset => '(?s)^(?!.*--without-http_charset_module)',
  55. gzip => '(?s)^(?!.*--without-http_gzip_module)',
  56. ssi => '(?s)^(?!.*--without-http_ssi_module)',
  57. userid => '(?s)^(?!.*--without-http_userid_module)',
  58. access => '(?s)^(?!.*--without-http_access_module)',
  59. auth_basic
  60. => '(?s)^(?!.*--without-http_auth_basic_module)',
  61. autoindex
  62. => '(?s)^(?!.*--without-http_autoindex_module)',
  63. geo => '(?s)^(?!.*--without-http_geo_module)',
  64. map => '(?s)^(?!.*--without-http_map_module)',
  65. referer => '(?s)^(?!.*--without-http_referer_module)',
  66. rewrite => '(?s)^(?!.*--without-http_rewrite_module)',
  67. proxy => '(?s)^(?!.*--without-http_proxy_module)',
  68. fastcgi => '(?s)^(?!.*--without-http_fastcgi_module)',
  69. uwsgi => '(?s)^(?!.*--without-http_uwsgi_module)',
  70. scgi => '(?s)^(?!.*--without-http_scgi_module)',
  71. memcached
  72. => '(?s)^(?!.*--without-http_memcached_module)',
  73. limit_zone
  74. => '(?s)^(?!.*--without-http_limit_zone_module)',
  75. limit_req
  76. => '(?s)^(?!.*--without-http_limit_req_module)',
  77. empty_gif
  78. => '(?s)^(?!.*--without-http_empty_gif_module)',
  79. browser => '(?s)^(?!.*--without-http_browser_module)',
  80. upstream_ip_hash
  81. => '(?s)^(?!.*--without-http_upstream_ip_hash_module)',
  82. http => '(?s)^(?!.*--without-http(?!\S))',
  83. cache => '(?s)^(?!.*--without-http-cache)',
  84. pop3 => '(?s)^(?!.*--without-mail_pop3_module)',
  85. imap => '(?s)^(?!.*--without-mail_imap_module)',
  86. smtp => '(?s)^(?!.*--without-mail_smtp_module)',
  87. pcre => '(?s)^(?!.*--without-pcre)',
  88. );
  89. my $re = $regex{$feature};
  90. $re = $feature if !defined $re;
  91. $self->{_configure_args} = `$NGINX -V 2>&1`
  92. if !defined $self->{_configure_args};
  93. return ($self->{_configure_args} =~ $re) ? 1 : 0;
  94. }
  95. sub has_daemon($) {
  96. my ($self, $daemon) = @_;
  97. Test::More::plan(skip_all => "$daemon not found")
  98. unless `which $daemon`;
  99. return $self;
  100. }
  101. sub plan($) {
  102. my ($self, $plan) = @_;
  103. Test::More::plan(tests => $plan);
  104. return $self;
  105. }
  106. sub run(;$) {
  107. my ($self, $conf) = @_;
  108. my $testdir = $self->{_testdir};
  109. if (defined $conf) {
  110. my $c = `cat $conf`;
  111. $self->write_file_expand('nginx.conf', $c);
  112. }
  113. my $pid = fork();
  114. die "Unable to fork(): $!\n" unless defined $pid;
  115. if ($pid == 0) {
  116. my @globals = $self->{_test_globals} ?
  117. () : ('-g', "pid $testdir/nginx.pid; "
  118. . "error_log $testdir/error.log debug;");
  119. exec($NGINX, '-c', "$testdir/nginx.conf", @globals)
  120. or die "Unable to exec(): $!\n";
  121. }
  122. # wait for nginx to start
  123. $self->waitforfile("$testdir/nginx.pid")
  124. or die "Can't start nginx";
  125. $self->{_started} = 1;
  126. return $self;
  127. }
  128. sub waitforfile($) {
  129. my ($self, $file) = @_;
  130. # wait for file to appear
  131. for (1 .. 30) {
  132. return 1 if -e $file;
  133. select undef, undef, undef, 0.1;
  134. }
  135. return undef;
  136. }
  137. sub waitforsocket($) {
  138. my ($self, $peer) = @_;
  139. # wait for socket to accept connections
  140. for (1 .. 30) {
  141. my $s = IO::Socket::INET->new(
  142. Proto => 'tcp',
  143. PeerAddr => $peer
  144. );
  145. return 1 if defined $s;
  146. select undef, undef, undef, 0.1;
  147. }
  148. return undef;
  149. }
  150. sub stop() {
  151. my ($self) = @_;
  152. return $self unless $self->{_started};
  153. kill 'QUIT', `cat $self->{_testdir}/nginx.pid`;
  154. wait;
  155. $self->{_started} = 0;
  156. return $self;
  157. }
  158. sub stop_daemons() {
  159. my ($self) = @_;
  160. while ($self->{_daemons} && scalar @{$self->{_daemons}}) {
  161. my $p = shift @{$self->{_daemons}};
  162. kill 'TERM', $p;
  163. wait;
  164. }
  165. return $self;
  166. }
  167. sub write_file($$) {
  168. my ($self, $name, $content) = @_;
  169. open F, '>' . $self->{_testdir} . '/' . $name
  170. or die "Can't create $name: $!";
  171. print F $content;
  172. close F;
  173. return $self;
  174. }
  175. sub write_file_expand($$) {
  176. my ($self, $name, $content) = @_;
  177. $content =~ s/%%TEST_GLOBALS%%/$self->test_globals()/gmse;
  178. $content =~ s/%%TEST_GLOBALS_HTTP%%/$self->test_globals_http()/gmse;
  179. $content =~ s/%%TESTDIR%%/$self->{_testdir}/gms;
  180. return $self->write_file($name, $content);
  181. }
  182. sub run_daemon($;@) {
  183. my ($self, $code, @args) = @_;
  184. my $pid = fork();
  185. die "Can't fork daemon: $!\n" unless defined $pid;
  186. if ($pid == 0) {
  187. if (ref($code) eq 'CODE') {
  188. $code->(@args);
  189. exit 0;
  190. } else {
  191. exec($code, @args);
  192. }
  193. }
  194. $self->{_daemons} = [] unless defined $self->{_daemons};
  195. push @{$self->{_daemons}}, $pid;
  196. return $self;
  197. }
  198. sub testdir() {
  199. my ($self) = @_;
  200. return $self->{_testdir};
  201. }
  202. sub test_globals() {
  203. my ($self) = @_;
  204. return $self->{_test_globals}
  205. if defined $self->{_test_globals};
  206. my $s = '';
  207. $s .= "pid $self->{_testdir}/nginx.pid;\n";
  208. $s .= "error_log $self->{_testdir}/error.log debug;\n";
  209. $self->{_test_globals} = $s;
  210. }
  211. sub test_globals_http() {
  212. my ($self) = @_;
  213. return $self->{_test_globals_http}
  214. if defined $self->{_test_globals_http};
  215. my $s = '';
  216. $s .= "root $self->{_testdir};\n";
  217. $s .= "access_log $self->{_testdir}/access.log;\n";
  218. $s .= "client_body_temp_path $self->{_testdir}/client_body_temp;\n";
  219. $s .= "fastcgi_temp_path $self->{_testdir}/fastcgi_temp;\n"
  220. if $self->has_module('fastcgi');
  221. $s .= "proxy_temp_path $self->{_testdir}/proxy_temp;\n"
  222. if $self->has_module('proxy');
  223. $s .= "uwsgi_temp_path $self->{_testdir}/uwsgi_temp;\n"
  224. if $self->has_module('uwsgi');
  225. $s .= "scgi_temp_path $self->{_testdir}/scgi_temp;\n"
  226. if $self->has_module('scgi');
  227. $self->{_test_globals_http} = $s;
  228. }
  229. ###############################################################################
  230. sub log_core {
  231. return unless $ENV{TEST_NGINX_VERBOSE};
  232. my ($prefix, $msg) = @_;
  233. ($prefix, $msg) = ('', $prefix) unless defined $msg;
  234. $prefix .= ' ' if length($prefix) > 0;
  235. if (length($msg) > 4096) {
  236. $msg = substr($msg, 0, 4096)
  237. . "(...logged only 4096 of " . length($msg)
  238. . " bytes)";
  239. }
  240. $msg =~ s/^/# $prefix/gm;
  241. $msg =~ s/([^\x20-\x7e])/sprintf('\\x%02x', ord($1)) . (($1 eq "\n") ? "\n" : '')/gmxe;
  242. $msg .= "\n" unless $msg =~ /\n\Z/;
  243. print $msg;
  244. }
  245. sub log_out {
  246. log_core('>>', @_);
  247. }
  248. sub log_in {
  249. log_core('<<', @_);
  250. }
  251. ###############################################################################
  252. sub http_get($;%) {
  253. my ($url, %extra) = @_;
  254. return http(<<EOF, %extra);
  255. GET $url HTTP/1.0
  256. Host: localhost
  257. EOF
  258. }
  259. sub http_head($;%) {
  260. my ($url, %extra) = @_;
  261. return http(<<EOF, %extra);
  262. HEAD $url HTTP/1.0
  263. Host: localhost
  264. EOF
  265. }
  266. sub http($;%) {
  267. my ($request, %extra) = @_;
  268. my $reply;
  269. eval {
  270. local $SIG{ALRM} = sub { die "timeout\n" };
  271. local $SIG{PIPE} = sub { die "sigpipe\n" };
  272. alarm(2);
  273. my $s = IO::Socket::INET->new(
  274. Proto => 'tcp',
  275. PeerAddr => '127.0.0.1:8080'
  276. );
  277. log_out($request);
  278. $s->print($request);
  279. local $/;
  280. select undef, undef, undef, $extra{sleep} if $extra{sleep};
  281. return '' if $extra{aborted};
  282. $reply = $s->getline();
  283. alarm(0);
  284. };
  285. alarm(0);
  286. if ($@) {
  287. log_in("died: $@");
  288. return undef;
  289. } else {
  290. log_in($reply);
  291. }
  292. return $reply;
  293. }
  294. ###############################################################################
  295. sub http_gzip_request {
  296. my ($url) = @_;
  297. my $r = http(<<EOF);
  298. GET $url HTTP/1.1
  299. Host: localhost
  300. Connection: close
  301. Accept-Encoding: gzip
  302. EOF
  303. }
  304. sub http_content {
  305. my ($text) = @_;
  306. return undef if !defined $text;
  307. if ($text !~ /(.*?)\x0d\x0a?\x0d\x0a?(.*)/ms) {
  308. return undef;
  309. }
  310. my ($headers, $body) = ($1, $2);
  311. if ($headers !~ /Transfer-Encoding: chunked/i) {
  312. return $body;
  313. }
  314. my $content = '';
  315. while ($body =~ /\G\x0d?\x0a?([0-9a-f]+)\x0d\x0a?/gcmsi) {
  316. my $len = hex($1);
  317. $content .= substr($body, pos($body), $len);
  318. pos($body) += $len;
  319. }
  320. return $content;
  321. }
  322. sub http_gzip_like {
  323. my ($text, $re, $name) = @_;
  324. SKIP: {
  325. eval { require IO::Uncompress::Gunzip; };
  326. Test::More->builder->skip(
  327. "IO::Uncompress::Gunzip not installed", 1) if $@;
  328. my $in = http_content($text);
  329. my $out;
  330. IO::Uncompress::Gunzip::gunzip(\$in => \$out);
  331. Test::More->builder->like($out, $re, $name);
  332. }
  333. }
  334. ###############################################################################
  335. 1;
  336. ###############################################################################