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 => 52; 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 /foobar HTTP/1.0 23EOF 24 ); 25$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404 } ]; 26ok($tf->handle_http($t) == 0, 'file not found'); 27 28$t->{REQUEST} = ( <<EOF 29GET /foobar?foobar HTTP/1.0 30EOF 31 ); 32$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404 } ]; 33ok($tf->handle_http($t) == 0, 'file not found + querystring'); 34 35$t->{REQUEST} = ( <<EOF 36GET /12345.txt HTTP/1.0 37Host: 123.example.org 38EOF 39 ); 40$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain' } ]; 41ok($tf->handle_http($t) == 0, 'GET, content == 12345, mimetype text/plain'); 42 43$t->{REQUEST} = ( <<EOF 44GET /12345.html HTTP/1.0 45Host: 123.example.org 46EOF 47 ); 48$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/html' } ]; 49ok($tf->handle_http($t) == 0, 'GET, content == 12345, mimetype text/html'); 50 51$t->{REQUEST} = ( <<EOF 52GET /dummyfile.bla HTTP/1.0 53Host: 123.example.org 54EOF 55 ); 56$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'application/octet-stream' } ]; 57ok($tf->handle_http($t) == 0, 'GET, content == 12345, mimetype application/octet-stream'); 58 59$t->{REQUEST} = ( <<EOF 60POST / HTTP/1.0 61EOF 62 ); 63$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 411 } ]; 64ok($tf->handle_http($t) == 0, 'POST request, no Content-Length'); 65 66 67$t->{REQUEST} = ( <<EOF 68POST / HTTP/1.0 69Content-type: application/x-www-form-urlencoded 70Content-length: 0 71EOF 72 ); 73$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ]; 74ok($tf->handle_http($t) == 0, 'POST request, empty request-body'); 75 76$t->{REQUEST} = ( <<EOF 77HEAD / HTTP/1.0 78EOF 79 ); 80$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '-HTTP-Content' => ''} ]; 81ok($tf->handle_http($t) == 0, 'HEAD request, no content'); 82 83$t->{REQUEST} = ( <<EOF 84HEAD /12345.html HTTP/1.0 85Host: 123.example.org 86EOF 87 ); 88$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '-HTTP-Content' => '', 'Content-Type' => 'text/html', 'Content-Length' => '6'} ]; 89ok($tf->handle_http($t) == 0, 'HEAD request, mimetype text/html, content-length'); 90 91$t->{REQUEST} = ( <<EOF 92HEAD http://123.example.org/12345.html HTTP/1.1 93Connection: close 94EOF 95 ); 96$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, '-HTTP-Content' => '', 'Content-Type' => 'text/html', 'Content-Length' => '6'} ]; 97ok($tf->handle_http($t) == 0, 'Hostname in first line, HTTP/1.1'); 98 99$t->{REQUEST} = ( <<EOF 100HEAD https://123.example.org/12345.html HTTP/1.0 101EOF 102 ); 103$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, '-HTTP-Content' => '', 'Content-Type' => 'text/html', 'Content-Length' => '6'} ]; 104ok($tf->handle_http($t) == 0, 'Hostname in first line as https url'); 105 106$t->{REQUEST} = ( <<EOF 107HEAD /foobar?foobar HTTP/1.0 108EOF 109 ); 110$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 404, '-HTTP-Content' => '' } ]; 111ok($tf->handle_http($t) == 0, 'HEAD request, file-not-found, query-string'); 112 113$t->{REQUEST} = ( <<EOF 114GET / HTTP/1.1 115Connection: close 116Expect: 100-continue 117EOF 118 ); 119$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 417 } ]; 120ok($tf->handle_http($t) == 0, 'Continue, Expect'); 121 122## ranges 123 124$t->{REQUEST} = ( <<EOF 125GET /12345.txt HTTP/1.0 126Host: 123.example.org 127Range: bytes=0-3 128EOF 129 ); 130$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 206, 'HTTP-Content' => '1234' } ]; 131ok($tf->handle_http($t) == 0, 'GET, Range 0-3'); 132 133$t->{REQUEST} = ( <<EOF 134GET /12345.txt HTTP/1.0 135Host: 123.example.org 136Range: bytes=-3 137EOF 138 ); 139$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 206, 'HTTP-Content' => '45'."\n" } ]; 140ok($tf->handle_http($t) == 0, 'GET, Range -3'); 141 142$t->{REQUEST} = ( <<EOF 143GET /12345.txt HTTP/1.0 144Host: 123.example.org 145Range: bytes=3- 146EOF 147 ); 148$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 206, 'HTTP-Content' => '45'."\n" } ]; 149ok($tf->handle_http($t) == 0, 'GET, Range 3-'); 150 151$t->{REQUEST} = ( <<EOF 152GET /12345.txt HTTP/1.0 153Host: 123.example.org 154Range: bytes=0-1,3-4 155EOF 156 ); 157$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 206, 'HTTP-Content' => <<EOF 158\r 159--fkj49sn38dcn3\r 160Content-Range: bytes 0-1/6\r 161Content-Type: text/plain\r 162\r 16312\r 164--fkj49sn38dcn3\r 165Content-Range: bytes 3-4/6\r 166Content-Type: text/plain\r 167\r 16845\r 169--fkj49sn38dcn3--\r 170EOF 171 } ]; 172ok($tf->handle_http($t) == 0, 'GET, Range 0-1,3-4'); 173 174$t->{REQUEST} = ( <<EOF 175GET /12345.txt HTTP/1.0 176Host: 123.example.org 177Range: bytes=0-- 178EOF 179 ); 180$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ]; 181ok($tf->handle_http($t) == 0, 'GET, Range 0--'); 182 183$t->{REQUEST} = ( <<EOF 184GET /12345.txt HTTP/1.0 185Host: 123.example.org 186Range: bytes=-2-3 187EOF 188 ); 189$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ]; 190ok($tf->handle_http($t) == 0, 'GET, Range -2-3'); 191 192$t->{REQUEST} = ( <<EOF 193GET /12345.txt HTTP/1.0 194Host: 123.example.org 195Range: bytes=-0 196EOF 197 ); 198$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 416, 'HTTP-Content' => <<EOF 199<?xml version="1.0" encoding="iso-8859-1"?> 200<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 201 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 202<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 203 <head> 204 <title>416 - Requested Range Not Satisfiable</title> 205 </head> 206 <body> 207 <h1>416 - Requested Range Not Satisfiable</h1> 208 </body> 209</html> 210EOF 211 } ]; 212ok($tf->handle_http($t) == 0, 'GET, Range -0'); 213 214$t->{REQUEST} = ( <<EOF 215GET /12345.txt HTTP/1.0 216Host: 123.example.org 217Range: bytes=25- 218EOF 219 ); 220$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 416, 'HTTP-Content' => <<EOF 221<?xml version="1.0" encoding="iso-8859-1"?> 222<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 223 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 224<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> 225 <head> 226 <title>416 - Requested Range Not Satisfiable</title> 227 </head> 228 <body> 229 <h1>416 - Requested Range Not Satisfiable</h1> 230 </body> 231</html> 232EOF 233 } ]; 234 235ok($tf->handle_http($t) == 0, 'GET, Range start out of range'); 236 237 238$t->{REQUEST} = ( <<EOF 239GET / HTTP/1.0 240Hsgfsdjf: asdfhdf 241hdhd: shdfhfdasd 242hfhr: jfghsdfg 243jfuuehdmn: sfdgjfdg 244jvcbzufdg: sgfdfg 245hrnvcnd: jfjdfg 246jfusfdngmd: gfjgfdusdfg 247nfj: jgfdjdfg 248jfue: jfdfdg 249EOF 250 ); 251$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ]; 252ok($tf->handle_http($t) == 0, 'larger headers'); 253 254 255$t->{REQUEST} = ( <<EOF 256GET / HTTP/1.0 257Host: www.example.org 258Host: 123.example.org 259EOF 260 ); 261$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ]; 262ok($tf->handle_http($t) == 0, 'Duplicate Host headers, Bug #25'); 263 264 265$t->{REQUEST} = ( <<EOF 266GET / HTTP/1.0 267Content-Length: 5 268Content-Length: 4 269EOF 270 ); 271$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ]; 272ok($tf->handle_http($t) == 0, 'Duplicate Content-Length headers'); 273 274$t->{REQUEST} = ( <<EOF 275GET / HTTP/1.0 276Content-Type: 5 277Content-Type: 4 278EOF 279 ); 280$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ]; 281ok($tf->handle_http($t) == 0, 'Duplicate Content-Type headers'); 282 283$t->{REQUEST} = ( <<EOF 284GET / HTTP/1.0 285Range: bytes=5-6 286Range: bytes=5-9 287EOF 288 ); 289$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ]; 290ok($tf->handle_http($t) == 0, 'Duplicate Range headers'); 291 292$t->{REQUEST} = ( <<EOF 293GET / HTTP/1.0 294If-None-Match: 5 295If-None-Match: 4 296EOF 297 ); 298$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ]; 299ok($tf->handle_http($t) == 0, 'Duplicate If-None-Match headers'); 300 301$t->{REQUEST} = ( <<EOF 302GET / HTTP/1.0 303If-Modified-Since: 5 304If-Modified-Since: 4 305EOF 306 ); 307$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ]; 308ok($tf->handle_http($t) == 0, 'Duplicate If-Modified-Since headers'); 309 310$t->{REQUEST} = ( <<EOF 311GET /range.pdf HTTP/1.0 312Range: bytes=0- 313EOF 314 ); 315$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ]; 316ok($tf->handle_http($t) == 0, 'GET, Range with range-requests-disabled'); 317 318$t->{REQUEST} = ( <<EOF 319GET / HTTP/1.0 320Content-Length: 4 321 3221234 323EOF 324 ); 325$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ]; 326ok($tf->handle_http($t) == 0, 'GET with Content-Length'); 327 328$t->{REQUEST} = ( <<EOF 329OPTIONS / HTTP/1.0 330Content-Length: 4 331 3321234 333EOF 334 ); 335$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ]; 336ok($tf->handle_http($t) == 0, 'OPTIONS with Content-Length'); 337 338$t->{REQUEST} = ( <<EOF 339OPTIONS rtsp://221.192.134.146:80 RTSP/1.1 340Host: 221.192.134.146:80 341EOF 342 ); 343$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ]; 344ok($tf->handle_http($t) == 0, 'OPTIONS for RTSP'); 345 346$t->{REQUEST} = ( <<EOF 347HEAD / HTTP/1.0 348Content-Length: 4 349 3501234 351EOF 352 ); 353$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ]; 354ok($tf->handle_http($t) == 0, 'HEAD with Content-Length'); 355 356$t->{REQUEST} = ( <<EOF 357GET /index.html HTTP/1.0 358If-Modified-Since: Sun, 01 Jan 2036 00:00:02 GMT 359If-Modified-Since: Sun, 01 Jan 2036 00:00:02 GMT 360EOF 361 ); 362$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 304 } ]; 363ok($tf->handle_http($t) == 0, 'Duplicate If-Mod-Since, with equal timestamps'); 364 365$t->{REQUEST} = ( "GET / HTTP/1.0\r\nIf-Modified-Since: \0\r\n\r\n" ); 366$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 400 } ]; 367ok($tf->handle_http($t) == 0, 'invalid chars in Header values (bug #1286)'); 368 369$t->{REQUEST} = ( "GET / HTTP/1.0\r\nIf-Modified-Since: \r\n\r\n" ); 370$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ]; 371ok($tf->handle_http($t) == 0, 'empty If-Modified-Since'); 372 373$t->{REQUEST} = ( "GET / HTTP/1.0\r\nIf-Modified-Since: foobar\r\n\r\n" ); 374$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ]; 375ok($tf->handle_http($t) == 0, 'broken If-Modified-Since'); 376 377$t->{REQUEST} = ( "GET / HTTP/1.0\r\nIf-Modified-Since: this string is too long to be a valid timestamp\r\n\r\n" ); 378$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200 } ]; 379ok($tf->handle_http($t) == 0, 'broken If-Modified-Since'); 380 381 382$t->{REQUEST} = ( <<EOF 383GET /index.html HTTP/1.0 384If-Modified-Since2: Sun, 01 Jan 2036 00:00:03 GMT 385If-Modified-Since: Sun, 01 Jan 2036 00:00:02 GMT 386EOF 387 ); 388$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 304 } ]; 389ok($tf->handle_http($t) == 0, 'Similar Headers (bug #1287)'); 390 391$t->{REQUEST} = ( <<EOF 392GET /index.html HTTP/1.0 393If-Modified-Since: Sun, 01 Jan 2036 00:00:02 GMT 394EOF 395 ); 396$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 304, 'Content-Type' => 'text/html' } ]; 397ok($tf->handle_http($t) == 0, 'If-Modified-Since'); 398 399$t->{REQUEST} = ( <<EOF 400GET /index.html HTTP/1.0 401If-Modified-Since: Sun, 01 Jan 2036 00:00:02 GMT 402EOF 403 ); 404$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 304, '-Content-Length' => '' } ]; 405ok($tf->handle_http($t) == 0, 'Status 304 has no Content-Length (#1002)'); 406 407$t->{REQUEST} = ( <<EOF 408GET /12345.txt HTTP/1.0 409Host: 123.example.org 410EOF 411 ); 412$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain' } ]; 413$t->{SLOWREQUEST} = 1; 414ok($tf->handle_http($t) == 0, 'GET, slow \\r\\n\\r\\n (#2105)'); 415 416print "\nPathinfo for static files\n"; 417$t->{REQUEST} = ( <<EOF 418GET /image.jpg/index.php HTTP/1.0 419EOF 420 ); 421$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 200, 'Content-Type' => 'image/jpeg' } ]; 422ok($tf->handle_http($t) == 0, 'static file accepting pathinfo by default'); 423 424$t->{REQUEST} = ( <<EOF 425GET /image.jpg/index.php HTTP/1.0 426Host: zzz.example.org 427EOF 428 ); 429$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.0', 'HTTP-Status' => 403 } ]; 430ok($tf->handle_http($t) == 0, 'static file with forbidden pathinfo'); 431 432 433print "\nConnection header\n"; 434$t->{REQUEST} = ( <<EOF 435GET /12345.txt HTTP/1.1 436Connection : close 437Host: 123.example.org 438EOF 439 ); 440$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain', 'Connection' => 'close' } ]; 441ok($tf->handle_http($t) == 0, 'Connection-header, spaces before ":"'); 442 443$t->{REQUEST} = ( <<EOF 444GET /12345.txt HTTP/1.1 445Connection: ,close 446Host: 123.example.org 447EOF 448 ); 449$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain', 'Connection' => 'close' } ]; 450ok($tf->handle_http($t) == 0, 'Connection-header, leading comma'); 451 452$t->{REQUEST} = ( <<EOF 453GET /12345.txt HTTP/1.1 454Connection: close,,TE 455Host: 123.example.org 456EOF 457 ); 458$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain', 'Connection' => 'close' } ]; 459ok($tf->handle_http($t) == 0, 'Connection-header, no value between two commas'); 460 461$t->{REQUEST} = ( <<EOF 462GET /12345.txt HTTP/1.1 463Connection: close, ,TE 464Host: 123.example.org 465EOF 466 ); 467$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain', 'Connection' => 'close' } ]; 468ok($tf->handle_http($t) == 0, 'Connection-header, space between two commas'); 469 470$t->{REQUEST} = ( <<EOF 471GET /12345.txt HTTP/1.1 472Connection: close, 473Host: 123.example.org 474EOF 475 ); 476$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain', 'Connection' => 'close' } ]; 477ok($tf->handle_http($t) == 0, 'Connection-header, comma after value'); 478 479$t->{REQUEST} = ( <<EOF 480GET /12345.txt HTTP/1.1 481Connection: close, 482Host: 123.example.org 483EOF 484 ); 485$t->{RESPONSE} = [ { 'HTTP-Protocol' => 'HTTP/1.1', 'HTTP-Status' => 200, 'HTTP-Content' => '12345'."\n", 'Content-Type' => 'text/plain', 'Connection' => 'close' } ]; 486ok($tf->handle_http($t) == 0, 'Connection-header, comma and space after value'); 487 488ok($tf->stop_proc == 0, "Stopping lighttpd"); 489 490