File Coverage

blib/lib/HTTP/Server/Simple/Er.pm
Criterion Covered Total %
statement 72 143 50.3
branch 8 38 21.0
condition 0 15 0.0
subroutine 19 28 67.8
pod 10 10 100.0
total 109 234 46.5


line stmt bran cond sub pod time code
1             package HTTP::Server::Simple::Er;
2             $VERSION = v0.0.4;
3              
4 3     3   48188 use warnings;
  3         9  
  3         118  
5 3     3   17 use strict;
  3         6  
  3         114  
6 3     3   30 use Carp;
  3         7  
  3         300  
7              
8 3     3   3882 use HTTP::Headers ();
  3         47602  
  3         100  
9 3     3   4224 use HTTP::Date ();
  3         16742  
  3         83  
10 3     3   3731 use HTTP::Status ();
  3         12375  
  3         88  
11              
12 3     3   2781 use URI::Escape ();
  3         4114  
  3         121  
13              
14             =head1 NAME
15              
16             HTTP::Server::Simple::Er - lightweight server and interface
17              
18             =head1 SYNOPSIS
19              
20             use HTTP::Server::Simple::Er;
21             HTTP::Server::Simple::Er->new(port => 8089,
22             req_handler => sub {
23             my $self = shift;
24             my $path = $self->path;
25             ...
26             $self->output(404, "can't find it");
27             }
28             )->run;
29              
30             =head1 ABOUT
31              
32             This is mostly an API experiment. You might be perfectly happy with
33             HTTP::Server::Simple, but I find that I often want to use it only in
34             tests and that the interface is a little clunky for that, so I'm
35             gathering some of the handiness that has been sitting on my hard drive
36             and starting to get it on CPAN.
37              
38             =cut
39              
40             my @PROPS = qw(method protocol query_string
41             request_uri path localname localport peername peeraddr);
42 3     3   3018 use Class::Accessor::Classy;
  3         15732  
  3         27  
43             with 'new';
44             ri 'listener_cb';
45             rw @PROPS;
46             ri 'port';
47             ri 'req_handler';
48             ro 'headers';
49             ri 'child_pid';
50 3     3   729 no Class::Accessor::Classy;
  3         6  
  3         17  
51              
52             # ick, make our accessors overwrite those
53 3     3   916 use base; base->import('HTTP::Server::Simple');
  3         3  
  3         4709  
54              
55             =head2 new
56              
57             my $server = HTTP::Server::Simple::Er->new(%props);
58              
59             =cut
60              
61             sub new {
62 2     2 1 50 my $class = shift;
63 2 50       12 croak('odd number of elements in argument list') if(@_ % 2);
64 2         10 my $self = {@_};
65 2         6 bless($self, $class);
66 2         6 return($self);
67             } # end subroutine new definition
68             ########################################################################
69              
70             =begin internals
71              
72             =head2 run
73              
74             $server->run;
75              
76             =cut
77              
78             sub run {
79 1     1 1 17870 my $self = shift;
80 1 50       85 $self->set_port(8080) unless($self->port);
81 1         290 $self->SUPER::run(@_);
82             } # end subroutine run definition
83             ########################################################################
84              
85             =head2 setup_listener
86              
87             Used by child_server() to callback once we're setup.
88              
89             $server->setup_listener;
90              
91             =cut
92              
93             sub setup_listener {
94 1     1 1 137 my $self = shift;
95 1         34 $self->SUPER::setup_listener;
96 1 50       1503 if(my $cb = $self->listener_cb) {
97 1         18 $cb->();
98             }
99             } # end subroutine setup_listener definition
100             ########################################################################
101              
102             =head2 setup
103              
104             $self->setup(%blah);
105              
106             =cut
107              
108             sub setup {
109 1     1 1 1568054 my $self = shift;
110 1         12 while(my ($item, $value) = splice(@_, 0, 2)) {
111 10         210 my $setter = 'set_' . $item;
112 10         724 $self->$setter($value);
113             }
114             } # end subroutine setup definition
115             ########################################################################
116              
117             =head2 headers
118              
119             $self->headers($ref);
120              
121             =cut
122              
123             sub headers {
124 0     0 1 0 my $self = shift;
125 0         0 my ($ref) = @_;
126 0 0       0 $ref or return($self->{headers});
127 0         0 my %headers = @$ref;
128 0         0 my $h = $self->{headers} = HTTP::Headers->new;
129 0         0 while(my ($k, $v) = each(%headers)) {
130 0         0 $h->header($k, $v);
131             }
132             } # end subroutine headers definition
133             ########################################################################
134              
135             =end internals
136              
137             =head2 child_server
138              
139             Starts the server as a child process.
140              
141             my $url = $server->child_server;
142              
143             =cut
144              
145             sub child_server {
146 2     2 1 12 my $self = shift;
147              
148 2         12 my $parent = $$;
149 2         4 my $win_event;
150             my $child;
151 0         0 my $cb;
152 0         0 my $kill_child;
153 2 50       14 if($^O eq 'MSWin32') {
154 0         0 require Win32::Event;
155 0         0 $win_event = Win32::Event->new();
156 0     0   0 $kill_child = sub { kill 9, $child; sleep 1 while kill 0, $child; };
  0         0  
  0         0  
157 0     0   0 $cb = sub {$win_event->pulse};
  0         0  
158             }
159             else {
160 2     1   10 $kill_child = sub { kill INT => $child; };
  1         55  
161 2     1   10 $cb = sub {kill USR1 => $parent};
  1         47  
162             }
163 2         92 $self->set_listener_cb($cb);
164              
165 2         34 my $child_loaded = 0;
166 2         6 local %SIG;
167 2 50       10 if(not $^O eq 'MSWin32') {
168 2     1   52 $SIG{USR1} = sub { $child_loaded = 1; };
  1         27  
169             }
170              
171 2     1   12 local *print_banner = sub {}; # silence this thing
  1         121  
172              
173 2 50       102 $self->set_port(8080) unless($self->port);
174 2         36 $child = $self->background;
175 1 50       1239 $child =~ /^-?\d+$/ or
176             croak("background() didn't return a valid pid");
177 1         716 $self->set_child_pid($child);
178              
179             # hooks to handle our zombies:
180             $SIG{INT} = sub { # TODO should really be stacked handlers?
181 0     0   0 warn "interrupt";
182 0         0 $kill_child->();
183             # rethrow: INT *shouldn't* run END blocks => exit/die is wrong
184 0         0 $SIG{INT} = 'DEFAULT'; kill INT => $$;
  0         0  
185 1         128 };
186 1         207 eval(q(END {&$kill_child}));
187              
188 1 50       5 if($win_event) {
189 0         0 $win_event->wait;
190             }
191             else {
192 1     0   48 local $SIG{CHLD} = sub { croak "child died"; };
  0         0  
193 1         17684 1 while(not $child_loaded);
194             }
195 1         80 return("http://localhost:" . $self->port);
196             } # end subroutine child_server definition
197             ########################################################################
198              
199             =head2 handler
200              
201             You may override this, or simply set C before calling run.
202              
203             $server->handler;
204              
205             =cut
206              
207             sub handler {
208 0     0 1   my $self = shift;
209 0 0         my $h = $self->req_handler or
210             croak("req_handler not defined or overridden");
211 0           $h->($self);
212             } # end subroutine handler definition
213             ########################################################################
214              
215             =head2 output
216              
217             Takes status code from $params{status} or a leading number. Otherwise,
218             sets it to 200.
219              
220             $self->output(\%params, @strings);
221              
222             $self->output(501, \%params, @strings);
223              
224             $self->output(501, @strings);
225              
226             $self->output(@strings);
227              
228             The code may also be an 'RC_*' string which corresponds to a constant
229             from HTTP::Status.
230              
231             $self->output(RC_NOT_FOUND => @error_html);
232              
233             =cut
234              
235             sub output {
236 0     0 1   my $self = shift;
237 0           my @args = @_;
238              
239             # allow leading code and/or leading params ref
240 0 0         my $code = ($args[0] =~ m/^RC_|^\d\d\d$/) ? shift(@args) : undef;
241 0           my %p;
242 0 0 0       if((ref($args[0])||'') eq 'HASH') {
243 0           %p = %{shift(@args)};
  0            
244 0 0 0       ($code and $p{status}) and die "cannot have status twice"
245             }
246             # let subclasses pass a trailing hashref
247 0 0 0       if(((ref($args[-1]))||'') eq 'HASH') {
248 0           my $also = pop(@args);
249 0           my @k = keys(%$also);
250 0           @p{@k} = @$also{@k};
251             }
252 0   0       $code = $p{status} ||= $code ||= 200;
      0        
253 0 0         if($code =~ m/^RC_/) {
254 0 0         my $sub = HTTP::Status->can($code) or
255             croak("$code is not a valid RC_* constant in HTTP::Status");
256 0           $p{status} = $code = $sub->();
257             }
258              
259             # "servers MUST include a Date header"
260 0   0       $p{Date} ||= HTTP::Date::time2str(time);
261              
262 0           my $h = HTTP::Headers->new(%p);
263 0 0         $h->content_type('text/html') unless($h->content_type);
264              
265 0           my $data = join("\r\n", @args);
266 0           $h->content_length(length($data));
267              
268 0           my $message = HTTP::Status::status_message($code);
269 0           print join("\r\n",
270             "HTTP/1.1 $code $message",
271             $h->as_string, '');
272 0           print $data;
273             } # end subroutine output definition
274             ########################################################################
275              
276             =head2 params
277              
278             Return a hash of parameters parsed from $self->query_string;
279              
280             my %params = $server->params;
281              
282             =cut
283              
284             sub params {
285 0     0 1   my $self = shift;
286              
287 0           my $s = $self->query_string;
288             # XXX check for correctness
289 0           return map({URI::Escape::uri_unescape($_)}
  0            
290 0           map({split(/=/, $_, 2)} split(/&/, $s)));
291             } # params #############################################################
292              
293             =head2 form_data
294              
295             Retrieve POSTed form data. If an element is mentioned twice, its value
296             automatically becomes an arrayref.
297              
298             my %form = $server->form_data;
299              
300             =cut
301              
302             sub form_data {
303 0     0 1   my $self = shift;
304              
305 0           my $h = $self->headers;
306 0           my $s;
307 0           my $fh = $self->stdio_handle;
308 0           read($fh, $s, $h->{'content-length'});
309              
310             # XXX check for correctness
311 0           my %d;
312 0           foreach my $pair (split(/&/, $s)) {
313 0           my ($k,$v) = map({$_ = URI::Escape::uri_unescape($_); s/\+/ /g; $_}
  0            
  0            
  0            
314             split(/=/, $pair, 2));
315 0 0         if($d{$k}) {
316 0 0         $d{$k} = [$d{$k}] unless(ref $d{$k});
317 0           push(@{$d{$k}}, $v);
  0            
318             }
319             else {
320 0           $d{$k} = $v;
321             }
322             }
323 0           return(%d);
324             } # form_data ##########################################################
325              
326             =head1 AUTHOR
327              
328             Eric Wilhelm @
329              
330             http://scratchcomputing.com/
331              
332             =head1 BUGS
333              
334             If you found this module on CPAN, please report any bugs or feature
335             requests through the web interface at L. I will be
336             notified, and then you'll automatically be notified of progress on your
337             bug as I make changes.
338              
339             If you pulled this development version from my /svn/, please contact me
340             directly.
341              
342             =head1 COPYRIGHT
343              
344             Copyright (C) 2008 Eric L. Wilhelm, All Rights Reserved.
345              
346             =head1 NO WARRANTY
347              
348             Absolutely, positively NO WARRANTY, neither express or implied, is
349             offered with this software. You use this software at your own risk. In
350             case of loss, no person or entity owes you anything whatsoever. You
351             have been warned.
352              
353             =head1 LICENSE
354              
355             This program is free software; you can redistribute it and/or modify it
356             under the same terms as Perl itself.
357              
358             =cut
359              
360             # vi:ts=2:sw=2:et:sta
361             1;