File Coverage

blib/lib/POE/Declare/HTTP/Server.pm
Criterion Covered Total %
statement 31 43 72.0
branch 0 6 0.0
condition n/a
subroutine 11 12 91.6
pod n/a
total 42 61 68.8


line stmt bran cond sub pod time code
1             package POE::Declare::HTTP::Server;
2              
3             =pod
4              
5             =head1 NAME
6              
7             POE::Declare::HTTP::Server - A simple HTTP server based on POE::Declare
8              
9             =head1 SYNOPSIS
10              
11             # Create the web server
12             my $http = POE::Declare::HTTP::Server->new(
13             Hostname => '127.0.0.1',
14             Port => '8010',
15             Handler => sub {
16             my $server = shift;
17             my $response = shift;
18            
19             # The request is not passed to you but is available if needed
20             my $request = $response->request;
21            
22             # Webby content generation stuff here
23             $response->code( 200 );
24             $response->header( 'Content-Type' => 'text/plain' );
25             $response->content( "Hello World!" );
26            
27             return;
28             },
29             );
30            
31             # Control with methods
32             $http->start;
33             $http->stop;
34              
35             =head1 DESCRIPTION
36              
37             This module provides a simple HTTP server based on L.
38              
39             The implemenetation is intentionally minimalist, making this module an ideal
40             choice for creating specialised web servers embedded in larger applications.
41              
42             =head1 METHODS
43              
44             =cut
45              
46 2     2   24164 use 5.008;
  2         5  
  2         62  
47 2     2   10 use strict;
  2         4  
  2         52  
48 2     2   8 use warnings;
  2         10  
  2         54  
49 2     2   1734 use Params::Util 1.00 ();
  2         7735  
  2         46  
50 2     2   1580 use HTTP::Request 5.827 ();
  2         44038  
  2         67  
51 2     2   1708 use HTTP::Response 5.830 ();
  2         13265  
  2         58  
52 2     2   1872 use POE 1.293 ();
  2         116523  
  2         63  
53 2     2   2513 use POE::Filter::HTTPD ();
  2         31876  
  2         55  
54 2     2   2431 use POE::Wheel::ReadWrite ();
  2         292264  
  2         58  
55 2     2   2646 use POE::Wheel::SocketFactory ();
  2         14557  
  2         387  
56              
57             our $VERSION = '0.05';
58              
59              
60              
61              
62              
63             ######################################################################
64             # Constructor and Accessors
65              
66             =pod
67              
68             =head2 new
69              
70             my $server = POE::Declare::HTTP::Server->new(
71             Hostname => '127.0.0.1',
72             Port => '8010',
73             Handler => \&content,
74              
75             StartupEvent => \&startup_done,
76             StartupError => \&startup_failed,
77             ShutdownEvent => \&shutdown_done,
78             );
79              
80             The C constructor sets up a reusable HTTP server that can be enabled
81             and disabled repeatedly as needed.
82              
83             It takes three required parameters parameters. C, C and
84             C.
85              
86             The C parameter should be a C reference that will be passed
87             the server object and a L object. Your code should
88             fill the provided response object, which will be sent to the client when the
89             function ends. If your content will change based on the request, you can obtain
90             the request from the L method.
91              
92             The server supports three messages you can register callbacks for.
93              
94             The C message fires after the server socket has been bound and is
95             available for clients to make requests, and before any connections have been
96             made from clients.
97              
98             The C message fires if the server fails to bind to the port, or
99             has some other error during the socket setup process.
100              
101             The C message fires on the completion of a controlled shutdown.
102              
103             There is currently no C event for unexpected server termination,
104             as this should not occur. An error of this type may, however, be added later.
105              
106             =cut
107              
108             sub new {
109 0     0     my $self = shift->SUPER::new(@_);
110              
111             # Check params
112 0 0         unless ( Params::Util::_STRING($self->Hostname) ) {
113 0           die "Missing or invalid Hostname param";
114             }
115 0 0         unless ( Params::Util::_POSINT($self->Port) ) {
116 0           die "Missing or invalid Port param";
117             }
118 0 0         unless ( Params::Util::_CODE($self->Handler) ) {
119 0           die "Missing or invalid Handler param";
120             }
121              
122             # The listening socket
123 0           $self->{server} = undef;
124              
125             # The active session (we only support one at a time at the moment)
126 0           $self->{client} = undef;
127              
128 0           return $self;
129             }
130              
131             =pod
132              
133             =head2 Hostname
134              
135             The C accessor returns the server to bind to, as originally
136             provided to the constructor.
137              
138             =head2 Port
139              
140             The C accessor returns the port number to bind to, as originally
141             provided to the constructor.
142              
143             =head2 Handler
144              
145             The C accessor returns the C reference that requests
146             will be passed to, as provided to the constructor.
147              
148             =cut
149              
150             use POE::Declare 0.50 {
151 0           Hostname => 'Param',
152             Port => 'Param',
153             Handler => 'Param',
154              
155             StartupEvent => 'Message',
156             StartupError => 'Message',
157             ShutdownEvent => 'Message',
158             ShutdownError => 'Message',
159              
160             server => 'Internal',
161             client => 'Internal',
162 2     2   2601 };
  0            
163              
164              
165              
166              
167              
168             ######################################################################
169             # Control Methods
170              
171             =pod
172              
173             =head2 start
174              
175             The C method enables the web server. If the server is already running,
176             this method will shortcut and do nothing.
177              
178             If called before L has been started, the web server will start
179             immediately once L is running.
180              
181             =cut
182              
183             sub start {
184             my $self = shift;
185             unless ( $self->spawned ) {
186             $self->spawn;
187             $self->post('startup');
188             }
189             return 1;
190             }
191              
192             =pod
193              
194             =head2 stop
195              
196             The C method disables the web server. If the server is not running,
197             this method will shortcut and do nothing.
198              
199             =cut
200              
201             sub stop {
202             my $self = shift;
203             if ( $self->spawned ) {
204             $self->post('shutdown');
205             }
206             return 1;
207             }
208              
209              
210              
211              
212              
213             ######################################################################
214             # Event Methods
215              
216             sub startup : Event {
217              
218             # Create the socket factory
219             $_[SELF]->{server} = POE::Wheel::SocketFactory->new(
220             Reuse => 1,
221             BindPort => $_[SELF]->Port,
222             SuccessEvent => 'connect',
223             FailureEvent => 'error',
224             );
225              
226             # If the server survives long enough for this event to fire,
227             # it has been started successfully.
228             $_[SELF]->post('started');
229             }
230              
231             # Signal the successful startup
232             sub started : Event {
233             # If the FailureEvent fired before us, so abort this event
234             $_[SELF]->{server} or return;
235              
236             # Failure didn't fire, so we must have bound successfully
237             $_[SELF]->StartupEvent;
238             }
239              
240             # Clean up and signal failure
241             sub error : Event {
242             $_[SELF]->finish;
243             $_[SELF]->StartupError;
244             }
245              
246             sub connect : Event {
247             # This initial implementation only deals with one request at a time.
248             # It has the side effect of allowing the request handler to block for
249             # a fairly long period of time without too much of an issue.
250             $_[SELF]->{server}->pause_accept;
251              
252             # Create the socket
253             $_[SELF]->{client} = POE::Wheel::ReadWrite->new(
254             Filter => POE::Filter::HTTPD->new,
255             Handle => $_[ARG0],
256             InputEvent => 'request',
257             FlushedEvent => 'disconnect',
258             ErrorEvent => 'disconnect',
259             );
260             }
261              
262             sub request : Event {
263              
264             # Create the default response.
265             # We default to a server error so that the appropriate return is used
266             # if the Handler fails or somehow does nothing to the response.
267             my $response = HTTP::Response->new( 500 );
268             $response->request( $_[ARG0] );
269              
270             # Pass the response (and the request within it) to the handler.
271             # Prevent an exception in the handler crashing the entire server.
272             eval {
273             $_[SELF]->Handler->( $_[SELF], $response );
274             };
275              
276             # Send the response back to the client.
277             # The just wait for the socket to flush
278             $_[SELF]->{client}->put( $response );
279             }
280              
281             sub disconnect : Event {
282             # Handle stray events arriving after intentional shutdown
283             $_[SELF]->{server} or return;
284              
285             # Clean up the current request, and open up for the next one
286             $_[SELF]->{client} = undef;
287             $_[SELF]->{server}->resume_accept;
288             }
289              
290             sub shutdown : Event {
291             $_[SELF]->finish;
292             $_[SELF]->ShutdownEvent;
293             }
294              
295              
296              
297              
298              
299             ######################################################################
300             # POE::Declare::Object Methods
301              
302             sub finish {
303             my $self = shift;
304              
305             # Clear out the server and any active connection
306             $self->{server} = undef;
307             $self->{client} = undef;
308              
309             # Call parent method to clean out other things
310             $self->SUPER::finish(@_);
311             }
312              
313             compile;
314              
315             =pod
316              
317             =head1 SUPPORT
318              
319             Bugs should be always be reported via the CPAN bug tracker at
320              
321             L
322              
323             For other issues, or commercial enhancement or support, contact the author.
324              
325             =head1 AUTHORS
326              
327             Adam Kennedy Eadamk@cpan.orgE
328              
329             =head1 SEE ALSO
330              
331             L, L
332              
333             =head1 COPYRIGHT
334              
335             Copyright 2006 - 2011 Adam Kennedy.
336              
337             This program is free software; you can redistribute
338             it and/or modify it under the same terms as Perl itself.
339              
340             The full text of the license can be found in the
341             LICENSE file included with this module.
342              
343             =cut