xref: /lighttpd1.4/tests/request.t (revision 0945ce4f)
1#!/usr/bin/env perl
2BEGIN {
3	# add current source dir to the include-path
4	# we need this for make distcheck
5	(my $srcdir = $0) =~ s,/[^/]+$,/,;
6	unshift @INC, $srcdir;
7}
8
9use strict;
10use IO::Socket;
11use Test::More tests => 164;
12use LightyTest;
13
14my $tf = LightyTest->new();
15my $t;
16
17ok($tf->start_proc == 0, "Starting lighttpd") or die();
18
19## Basic Request-Handling
20
21$t->{REQUEST}  = ( <<EOF
22GET / HTTP/1.0
23EOF
24 );
25$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
26ok($tf->handle_http($t) == 0, 'Valid HTTP/1.0 Request') or ($tf->stop_proc, die());
27
28$t->{REQUEST}  = ( <<EOF
29OPTIONS * HTTP/1.0
30EOF
31 );
32$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
33ok($tf->handle_http($t) == 0, 'OPTIONS');
34
35$t->{REQUEST}  = ( <<EOF
36OPTIONS / HTTP/1.1
37Host: www.example.org
38Connection: close
39EOF
40 );
41$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } ];
42ok($tf->handle_http($t) == 0, 'OPTIONS');
43
44$t->{REQUEST}  = ( <<EOF
45GET /index.html%00 HTTP/1.0
46EOF
47 );
48$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ];
49ok($tf->handle_http($t) == 0, 'URL-encoding, %00');
50
51$t->{REQUEST}  = ( <<EOF
52POST /12345.txt HTTP/1.0
53Host: 123.example.org
54Content-Length: 2147483648
55EOF
56 );
57$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 413 } ];
58ok($tf->handle_http($t) == 0, 'Content-Length > max-request-size');
59
60$t->{REQUEST}  = ( <<EOF
61GET /image.jpg HTTP/1.0
62EOF
63 );
64$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'Content-Type' => 'image/jpeg' } ];
65ok($tf->handle_http($t) == 0, 'Content-Type - image/jpeg');
66
67$t->{REQUEST}  = ( <<EOF
68GET /image.JPG HTTP/1.0
69EOF
70 );
71$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'Content-Type' => 'image/jpeg' } ];
72ok($tf->handle_http($t) == 0, 'Content-Type - image/jpeg (upper case)');
73
74$t->{REQUEST}  = ( <<EOF
75GET /Foo.txt HTTP/1.0
76EOF
77 );
78$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
79ok($tf->handle_http($t) == 0, 'uppercase filenames');
80
81$t->{REQUEST}  = ( <<EOF
82GET /foobar?foobar HTTP/1.0
83EOF
84 );
85$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404 } ];
86ok($tf->handle_http($t) == 0, 'file not found + querystring');
87
88$t->{REQUEST}  = ( <<EOF
89GET /12345.txt HTTP/1.0
90Host: 123.example.org
91EOF
92 );
93$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain' } ];
94ok($tf->handle_http($t) == 0, 'GET, content == 12345, mimetype text/plain');
95
96$t->{REQUEST}  = ( <<EOF
97GET /12345.html HTTP/1.0
98Host: 123.example.org
99EOF
100 );
101$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/html' } ];
102ok($tf->handle_http($t) == 0, 'GET, content == 12345, mimetype text/html');
103
104
105$t->{REQUEST}  = ( <<EOF
106POST / HTTP/1.0
107Content-type: application/x-www-form-urlencoded
108Content-length: 0
109EOF
110 );
111$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
112ok($tf->handle_http($t) == 0, 'POST request, empty request-body');
113
114$t->{REQUEST}  = ( <<EOF
115HEAD / HTTP/1.0
116EOF
117 );
118$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '-HTTP-Content' => ''} ];
119ok($tf->handle_http($t) == 0, 'HEAD request, no content');
120
121$t->{REQUEST}  = ( <<EOF
122HEAD /12345.html HTTP/1.0
123Host: 123.example.org
124EOF
125 );
126$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '-HTTP-Content' => '', 'Content-Type' => 'text/html', 'Content-Length' => '6'} ];
127ok($tf->handle_http($t) == 0, 'HEAD request, mimetype text/html, content-length');
128
129$t->{REQUEST}  = ( <<EOF
130HEAD http://123.example.org/12345.html HTTP/1.1
131Connection: close
132EOF
133 );
134$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, '-HTTP-Content' => '', 'Content-Type' => 'text/html', 'Content-Length' => '6'} ];
135ok($tf->handle_http($t) == 0, 'Hostname in first line, HTTP/1.1');
136
137$t->{REQUEST}  = ( <<EOF
138HEAD https://123.example.org/12345.html HTTP/1.0
139EOF
140 );
141$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '-HTTP-Content' => '', 'Content-Type' => 'text/html', 'Content-Length' => '6'} ];
142ok($tf->handle_http($t) == 0, 'Hostname in first line as https url');
143
144$t->{REQUEST}  = ( <<EOF
145HEAD /foobar?foobar HTTP/1.0
146EOF
147 );
148$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404, '-HTTP-Content' => '' } ];
149ok($tf->handle_http($t) == 0, 'HEAD request, file-not-found, query-string');
150
151# (expect 200 OK instead of 100 Continue since request body sent with request)
152# (if we waited to send request body, would expect 100 Continue, first)
153$t->{REQUEST}  = ( <<EOF
154POST /cgi.pl?post-len HTTP/1.1
155Host: www.example.org
156Connection: close
157Content-Type: application/x-www-form-urlencoded
158Content-Length: 4
159Expect: 100-continue
160
161123
162EOF
163 );
164$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } ];
165ok($tf->handle_http($t) == 0, 'Continue, Expect');
166
167# note Transfer-Encoding: chunked tests will fail with 411 Length Required if
168#   server.stream-request-body != 0 in lighttpd.conf
169$t->{REQUEST}  = ( <<EOF
170POST /cgi.pl?post-len HTTP/1.1
171Host: www.example.org
172Connection: close
173Content-Type: application/x-www-form-urlencoded
174Transfer-Encoding: chunked
175
176a
1770123456789
1780
179
180EOF
181 );
182$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } ];
183ok($tf->handle_http($t) == 0, 'POST via Transfer-Encoding: chunked, lc hex');
184
185$t->{REQUEST}  = ( <<EOF
186POST /cgi.pl?post-len HTTP/1.1
187Host: www.example.org
188Connection: close
189Content-Type: application/x-www-form-urlencoded
190Transfer-Encoding: chunked
191
192A
1930123456789
1940
195
196EOF
197 );
198$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } ];
199ok($tf->handle_http($t) == 0, 'POST via Transfer-Encoding: chunked, uc hex');
200
201$t->{REQUEST}  = ( <<EOF
202POST /cgi.pl?post-len HTTP/1.1
203Host: www.example.org
204Connection: close
205Content-Type: application/x-www-form-urlencoded
206Transfer-Encoding: chunked
207
20810
2090123456789abcdef
2100
211
212EOF
213 );
214$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } ];
215ok($tf->handle_http($t) == 0, 'POST via Transfer-Encoding: chunked, two hex');
216
217$t->{REQUEST}  = ( <<EOF
218POST /cgi.pl?post-len HTTP/1.1
219Host: www.example.org
220Connection: close
221Content-Type: application/x-www-form-urlencoded
222Transfer-Encoding: chunked
223
224a
2250123456789
2260
227Test-Trailer: testing
228
229EOF
230 );
231$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } ];
232ok($tf->handle_http($t) == 0, 'POST via Transfer-Encoding: chunked, with trailer');
233
234$t->{REQUEST}  = ( <<EOF
235POST /cgi.pl?post-len HTTP/1.1
236Host: www.example.org
237Connection: close
238Content-Type: application/x-www-form-urlencoded
239Transfer-Encoding: chunked
240
241a; comment
2420123456789
2430
244
245EOF
246 );
247$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } ];
248ok($tf->handle_http($t) == 0, 'POST via Transfer-Encoding: chunked, chunked header comment');
249
250$t->{REQUEST}  = ( <<EOF
251POST /cgi.pl?post-len HTTP/1.1
252Host: www.example.org
253Connection: close
254Content-Type: application/x-www-form-urlencoded
255Transfer-Encoding: chunked
256
257az
2580123456789
2590
260
261EOF
262 );
263$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 400 } ];
264ok($tf->handle_http($t) == 0, 'POST via Transfer-Encoding: chunked; bad chunked header');
265
266$t->{REQUEST}  = ( <<EOF
267POST /cgi.pl?post-len HTTP/1.1
268Host: www.example.org
269Connection: close
270Content-Type: application/x-www-form-urlencoded
271Transfer-Encoding: chunked
272
273a
2740123456789xxxxxxxx
2750
276
277EOF
278 );
279$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 400 } ];
280ok($tf->handle_http($t) == 0, 'POST via Transfer-Encoding: chunked; mismatch chunked header size and chunked data size');
281
282$t->{REQUEST}  = ( <<EOF
283POST /cgi.pl?post-len HTTP/1.1
284Host: www.example.org
285Connection: close
286Content-Type: application/x-www-form-urlencoded
287Transfer-Encoding: chunked
288
289a ; xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2900123456789
2910
292
293EOF
294 );
295$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 400 } ];
296ok($tf->handle_http($t) == 0, 'POST via Transfer-Encoding: chunked; chunked header too long');
297
298## ranges
299
300$t->{REQUEST}  = ( <<EOF
301GET /12345.txt HTTP/1.1
302Host: 123.example.org
303Connection: close
304Range: bytes=0-3
305EOF
306 );
307$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 206, 'HTTP-Content' => '1234' } ];
308ok($tf->handle_http($t) == 0, 'GET, Range 0-3');
309
310$t->{REQUEST}  = ( <<EOF
311GET /12345.txt HTTP/1.1
312Host: 123.example.org
313Connection: close
314Range: bytes=-3
315EOF
316 );
317$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 206, 'HTTP-Content' => '45'."\n" } ];
318ok($tf->handle_http($t) == 0, 'GET, Range -3');
319
320$t->{REQUEST}  = ( <<EOF
321GET /12345.txt HTTP/1.1
322Host: 123.example.org
323Connection: close
324Range: bytes=3-
325EOF
326 );
327$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 206, 'HTTP-Content' => '45'."\n" } ];
328ok($tf->handle_http($t) == 0, 'GET, Range 3-');
329
330$t->{REQUEST}  = ( <<EOF
331GET /12345.txt HTTP/1.1
332Host: 123.example.org
333Connection: close
334Range: bytes=0-1,3-4
335EOF
336 );
337$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 206, 'HTTP-Content' => '12345' } ];
338ok($tf->handle_http($t) == 0, 'GET, Range 0-1,3-4 (ranges merged)');
339
340$t->{REQUEST}  = ( <<EOF
341GET /100.txt HTTP/1.1
342Host: 123.example.org
343Connection: close
344Range: bytes=0-1,97-98
345EOF
346 );
347$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 206, 'HTTP-Content' => <<EOF
348--fkj49sn38dcn3\r
349Content-Type: text/plain\r
350Content-Range: bytes 0-1/100\r
351\r
35212\r
353--fkj49sn38dcn3\r
354Content-Type: text/plain\r
355Content-Range: bytes 97-98/100\r
356\r
357hi\r
358--fkj49sn38dcn3--\r
359EOF
360 } ];
361ok($tf->handle_http($t) == 0, 'GET, Range 0-1,97-98 (ranges not merged)');
362
363$t->{REQUEST}  = ( <<EOF
364GET /12345.txt HTTP/1.1
365Host: 123.example.org
366Connection: close
367Range: bytes=0-
368EOF
369 );
370$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 206, 'Content-Range' => 'bytes 0-5/6' } ];
371ok($tf->handle_http($t) == 0, 'GET, Range 0-');
372
373$t->{REQUEST}  = ( <<EOF
374GET /12345.txt HTTP/1.1
375Host: 123.example.org
376Connection: close
377Range: bytes=0--
378EOF
379 );
380$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 416 } ];
381ok($tf->handle_http($t) == 0, 'GET, Range 0--');
382
383$t->{REQUEST}  = ( <<EOF
384GET /12345.txt HTTP/1.1
385Host: 123.example.org
386Connection: close
387Range: bytes=-2-3
388EOF
389 );
390$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 416 } ];
391ok($tf->handle_http($t) == 0, 'GET, Range -2-3');
392
393$t->{REQUEST}  = ( <<EOF
394GET /12345.txt HTTP/1.1
395Host: 123.example.org
396Connection: close
397Range: bytes=-0
398EOF
399 );
400$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 416, 'HTTP-Content' => <<EOF
401<?xml version="1.0" encoding="iso-8859-1"?>
402<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
403         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
404<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
405 <head>
406  <title>416 Range Not Satisfiable</title>
407 </head>
408 <body>
409  <h1>416 Range Not Satisfiable</h1>
410 </body>
411</html>
412EOF
413 } ];
414ok($tf->handle_http($t) == 0, 'GET, Range -0');
415
416$t->{REQUEST}  = ( <<EOF
417GET /12345.txt HTTP/1.1
418Host: 123.example.org
419Connection: close
420Range: bytes=25-
421EOF
422 );
423$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 416, 'HTTP-Content' => <<EOF
424<?xml version="1.0" encoding="iso-8859-1"?>
425<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
426         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
427<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
428 <head>
429  <title>416 Range Not Satisfiable</title>
430 </head>
431 <body>
432  <h1>416 Range Not Satisfiable</h1>
433 </body>
434</html>
435EOF
436 } ];
437
438ok($tf->handle_http($t) == 0, 'GET, Range start out of range');
439
440
441$t->{REQUEST}  = ( <<EOF
442GET /range.disabled HTTP/1.1
443Host: 123.example.org
444Range: bytes=0-
445Connection: close
446EOF
447 );
448$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } ];
449ok($tf->handle_http($t) == 0, 'GET, Range with range-requests-disabled');
450
451$t->{REQUEST}  = ( <<EOF
452GET /12345.txt HTTP/1.1
453Host: 123.example.org
454Connection: close
455Range: 0
456Range: bytes=0-3
457EOF
458 );
459$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => "12345\n" } ];
460ok($tf->handle_http($t) == 0, 'GET, Range invalid range-unit (first)');
461
462$t->{REQUEST}  = ( <<EOF
463GET /12345.txt HTTP/1.1
464Host: 123.example.org
465Connection: close
466Range: bytes=0-3
467Range: 0
468EOF
469 );
470$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 206 } ];
471ok($tf->handle_http($t) == 0, 'GET, Range ignore invalid range (second)');
472
473$t->{REQUEST}  = ( <<EOF
474OPTIONS / HTTP/1.0
475Content-Length: 4
476
4771234
478EOF
479 );
480$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
481ok($tf->handle_http($t) == 0, 'OPTIONS with Content-Length');
482
483$t->{REQUEST}  = ( <<EOF
484OPTIONS rtsp://221.192.134.146:80 RTSP/1.1
485Host: 221.192.134.146:80
486EOF
487 );
488$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ];
489ok($tf->handle_http($t) == 0, 'OPTIONS for RTSP');
490
491my $nextyr = (gmtime(time()))[5] + 1900 + 1;
492
493$t->{REQUEST}  = ( <<EOF
494GET /index.html HTTP/1.0
495If-Modified-Since2: Sun, 01 Jan $nextyr 00:00:03 GMT
496If-Modified-Since: Sun, 01 Jan $nextyr 00:00:02 GMT
497EOF
498 );
499$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 304 } ];
500ok($tf->handle_http($t) == 0, 'Similar Headers (bug #1287)');
501
502$t->{REQUEST}  = ( <<EOF
503GET /index.html HTTP/1.0
504If-Modified-Since: Sun, 01 Jan $nextyr 00:00:02 GMT
505EOF
506 );
507$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 304, '-Content-Length' => '', 'Content-Type' => 'text/html' } ];
508ok($tf->handle_http($t) == 0, 'Status 304 has no Content-Length (#1002)');
509
510$t->{REQUEST}  = ( <<EOF
511GET /12345.txt HTTP/1.0
512Host: 123.example.org
513EOF
514 );
515$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain' } ];
516$t->{SLOWREQUEST} = 1;
517ok($tf->handle_http($t) == 0, 'GET, slow \\r\\n\\r\\n (#2105)');
518undef $t->{SLOWREQUEST};
519
520$t->{REQUEST}  = ( <<EOF
521GET /www/abc/def HTTP/1.0
522EOF
523 );
524$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404 } ];
525ok($tf->handle_http($t) == 0, 'pathinfo on a directory');
526
527
528$t->{REQUEST}  = ( <<EOF
529GET /12345.txt HTTP/1.1
530Connection: ,close
531Host: 123.example.org
532EOF
533 );
534$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain', 'Connection' => 'close' } ];
535ok($tf->handle_http($t) == 0, 'Connection-header, leading comma');
536
537$t->{REQUEST}  = ( <<EOF
538GET /12345.txt HTTP/1.1
539Connection: close,,TE
540Host: 123.example.org
541EOF
542 );
543$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain', 'Connection' => 'close' } ];
544ok($tf->handle_http($t) == 0, 'Connection-header, no value between two commas');
545
546$t->{REQUEST}  = ( <<EOF
547GET /12345.txt HTTP/1.1
548Connection: close, ,TE
549Host: 123.example.org
550EOF
551 );
552$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain', 'Connection' => 'close' } ];
553ok($tf->handle_http($t) == 0, 'Connection-header, space between two commas');
554
555$t->{REQUEST}  = ( <<EOF
556GET /12345.txt HTTP/1.1
557Connection: close,
558Host: 123.example.org
559EOF
560 );
561$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain', 'Connection' => 'close' } ];
562ok($tf->handle_http($t) == 0, 'Connection-header, comma after value');
563
564$t->{REQUEST}  = ( <<EOF
565GET /12345.txt HTTP/1.1
566Connection: close,
567Host: 123.example.org
568EOF
569 );
570$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain', 'Connection' => 'close' } ];
571ok($tf->handle_http($t) == 0, 'Connection-header, comma and space after value');
572
573
574## Low-Level Response-Header Parsing - HTTP/1.1
575
576$t->{REQUEST}  = ( <<EOF
577GET / HTTP/1.1
578Host: www.example.org
579Connection: close
580EOF
581 );
582$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, '+Date' => '' } ];
583ok($tf->handle_http($t) == 0, 'Date header');
584
585
586## Low-Level Response-Header Parsing - Content-Length
587
588
589$t->{REQUEST}  = ( <<EOF
590GET /12345.html HTTP/1.0
591Host: 123.example.org
592EOF
593 );
594$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'Content-Length' => '6' } ];
595ok($tf->handle_http($t) == 0, 'Content-Length for text/html');
596
597$t->{REQUEST}  = ( <<EOF
598GET /12345.txt HTTP/1.0
599Host: 123.example.org
600EOF
601 );
602$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'Content-Length' => '6' } ];
603ok($tf->handle_http($t) == 0, 'Content-Length for text/plain');
604
605
606## Low-Level Response-Header Parsing - Location
607
608$t->{REQUEST}  = ( <<EOF
609GET /dummydir HTTP/1.0
610EOF
611 );
612$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => '/dummydir/' } ];
613ok($tf->handle_http($t) == 0, 'internal redirect in directory');
614
615$t->{REQUEST}  = ( <<EOF
616GET /dummydir?foo HTTP/1.0
617EOF
618 );
619$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => '/dummydir/?foo' } ];
620ok($tf->handle_http($t) == 0, 'internal redirect in directory + querystring');
621
622$t->{REQUEST}  = ( <<EOF
623GET /~test%20ä_ HTTP/1.0
624EOF
625 );
626$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => '/~test%20%C3%A4_/' } ];
627ok($tf->handle_http($t) == 0, 'internal redirect in directory with special characters');
628
629$t->{REQUEST}  = ( <<EOF
630GET /~test%20ä_?foo HTTP/1.0
631EOF
632 );
633$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 301, 'Location' => '/~test%20%C3%A4_/?foo' } ];
634ok($tf->handle_http($t) == 0, 'internal redirect in directory with special characters + querystring');
635
636
637## simple-vhost
638
639$t->{REQUEST}  = ( <<EOF
640GET /12345.txt HTTP/1.0
641Host: no-simple.example.org
642EOF
643 );
644$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'Content-Length' => '6' } ];
645ok($tf->handle_http($t) == 0, 'disabling simple-vhost via conditionals');
646
647$t->{REQUEST}  = ( <<EOF
648GET /12345.txt HTTP/1.0
649Host: simple.example.org
650EOF
651 );
652$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404 } ];
653ok($tf->handle_http($t) == 0, 'simple-vhost via conditionals');
654
655
656## keep-alive
657
658$t->{REQUEST} = ( <<EOF
659GET /12345.txt HTTP/1.0
660Connection: keep-alive
661Host: 123.example.org
662
663GET /12345.txt HTTP/1.0
664Host: 123.example.org
665Connection: close
666EOF
667 );
668$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } , { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
669ok($tf->handle_http($t) == 0, 'Explicit HTTP/1.0 Keep-Alive');
670undef $t->{RESPONSE};
671
672$t->{REQUEST} = ( <<EOF
673GET /12345.txt HTTP/1.0
674Connection: keep-alive
675Host: 123.example.org
676
677GET /12345.txt HTTP/1.0
678Host: 123.example.org
679Connection: close
680EOF
681 );
682$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } , { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
683ok($tf->handle_http($t) == 0, 'Explicit HTTP/1.0 Keep-Alive');
684undef $t->{RESPONSE};
685
686$t->{REQUEST} = ( <<EOF
687GET /12345.txt HTTP/1.0
688Connection: keep-alive
689Host: 123.example.org
690
691GET /12345.txt HTTP/1.0
692Host: 123.example.org
693EOF
694 );
695$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } , { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
696ok($tf->handle_http($t) == 0, 'Implicit HTTP/1.0 Keep-Alive');
697
698$t->{REQUEST} = ( <<EOF
699GET /12345.txt HTTP/1.1
700Connection: keep-alive
701Host: 123.example.org
702
703GET /12345.txt HTTP/1.1
704Host: 123.example.org
705Connection: close
706EOF
707 );
708$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } , { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } ];
709ok($tf->handle_http($t) == 0, 'Explicit HTTP/1.1 Keep-Alive');
710
711$t->{REQUEST} = ( <<EOF
712GET /12345.txt HTTP/1.1
713Host: 123.example.org
714
715GET /12345.txt HTTP/1.1
716Host: 123.example.org
717Connection: close
718EOF
719 );
720$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } , { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } ];
721ok($tf->handle_http($t) == 0, 'Implicit HTTP/1.1 Keep-Alive');
722
723$t->{REQUEST} = ( <<EOF
724GET /12345.txt HTTP/1.1
725Host: 123.example.org
726
727
728GET /12345.txt HTTP/1.1
729Host: 123.example.org
730Connection: close
731EOF
732 );
733$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } , { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } ];
734ok($tf->handle_http($t) == 0, 'Implicit HTTP/1.1 Keep-Alive w/ extra blank b/w requests');
735
736$t->{REQUEST} = ( <<EOF
737GET /12345.txt HTTP/1.1
738Host: 123.example.org
739
740
741
742GET /12345.txt HTTP/1.1
743Host: 123.example.org
744Connection: close
745EOF
746 );
747$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200 } , { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ];
748ok($tf->handle_http($t) == 0, 'Implicit HTTP/1.1 Keep-Alive w/ excess blank b/w requests');
749
750
751## 404 handlers
752
753$t->{REQUEST}  = ( <<EOF
754GET /static/notfound HTTP/1.0
755Host: errors.example.org
756EOF
757 );
758$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => "static not found\n" } ];
759ok($tf->handle_http($t) == 0, '404 handler => static');
760
761$t->{REQUEST}  = ( <<EOF
762GET /dynamic/200/notfound HTTP/1.0
763Host: errors.example.org
764EOF
765 );
766$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => "found here\n" } ];
767ok($tf->handle_http($t) == 0, '404 handler => dynamic(200)');
768
769$t->{REQUEST}  = ( <<EOF
770GET /dynamic/302/notfound HTTP/1.0
771Host: errors.example.org
772EOF
773 );
774$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 302, 'Location' => "http://www.example.org/" } ];
775ok($tf->handle_http($t) == 0, '404 handler => dynamic(302)');
776
777$t->{REQUEST}  = ( <<EOF
778GET /dynamic/404/notfound HTTP/1.0
779Host: errors.example.org
780EOF
781 );
782$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404, 'HTTP-Content' => "Not found here\n" } ];
783ok($tf->handle_http($t) == 0, '404 handler => dynamic(404)');
784
785$t->{REQUEST}  = ( <<EOF
786GET /dynamic/redirect_status/ HTTP/1.0
787Host: errors.example.org
788EOF
789 );
790$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404, 'HTTP-Content' => "REDIRECT_STATUS\n" } ];
791ok($tf->handle_http($t) == 0, 'error handler => dynamic(REDIRECT_STATUS)');
792
793$t->{REQUEST}  = ( <<EOF
794GET /dynamic/nostatus/notfound HTTP/1.0
795Host: errors.example.org
796EOF
797 );
798$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => "found here\n" } ];
799ok($tf->handle_http($t) == 0, '404 handler => dynamic(nostatus)');
800
801$t->{REQUEST}  = ( <<EOF
802GET /cgi.pl?send404 HTTP/1.0
803Host: errors.example.org
804EOF
805 );
806$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404, 'HTTP-Content' => "send404\n" } ];
807ok($tf->handle_http($t) == 0, '404 generated by CGI should stay 404');
808
809
810## config conditions
811
812$t->{REQUEST}  = ( <<EOF
813GET /nofile.png HTTP/1.0
814Host: referer.example.org
815EOF
816 );
817$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404 } ];
818ok($tf->handle_http($t) == 0, 'condition: Referer - no referer');
819
820$t->{REQUEST}  = ( <<EOF
821GET /nofile.png HTTP/1.0
822Host: referer.example.org
823Referer: http://referer.example.org/
824EOF
825 );
826$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404 } ];
827ok($tf->handle_http($t) == 0, 'condition: Referer - referer matches regex');
828
829$t->{REQUEST}  = ( <<EOF
830GET /image.jpg HTTP/1.0
831Host: www.example.org
832EOF
833 );
834$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
835ok($tf->handle_http($t) == 0, 'condition: Referer - no referer');
836
837$t->{REQUEST}  = ( <<EOF
838GET /image.jpg HTTP/1.0
839Host: www.example.org
840Referer: http://referer.example.org/
841EOF
842 );
843$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
844ok($tf->handle_http($t) == 0, 'condition: Referer - referer matches regex');
845
846$t->{REQUEST}  = ( <<EOF
847GET /image.jpg HTTP/1.0
848Host: www.example.org
849Referer: http://evil-referer.example.org/
850EOF
851 );
852$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ];
853ok($tf->handle_http($t) == 0, 'condition: Referer - referer doesn\'t match');
854
855$t->{REQUEST} = ( <<EOF
856GET /nofile HTTP/1.1
857Host: bug255.example.org
858
859GET /nofile HTTP/1.1
860Host: bug255.example.org
861Connection: close
862EOF
863 );
864$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 403 },  { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 403 } ];
865ok($tf->handle_http($t) == 0, 'remote ip cache (#255)');
866
867$t->{REQUEST}  = ( <<EOF
868GET /empty-ref.noref HTTP/1.0
869Cookie: empty-ref
870EOF
871 );
872$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ];
873ok($tf->handle_http($t) == 0, 'condition: $HTTP["referer"] == "" and Referer is no set');
874
875$t->{REQUEST}  = ( <<EOF
876GET /empty-ref.noref HTTP/1.0
877Cookie: empty-ref
878Referer:
879EOF
880 );
881$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ];
882ok($tf->handle_http($t) == 0, 'condition: $HTTP["referer"] == "" and Referer is empty');
883
884$t->{REQUEST}  = ( <<EOF
885GET /empty-ref.noref HTTP/1.0
886Cookie: empty-ref
887Referer: foobar
888EOF
889 );
890$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404 } ];
891ok($tf->handle_http($t) == 0, 'condition: $HTTP["referer"] == "" and Referer: foobar');
892
893
894## case-insensitive filesystem policy
895
896## check if lower-casing works
897
898$t->{REQUEST}  = ( <<EOF
899GET /image.JPG HTTP/1.0
900Host: lowercase-allow
901EOF
902 );
903$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
904ok($tf->handle_http($t) == 0, 'uppercase access');
905
906$t->{REQUEST}  = ( <<EOF
907GET /image.jpg HTTP/1.0
908Host: lowercase-allow
909EOF
910 );
911$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
912ok($tf->handle_http($t) == 0, 'lowercase access');
913
914## check that mod_auth works
915
916$t->{REQUEST}  = ( <<EOF
917GET /image.JPG HTTP/1.0
918Host: lowercase-auth
919EOF
920 );
921$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ];
922ok($tf->handle_http($t) == 0, 'uppercase access');
923
924$t->{REQUEST}  = ( <<EOF
925GET /image.jpg HTTP/1.0
926Host: lowercase-auth
927EOF
928 );
929$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ];
930ok($tf->handle_http($t) == 0, 'lowercase access');
931
932
933## check that mod_staticfile exclude works
934$t->{REQUEST}  = ( <<EOF
935GET /image.JPG HTTP/1.0
936Host: lowercase-exclude
937EOF
938 );
939$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ];
940ok($tf->handle_http($t) == 0, 'upper case access to staticfile.exclude-extension');
941
942$t->{REQUEST}  = ( <<EOF
943GET /image.jpg HTTP/1.0
944Host: lowercase-exclude
945EOF
946 );
947$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ];
948ok($tf->handle_http($t) == 0, 'lowercase access');
949
950
951## check that mod_access exclude works
952$t->{REQUEST}  = ( <<EOF
953GET /image.JPG HTTP/1.0
954Host: lowercase-deny
955EOF
956 );
957$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ];
958ok($tf->handle_http($t) == 0, 'uppercase access to url.access-deny protected location');
959
960$t->{REQUEST}  = ( <<EOF
961GET /image.jpg HTTP/1.0
962Host: lowercase-deny
963EOF
964 );
965$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ];
966ok($tf->handle_http($t) == 0, 'lowercase access');
967
968
969## symlink policy
970
971my $docroot = $tf->{'TESTDIR'}."/tmp/lighttpd/servers/www.example.org/pages";
972
973sub init_testbed {
974    return 0 unless eval { symlink("",""); 1 };
975    my $f = "$docroot/index.html";
976    my $l = "$docroot/index.xhtml";
977    my $rc = undef;
978    unless (-l $l) {
979        return 0 unless symlink($f,$l);
980    };
981    $f = "$docroot/expire";
982    $l = "$docroot/symlinked";
983    $rc = undef;
984    unless (-l $l) {
985        return 0 unless symlink($f,$l);
986    }
987    return 1;
988};
989
990SKIP: {
991    skip "perl does not support symlinking or setting up the symlinks failed.", 8 unless init_testbed;
992
993# allow case
994# simple file
995	$t->{REQUEST} = ( <<EOF
996GET /index.html HTTP/1.0
997Host: symlink.example.org
998EOF
999 );
1000	$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1001	ok($tf->handle_http($t) == 0, 'allow: simple file');
1002
1003# symlinked file
1004	$t->{REQUEST} = ( <<EOF
1005GET /index.xhtml HTTP/1.0
1006Host: symlink.example.org
1007EOF
1008 );
1009	$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1010	ok($tf->handle_http($t) == 0, 'allow: symlinked file');
1011
1012# directly symlinked dir
1013	$t->{REQUEST} = ( <<EOF
1014GET /symlinked/ HTTP/1.0
1015Host: symlink.example.org
1016EOF
1017 );
1018	$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1019	ok($tf->handle_http($t) == 0, 'allow: directly symlinked dir');
1020
1021# symlinked dir in path
1022	$t->{REQUEST} = ( <<EOF
1023GET /symlinked/access.txt HTTP/1.0
1024Host: symlink.example.org
1025EOF
1026 );
1027	$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1028	ok($tf->handle_http($t) == 0, 'allow: symlinked dir in path');
1029
1030# deny case
1031# simple file
1032	$t->{REQUEST} = ( <<EOF
1033GET /index.html HTTP/1.0
1034Host: nosymlink.example.org
1035EOF
1036 );
1037	$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1038	ok($tf->handle_http($t) == 0, 'deny: simple file');
1039
1040# symlinked file
1041	$t->{REQUEST} = ( <<EOF
1042GET /index.xhtml HTTP/1.0
1043Host: nosymlink.example.org
1044EOF
1045 );
1046	$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ];
1047	ok($tf->handle_http($t) == 0, 'deny: symlinked file');
1048
1049# directly symlinked dir
1050	$t->{REQUEST} = ( <<EOF
1051GET /symlinked/ HTTP/1.0
1052Host: nosymlink.example.org
1053EOF
1054 );
1055	$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ];
1056	ok($tf->handle_http($t) == 0, 'deny: directly symlinked dir');
1057
1058# symlinked dir in path
1059	$t->{REQUEST} = ( <<EOF
1060GET /symlinked/access.txt HTTP/1.0
1061Host: nosymlink.example.org
1062EOF
1063 );
1064	$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ];
1065	ok($tf->handle_http($t) == 0, 'deny: symlinked dir in path');
1066
1067};
1068
1069
1070## mod_auth
1071
1072$t->{REQUEST}  = ( <<EOF
1073GET /server-status HTTP/1.0
1074Host: auth-plain.example.org
1075EOF
1076 );
1077$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ];
1078ok($tf->handle_http($t) == 0, 'Missing Auth-token');
1079
1080$t->{REQUEST}  = ( <<EOF
1081GET /server-config HTTP/1.0
1082Host: auth-plain.example.org
1083Authorization: Basic \x80mFuOmphb
1084EOF
1085 );
1086$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ];
1087ok($tf->handle_http($t) == 0, 'Basic-Auth: Invalid base64 Auth-token');
1088
1089$t->{REQUEST}  = ( <<EOF
1090GET /server-config HTTP/1.0
1091Host: auth-plain.example.org
1092Authorization: Basic bm90Oml0Cg==
1093EOF
1094 );
1095$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ];
1096ok($tf->handle_http($t) == 0, 'Basic-Auth: Wrong Auth-token');
1097
1098$t->{REQUEST}  = ( <<EOF
1099GET /server-config HTTP/1.0
1100Host: auth-plain.example.org
1101Authorization: Basic amFuOmphbg==
1102EOF
1103 );
1104$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1105ok($tf->handle_http($t) == 0, 'Basic-Auth: Valid Auth-token - plain');
1106
1107SKIP: {
1108	skip "no crypt-des under openbsd", 2 if $^O eq 'openbsd';
1109$t->{REQUEST}  = ( <<EOF
1110GET /server-config HTTP/1.0
1111Host: auth-htpasswd.example.org
1112Authorization: Basic ZGVzOmRlcw==
1113EOF
1114 );
1115$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1116ok($tf->handle_http($t) == 0, 'Basic-Auth: Valid Auth-token - htpasswd (des)');
1117
1118$t->{REQUEST}  = ( <<EOF
1119GET /server-config HTTP/1.0
1120Host: auth-htpasswd.example.org
1121Authorization: basic ZGVzOmRlcw==
1122EOF
1123 );
1124$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1125ok($tf->handle_http($t) == 0, 'Basic-Auth: Valid Auth-token - htpasswd (des) (lowercase)');
1126}
1127
1128$t->{REQUEST}  = ( <<EOF
1129GET /server-config HTTP/1.0
1130Host: auth-htpasswd.example.org
1131Authorization: Basic c2hhOnNoYQ==
1132EOF
1133 );
1134$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1135ok($tf->handle_http($t) == 0, 'Basic-Auth: Valid Auth-token - htpasswd (sha)');
1136
1137$t->{REQUEST}  = ( <<EOF
1138GET /server-config HTTP/1.0
1139Host: auth-htpasswd.example.org
1140Authorization: Basic c2hhOnNoYg==
1141EOF
1142 );
1143$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ];
1144ok($tf->handle_http($t) == 0, 'Basic-Auth: Valid Auth-token - htpasswd (sha, wrong password)');
1145
1146$t->{REQUEST}  = ( <<EOF
1147GET /server-config HTTP/1.0
1148Host: auth-htpasswd.example.org
1149Authorization: Basic YXByLW1kNTphcHItbWQ1
1150EOF
1151 );
1152$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1153ok($tf->handle_http($t) == 0, 'Basic-Auth: Valid Auth-token - htpasswd (apr-md5)');
1154
1155$t->{REQUEST}  = ( <<EOF
1156GET /server-config HTTP/1.0
1157Host: auth-htpasswd.example.org
1158Authorization: Basic YXByLW1kNTphcHItbWQ2
1159EOF
1160 );
1161$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ];
1162ok($tf->handle_http($t) == 0, 'Basic-Auth: Valid Auth-token - htpasswd (apr-md5, wrong password)');
1163
1164SKIP: {
1165	skip "no crypt-md5 under cygwin", 1 if $^O eq 'cygwin';
1166	skip "no crypt-md5 under darwin", 1 if $^O eq 'darwin';
1167	skip "no crypt-md5 under openbsd",1 if $^O eq 'openbsd';
1168$t->{REQUEST}  = ( <<EOF
1169GET /server-config HTTP/1.0
1170Host: auth-htpasswd.example.org
1171Authorization: Basic bWQ1Om1kNQ==
1172EOF
1173 );
1174$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1175ok($tf->handle_http($t) == 0, 'Basic-Auth: Valid Auth-token - htpasswd (crypt-md5)');
1176}
1177
1178$t->{REQUEST}  = ( <<EOF
1179GET /server-config HTTP/1.0
1180Host: auth-plain.example.org
1181Authorization: Basic bWQ1Om1kNA==
1182EOF
1183 );
1184$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ];
1185ok($tf->handle_http($t) == 0, 'Basic-Auth: Valid Auth-token');
1186
1187## this should not crash
1188$t->{REQUEST}  = ( <<EOF
1189GET /server-status HTTP/1.0
1190Host: auth-plain.example.org
1191User-Agent: Wget/1.9.1
1192Authorization: Digest username="jan", realm="jan", nonce="9a5428ccc05b086a08d918e73b01fc6f",
1193                uri="/server-status", response="ea5f7d9a30b8b762f9610ccb87dea74f"
1194EOF
1195 );
1196$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401 } ];
1197ok($tf->handle_http($t) == 0, 'Digest-Auth: missing qop, no crash');
1198
1199# (Note: test case is invalid; mismatch between request line and uri="..."
1200#  is not what is intended to be tested here, but that is what is invalid)
1201# https://redmine.lighttpd.net/issues/477
1202## this should not crash
1203$t->{REQUEST}  = ( <<EOF
1204GET /server-status HTTP/1.0
1205Host: auth-plain.example.org
1206User-Agent: Wget/1.9.1
1207Authorization: Digest username="jan", realm="jan",
1208	nonce="b1d12348b4620437c43dd61c50ae4639",
1209	uri="/MJ-BONG.xm.mpc", qop=auth, noncecount=00000001",
1210	cnonce="036FCA5B86F7E7C4965C7F9B8FE714B7",
1211	response="29B32C2953C763C6D033C8A49983B87E"
1212EOF
1213 );
1214$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ];
1215ok($tf->handle_http($t) == 0, 'Digest-Auth: missing nc (noncecount instead), no crash');
1216
1217$t->{REQUEST}  = ( <<EOF
1218GET /server-config HTTP/1.0
1219Host: auth-plain.example.org
1220Authorization: Basic =
1221EOF
1222 );
1223$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ];
1224ok($tf->handle_http($t) == 0, 'Basic-Auth: Invalid Base64');
1225
1226$t->{REQUEST}  = ( <<EOF
1227GET /server-status HTTP/1.0
1228Host: auth-plain.example.org
1229Authorization: Digest username="jan", realm="download archiv",
1230	nonce="b3b26457000000003a9b34a3cd56d26e48a52a498ac9765d4b",
1231	uri="/server-status", qop=auth, nc=00000001,
1232	algorithm="md5-sess", response="049b000fb00ab51dddea6f093a96aa2e"
1233EOF
1234 );
1235$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ];
1236ok($tf->handle_http($t) == 0, 'Digest-Auth: md5-sess + missing cnonce');
1237
1238 $t->{REQUEST}  = ( <<EOF
1239GET /server-status HTTP/1.0
1240Host: auth-plain.example.org
1241Authorization: Digest username="jan", realm="download archiv",
1242	nonce="b3b26457000000003a9b34a3cd56d26e48a52a498ac9765d4b",
1243	uri="/server-status", qop=auth, nc=00000001, cnonce="65ee1b37",
1244	algorithm="md5", response="049b000fb00ab51dddea6f093a96aa2e"
1245EOF
1246  );
1247$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401, 'WWW-Authenticate' => '/, stale=true$/' } ];
1248ok($tf->handle_http($t) == 0, 'Digest-Auth: stale nonce');
1249
1250$t->{REQUEST}  = ( <<EOF
1251GET /server-status HTTP/1.0
1252Host: auth-plain.example.org
1253Authorization: Digest username = "jan", realm = "download archiv",
1254	nonce = "b3b26457000000003a9b34a3cd56d26e48a52a498ac9765d4b",
1255	uri = "/server-status", qop = auth, nc = 00000001, cnonce = "65ee1b37",
1256	algorithm = "md5", response = "049b000fb00ab51dddea6f093a96aa2e"
1257EOF
1258 ); # note: trailing whitespace at end of request line above is intentional
1259$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 401, 'WWW-Authenticate' => '/, stale=true$/' } ];
1260ok($tf->handle_http($t) == 0, 'Digest-Auth: BWS, trailing WS, stale nonce');
1261
1262
1263## mod_cgi
1264
1265$t->{REQUEST}  = ( <<EOF
1266GET /cgi.pl HTTP/1.0
1267EOF
1268 );
1269$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1270ok($tf->handle_http($t) == 0, 'perl via cgi');
1271
1272if ($^O ne "cygwin") {
1273    $t->{REQUEST}  = ( <<EOF
1274GET /cgi.pl%20%20%20 HTTP/1.0
1275EOF
1276 );
1277    $t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404 } ];
1278    ok($tf->handle_http($t) == 0, 'No source retrieval');
1279} else {
1280    ok(1, 'No source retrieval; skipped on cygwin; see response.c');
1281}
1282
1283$t->{REQUEST}  = ( <<EOF
1284GET /cgi.pl/foo?env=SCRIPT_NAME HTTP/1.0
1285EOF
1286 );
1287$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '/cgi.pl' } ];
1288ok($tf->handle_http($t) == 0, 'perl via cgi + pathinfo');
1289
1290$t->{REQUEST}  = ( <<EOF
1291GET /cgi.pl?internal-redir HTTP/1.0
1292EOF
1293 );
1294$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1295ok($tf->handle_http($t) == 0, 'perl via cgi and internal redirect from CGI');
1296
1297$t->{REQUEST}  = ( <<EOF
1298GET /cgi.pl?xsendfile HTTP/1.0
1299Host: cgi.example.org
1300EOF
1301 );
1302$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'Content-Length' => 4348 } ];
1303ok($tf->handle_http($t) == 0, 'X-Sendfile');
1304
1305$t->{REQUEST}  = ( <<EOF
1306GET /cgi.pl?external-redir HTTP/1.0
1307Host: www.example.org
1308EOF
1309 );
1310$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 302, 'Location' => 'http://www.example.org:2048/' } ];
1311ok($tf->handle_http($t) == 0, 'Status + Location via FastCGI');
1312
1313$t->{REQUEST}  = ( <<EOF
1314GET /cgi.pl/?external-redir HTTP/1.0
1315Host: www.example.org
1316EOF
1317 );
1318$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 302, 'Location' => 'http://www.example.org:2048/' } ];
1319ok($tf->handle_http($t) == 0, 'Trailing slash as path-info (#1989: workaround broken operating systems)');
1320
1321$t->{REQUEST}  = ( <<EOF
1322GET /cgi.pl?nph=30 HTTP/1.0
1323EOF
1324 );
1325$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 502 } ];
1326ok($tf->handle_http($t) == 0, 'NPH + perl, invalid status-code (#14)');
1327
1328$t->{REQUEST}  = ( <<EOF
1329GET /cgi.pl?nph=304 HTTP/1.0
1330EOF
1331 );
1332$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 304 } ];
1333ok($tf->handle_http($t) == 0, 'NPH + perl, setting status-code (#1125)');
1334
1335$t->{REQUEST}  = ( <<EOF
1336GET /cgi.pl?nph=200 HTTP/1.0
1337EOF
1338 );
1339$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1340ok($tf->handle_http($t) == 0, 'NPH + perl, setting status-code');
1341
1342$t->{REQUEST} = ( <<EOF
1343GET /cgi.pl?env=GATEWAY_INTERFACE HTTP/1.0
1344EOF
1345 );
1346$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'CGI/1.1' } ];
1347ok($tf->handle_http($t) == 0, 'cgi-env: GATEWAY_INTERFACE');
1348
1349$t->{REQUEST} = ( <<EOF
1350GET /cgi.pl?query_string HTTP/1.0
1351EOF
1352 );
1353$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'query_string', 'Content-Type' => 'text/plain' } ];
1354ok($tf->handle_http($t) == 0, 'cgi-env: QUERY_STRING');
1355
1356$t->{REQUEST} = ( <<EOF
1357GET /cgi.pl?env=SCRIPT_NAME HTTP/1.0
1358EOF
1359 );
1360$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '/cgi.pl' } ];
1361ok($tf->handle_http($t) == 0, 'cgi-env: SCRIPT_NAME');
1362
1363$t->{REQUEST} = ( <<EOF
1364GET /cgi.pl/path/info?env=SCRIPT_NAME HTTP/1.0
1365EOF
1366 );
1367$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '/cgi.pl' } ];
1368ok($tf->handle_http($t) == 0, 'cgi-env: SCRIPT_NAME w/ PATH_INFO');
1369
1370$t->{REQUEST} = ( <<EOF
1371GET /cgi.pl/path/info?env=PATH_INFO HTTP/1.0
1372EOF
1373 );
1374$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '/path/info' } ];
1375ok($tf->handle_http($t) == 0, 'cgi-env: PATH_INFO');
1376
1377$t->{REQUEST}  = ( <<EOF
1378GET /cgi.pl?env=HTTP_XX_YY123 HTTP/1.0
1379xx-yy123: foo
1380EOF
1381 );
1382$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'foo' } ];
1383ok($tf->handle_http($t) == 0, 'cgi-env: quoting headers with numbers');
1384
1385$t->{REQUEST}  = ( <<EOF
1386GET /cgi.pl?env=HTTP_HOST HTTP/1.0
1387Host: www.example.org
1388EOF
1389 );
1390$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'www.example.org' } ];
1391ok($tf->handle_http($t) == 0, 'cgi-env: HTTP_HOST');
1392
1393$t->{REQUEST}  = ( <<EOF
1394GET /cgi.pl?env=HTTP_HOST HTTP/1.1
1395Host: www.example.org
1396Connection: close
1397EOF
1398 );
1399$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, '+Content-Length' => '' } ];
1400ok($tf->handle_http($t) == 0, 'cgi-env: HTTP_HOST');
1401
1402$t->{REQUEST}  = ( <<EOF
1403GET /cgi.pl?env=ABSENT HTTP/1.1
1404Host: www.example.org
1405Connection: close
1406EOF
1407 );
1408$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => '[ABSENT not found]' } ];
1409ok($tf->handle_http($t) == 0, 'cgi-env: ABSENT');
1410
1411$t->{REQUEST}  = ( <<EOF
1412GET /cgi.pl?env=BLANK_VALUE HTTP/1.1
1413Host: www.example.org
1414Connection: close
1415EOF
1416 );
1417$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => '' } ];
1418ok($tf->handle_http($t) == 0, 'cgi-env: BLANK_VALUE');
1419
1420# broken header crash
1421$t->{REQUEST}  = ( <<EOF
1422GET /cgi.pl?crlfcrash HTTP/1.0
1423EOF
1424 );
1425$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 302, 'Location' => 'http://www.example.org/' } ];
1426ok($tf->handle_http($t) == 0, 'broken header via perl cgi');
1427
1428
1429## mod_deflate
1430
1431$t->{REQUEST}  = ( <<EOF
1432GET /index.html HTTP/1.0
1433Host: deflate.example.org
1434Accept-Encoding: deflate
1435EOF
1436 );
1437$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '+Vary' => '' } ];
1438ok($tf->handle_http($t) == 0, 'Vary is set');
1439
1440$t->{REQUEST}  = ( <<EOF
1441GET /index.html HTTP/1.0
1442Accept-Encoding: deflate
1443Host: deflate.example.org
1444EOF
1445 );
1446$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '+Vary' => '', 'Content-Length' => '1294', '+Content-Encoding' => '' } ];
1447ok($tf->handle_http($t) == 0, 'deflate - Content-Length and Content-Encoding is set');
1448
1449$t->{REQUEST}  = ( <<EOF
1450GET /index.html HTTP/1.0
1451Accept-Encoding: deflate
1452Host: deflate-cache.example.org
1453EOF
1454 );
1455$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '+Vary' => '', 'Content-Length' => '1294', '+Content-Encoding' => '' } ];
1456ok($tf->handle_http($t) == 0, 'deflate - Content-Length and Content-Encoding is set');
1457
1458$t->{REQUEST}  = ( <<EOF
1459GET /index.html HTTP/1.0
1460Accept-Encoding: gzip
1461Host: deflate.example.org
1462EOF
1463 );
1464$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '+Vary' => '', 'Content-Length' => '1306', '+Content-Encoding' => '' } ];
1465ok($tf->handle_http($t) == 0, 'gzip - Content-Length and Content-Encoding is set');
1466
1467$t->{REQUEST}  = ( <<EOF
1468GET /index.html HTTP/1.0
1469Accept-Encoding: gzip
1470Host: deflate-cache.example.org
1471EOF
1472 );
1473$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '+Vary' => '', 'Content-Length' => '1306', '+Content-Encoding' => '' } ];
1474ok($tf->handle_http($t) == 0, 'gzip - Content-Length and Content-Encoding is set');
1475
1476
1477$t->{REQUEST}  = ( <<EOF
1478GET /index.txt HTTP/1.0
1479Host: deflate.example.org
1480Accept-Encoding: gzip, deflate
1481EOF
1482 );
1483$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '+Vary' => '', '+Content-Encoding' => '' } ];
1484ok($tf->handle_http($t) == 0, 'gzip, deflate - Content-Length and Content-Encoding is set');
1485
1486$t->{REQUEST}  = ( <<EOF
1487GET /index.txt HTTP/1.0
1488Host: deflate.example.org
1489Accept-Encoding: gzip, deflate
1490EOF
1491 );
1492$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '+Vary' => '', '+Content-Encoding' => '', 'Content-Type' => "text/plain; charset=utf-8" } ];
1493ok($tf->handle_http($t) == 0, 'Content-Type is from the original file');
1494
1495$t->{REQUEST}  = ( <<EOF
1496GET /index.txt HTTP/1.0
1497Host: deflate.example.org
1498Accept-encoding:
1499X-Accept-encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0
1500User-Agent: MYOB/6.66 (AN/ON)
1501Connection: close
1502EOF
1503 );
1504$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '-Content-Encoding' => '', 'Content-Type' => "text/plain; charset=utf-8" } ];
1505ok($tf->handle_http($t) == 0, 'Empty Accept-Encoding');
1506
1507$t->{REQUEST}  = ( <<EOF
1508GET /index.txt HTTP/1.0
1509Accept-Encoding: bzip2, gzip, deflate
1510Host: deflate-cache.example.org
1511EOF
1512 );
1513$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '+Vary' => '', 'Content-Encoding' => 'gzip', 'Content-Type' => "text/plain" } ];
1514ok($tf->handle_http($t) == 0, 'bzip2 requested but disabled');
1515
1516
1517## mod_extforward
1518
1519$t->{REQUEST} = ( <<EOF
1520GET /cgi.pl?env=REMOTE_ADDR HTTP/1.0
1521Host: www.example.org
1522X-Forwarded-For: 127.0.10.1
1523EOF
1524);
1525$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '127.0.10.1' } ];
1526ok($tf->handle_http($t) == 0, 'expect 127.0.10.1, from single ip');
1527
1528$t->{REQUEST} = ( <<EOF
1529GET /cgi.pl?env=REMOTE_ADDR HTTP/1.0
1530Host: www.example.org
1531X-Forwarded-For: 127.0.10.1, 127.0.20.1
1532EOF
1533);
1534$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '127.0.20.1' } ];
1535ok($tf->handle_http($t) == 0, 'expect 127.0.20.1, from two ips');
1536
1537$t->{REQUEST} = ( <<EOF
1538GET /cgi.pl?env=REMOTE_ADDR HTTP/1.0
1539Host: www.example.org
1540X-Forwarded-For: 127.0.10.1, 127.0.20.1, 127.0.30.1
1541EOF
1542);
1543$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '127.0.20.1' } ];
1544ok($tf->handle_http($t) == 0, 'expect 127.0.20.1, from chained proxies');
1545
1546$t->{REQUEST} = ( <<EOF
1547GET /cgi.pl?env=REMOTE_ADDR HTTP/1.0
1548Host: www.example.org
1549Forwarded: for=127.0.10.1, for=127.0.20.1;proto=https, for=127.0.30.1;proto=http
1550EOF
1551);
1552$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '127.0.20.1' } ];
1553ok($tf->handle_http($t) == 0, 'expect 127.0.20.1, from chained proxies');
1554
1555
1556## mod_proxy
1557
1558do {
1559
1560my $tf_proxy = LightyTest->new();
1561$tf_proxy->{CONFIGFILE} = 'proxy.conf';
1562
1563local $ENV{EPHEMERAL_PORT} = $tf->{PORT};
1564ok($tf_proxy->start_proc == 0, "Starting lighttpd as proxy") or last;
1565
1566$t->{REQUEST}  = ( <<EOF
1567GET /index.html HTTP/1.0
1568Host: www.example.org
1569EOF
1570 );
1571$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ];
1572ok($tf_proxy->handle_http($t) == 0, 'valid request');
1573
1574$t->{REQUEST}  = ( <<EOF
1575GET /index.html HTTP/1.0
1576Host: www.example.org
1577EOF
1578 );
1579$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'Server' => 'lighttpd-1.4.x' } ];
1580ok($tf_proxy->handle_http($t) == 0, 'drop Server from real server');
1581
1582$t->{REQUEST}  = ( <<EOF
1583GET /rewrite/all/some+test%3axxx%20with%20space HTTP/1.0
1584Host: www.example.org
1585EOF
1586 );
1587$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '/some+test%3Axxx%20with%20space' } ];
1588ok($tf_proxy->handle_http($t) == 0, 'rewrited urls work with encoded path');
1589
1590ok($tf_proxy->stop_proc == 0, "Stopping lighttpd proxy");
1591
1592} while (0);
1593
1594
1595## mod_setenv
1596
1597$t->{REQUEST} = ( <<EOF
1598GET /cgi.pl?env=TRAC_ENV HTTP/1.0
1599Host: www.example.org
1600EOF
1601 );
1602$t->{RESPONSE}  = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'tracenv' } ];
1603ok($tf->handle_http($t) == 0, 'query first setenv');
1604
1605$t->{REQUEST}  = ( <<EOF
1606GET /cgi.pl?env=SETENV HTTP/1.0
1607Host: www.example.org
1608EOF
1609 );
1610$t->{RESPONSE}  = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'setenv' } ];
1611ok($tf->handle_http($t) == 0, 'query second setenv');
1612
1613$t->{REQUEST}  = ( <<EOF
1614GET /cgi.pl?env=NEWENV HTTP/1.0
1615Host: www.example.org
1616EOF
1617 );
1618$t->{RESPONSE}  = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'newenv' } ];
1619ok($tf->handle_http($t) == 0, 'query set-environment');
1620
1621$t->{REQUEST}  = ( <<EOF
1622GET /cgi.pl?env=HTTP_FOO HTTP/1.0
1623Host: www.example.org
1624EOF
1625 );
1626$t->{RESPONSE}  = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'foo' } ];
1627ok($tf->handle_http($t) == 0, 'query add-request-header');
1628
1629$t->{REQUEST}  = ( <<EOF
1630GET /cgi.pl?env=HTTP_FOO2 HTTP/1.0
1631Host: www.example.org
1632EOF
1633 );
1634$t->{RESPONSE}  = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => 'foo2' } ];
1635ok($tf->handle_http($t) == 0, 'query set-request-header');
1636
1637$t->{REQUEST} = ( <<EOF
1638GET /index.html HTTP/1.0
1639Host: www.example.org
1640EOF
1641 );
1642$t->{RESPONSE}  = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'BAR' => 'foo' } ];
1643ok($tf->handle_http($t) == 0, 'query add-response-header');
1644
1645$t->{REQUEST} = ( <<EOF
1646GET /index.html HTTP/1.0
1647Host: www.example.org
1648EOF
1649 );
1650$t->{RESPONSE}  = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'BAR2' => 'bar2' } ];
1651ok($tf->handle_http($t) == 0, 'query set-response-header');
1652
1653
1654ok($tf->stop_proc == 0, "Stopping lighttpd");
1655