File Coverage

blib/lib/Test/HTTP/log-server
Criterion Covered Total %
statement 105 159 66.0
branch 47 88 53.4
condition 6 10 60.0
subroutine 11 13 84.6
pod n/a
total 169 270 62.5


line stmt bran cond sub pod time code
1             # Thanks to merlyn for nudging me and giving me this snippet!
2 8     8   64435 use strict;
  8         18  
  8         431  
3 8     8   4980 use HTTP::Daemon 6.05;
  8         1139100  
  8         126  
4 8     8   5156 use URI;
  8         17  
  8         244  
5 8     8   42500 use CGI;
  8         382212  
  8         58  
6 8     8   6360 use HTTP::Request::AsCGI;
  8         88642  
  8         65  
7 8     8   6224 use Getopt::Long;
  8         102802  
  8         44  
8 8     8   1345 use Socket();
  8         17  
  8         282  
9 8     8   4322 use Time::HiRes 'sleep';
  8         10892  
  8         59  
10 8         1945923 our $VERSION = '0.74';
11              
12 8         55 $|++;
13              
14 8         88 GetOptions(
15             'e=s' => \my $expression,
16             'f=s' => \my $url_filename,
17             's=s' => \my $request_pause,
18             );
19              
20 8 50       9414 if( ! defined $request_pause ) {
21 0         0 $request_pause = 1;
22             }
23              
24             # HTTP::Daemon(IO::Socket::IP) sets $@ in case of error
25 8 50       106 my $d = HTTP::Daemon->new or die "Couldn't create HTTP::Daemon: $@";
26              
27 8         5939 my $url = URI->new( $d->url );
28 8 50       85958 if( $d->sockdomain == Socket::AF_INET ) {
    0          
29 8         206 $url->host('127.0.0.1');
30             } elsif ($d->sockdomain == Socket::AF_INET6 ) {
31 0         0 $url->host('[::1]');
32             } else {
33 0         0 die "Unexpected sockdomain: " . $d->sockdomain;
34             };
35              
36             {
37 8         3816 my $fh;
  8         36  
38 8 50       37 if( $url_filename ) {
39 8 50       997 open $fh, '>', $url_filename
40             or die "Couldn't write URL to tempfile '$url_filename': $!";
41             } else {
42 0         0 $fh = \*STDOUT;
43             };
44 8         52 print {$fh} "$url\n";
  8         70  
45 8 50       1530 close $fh unless $url_filename;
46             }
47              
48 8         64 my ($filename,$logfile) = @ARGV[0,1];
49 8 50       38 if ($filename) {
50 0 0       0 open DATA, "< $filename"
51             or die "Couldn't read page '$filename' : $!\n";
52             };
53             #open LOG, ">", $logfile
54             # or die "Couldn't create logfile '$logfile' : $!\n";
55 8         20 my $log;
56 8         644 my $body = join "", ;
57              
58             sub debug($) {
59 70     70   7185 my $message = $_[0];
60 70         482 $message =~ s!\n!\n#SERVER:!g;
61             warn "#SERVER: $message"
62 70 50       348 if $ENV{TEST_HTTP_VERBOSE};
63             };
64              
65 8 50       85 my $multi_param = eval { CGI->can('multi_param') } ? 'multi_param' : 'param';
  8         267  
66              
67             sub respond_200 {
68 4     4   15 my( $location, $r ) = @_;
69 4         62 my $context = HTTP::Request::AsCGI->new( $r )->setup;
70 4         12574 my $q = CGI->new();
71              
72             # Make sticky form fields
73 4         1737 my ($filename, $filetype, $filecontent, $query,$botcheck_query,$query2,$session,%cat);
74 4 100       15 $query = defined $q->param('query') ? $q->param('query') : "(empty)";
75 4 50       111 $botcheck_query = defined $q->param('botcheck_query') ? $q->param('botcheck_query') : "(empty)";
76 4 50       278 $query2 = defined $q->param('query2') ? $q->param('query2') : "(empty)";
77 4 50       80 $session = defined $q->param('session') ? $q->param('session') : 1;
78 4         87 my @cats = $q->$multi_param('cat');
79 4 50       102 %cat = map { $_ => 1 } ( @cats ? @cats : qw( cat_foo cat_bar ));
  8         29  
80 4 100       39 my @categories = map { $cat{$_} ? "checked" : "" } qw( cat_foo cat_bar cat_baz );
  12         39  
81 4         24 my $headers = CGI::escapeHTML( $r->headers->as_string );
82 4         22508 my $rbody = sprintf $body,$headers, $location,
83             $filename, $filetype, $filecontent,
84             $session,$query,$botcheck_query,$query2,@categories,
85             ;
86 4         43 my $res = HTTP::Response->new(200, "OK", [
87             'Set-Cookie' => $q->cookie(-name => 'log-server-httponly',-value=>'supersecret', -discard => 1, -httponly=>1),
88             'Set-Cookie' => $q->cookie(-name => 'log-server',-value=>'shazam2', -discard=>1,),
89             'Cache-Control' => 'no-cache',
90             'Pragma' => 'no-cache',
91             'Max-Age' => 0,
92             'Connection' => 'close',
93             'Content-Length' => length($rbody),
94             ], $rbody);
95 4         15901 $res->content_type('text/html; charset=ISO-8859-1');
96 4   50     286 debug "Request " . ($r->uri->path || "/");
97 4         89 $res
98             }
99              
100             SERVERLOOP: {
101 8         21 my $quitserver;
  8         42  
102 8         58 while (my $c = $d->accept) {
103 16         593102 debug "New connection";
104 16         166 while (my $r = $c->get_request) {
105 16         33964 debug "Request:\n" . $r->as_string;
106 16   50     79 my $location = ($r->uri->path || "/");
107 16         637 my ($link1,$link2) = ('','');
108 16 50       82 if ($location =~ m!^/link/([^/]+)/(.*)$!) {
109 0         0 ($link1,$link2) = ($1,$2);
110             };
111 16         35 my $res;
112 16 100       90 if ($location eq '/get_server_log') {
    100          
113 2         21 $res = HTTP::Response->new(200, "OK", undef, $log);
114 2         109 $log = '';
115             } elsif ( $location eq '/quit_server') {
116 8         36 debug "Quitting";
117 8         86 $res = HTTP::Response->new(200, "OK", [Connection => 'close'], "quit");
118 8         1049 $quitserver = 1;
119             } else {
120 6 50       26 eval $expression
121             if $expression;
122 6 50       62 warn "eval: $@" if $@;
123 6         37 $log .= "Request:\n" . $r->as_string . "\n";
124 6 50 33     922 if ($location =~ m!^/redirect/(.*)$!) {
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    50          
    100          
125 0         0 $res = HTTP::Response->new(302);
126 0         0 $res->header('location', $url . $1);
127              
128             } elsif ($location =~ m!^/local/(.*)$!) {
129 0         0 my $rbody= do { open my $fh, '<', $1; binmode $fh; local $/; <$fh> };
  0         0  
  0         0  
  0         0  
  0         0  
130 0         0 $res = HTTP::Response->new(200, "OK", [
131             'Cache-Control' => 'no-cache',
132             'Pragma' => 'no-cache',
133             'Max-Age' => 0,
134             'Connection' => 'close',
135             'Content-Length' => length($rbody),
136             ], $rbody);
137              
138             } elsif ($location =~ m!^/download/([\w.-]+)$!) {
139 0         0 my $rbody= do { open my $fh, '<', $0; binmode $fh; local $/; <$fh> };
  0         0  
  0         0  
  0         0  
  0         0  
140 0         0 $res = HTTP::Response->new(200, "OK", [
141             'Cache-Control' => 'no-cache',
142             'Pragma' => 'no-cache',
143             'Max-Age' => 0,
144             'Connection' => 'close',
145             'Content-Length' => length($rbody),
146             'Content-Disposition' => qq{attachment; filename=$1;},
147             ], $rbody);
148              
149             } elsif ($location =~ m!^/error/notfound/(.*)$! or $location =~ m!^/favicon.ico!) {
150 0         0 $res = HTTP::Response->new(404, "Not found", [Connection => 'close']);
151             } elsif ($location =~ m!^/error/timeout/(\d+)$!) {
152 0         0 sleep $1;
153 0         0 $res = HTTP::Response->new(599, "Timeout reached", [Connection => 'close']);
154              
155             } elsif ($location =~ m!^/error/close/(\d+)$!) {
156 0         0 sleep $1;
157 0         0 $res = undef;
158              
159             } elsif ( $location =~ m!^/chunks!) {
160 0         0 my $count = 5;
161             $res = HTTP::Response->new(200, "OK", undef, sub {
162 0     0   0 sleep 1;
163 0         0 my $buf = 'x' x 16;
164 0 0       0 return $buf if $count-- > 0;
165 0         0 return undef; # done
166 0         0 });
167              
168             } elsif ($location =~ m!^/error/after_headers$!) {
169 0         0 my $count = 2;
170             $res = HTTP::Response->new(200, "OK", undef, sub {
171 0     0   0 sleep 1;
172 0         0 my $buf = 'x' x 16;
173 0 0       0 return $buf if $count-- > 0;
174 0         0 die "Planned error after headers";
175 0         0 });
176              
177             } elsif ($location =~ m!^/large/bzip/16M$!) {
178 0         0 my $headers = HTTP::Headers->new(
179             Content_Type => "application/xml",
180             Content_Encoding => 'bzip2,bzip2,bzip2', # say my name three times
181             );
182             # 16M bzip thrice-encoded, see gen-bzipbomb.pl
183 0         0 $body = join "",
184             "BZh11AY&SY\tPFN\0\0'\177\377\355\e\177v\363\267|\344?\226]pVbW\25\313|F",
185             "]h0\30\303\305i\272CF9fS\260\0\271\b\32\32h\323\32414\304ddbh4\304h4\304z\231\6h",
186             "#\32\2154\310\365=\4`\32 fQ\341O)\371Q\6L\0\230\0\t\200#L#\0\0\0\4\311\246&\203",
187             "\0#\0\0\0\0\203&\322a11\240\0&\21\200\320\232`\1\0310\4\323\0#4\20d\300L\4d`\34",
188             "\370I\21o\f\304\0\205b\344\365u\326\334O\301\0054}\306\274\215\246\240\351\247\240",
189             "M\252\333Je)\25\217\231\230\00046\236)\4(R\301\370\363\371\350\277\b0\26\275\16&",
190             "W\260\2\2151\272\177\301\366}\327b\213\374\t\264g~\245\203\225\220\2660,\226\213",
191             "\247\246l\351\303\304\300\$z0Hg\272;\31\226B\244\266\376\301\364\355I~\222\273",
192             "\226*S\"\3\263\360\200Iv\241}|\344\227q\1I\6\217I\30\302\2\261\207\224h\305\16\17",
193             "\324\1779\1\247\\R{\335\$pM8cL\"\201\311 \374\364P\274\227p\237\300\320`\36pJ\264",
194             "\21\277\305\334\221N\24\$\2T\21\223\200"
195             ;
196 0         0 $res = HTTP::Response->new(200, "OK", $headers, $body);
197              
198             } elsif ($location =~ m!^/large/gzip/16M$!) {
199 0         0 my $headers = HTTP::Headers->new(
200             Content_Type => "application/xml",
201             Content_Encoding => 'gzip,gzip,gzip', # say my name three times
202             );
203             # 16M bzip thrice-encoded, see gen-gzipbomb.pl
204 0         0 $body = join "",
205             "\37\213\b\0\0\0\0\0\0\377\223\357\346`\0\203\377o/l\344mr`h}h\235\321\341",
206             "- T^\300^\225-\276p\307\221Km\242>/b\31\237%\260>\346\220S7\2760\243&\376\363",
207             "\277_[\373\325\336|\252\356\334\230#\265\177\275\1771\27\304\f\206\3\363\275_",
208             "\357]Ww\361\351\355\247o\370\241b\26\aj\336\316?\34\242\224\27a\347\24\270",
209             "\336\236\201\1\0!\203w\217s\0\0\0",
210             ;
211 0         0 $res = HTTP::Response->new(200, "OK", $headers, $body);
212              
213             } elsif ($location =~ m!^/content/(.*)$!) {
214 0         0 my $headers = HTTP::Headers->new(
215             Content_Type => "text/html",
216             );
217 0         0 (my $html = $1) =~ s!%([a-fA-F0-9]{2})!chr(hex($1))!ge;
  0         0  
218 0         0 $body = join "",
219             "",
220             "$html",
221             "",
222             ;
223 0         0 $res = HTTP::Response->new(200, "OK", $headers, $body);
224              
225             } elsif ($location =~ m!^/basic_auth/([^/]+)/([^/]+)$!) {
226 3         22 my ($user, $pass) = $r->authorization_basic;
227 3         2294 my( $ex_user, $ex_pass ) = ($1,$2);
228 3 100 100     24 if( $user eq $ex_user
229             and $pass eq $ex_pass) {
230 1         8 $res = respond_200( $location, $r );
231              
232             } else {
233 2         14 debug "# User : '$user' Password : '$pass'\n";
234 2         28 $res = HTTP::Response->new(401, "Auth Required", undef,
235             "auth required ($user/$pass)");
236 2         265 $res->www_authenticate("Basic realm=\"testing realm\"");
237             };
238              
239             } else {
240 3         14 $res = respond_200( $location, $r );
241             };
242             };
243 16 50       2250 debug "Response:\n" . $res->as_string
244             if $res;
245 16         47 eval {
246 16 50       181 $c->send_response($res)
247             if $res;
248             };
249 16 50       11725 if (my $err = $@) {
250 0         0 debug "Server raised error: $err";
251 0 0       0 if ($err !~ /^Planned error\b/) {
252 0         0 warn $err;
253             };
254 0         0 $c->close;
255             };
256 16 50       367 if (! $res) {
257 0         0 $c->close;
258             };
259 16 100       300 last if $quitserver;
260             }
261 16         15169 sleep $request_pause;
262 16         77 undef($c);
263             last SERVERLOOP
264 16 100       2788 if $quitserver;
265             };
266 0         0 undef $d;
267             };
268 8     8   125 END { debug "Server $$ stopped" };
269              
270             # The below tag should stop the browser from requesting a favicon.ico, but we still see it...
271             __DATA__