1POE::Component::Server:U:sSeirmpCloenHtTrTiPb(u3t)ed PerPlOED:o:cCuommepnotnaetnito:n:Server::SimpleHTTP(3)
2
3
4

NAME

6       POE::Component::Server::SimpleHTTP - Perl extension to serve HTTP
7       requests in POE.
8

SYNOPSIS

10               use POE;
11               use POE::Component::Server::SimpleHTTP;
12
13               # Start the server!
14               POE::Component::Server::SimpleHTTP->new(
15                       'ALIAS'         =>      'HTTPD',
16                       'ADDRESS'       =>      '192.168.1.1',
17                       'PORT'          =>      11111,
18                       'HOSTNAME'      =>      'MySite.com',
19                       'HANDLERS'      =>      [
20                               {
21                                       'DIR'           =>      '^/bar/.*',
22                                       'SESSION'       =>      'HTTP_GET',
23                                       'EVENT'         =>      'GOT_BAR',
24                               },
25                               {
26                                       'DIR'           =>      '^/$',
27                                       'SESSION'       =>      'HTTP_GET',
28                                       'EVENT'         =>      'GOT_MAIN',
29                               },
30                               {
31                                       'DIR'           =>      '^/foo/.*',
32                                       'SESSION'       =>      'HTTP_GET',
33                                       'EVENT'         =>      'GOT_NULL',
34                               },
35                               {
36                                       'DIR'           =>      '.*',
37                                       'SESSION'       =>      'HTTP_GET',
38                                       'EVENT'         =>      'GOT_ERROR',
39                               },
40                       ],
41
42                       'LOGHANDLER' => { 'SESSION' => 'HTTP_GET',
43                                         'EVENT'   => 'GOT_LOG',
44                       },
45
46                       # In the testing phase...
47                       'SSLKEYCERT'    =>      [ 'public-key.pem', 'public-cert.pem' ],
48               ) or die 'Unable to create the HTTP Server';
49
50               # Create our own session to receive events from SimpleHTTP
51               POE::Session->create(
52                       inline_states => {
53                               '_start'        =>      sub {   $_[KERNEL]->alias_set( 'HTTP_GET' );
54                                                               $_[KERNEL]->post( 'HTTPD', 'GETHANDLERS', $_[SESSION], 'GOT_HANDLERS' );
55                                                       },
56
57                               'GOT_BAR'       =>      \&GOT_REQ,
58                               'GOT_MAIN'      =>      \&GOT_REQ,
59                               'GOT_ERROR'     =>      \&GOT_ERR,
60                               'GOT_NULL'      =>      \&GOT_NULL,
61                               'GOT_HANDLERS'  =>      \&GOT_HANDLERS,
62                               'GOT_LOG'       =>      \&GOT_LOG,
63                       },
64               );
65
66               # Start POE!
67               POE::Kernel->run();
68
69               sub GOT_HANDLERS {
70                       # ARG0 = HANDLERS array
71                       my $handlers = $_[ ARG0 ];
72
73                       # Move the first handler to the last one
74                       push( @$handlers, shift( @$handlers ) );
75
76                       # Send it off!
77                       $_[KERNEL]->post( 'HTTPD', 'SETHANDLERS', $handlers );
78               }
79
80               sub GOT_NULL {
81                       # ARG0 = HTTP::Request object, ARG1 = HTTP::Response object, ARG2 = the DIR that matched
82                       my( $request, $response, $dirmatch ) = @_[ ARG0 .. ARG2 ];
83
84                       # Kill this!
85                       $_[KERNEL]->post( 'HTTPD', 'CLOSE', $response );
86               }
87
88               sub GOT_REQ {
89                       # ARG0 = HTTP::Request object, ARG1 = HTTP::Response object, ARG2 = the DIR that matched
90                       my( $request, $response, $dirmatch ) = @_[ ARG0 .. ARG2 ];
91
92                       # Do our stuff to HTTP::Response
93                       $response->code( 200 );
94                       $response->content( 'Some funky HTML here' );
95
96                       # We are done!
97                       # For speed, you could use $_[KERNEL]->call( ... )
98                       $_[KERNEL]->post( 'HTTPD', 'DONE', $response );
99               }
100
101               sub GOT_ERR {
102                       # ARG0 = HTTP::Request object, ARG1 = HTTP::Response object, ARG2 = the DIR that matched
103                       my( $request, $response, $dirmatch ) = @_[ ARG0 .. ARG2 ];
104
105                       # Check for errors
106                       if ( ! defined $request ) {
107                               $_[KERNEL]->post( 'HTTPD', 'DONE', $response );
108                               return;
109                       }
110
111                       # Do our stuff to HTTP::Response
112                       $response->code( 404 );
113                       $response->content( "Hi visitor from " . $response->connection->remote_ip . ", Page not found -> '" . $request->uri->path . "'" );
114
115                       # We are done!
116                       # For speed, you could use $_[KERNEL]->call( ... )
117                       $_[KERNEL]->post( 'HTTPD', 'DONE', $response );
118               }
119
120               sub GOT_LOG {
121                       # ARG0 = HTTP::Request object, ARG1 = remote IP
122                       my ($request, $remote_ip) = @_[ARG0,ARG1];
123
124                       # Do some sort of logging activity.
125                       # If the request was malformed, $request = undef
126                       # CHECK FOR A REQUEST OBJECT BEFORE USING IT.
127               if( $request ) {
128               {
129                       warn join(' ', time(), $remote_ip, $request->uri ), "\n";
130               } else {
131                       warn join(' ', time(), $remote_ip, 'Bad request' ), "\n";
132               }
133
134                       return;
135               }
136

ABSTRACT

138               An easy to use HTTP daemon for POE-enabled programs
139

DESCRIPTION

141       This module makes serving up HTTP requests a breeze in POE.
142
143       The hardest thing to understand in this module is the HANDLERS. That's
144       it!
145
146       The standard way to use this module is to do this:
147
148               use POE;
149               use POE::Component::Server::SimpleHTTP;
150
151               POE::Component::Server::SimpleHTTP->new( ... );
152
153               POE::Session->create( ... );
154
155               POE::Kernel->run();
156
157       Starting SimpleHTTP
158
159       To start SimpleHTTP, just call it's new method:
160
161               POE::Component::Server::SimpleHTTP->new(
162                       'ALIAS'         =>      'HTTPD',
163                       'ADDRESS'       =>      '192.168.1.1',
164                       'PORT'          =>      11111,
165                       'HOSTNAME'      =>      'MySite.com',
166                       'HEADERS'       =>      {},
167                       'HANDLERS'      =>      [ ],
168               );
169
170       This method will die on error or return success.
171
172       This constructor accepts only 7 options.
173
174       "ALIAS"
175           This will set the alias SimpleHTTP uses in the POE Kernel.  This
176           will default to "SimpleHTTP"
177
178       "ADDRESS"
179           This value will be passed to POE::Component::Server::TCP to bind
180           to.
181
182       "PORT"
183           This value will be passed to POE::Component::Server::TCP to bind
184           to.
185
186       "HOSTNAME"
187           This value is for the HTTP::Request's URI to point to.  If this is
188           not supplied, SimpleHTTP will use Sys::Hostname to find it.
189
190       "HEADERS"
191           This should be a hashref, that will become the default headers on
192           all HTTP::Response objects.  You can override this in individual
193           requests by setting it via $request->header( ... )
194
195           For more information, consult the HTTP::Headers module.
196
197       "HANDLERS"
198           This is the hardest part of SimpleHTTP :)
199
200           You supply an array, with each element being a hash. All the hashes
201           should contain those 3 keys:
202
203           DIR  ->   The regexp that will be used, more later.
204
205           SESSION   ->   The session to send the input
206
207           EVENT     ->   The event to trigger
208
209           The DIR key should be a valid regexp. This will be matched against
210           the current request path.  Pseudocode is: if ( $path =~ /$DIR/ )
211
212           NOTE: The path is UNIX style, not MSWIN style ( /blah/foo not
213           \blah\foo )
214
215           Now, if you supply 100 handlers, how will SimpleHTTP know what to
216           do? Simple! By passing in an array in the first place, you have
217           already told SimpleHTTP the order of your handlers. They will be
218           tried in order, and if a match is not found, SimpleHTTP will return
219           a 404 response.
220
221           This allows some cool things like specifying 3 handlers with DIR
222           of: '^/foo/.*', '^/$', '.*'
223
224           Now, if the request is not in /foo or not root, your 3rd handler
225           will catch it, becoming the "404 not found" handler!
226
227           NOTE: You might get weird Session/Events, make sure your handlers
228           are in order, for example: '^/', '^/foo/.*'  The 2nd handler will
229           NEVER get any requests, as the first one will match ( no $ in the
230           regex )
231
232           Now, here's what a handler receives:
233
234           ARG0 -> HTTP::Request object
235
236           ARG1 -> POE::Component::Server::SimpleHTTP::Response object
237
238           ARG2 -> The exact DIR that matched, so you can see what triggered
239           what
240
241           NOTE: If ARG0 is undef, that means POE::Filter::HTTPD encountered
242           an error parsing the client request, simply modify the
243           HTTP::Response object and send some sort of generic error. Simpleā€
244           HTTP will set the path used in matching the DIR regexes to an empty
245           string, so if there is a "catch-all" DIR regex like '.*', it will
246           catch the errors, and only that one.
247
248           NOTE: The only way SimpleHTTP will leak memory ( hopefully heh ) is
249           if you discard the SimpleHTTP::Response object without sending it
250           back to SimpleHTTP via the DONE/CLOSE events, so never do that!
251
252       "LOGHANDLER"
253           Expects a hashref with the following key, values:
254
255           SESSION   ->   The session to send the input
256
257           EVENT     ->   The event to trigger
258
259           You will receive an event for each request to the server from
260           clients.  Malformed client requests will not be passed into the
261           handler.  Instead undef will be passed.
262
263           The event will have the following parameters:
264
265           ARG0 -> HTTP::Request object/undef if client request was malformed.
266
267           ARG1 -> the IP address of the client
268
269       "SSLKEYCERT"
270           This should be an arrayref of only 2 elements - the public key and
271           certificate location. Now, this is still in the experimental stage,
272           and testing is greatly welcome!
273
274           Again, this will automatically turn every incoming connection into
275           a SSL socket. Once enough testing has been done, this option will
276           be augmented with more SSL stuff!
277
278       Events
279
280       SimpleHTTP is so simple, there are only 8 events available.
281
282       "DONE"
283                   This event accepts only one argument: the HTTP::Response object we sent to the handler.
284
285                   Calling this event implies that this particular request is done, and will proceed to close the socket.
286
287                   NOTE: This method automatically sets those 3 headers if they are not already set:
288                           Date            ->      Current date stringified via HTTP::Date->time2str
289                           Content-Type    ->      text/html
290                           Content-Length  ->      length( $response->content )
291
292                   To get greater throughput and response time, do not post() to the DONE event, call() it!
293                   However, this will force your program to block while servicing web requests...
294
295       "CLOSE"
296                   This event accepts only one argument: the HTTP::Response object we sent to the handler.
297
298                   Calling this event will close the socket, not sending any output
299
300       "GETHANDLERS"
301                   This event accepts 2 arguments: The session + event to send the response to
302
303                   This event will send back the current HANDLERS array ( deep-cloned via Storable::dclone )
304
305                   The resulting array can be played around to your tastes, then once you are done...
306
307       "SETHANDLERS"
308                   This event accepts only one argument: pointer to HANDLERS array
309
310                   BEWARE: if there is an error in the HANDLERS, SimpleHTTP will die!
311
312       "STARTLISTEN"
313                   Starts the listening socket, if it was shut down
314
315       "STOPLISTEN"
316                   Simply a wrapper for SHUTDOWN GRACEFUL, but will not shutdown SimpleHTTP if there is no more requests
317
318       "SHUTDOWN"
319                   Without arguments, SimpleHTTP does this:
320                           Close the listening socket
321                           Kills all pending requests by closing their sockets
322                           Removes it's alias
323
324                   With an argument of 'GRACEFUL', SimpleHTTP does this:
325                           Close the listening socket
326                           Waits for all pending requests to come in via DONE/CLOSE, then removes it's alias
327
328       Streaming with SimpleHTTP
329
330       It's now possible to send data as a stream to clients (unbuffered and
331       integrated in the POE loop).
332
333       Just create your sessions as usually and add a streaming event, this
334       event will be triggered each time you set the $response to a streaming
335       state:
336
337          # sets the response as streamed within our session with the stream event
338          $response->stream(
339             session  => 'HTTP_GET',
340             event    => 'GOT_STREAM'
341          );
342
343       This will call the GOT_STREAM event of the HTTP_GET session with as
344       first arg (ARG0) bundled within a hash the wheel, request, response and
345       id.
346
347       You can now send data by chunks and either call yourself back (via POE)
348       or shutdown when your streaming is done (EOF for example).
349
350          sub GOT_STREAM {
351             my ( $kernel, $heap, $stream ) = @_[KERNEL, HEAP, ARG0];
352
353             # $stream contains the wheel, the request, the response
354             # and an id associated the the wheel
355             $stream->{'wheel'}->put("Hello World\n");
356
357             # lets go on streaming ... with some delay actually but
358             # that should be a post unless the client needs the data
359             # slowly ..
360             POE::Kernel->delay('GOT_STREAM', 1, $stream );
361          }
362
363       SimpleHTTP Notes
364
365       This module is very picky about capitalization!
366
367       All of the options are uppercase, to avoid confusion.
368
369       You can enable debugging mode by doing this:
370
371               sub POE::Component::Server::SimpleHTTP::DEBUG () { 1 }
372               use POE::Component::Server::SimpleHTTP;
373
374       Also, this module will try to keep the Listening socket alive.  if it
375       dies, it will open it again for a max of 5 retries.
376
377       You can override this behavior by doing this:
378
379               sub POE::Component::Server::SimpleHTTP::MAX_RETRIES () { 10 }
380               use POE::Component::Server::SimpleHTTP;
381
382       For those who are pondering about basic-authentication, here's a tiny
383       snippet to put in the Event handler
384
385               # Contributed by Rocco Caputo
386               sub Got_Request {
387                       # ARG0 = HTTP::Request, ARG1 = HTTP::Response
388                       my( $request, $response ) = @_[ ARG0, ARG1 ];
389
390                       # Get the login
391                       my ( $login, $password ) = $request->authorization_basic();
392
393                       # Decide what to do
394                       if ( ! defined $login or ! defined $password ) {
395                               # Set the authorization
396                               $response->header( 'WWW-Authenticate' => 'Basic realm="MyRealm"' );
397                               $response->code( 401 );
398                               $response->content( 'FORBIDDEN.' );
399
400                               # Send it off!
401                               $_[KERNEL]->post( 'SimpleHTTP', 'DONE', $response );
402                       } else {
403                               # Authenticate the user and move on
404                       }
405               }
406
407       EXPORT
408
409       Nothing.
410

SEE ALSO

412               L<POE>
413
414               L<POE::Filter::HTTPD>
415
416               L<HTTP::Request>
417
418               L<HTTP::Response>
419
420               L<POE::Component::Server::SimpleHTTP::Connection>
421
422               L<POE::Component::Server::SimpleHTTP::Response>
423
424               L<POE::Component::Server::SimpleHTTP::PreFork>
425
426               L<POE::Component::SSLify>
427

AUTHOR

429       Apocalypse <apocal@cpan.org>
430
432       Copyright 2006 by Apocalypse
433
434       This library is free software; you can redistribute it and/or modify it
435       under the same terms as Perl itself.
436
437
438
439perl v5.8.8                       2007-03-P2O1E::Component::Server::SimpleHTTP(3)
Impressum