1# -*- Mode: Perl; indent-tabs-mode: nil; -*- 2 3package Servlet::Http::HttpServlet; 4 5use base qw(Servlet::GenericServlet); 6use strict; 7use warnings; 8 9use Servlet::ServletException (); 10use Servlet::Http::HttpServletResponse (); 11 12use constant METHOD_DELETE => 'DELETE'; 13use constant METHOD_HEAD => 'HEAD'; 14use constant METHOD_GET => 'GET'; 15use constant METHOD_OPTIONS => 'OPTIONS'; 16use constant METHOD_POST => 'POST'; 17use constant METHOD_PUT => 'PUT'; 18use constant METHOD_TRACE => 'TRACE'; 19 20use constant HEADER_IFMODSINCE => 'If-Modified-Since'; 21use constant HEADER_LASTMOD => 'Last-Modified'; 22 23sub new { 24 my $self = shift; 25 26 $self = fields::new($self) unless ref $self; 27 $self->SUPER::new(@_); 28 29 return $self; 30} 31 32sub doDelete { 33 my $self = shift; 34 my $request = shift; 35 my $response = shift; 36 37 my $msg = 'HTTP method DELETE is not supported'; 38 my $protocol = $request->getProtocol(); 39 my $code; 40 if ($protocol =~ /1\.1$/) { 41 $code = Servlet::Http::HttpServletResponse::SC_METHOD_NOT_ALLOWED; 42 } else { 43 $code = Servlet::Http::HttpServletResponse::SC_BAD_REQUEST; 44 } 45 46 $response->sendError($code, $msg); 47 48 return 1; 49} 50 51sub doGet { 52 my $self = shift; 53 my $request = shift; 54 my $response = shift; 55 56 my $method = $request->getMethod(); 57 my $msg = "HTTP method $method is not supported"; 58 my $protocol = $request->getProtocol(); 59 my $code; 60 if ($protocol =~ /1\.1$/) { 61 $code = Servlet::Http::HttpServletResponse::SC_METHOD_NOT_ALLOWED; 62 } else { 63 $code = Servlet::Http::HttpServletResponse::SC_BAD_REQUEST; 64 } 65 66 $response->sendError($code, $msg); 67 68 return 1; 69} 70 71sub doHead { 72 my $self = shift; 73 my $request = shift; 74 my $response = shift; 75 76 # use a response wrapper that eats the output handle but sets the 77 # content length appropriately 78 79 my $noBodyResponse = 80 Servlet::Http::HttpServlet::NoBodyResponse->new($response); 81 82 $self->doGet($request, $noBodyResponse); 83 $noBodyResponse->setContentLength(); 84 85 return 1; 86} 87 88sub doPost { 89 my $self = shift; 90 my $request = shift; 91 my $response = shift; 92 93 my $msg = 'HTTP method POST is not supported'; 94 my $protocol = $request->getProtocol(); 95 my $code; 96 if ($protocol =~ /1\.1$/) { 97 $code = Servlet::Http::HttpServletResponse::SC_METHOD_NOT_ALLOWED; 98 } else { 99 $code = Servlet::Http::HttpServletResponse::SC_BAD_REQUEST; 100 } 101 102 $response->sendError($code, $msg); 103 104 return 1; 105} 106 107sub doOptions { 108 my $self = shift; 109 my $request = shift; 110 my $response = shift; 111 112 my @meth; 113 114 # XXX: shouldn't be using can(), since it traverses the 115 # inheritance tree, and we just want to examine the classes 116 # that are descendents of HttpServlet 117 118 if ($self->can('doDelete')) { 119 push @meth, qw(DELETE); 120 } 121 if ($self->can('doGet')) { 122 push @meth, qw(GET HEAD); 123 } 124 if ($self->can('doOptions')) { 125 push @meth, qw(OPTIONS); 126 } 127 if ($self->can('doPost')) { 128 push @meth, qw(POST); 129 } 130 if ($self->can('doPut')) { 131 push @meth, qw(PUT); 132 } 133 if ($self->can('doTrace')) { 134 push @meth, qw(TRACE); 135 } 136 137 $response->setHeader('Allow', join(', ', @meth)); 138 139 return 1; 140} 141 142sub doPut { 143 my $self = shift; 144 my $request = shift; 145 my $response = shift; 146 147 my $msg = 'HTTP method PUT is not supported'; 148 my $protocol = $request->getProtocol(); 149 my $code; 150 if ($protocol =~ /1\.1$/) { 151 $code = Servlet::Http::HttpServletResponse::SC_METHOD_NOT_ALLOWED; 152 } else { 153 $code = Servlet::Http::HttpServletResponse::SC_BAD_REQUEST; 154 } 155 156 $response->sendError($code, $msg); 157 158 return 1; 159} 160 161sub doTrace { 162 my $self = shift; 163 my $request = shift; 164 my $response = shift; 165 166 my $str = sprintf("TRACE %s %s\r\n", 167 $request->getRequestURI(), 168 $request->getProtocol()); 169 170 for my $name ($request->getHeaderNames()) { 171 $str .= sprintf ("%s: %s\r\n", $name, $request->getHeader($name)); 172 } 173 174 $response->setContentType('message/http'); 175 $response->setContentLength(length($str)); 176 my $out = $response->getOutputHandle(); 177 $out->print($str); 178 $out->close(); 179 180 return 1; 181} 182 183sub getLastModified { 184 my $self = shift; 185 my $request = shift; 186 187 return -1; 188} 189 190sub service { 191 my $self = shift; 192 my $request = shift; 193 my $response = shift; 194 195 unless ($request->isa('Servlet::Http::HttpServletRequest') && 196 $response->isa('Servlet::Http::HttpServletResponse')) { 197 my $msg = 'non-HTTP request or response'; 198 Servlet::ServletException->throw($msg); 199 } 200 201 my $method = $request->getMethod(); 202 203 if ($method eq METHOD_DELETE) { 204 $self->doDelete($request, $response); 205 } elsif ($method eq METHOD_GET) { 206 my $lastmod = $self->getLastModified($request); 207 if ($lastmod == -1) { 208 $self->doGet($request, $response); 209 } else { 210 my $ifmodsince = $request->getDateHeader(HEADER_IFMODSINCE); 211 if ($ifmodsince < ($lastmod / 1000 * 1000)) { 212 $self->maybeSetLastModified($response, $lastmod); 213 $self->doGet($request, $response); 214 } else { 215 my $code = Servlet::Http::HttpServletResponse::SC_NOT_MODIFIED; 216 $response->setStatus($code); 217 } 218 } 219 } elsif ($method eq METHOD_HEAD) { 220 my $lastmod = $self->getLastModified($request); 221 $self->maybeSetLastModified($response, $lastmod); 222 $self->doHead($request, $response); 223 } elsif ($method eq METHOD_OPTIONS) { 224 $self->doOptions($request, $response); 225 } elsif ($method eq METHOD_POST) { 226 $self->doPost($request, $response); 227 } elsif ($method eq METHOD_PUT) { 228 $self->doPut($request, $response); 229 } elsif ($method eq METHOD_TRACE) { 230 $self->doTrace($request, $response); 231 } else { 232 my $msg = "HTTP method $method is not supported"; 233 my $code = Servlet::Http::HttpServletResponse::SC_NOT_IMPLEMENTED; 234 $response->sendError($code, $msg); 235 } 236 237 return 1; 238} 239 240sub maybeSetLastModified { 241 my $self = shift; 242 my $response = shift; 243 my $lastmod = shift; 244 245 # don't set the header if it's already been set 246 return 1 if $response->containsHeader(HEADER_LASTMOD); 247 248 $response->setDateHeader(HEADER_LASTMOD, $lastmod) if $lastmod >= 0; 249 250 return 1; 251} 252 2531; 254 255package Servlet::Http::HttpServlet::NoBodyResponse; 256 257use base qw(Servlet::Http::HttpServletResponseWrapper); 258use fields qw(output writer didSetContentLength); 259use strict; 260use warnings; 261 262# simple response wrapper class that gets content length from output 263# handle class 264 265sub new { 266 my $self = shift; 267 268 $self = fields::new($self) unless ref $self; 269 $self->SUPER::new(@_); 270 271 $self->{output} = Servlet::Http::HttpServlet::NoBodyOutputHandle->new(); 272 $self->{writer} = undef; 273 $self->{didSetContentLength} = undef; 274 275 return $self; 276} 277 278sub setContentLength { 279 my $self = shift; 280 my $len = shift; 281 282 if ($len) { 283 $self->{response}->setContentLength($len); 284 $self->{didSetContentLength} = 1; 285 } else { 286 unless ($self->{didSetContentLength}) { 287 my $len = $self->{output}->getContentLength(); 288 $self->{response}->setContentLength($len); 289 } 290 } 291 292 return 1; 293} 294 295sub getOutputHandle { 296 my $self = shift; 297 298 return $self->{output}; 299} 300 301sub getWriter { 302 my $self = shift; 303 304 unless ($self->{writer}) { 305 # XXX 306 return $self->{output}; 307 } 308 309 return $self->{writer}; 310} 311 3121; 313 314package Servlet::Http::HttpServlet::NoBodyOutputHandle; 315 316use base qw(IO::Handle); 317use fields qw(contentLength); 318use strict; 319use warnings; 320 321# simple output handle class that eats the output data but calculates 322# content length correctly 323 324sub new { 325 my $self = shift; 326 327 $self = $self->SUPER::new(@_); 328 ${*self}{servlet_http_httpservlet_nobodyoutputhandle_contentlength} = 0; 329 330 return $self; 331} 332 333sub getContentLength { 334 my $self = shift; 335 336 return ${*self}{servlet_http_httpservlet_nobodyoutputhandle_contentlength}; 337} 338 339sub print { 340 my $self = shift; 341 342 return $self->write(@_); 343} 344 345sub write { 346 my $self = shift; 347 my $str = shift; 348 my $len = shift || length $str; 349 350 ${*self}{servlet_http_httpservlet_nobodyoutputhandle_contentlength} += 351 $len; 352 353 return 1; 354} 355 3561; 357__END__ 358 359=pod 360 361=head1 NAME 362 363Servlet::Http::HttpServlet - HTTP servlet base class 364 365=head1 SYNOPSIS 366 367 $servlet->doDelete($request, $response); 368 369 $servlet->doGet($request, $response); 370 371 $servlet->doHead($request, $response); 372 373 $servlet->doOptions($request, $response); 374 375 $servlet->doPost($request, $response); 376 377 $servlet->doPut($request, $response); 378 379 $servlet->doTrace($request, $response); 380 381 my $time = $servlet->getLastModified($request); 382 383 $servlet->service($request, $response); 384 385=head1 DESCRIPTION 386 387This class acts as a base class for HTTP servlets. Subclasses must 388override at least one method, usually one of these: 389 390=over 391 392=item C<doGet()> 393 394if the servlet supports HTTP GET requests 395 396=item C<doPost()> 397 398for HTTP POST requests 399 400=item C<doPut()> 401 402for HTTP PUT requests 403 404=item C<doDelete()> 405 406for HTTP DELETE requests 407 408=item C<init()> and C<destroy()> 409 410to manage resources that are held for the life of the servlet 411 412=item C<getServletInfo()> 413 414which the servlet uses to provide information about itself 415 416=back 417 418There's almost no reason to override the C<service()> method, which 419handles standard HTTP requests by dispatching them to the handler 420methods for each HTTP request type (the C<doXXX()> methods listed 421above). 422 423Likewise, there's almost no reason to override the C<doOptions()> and 424C<doTrace()> methods. 425 426Servlets typically run on multithreaded servers, so be aware that a 427servlet must handle concurrent requets and be careful to synchronize 428access to shared resources. Shared resources include in-memory data 429such as instance or class variables and external objects such as 430files, database connections, and network connections. See 431L<perlthrtut> for more information on handling multiple threads in a 432Perl program. 433 434=head1 CONSTRUCTOR 435 436=over 437 438=item new() 439 440Does nothing. All of the servlet initialization is done by the 441C<init()> method. 442 443=back 444 445=head1 METHODS 446 447=over 448 449=item doDelete($request, $response) 450 451Called by the server (via the C<service()> method) to allow a servlet 452to handle a DELETE request. The DELETE operation allows a client to 453remove a document or Web page from the server. 454 455This method does not need to be either safe or idempotent. Operations 456requested through DELETE can have side effects for which users can be 457held accountable. When using this method, it may be useful to save a 458copy of the affected resource in temporary storage. 459 460If the request is incorrectly formatted, the method returns an HTTP 461"Bad Request" message. 462 463B<Parameters:> 464 465=over 466 467=item I<$request> 468 469the B<Servlet::Http::HttpServletRequest> object that contains the 470client request 471 472=item I<$response> 473 474the B<Servlet::Http::HttpServletResponse> object that contains the 475servlet response 476 477=back 478 479B<Throws:> 480 481=over 482 483=item B<Servlet::ServletException> 484 485if the request cannot be handled 486 487=item B<Servlet::Util::IOException> 488 489if an input or output error occurs 490 491=back 492 493=item doGet($request, $response) 494 495Called by the server (via the C<service()> method) to allow a servlet 496to handle a GET request. 497 498Overriding this method to support a GET request also automatically 499supports an HTTP HEAD request. A HEAD request is a GET request that 500returns no body in the response, only the response headers. 501 502When overriding this method, read the request data, write the response 503headers, get the response's writer or output handle object, and 504finally, write the response data. It's best to include content type 505and encoding. 506 507The servlet container must write the headers before committing the 508response, because in HTTP the headers must be sent before the response 509body. 510 511Where possible, set the content length, to allow the servlet container 512to use a persistent connection to return its response to the client, 513improving performance. The content length is automatically set if the 514entire response fits inside the response buffer. 515 516The GET method should be safe, that is, without any side effects for 517which users are held responsible. For example, most form queries have 518no side effects. If a client request is intended to change stored 519data, the request should use some other HTTP method. 520 521The GET method should also be idempotent, meaning that it can be 522safely repeated. Sometimes making a method safe also makes it 523idempotent. For example, repeating queries is both safe and 524idempotent, but buying a product online or modifying data is neither 525safe nor idempotent. 526 527If the request is incorrectly formatted, the method returns an HTTP 528"Bad Request" message. 529 530B<Parameters:> 531 532=over 533 534=item I<$request> 535 536the B<Servlet::Http::HttpServletRequest> object that contains the 537client request 538 539=item I<$response> 540 541the B<Servlet::Http::HttpServletResponse> object that contains the 542servlet response 543 544=back 545 546B<Throws:> 547 548=over 549 550=item B<Servlet::ServletException> 551 552if the request cannot be handled 553 554=item B<Servlet::Util::IOException> 555 556if an input or output error occurs 557 558=back 559 560=item doHead($request, $response) 561 562Called by the server (via the C<service()> method) to allow a servlet 563to handle a HEAD request. The client sends a HEAD request when it 564wants to see only the headers. The HEAD method counts the output bytes 565in the response to set the content length accurately. 566 567If you override this method, you can avoide computing the response 568body and just set the response ehaders directly to improve 569performance. Make sure the method you write is both safe and 570idempotent. 571 572If the request is incorrectly formatted, the method returns an HTTP 573"Bad Request" message. 574 575B<Parameters:> 576 577=over 578 579=item I<$request> 580 581the B<Servlet::Http::HttpServletRequest> object that contains the 582client request 583 584=item I<$response> 585 586the B<Servlet::Http::HttpServletResponse> object that contains the 587servlet response 588 589=back 590 591B<Throws:> 592 593=over 594 595=item B<Servlet::ServletException> 596 597if the request cannot be handled 598 599=item B<Servlet::Util::IOException> 600 601if an input or output error occurs 602 603=back 604 605=item doOptions($request, $response) 606 607Called by the server (via the C<service()> method) to allow a servlet 608to handle a OPTIONS request. The OPTIONS request determines which HTTP 609methods the server supports and returns an appropriate header. For 610example, if a servlet overrides C<doGet()>, this method returns the 611following header: 612 613 Allow: GET, HEAD, TRACE, OPTIONS 614 615There's no need to override this method unless the servlet implements 616new HTTP methods beyond those implemented by HTTP 1.1. 617 618If the request is incorrectly formatted, the method returns an HTTP 619"Bad Request" message. 620 621B<Parameters:> 622 623=over 624 625=item I<$request> 626 627the B<Servlet::Http::HttpServletRequest> object that contains the 628client request 629 630=item I<$response> 631 632the B<Servlet::Http::HttpServletResponse> object that contains the 633servlet response 634 635=back 636 637B<Throws:> 638 639=over 640 641=item B<Servlet::ServletException> 642 643if the request cannot be handled 644 645=item B<Servlet::Util::IOException> 646 647if an input or output error occurs 648 649=back 650 651=item doPost($request, $response) 652 653Called by the server (via the C<service()> method) to allow a servlet 654to handle a POST request. The POST method allows the client to send 655data of unlimited length to the Web server. 656 657When overriding this method, read the request data, write the response 658headers, get the response's writer or output handle object, and 659finally, write the response data. It's best to include content type 660and encoding. 661 662The servlet container must write the headers before committing the 663response, because in HTTP the headers must be sent before the response 664body. 665 666Where possible, set the content length, to allow the servlet container 667to use a persistent connection to return its response to the client, 668improving performance. The content length is automatically set if the 669entire response fits inside the response buffer. 670 671When using HTTP 1.1 chunked encoding (which means that the response 672has a Transfer-Encoding header), do not set the content length. 673 674This method does not need to be either safe or idempotent. Operations 675requested through POST can have side effects for which the user can be 676held accountable, for example, updating stored data or buying items 677online. 678 679If the request is incorrectly formatted, the method returns an HTTP 680"Bad Request" message. 681 682B<Parameters:> 683 684=over 685 686=item I<$request> 687 688the B<Servlet::Http::HttpServletRequest> object that contains the 689client request 690 691=item I<$response> 692 693the B<Servlet::Http::HttpServletResponse> object that contains the 694servlet response 695 696=back 697 698B<Throws:> 699 700=over 701 702=item B<Servlet::ServletException> 703 704if the request cannot be handled 705 706=item B<Servlet::Util::IOException> 707 708if an input or output error occurs 709 710=back 711 712=item doPut($request, $response) 713 714Called by the server (via the C<service()> method) to allow a servlet 715to handle a Put request. The PUT operation allows a client to place a 716file on the server and is similar to sending a file by FTP. 717 718When overriding this method, leave intact any content headers sent 719with the request (including Content-Length, Content-Type, 720Content-Transfer-Encoding, Content-Encoding, Content-Base, 721Content-Language, Content-Location, Content-MD5 and Content-Range). If 722your method cannot handle a content header, it must issue an error 723message (HTTP 501 - Not Implemented) and discard the request. For more 724information on HTTP 1.1, see RFC 2068. 725 726This method does not need to be either safe or idempotent. Operations 727that it performs can have side effects for which the user can be held 728accountable. When using this method, it may be useful to save a copy 729of the affected URL in temporary storage. 730 731If the request is incorrectly formatted, the method returns an HTTP 732"Bad Request" message. 733 734B<Parameters:> 735 736=over 737 738=item I<$request> 739 740the B<Servlet::Http::HttpServletRequest> object that contains the 741client request 742 743=item I<$response> 744 745the B<Servlet::Http::HttpServletResponse> object that contains the 746servlet response 747 748=back 749 750B<Throws:> 751 752=over 753 754=item B<Servlet::ServletException> 755 756if the request cannot be handled 757 758=item B<Servlet::Util::IOException> 759 760if an input or output error occurs 761 762=back 763 764=item getLastModified($request) 765 766Returns the time the requested resource was last modified, in 767milliseconds since midnight January 1, 1970 GMT. IF the time is 768unknown, this method returns a negative number (the default). 769 770Servlets that support HTTP GET requests and can quickly determine 771their last modification time should override this method. This makes 772browser and proxy caches work more effectively, reducing the load on 773server and network resources. 774 775B<Parameters:> 776 777=over 778 779=item I<$request> 780 781the B<Servlet::Http::HttpServletRequest> object that contains the 782client request 783 784=back 785 786=item service($request, $response) 787 788Dispatches client requests to the I<doXXX> methods defined in this 789class. There's no need to override this method. 790 791B<Parameters:> 792 793=over 794 795=item I<$request> 796 797the B<Servlet::Http::HttpServletRequest> object that contains the 798client request 799 800=item I<$response> 801 802the B<Servlet::Http::HttpServletResponse> object that contains the 803servlet response 804 805=back 806 807B<Throws:> 808 809=over 810 811=item B<Servlet::ServletException> 812 813if the request cannot be handled 814 815=item B<Servlet::Util::IOException> 816 817if an input or output error occurs 818 819=back 820 821=back 822 823=head1 SEE ALSO 824 825L<Servlet::GenericServlet>, 826L<Servlet::Http::HttpServletRequest>, 827L<Servlet::Http::HttpServletResponse> 828 829=head1 AUTHOR 830 831Brian Moseley, bcm@maz.org 832 833=cut 834