1POE::Component::Server:U:sSeirmpCloenHtTrTiPb(u3t)ed PerPlOED:o:cCuommepnotnaetnito:n:Server::SimpleHTTP(3)
2
3
4
6 POE::Component::Server::SimpleHTTP - Perl extension to serve HTTP
7 requests in POE.
8
10 use POE;
11 use POE::Component::Server::SimpleHTTP;
12
13 # Start the server!
14 POE::Component::Server::SimpleHTTP->new(
15 'ALIAS' => 'HTTPD',
16 'PORT' => 11111,
17 'HOSTNAME' => 'MySite.com',
18 'HANDLERS' => [
19 {
20 'DIR' => '^/bar/.*',
21 'SESSION' => 'HTTP_GET',
22 'EVENT' => 'GOT_BAR',
23 },
24 {
25 'DIR' => '^/$',
26 'SESSION' => 'HTTP_GET',
27 'EVENT' => 'GOT_MAIN',
28 },
29 {
30 'DIR' => '^/foo/.*',
31 'SESSION' => 'HTTP_GET',
32 'EVENT' => 'GOT_NULL',
33 },
34 {
35 'DIR' => '.*',
36 'SESSION' => 'HTTP_GET',
37 'EVENT' => 'GOT_ERROR',
38 },
39 ],
40
41 'LOGHANDLER' => { 'SESSION' => 'HTTP_GET',
42 'EVENT' => 'GOT_LOG',
43 },
44
45 'LOG2HANDLER' => { 'SESSION' => 'HTTP_GET',
46 'EVENT' => 'POSTLOG',
47 },
48
49 # In the testing phase...
50 'SSLKEYCERT' => [ 'private-key.pem', 'public-cert.pem' ],
51 'SSLINTERMEDIATECACERT' => 'intermediate-ca-cert.pem',
52 ) or die 'Unable to create the HTTP Server';
53
54 # Create our own session to receive events from SimpleHTTP
55 POE::Session->create(
56 inline_states => {
57 '_start' => sub { $_[KERNEL]->alias_set( 'HTTP_GET' );
58 $_[KERNEL]->post( 'HTTPD', 'GETHANDLERS', $_[SESSION], 'GOT_HANDLERS' );
59 },
60
61 'GOT_BAR' => \&GOT_REQ,
62 'GOT_MAIN' => \&GOT_REQ,
63 'GOT_ERROR' => \&GOT_ERR,
64 'GOT_NULL' => \&GOT_NULL,
65 'GOT_HANDLERS' => \&GOT_HANDLERS,
66 'GOT_LOG' => \&GOT_LOG,
67 },
68 );
69
70 # Start POE!
71 POE::Kernel->run();
72
73 sub GOT_HANDLERS {
74 # ARG0 = HANDLERS array
75 my $handlers = $_[ ARG0 ];
76
77 # Move the first handler to the last one
78 push( @$handlers, shift( @$handlers ) );
79
80 # Send it off!
81 $_[KERNEL]->post( 'HTTPD', 'SETHANDLERS', $handlers );
82 }
83
84 sub GOT_NULL {
85 # ARG0 = HTTP::Request object, ARG1 = HTTP::Response object, ARG2 = the DIR that matched
86 my( $request, $response, $dirmatch ) = @_[ ARG0 .. ARG2 ];
87
88 # Kill this!
89 $_[KERNEL]->post( 'HTTPD', 'CLOSE', $response );
90 }
91
92 sub GOT_REQ {
93 # ARG0 = HTTP::Request object, ARG1 = HTTP::Response object, ARG2 = the DIR that matched
94 my( $request, $response, $dirmatch ) = @_[ ARG0 .. ARG2 ];
95
96 # Do our stuff to HTTP::Response
97 $response->code( 200 );
98 $response->content( 'Some funky HTML here' );
99
100 # We are done!
101 # For speed, you could use $_[KERNEL]->call( ... )
102 $_[KERNEL]->post( 'HTTPD', 'DONE', $response );
103 }
104
105 sub GOT_ERR {
106 # ARG0 = HTTP::Request object, ARG1 = HTTP::Response object, ARG2 = the DIR that matched
107 my( $request, $response, $dirmatch ) = @_[ ARG0 .. ARG2 ];
108
109 # Check for errors
110 if ( ! defined $request ) {
111 $_[KERNEL]->post( 'HTTPD', 'DONE', $response );
112 return;
113 }
114
115 # Do our stuff to HTTP::Response
116 $response->code( 404 );
117 $response->content( "Hi visitor from " . $response->connection->remote_ip . ", Page not found -> '" . $request->uri->path . "'" );
118
119 # We are done!
120 # For speed, you could use $_[KERNEL]->call( ... )
121 $_[KERNEL]->post( 'HTTPD', 'DONE', $response );
122 }
123
124 sub GOT_LOG {
125 # ARG0 = HTTP::Request object, ARG1 = remote IP
126 my ($request, $remote_ip) = @_[ARG0,ARG1];
127
128 # Do some sort of logging activity.
129 # If the request was malformed, $request = undef
130 # CHECK FOR A REQUEST OBJECT BEFORE USING IT.
131 if( $request ) {
132 {
133 warn join(' ', time(), $remote_ip, $request->uri ), "\n";
134 } else {
135 warn join(' ', time(), $remote_ip, 'Bad request' ), "\n";
136 }
137
138 return;
139 }
140
142 An easy to use HTTP daemon for POE-enabled programs
143
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.
184
185 "PORT"
186 This value will be passed to POE::Wheel::SocketFactory to bind to.
187
188 "HOSTNAME"
189 This value is for the HTTP::Request's URI to point to. If this is
190 not supplied, SimpleHTTP will use Sys::Hostname to find it.
191
192 "HEADERS"
193 This should be a hashref, that will become the default headers on
194 all HTTP::Response objects. You can override this in individual
195 requests by setting it via $request->header( ... )
196
197 For more information, consult the HTTP::Headers module.
198
199 "HANDLERS"
200 This is the hardest part of SimpleHTTP :)
201
202 You supply an array, with each element being a hash. All the hashes
203 should contain those 3 keys:
204
205 DIR -> The regexp that will be used, more later.
206
207 SESSION -> The session to send the input
208
209 EVENT -> The event to trigger
210
211 The DIR key should be a valid regexp. This will be matched against
212 the current request path. Pseudocode is: if ( $path =~ /$DIR/ )
213
214 NOTE: The path is UNIX style, not MSWIN style ( /blah/foo not
215 \blah\foo )
216
217 Now, if you supply 100 handlers, how will SimpleHTTP know what to
218 do? Simple! By passing in an array in the first place, you have
219 already told SimpleHTTP the order of your handlers. They will be
220 tried in order, and if a match is not found, SimpleHTTP will return
221 a 404 response.
222
223 This allows some cool things like specifying 3 handlers with DIR
224 of: '^/foo/.*', '^/$', '.*'
225
226 Now, if the request is not in /foo or not root, your 3rd handler
227 will catch it, becoming the "404 not found" handler!
228
229 NOTE: You might get weird Session/Events, make sure your handlers
230 are in order, for example: '^/', '^/foo/.*' The 2nd handler will
231 NEVER get any requests, as the first one will match ( no $ in the
232 regex )
233
234 Now, here's what a handler receives:
235
236 ARG0 -> HTTP::Request object
237
238 ARG1 -> POE::Component::Server::SimpleHTTP::Response object
239
240 ARG2 -> The exact DIR that matched, so you can see what triggered
241 what
242
243 NOTE: If ARG0 is undef, that means POE::Filter::HTTPD encountered
244 an error parsing the client request, simply modify the
245 HTTP::Response object and send some sort of generic error.
246 SimpleHTTP will set the path used in matching the DIR regexes to an
247 empty string, so if there is a "catch-all" DIR regex like '.*', it
248 will catch the errors, and only that one.
249
250 NOTE: The only way SimpleHTTP will leak memory ( hopefully heh ) is
251 if you discard the SimpleHTTP::Response object without sending it
252 back to SimpleHTTP via the DONE/CLOSE events, so never do that!
253
254 "KEEPALIVE"
255 Set to true to enable HTTP keep-alive support. Connections will be
256 kept alive until the client closes the connection. All HTTP/1.1
257 connections are kept-open, unless you set the response "Connection"
258 header to "close".
259
260 $response->header( Connection => 'close' );
261
262 If you want more control, use
263 POE::Component::Server::HTTP::KeepAlive.
264
265 "LOGHANDLER"
266 Expects a hashref with the following key, values:
267
268 SESSION -> The session to send the input
269
270 EVENT -> The event to trigger
271
272 You will receive an event for each request to the server from
273 clients. Malformed client requests will not be passed into the
274 handler. Instead undef will be passed. Event is called before ANY
275 content handler is called.
276
277 The event will have the following parameters:
278
279 ARG0 -> HTTP::Request object/undef if client request was malformed.
280
281 ARG1 -> the IP address of the client
282
283 "LOG2HANDLER"
284 Expect a hashref with the following key, valyes:
285
286 SESSION -> The session to send the input
287
288 EVENT -> The event to trigger
289
290 You will receive an event for each response that hit DONE call.
291 Malformed client requests will not be passed into the handler.
292 Event is after processing all content handlers.
293
294 The event will have the following parameters:
295
296 ARG0 -> HTTP::Request object
297
298 ARG1 -> HTTP::Response object
299
300 That makes possible following code:
301
302 my ($login, $password) = $request->authorization_basic();
303 printf STDERR "%s - %s [%s] \"%s %s %s\" %d %d\n",
304 $response->connection->remote_ip, $login||'-', POSIX::strftime("%d/%b/%Y:%T %z",localtime(time())),
305 $request->method(), $request->uri()->path(), $request->protocol(),
306 $response->code(), length($response->content());
307
308 Emulate apache-like logs for PoCo::Server::SimpleHTTP
309
310 "SETUPHANDLER"
311 Expects a hashref with the following key, values:
312
313 SESSION -> The session to send the input
314
315 EVENT -> The event to trigger
316
317 You will receive an event when the listener wheel has been setup.
318
319 Currently there are no parameters returned.
320
321 "SSLKEYCERT"
322 This should be an arrayref of only 2 elements - the private key and
323 public certificate locations. Now, this is still in the
324 experimental stage, and testing is greatly welcome!
325
326 Again, this will automatically turn every incoming connection into
327 a SSL socket. Once enough testing has been done, this option will
328 be augmented with more SSL stuff!
329
330 "SSLINTERMEDIATECACERT"
331 This option is needed in case the SSL certificate references an
332 intermediate certification authority certificate.
333
334 "PROXYMODE"
335 Set this to a true value to enable the server to act as a proxy
336 server, ie. it won't mangle the HTTP::Request URI.
337
338 Events
339 SimpleHTTP is so simple, there are only 8 events available.
340
341 "DONE"
342 This event accepts only one argument: the HTTP::Response object we sent to the handler.
343
344 Calling this event implies that this particular request is done, and will proceed to close the socket.
345
346 NOTE: This method automatically sets those 3 headers if they are not already set:
347 Date -> Current date stringified via HTTP::Date->time2str
348 Content-Type -> text/html
349 Content-Length -> length( $response->content )
350
351 To get greater throughput and response time, do not post() to the DONE event, call() it!
352 However, this will force your program to block while servicing web requests...
353
354 "CLOSE"
355 This event accepts only one argument: the HTTP::Response object we sent to the handler.
356
357 Calling this event will close the socket, not sending any output
358
359 "GETHANDLERS"
360 This event accepts 2 arguments: The session + event to send the response to
361
362 This event will send back the current HANDLERS array ( deep-cloned via Storable::dclone )
363
364 The resulting array can be played around to your tastes, then once you are done...
365
366 "SETHANDLERS"
367 This event accepts only one argument: pointer to HANDLERS array
368
369 BEWARE: if there is an error in the HANDLERS, SimpleHTTP will die!
370
371 "SETCLOSEHANDLER"
372 $_[KERNEL]->call( $_[SENDER], 'SETCLOSEHANDLER', $connection,
373 $event, @args );
374
375 Calls $event in the current session when $connection is closed.
376 You could use for persistent connection handling.
377
378 Multiple session may register close handlers.
379
380 Calling SETCLOSEHANDLER without $event to remove the current
381 session's handler:
382
383 $_[KERNEL]->call( $_[SENDER], 'SETCLOSEHANDLER', $connection );
384
385 You must make sure that @args doesn't cause a circular reference.
386 Ideally, use "$connection-"ID> or some other unique value
387 associated with this $connection.
388
389 "STARTLISTEN"
390 Starts the listening socket, if it was shut down
391
392 "STOPLISTEN"
393 Simply a wrapper for SHUTDOWN GRACEFUL, but will not shutdown SimpleHTTP if there is no more requests
394
395 "SHUTDOWN"
396 Without arguments, SimpleHTTP does this:
397 Close the listening socket
398 Kills all pending requests by closing their sockets
399 Removes it's alias
400
401 With an argument of 'GRACEFUL', SimpleHTTP does this:
402 Close the listening socket
403 Waits for all pending requests to come in via DONE/CLOSE, then removes it's alias
404
405 "STREAM"
406 With a $response argument it streams the content and calls back the streaming event
407 of the user's session (or with the dont_flush option you're responsible for calling
408 back your session's streaming event).
409
410 To use the streaming feature see below.
411
412 Streaming with SimpleHTTP
413 It's possible to send data as a stream to clients (unbuffered and
414 integrated in the POE loop).
415
416 Just create your session to receive events from SimpleHTTP as usually
417 and add a streaming event, this event will be triggered over and over
418 each time you set the $response to a streaming state and once you
419 trigger it:
420
421 # sets the response as streamed within our session which alias is HTTP_GET
422 # with the event GOT_STREAM
423 $response->stream(
424 session => 'HTTP_GET',
425 event => 'GOT_STREAM',
426 dont_flush => 1
427 );
428
429 # then you can simply yield your streaming event, once the GOT_STREAM event
430 # has reached its end it will be triggered again and again, until you
431 # send a CLOSE event to the kernel with the appropriate response as parameter
432 $kernel->yield('GOT_STREAM', $response);
433
434 The optionnal dont_flush option gives the user the ability to control
435 the callback to the streaming event, which means once your stream event
436 has reached its end it won't be called, you have to call it back.
437
438 You can now send data by chunks and either call yourself back (via POE)
439 or shutdown when your streaming is done (EOF for example).
440
441 sub GOT_STREAM {
442 my ( $kernel, $heap, $response ) = @_[KERNEL, HEAP, ARG0];
443
444 # sets the content of the response
445 $response->content("Hello World\n");
446
447 # send it to the client
448 POE::Kernel->post('HTTPD', 'STREAM', $response);
449
450 # if we have previously set the dont_flush option
451 # we have to trigger our event back until the end of
452 # the stream like this (that can be a yield, of course):
453 #
454 # $kernel->delay('GOT_STREAM', 1, $stream );
455
456 # otherwise the GOT_STREAM event is triggered continously until
457 # we call the CLOSE event on the response like that :
458 #
459 if ($heap{'streaming_is_done'}) {
460 # close the socket and end the stream
461 POE::Kernel->post('HTTPD', 'CLOSE', $response );
462 }
463 }
464
465 The dont_flush option is there to be able to control the frequency of
466 flushes to the client.
467
468 SimpleHTTP Notes
469 You can enable debugging mode by doing this:
470
471 sub POE::Component::Server::SimpleHTTP::DEBUG () { 1 }
472 use POE::Component::Server::SimpleHTTP;
473
474 Also, this module will try to keep the Listening socket alive. if it
475 dies, it will open it again for a max of 5 retries.
476
477 You can override this behavior by doing this:
478
479 sub POE::Component::Server::SimpleHTTP::MAX_RETRIES () { 10 }
480 use POE::Component::Server::SimpleHTTP;
481
482 For those who are pondering about basic-authentication, here's a tiny
483 snippet to put in the Event handler
484
485 # Contributed by Rocco Caputo
486 sub Got_Request {
487 # ARG0 = HTTP::Request, ARG1 = HTTP::Response
488 my( $request, $response ) = @_[ ARG0, ARG1 ];
489
490 # Get the login
491 my ( $login, $password ) = $request->authorization_basic();
492
493 # Decide what to do
494 if ( ! defined $login or ! defined $password ) {
495 # Set the authorization
496 $response->header( 'WWW-Authenticate' => 'Basic realm="MyRealm"' );
497 $response->code( 401 );
498 $response->content( 'FORBIDDEN.' );
499
500 # Send it off!
501 $_[KERNEL]->post( 'SimpleHTTP', 'DONE', $response );
502 } else {
503 # Authenticate the user and move on
504 }
505 }
506
507 EXPORT
508 Nothing.
509
511 L<POE>
512
513 L<POE::Filter::HTTPD>
514
515 L<HTTP::Request>
516
517 L<HTTP::Response>
518
519 L<POE::Component::Server::SimpleHTTP::Connection>
520
521 L<POE::Component::Server::SimpleHTTP::Response>
522
523 L<POE::Component::Server::SimpleHTTP::PreFork>
524
525 L<POE::Component::SSLify>
526
528 Apocalypse <apocal@cpan.org>
529
531 Copyright 2006 by Apocalypse
532
533 This library is free software; you can redistribute it and/or modify it
534 under the same terms as Perl itself.
535
536
537
538perl v5.12.1 2010-05-P1O9E::Component::Server::SimpleHTTP(3)