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

VERSION

10       version 2.28
11

SYNOPSIS

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

DESCRIPTION

145       This module makes serving up HTTP requests a breeze in POE.
146
147       The hardest thing to understand in this module is the HANDLERS. That's
148       it!
149
150       The standard way to use this module is to do this:
151
152               use POE;
153               use POE::Component::Server::SimpleHTTP;
154
155               POE::Component::Server::SimpleHTTP->new( ... );
156
157               POE::Session->create( ... );
158
159               POE::Kernel->run();
160
161   Starting SimpleHTTP
162       To start SimpleHTTP, just call it's new method:
163
164               POE::Component::Server::SimpleHTTP->new(
165                       'ALIAS'         =>      'HTTPD',
166                       'ADDRESS'       =>      '192.168.1.1',
167                       'PORT'          =>      11111,
168                       'HOSTNAME'      =>      'MySite.com',
169                       'HEADERS'       =>      {},
170                       'HANDLERS'      =>      [ ],
171               );
172
173       This method will die on error or return success.
174
175       This constructor accepts only 7 options.
176
177       "ALIAS"
178           This will set the alias SimpleHTTP uses in the POE Kernel.  This
179           will default to "SimpleHTTP"
180
181       "ADDRESS"
182           This value will be passed to POE::Wheel::SocketFactory to bind to,
183           will use INADDR_ANY if it is nothing is provided (or IN6ADDR_ANY if
184           DOMAIN is AF_INET6).  For UNIX domain sockets, it should be a path
185           describing the socket's filename.
186
187           If neither DOMAIN nor ADDRESS are specified, it will use
188           IN6ADDR_ANY and AF_INET6.
189
190       "PORT"
191           This value will be passed to POE::Wheel::SocketFactory to bind to.
192
193       "DOMAIN"
194           This value will be passed to POE::Wheel::SocketFactory to define
195           the socket domain used (AF_INET, AF_INET6, AF_UNIX).
196
197       "HOSTNAME"
198           This value is for the HTTP::Request's URI to point to.  If this is
199           not supplied, SimpleHTTP will use Sys::Hostname to find it.
200
201       "HEADERS"
202           This should be a hashref, that will become the default headers on
203           all HTTP::Response objects.  You can override this in individual
204           requests by setting it via $request->header( ... )
205
206           For more information, consult the HTTP::Headers module.
207
208       "HANDLERS"
209           This is the hardest part of SimpleHTTP :)
210
211           You supply an array, with each element being a hash. All the hashes
212           should contain those 3 keys:
213
214           DIR  ->   The regexp that will be used, more later.
215
216           SESSION   ->   The session to send the input
217
218           EVENT     ->   The event to trigger
219
220           The DIR key should be a valid regexp. This will be matched against
221           the current request path.  Pseudocode is: if ( $path =~ /$DIR/ )
222
223           NOTE: The path is UNIX style, not MSWIN style ( /blah/foo not
224           \blah\foo )
225
226           Now, if you supply 100 handlers, how will SimpleHTTP know what to
227           do? Simple! By passing in an array in the first place, you have
228           already told SimpleHTTP the order of your handlers. They will be
229           tried in order, and if a match is not found, SimpleHTTP will return
230           a 404 response.
231
232           This allows some cool things like specifying 3 handlers with DIR
233           of: '^/foo/.*', '^/$', '.*'
234
235           Now, if the request is not in /foo or not root, your 3rd handler
236           will catch it, becoming the "404 not found" handler!
237
238           NOTE: You might get weird Session/Events, make sure your handlers
239           are in order, for example: '^/', '^/foo/.*'  The 2nd handler will
240           NEVER get any requests, as the first one will match ( no $ in the
241           regex )
242
243           Now, here's what a handler receives:
244
245           ARG0 -> HTTP::Request object
246
247           ARG1 -> POE::Component::Server::SimpleHTTP::Response object
248
249           ARG2 -> The exact DIR that matched, so you can see what triggered
250           what
251
252           NOTE: If ARG0 is undef, that means POE::Filter::HTTPD encountered
253           an error parsing the client request, simply modify the
254           HTTP::Response object and send some sort of generic error.
255           SimpleHTTP will set the path used in matching the DIR regexes to an
256           empty string, so if there is a "catch-all" DIR regex like '.*', it
257           will catch the errors, and only that one.
258
259           NOTE: The only way SimpleHTTP will leak memory ( hopefully heh ) is
260           if you discard the SimpleHTTP::Response object without sending it
261           back to SimpleHTTP via the DONE/CLOSE events, so never do that!
262
263       "KEEPALIVE"
264           Set to true to enable HTTP keep-alive support.  Connections will be
265           kept alive until the client closes the connection.  All HTTP/1.1
266           connections are kept-open, unless you set the response "Connection"
267           header to "close".
268
269               $response->header( Connection => 'close' );
270
271           If you want more control, use
272           POE::Component::Server::HTTP::KeepAlive.
273
274       "LOGHANDLER"
275           Expects a hashref with the following key, values:
276
277           SESSION   ->   The session to send the input
278
279           EVENT     ->   The event to trigger
280
281           You will receive an event for each request to the server from
282           clients.  Malformed client requests will not be passed into the
283           handler.  Instead undef will be passed.  Event is called before ANY
284           content handler is called.
285
286           The event will have the following parameters:
287
288           ARG0 -> HTTP::Request object/undef if client request was malformed.
289
290           ARG1 -> the IP address of the client
291
292       "LOG2HANDLER"
293           Expect a hashref with the following key, values:
294
295           SESSION   ->   The session to send the input
296
297           EVENT     ->   The event to trigger
298
299           You will receive an event for each response that hit DONE call.
300           Malformed client requests will not be passed into the handler.
301           Event is after processing all content handlers.
302
303           The event will have the following parameters:
304
305           ARG0 -> HTTP::Request object
306
307           ARG1 -> HTTP::Response object
308
309           That makes possible following code:
310
311                   my ($login, $password) = $request->authorization_basic();
312                   printf STDERR "%s - %s [%s] \"%s %s %s\" %d %d\n",
313                           $response->connection->remote_ip, $login||'-', POSIX::strftime("%d/%b/%Y:%T %z",localtime(time())),
314                           $request->method(), $request->uri()->path(), $request->protocol(),
315                           $response->code(), length($response->content());
316
317           Emulate apache-like logs for PoCo::Server::SimpleHTTP
318
319       "SETUPHANDLER"
320           Expects a hashref with the following key, values:
321
322           SESSION   ->   The session to send the input
323
324           EVENT     ->   The event to trigger
325
326           You will receive an event when the listener wheel has been setup.
327
328           Currently there are no parameters returned.
329
330       "SSLKEYCERT"
331           This should be an arrayref of only 2 elements - the private key and
332           public certificate locations. Now, this is still in the
333           experimental stage, and testing is greatly welcome!
334
335           Again, this will automatically turn every incoming connection into
336           a SSL socket. Once enough testing has been done, this option will
337           be augmented with more SSL stuff!
338
339       "SSLINTERMEDIATECACERT"
340           This option is needed in case the SSL certificate references an
341           intermediate certification authority certificate.
342
343       "PROXYMODE"
344           Set this to a true value to enable the server to act as a proxy
345           server, ie. it won't mangle the HTTP::Request URI.
346
347   Events
348       SimpleHTTP is so simple, there are only 8 events available.
349
350       "DONE"
351                   This event accepts only one argument: the HTTP::Response object we sent to the handler.
352
353                   Calling this event implies that this particular request is done, and will proceed to close the socket.
354
355                   NOTE: This method automatically sets those 3 headers if they are not already set:
356                           Date            ->      Current date stringified via HTTP::Date->time2str
357                           Content-Type    ->      text/html
358                           Content-Length  ->      length( $response->content )
359
360                   To get greater throughput and response time, do not post() to the DONE event, call() it!
361                   However, this will force your program to block while servicing web requests...
362
363       "CLOSE"
364                   This event accepts only one argument: the HTTP::Response object we sent to the handler.
365
366                   Calling this event will close the socket, not sending any output
367
368       "GETHANDLERS"
369                   This event accepts 2 arguments: The session + event to send the response to
370
371                   This event will send back the current HANDLERS array ( deep-cloned via Storable::dclone )
372
373                   The resulting array can be played around to your tastes, then once you are done...
374
375       "SETHANDLERS"
376                   This event accepts only one argument: pointer to HANDLERS array
377
378                   BEWARE: if there is an error in the HANDLERS, SimpleHTTP will die!
379
380       "SETCLOSEHANDLER"
381               $_[KERNEL]->call( $_[SENDER], 'SETCLOSEHANDLER', $connection,
382                                 $event, @args );
383
384           Calls $event in the current session when $connection is closed.
385           You could use for persistent connection handling.
386
387           Multiple session may register close handlers.
388
389           Calling SETCLOSEHANDLER without $event to remove the current
390           session's handler:
391
392              $_[KERNEL]->call( $_[SENDER], 'SETCLOSEHANDLER', $connection );
393
394           You must make sure that @args doesn't cause a circular reference.
395           Ideally, use "$connection-"ID> or some other unique value
396           associated with this $connection.
397
398       "STARTLISTEN"
399                   Starts the listening socket, if it was shut down
400
401       "STOPLISTEN"
402                   Simply a wrapper for SHUTDOWN GRACEFUL, but will not shutdown SimpleHTTP if there is no more requests
403
404       "SHUTDOWN"
405                   Without arguments, SimpleHTTP does this:
406                           Close the listening socket
407                           Kills all pending requests by closing their sockets
408                           Removes it's alias
409
410                   With an argument of 'GRACEFUL', SimpleHTTP does this:
411                           Close the listening socket
412                           Waits for all pending requests to come in via DONE/CLOSE, then removes it's alias
413
414       "STREAM"
415                   With a $response argument it streams the content and calls back the streaming event
416                   of the user's session (or with the dont_flush option you're responsible for calling
417                   back your session's streaming event).
418
419                   To use the streaming feature see below.
420
421   Streaming with SimpleHTTP
422       It's possible to send data as a stream to clients (unbuffered and
423       integrated in the POE loop).
424
425       Just create your session to receive events from SimpleHTTP as usually
426       and add a streaming event, this event will be triggered over and over
427       each time you set the $response to a streaming state and once you
428       trigger it:
429
430          # sets the response as streamed within our session which alias is HTTP_GET
431          # with the event GOT_STREAM
432          $response->stream(
433             session     => 'HTTP_GET',
434             event       => 'GOT_STREAM',
435             dont_flush  => 1
436          );
437
438          # then you can simply yield your streaming event, once the GOT_STREAM event
439          # has reached its end it will be triggered again and again, until you
440          # send a CLOSE event to the kernel with the appropriate response as parameter
441          $kernel->yield('GOT_STREAM', $response);
442
443       The optional dont_flush option gives the user the ability to control
444       the callback to the streaming event, which means once your stream event
445       has reached its end it won't be called, you have to call it back.
446
447       You can now send data by chunks and either call yourself back (via POE)
448       or shutdown when your streaming is done (EOF for example).
449
450          sub GOT_STREAM {
451             my ( $kernel, $heap, $response ) = @_[KERNEL, HEAP, ARG0];
452
453             # sets the content of the response
454             $response->content("Hello World\n");
455
456             # send it to the client
457             POE::Kernel->post('HTTPD', 'STREAM', $response);
458
459             # if we have previously set the dont_flush option
460             # we have to trigger our event back until the end of
461             # the stream like this (that can be a yield, of course):
462             #
463             # $kernel->delay('GOT_STREAM', 1, $stream );
464
465             # otherwise the GOT_STREAM event is triggered continuously until
466             # we call the CLOSE event on the response like that :
467             #
468             if ($heap{'streaming_is_done'}) {
469                # close the socket and end the stream
470                POE::Kernel->post('HTTPD', 'CLOSE', $response );
471             }
472          }
473
474       The dont_flush option is there to be able to control the frequency of
475       flushes to the client.
476
477   SimpleHTTP Notes
478       You can enable debugging mode by doing this:
479
480               sub POE::Component::Server::SimpleHTTP::DEBUG () { 1 }
481               use POE::Component::Server::SimpleHTTP;
482
483       Also, this module will try to keep the Listening socket alive.  if it
484       dies, it will open it again for a max of 5 retries.
485
486       You can override this behavior by doing this:
487
488               sub POE::Component::Server::SimpleHTTP::MAX_RETRIES () { 10 }
489               use POE::Component::Server::SimpleHTTP;
490
491       For those who are pondering about basic-authentication, here's a tiny
492       snippet to put in the Event handler
493
494               # Contributed by Rocco Caputo
495               sub Got_Request {
496                       # ARG0 = HTTP::Request, ARG1 = HTTP::Response
497                       my( $request, $response ) = @_[ ARG0, ARG1 ];
498
499                       # Get the login
500                       my ( $login, $password ) = $request->authorization_basic();
501
502                       # Decide what to do
503                       if ( ! defined $login or ! defined $password ) {
504                               # Set the authorization
505                               $response->header( 'WWW-Authenticate' => 'Basic realm="MyRealm"' );
506                               $response->code( 401 );
507                               $response->content( 'FORBIDDEN.' );
508
509                               # Send it off!
510                               $_[KERNEL]->post( 'SimpleHTTP', 'DONE', $response );
511                       } else {
512                               # Authenticate the user and move on
513                       }
514               }
515
516   EXPORT
517       Nothing.
518

ABSTRACT

520               An easy to use HTTP daemon for POE-enabled programs
521

SEE ALSO

523               L<POE>
524
525               L<POE::Filter::HTTPD>
526
527               L<HTTP::Request>
528
529               L<HTTP::Response>
530
531               L<POE::Component::Server::SimpleHTTP::Connection>
532
533               L<POE::Component::Server::SimpleHTTP::Response>
534
535               L<POE::Component::Server::SimpleHTTP::PreFork>
536
537               L<POE::Component::SSLify>
538

AUTHOR

540       Apocalypse <APOCAL@cpan.org>
541
543       This software is copyright (c) 2018 by Apocalypse, Chris Williams,
544       Eriam Schaffter, Marlon Bailey and Philip Gwyn.
545
546       This is free software; you can redistribute it and/or modify it under
547       the same terms as the Perl 5 programming language system itself.
548
549
550
551perl v5.32.0                      2020-07-P2O8E::Component::Server::SimpleHTTP(3)
Impressum