File Coverage

blib/lib/Servlet/Http/HttpServlet.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             # -*- Mode: Perl; indent-tabs-mode: nil; -*-
2              
3             package Servlet::Http::HttpServlet;
4              
5 1     1   9860 use base qw(Servlet::GenericServlet);
  1         3  
  1         573  
6 1     1   5 use strict;
  1         2  
  1         27  
7 1     1   4 use warnings;
  1         2  
  1         22  
8              
9 1     1   556 use Servlet::ServletException ();
  0            
  0            
10             use Servlet::Http::HttpServletResponse ();
11              
12             use constant METHOD_DELETE => 'DELETE';
13             use constant METHOD_HEAD => 'HEAD';
14             use constant METHOD_GET => 'GET';
15             use constant METHOD_OPTIONS => 'OPTIONS';
16             use constant METHOD_POST => 'POST';
17             use constant METHOD_PUT => 'PUT';
18             use constant METHOD_TRACE => 'TRACE';
19              
20             use constant HEADER_IFMODSINCE => 'If-Modified-Since';
21             use constant HEADER_LASTMOD => 'Last-Modified';
22              
23             sub new {
24             my $self = shift;
25              
26             $self = fields::new($self) unless ref $self;
27             $self->SUPER::new(@_);
28              
29             return $self;
30             }
31              
32             sub 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              
51             sub 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              
71             sub 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              
88             sub 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              
107             sub 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              
142             sub 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              
161             sub 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              
183             sub getLastModified {
184             my $self = shift;
185             my $request = shift;
186              
187             return -1;
188             }
189              
190             sub 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              
240             sub 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              
253             1;
254              
255             package Servlet::Http::HttpServlet::NoBodyResponse;
256              
257             use base qw(Servlet::Http::HttpServletResponseWrapper);
258             use fields qw(output writer didSetContentLength);
259             use strict;
260             use warnings;
261              
262             # simple response wrapper class that gets content length from output
263             # handle class
264              
265             sub 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              
278             sub 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              
295             sub getOutputHandle {
296             my $self = shift;
297              
298             return $self->{output};
299             }
300              
301             sub getWriter {
302             my $self = shift;
303              
304             unless ($self->{writer}) {
305             # XXX
306             return $self->{output};
307             }
308              
309             return $self->{writer};
310             }
311              
312             1;
313              
314             package Servlet::Http::HttpServlet::NoBodyOutputHandle;
315              
316             use base qw(IO::Handle);
317             use fields qw(contentLength);
318             use strict;
319             use warnings;
320              
321             # simple output handle class that eats the output data but calculates
322             # content length correctly
323              
324             sub 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              
333             sub getContentLength {
334             my $self = shift;
335              
336             return ${*self}{servlet_http_httpservlet_nobodyoutputhandle_contentlength};
337             }
338              
339             sub print {
340             my $self = shift;
341              
342             return $self->write(@_);
343             }
344              
345             sub 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              
356             1;
357             __END__