stas        2002/08/22 04:26:42

  Modified:    src/docs/2.0/user/handlers handlers.pod
  Log:
  working on the handlers docs: covered startup and protocol handlers
  
  Revision  Changes    Path
  1.12      +409 -79   modperl-docs/src/docs/2.0/user/handlers/handlers.pod
  
  Index: handlers.pod
  ===================================================================
  RCS file: /home/cvs/modperl-docs/src/docs/2.0/user/handlers/handlers.pod,v
  retrieving revision 1.11
  retrieving revision 1.12
  diff -u -r1.11 -r1.12
  --- handlers.pod      14 Aug 2002 14:01:43 -0000      1.11
  +++ handlers.pod      22 Aug 2002 11:26:42 -0000      1.12
  @@ -63,6 +63,18 @@
   Now when a request is issued to I</time> this response handler will be
   executed.
   
  +
  +
  +
  +
  +
  +
  +
  +
  +
  +
  +
  +
   =head1 Server Life Cycle
   
   The following diagram depicts the Apache 2.0 server life cycle and
  @@ -90,7 +102,110 @@
   From that moment on each working thread processes connections until
   it's killed by the server or the server is shutdown.
   
  -Now let's discuss each of the mentioned startup handlers in detail.
  +=head2 Startup Phases Demonstration Module
  +
  +Let's look at the following example that demonstrates all the startup
  +phases:
  +
  +  file:MyApache/StartupLog.pm
  +  ---------------------------
  +  package MyApache::StartupLog;
  +  
  +  use strict;
  +  use warnings;
  +  
  +  use Apache::Log ();
  +  
  +  use File::Spec::Functions;
  +  
  +  use Apache::Const -compile => 'OK';
  +  
  +  my $log_file = catfile "logs", "startup_log";
  +  my $log_fh;
  +  
  +  sub open_logs {
  +      my($conf_pool, $log_pool, $temp_pool, $s) = @_;
  +      my $log_path = Apache::server_root_relative($conf_pool, $log_file);
  +  
  +      $s->warn("opening the log file: $log_path");
  +      open $log_fh, ">>$log_path" or die "can't open $log_path: $!";
  +      my $oldfh = select($log_fh); $| = 1; select($oldfh);
  +  
  +      say("process $$ is born to reproduce");
  +      return Apache::OK;
  +  }
  +  
  +  sub post_config {
  +      my($conf_pool, $log_pool, $temp_pool, $s) = @_;
  +      say("configuration is completed");
  +      return Apache::OK;
  +  }
  +  
  +  sub child_init {
  +      my($child_pool, $s) = @_;
  +      say("process $$ is born to serve");
  +      return Apache::OK;
  +  }
  +  
  +  sub say {
  +      my($caller) = (caller(1))[3] =~ /([^:]+)$/;
  +      printf $log_fh "[%s] - %-11s: %s\n", scalar(localtime), $caller, $_[0];
  +  }
  +  
  +  END {
  +      say("process $$ is shutdown\n");
  +  }
  +  
  +  1;
  +
  +
  +And the I<httpd.conf> configuration section:
  +
  +  PerlModule            MyApache::StartupLog
  +  PerlOpenLogsHandler   MyApache::StartupLog::open_logs
  +  PerlPostConfigHandler MyApache::StartupLog::post_config
  +  PerlChildInitHandler  MyApache::StartupLog::child_init
  +
  +When we perform a server startup followed by a shutdown, the
  +I<logs/startup_log> is created if it didn't exist already (it shares
  +the same directory with I<error_log> and other standard log files),
  +and each stage appends to it its log information. So when we perform:
  +
  +  % bin/apachectl start && bin/apachectl stop
  +
  +the following is getting logged to I<logs/startup_log>:
  +
  +  [Thu Aug 22 15:57:08 2002] - open_logs  : process 21823 is born to 
reproduce
  +  [Thu Aug 22 15:57:08 2002] - post_config: configuration is completed
  +  [Thu Aug 22 15:57:09 2002] - END        : process 21823 is shutdown
  +  
  +  [Thu Aug 22 15:57:10 2002] - open_logs  : process 21825 is born to 
reproduce
  +  [Thu Aug 22 15:57:10 2002] - post_config: configuration is completed
  +  [Thu Aug 22 15:57:11 2002] - child_init : process 21830 is born to serve
  +  [Thu Aug 22 15:57:11 2002] - child_init : process 21831 is born to serve
  +  [Thu Aug 22 15:57:11 2002] - child_init : process 21832 is born to serve
  +  [Thu Aug 22 15:57:11 2002] - child_init : process 21833 is born to serve
  +  [Thu Aug 22 15:57:12 2002] - END        : process 21825 is shutdown
  +
  +First of all, we can clearly see that Apache always restart itself
  +after the first I<post_config> phase is over. The logs show that the
  +I<post_config> phase is preceeded by the I<open_logs> phase. Only
  +after Apache has restarted itself and has completed the I<open_logs>
  +and I<post_config> phase again the I<child_init> phase is run for each
  +child process. In our example we have had the setting
  +C<StartServers=4>, therefore you can see four child processes were
  +started.
  +
  +Finally you can see that on server shutdown the END {} block has been
  +executed by the parent server only.
  +
  +Apache also specifies the I<pre_config> phase, which is executed
  +before the configuration files are parsed, but this is of no use to
  +mod_perl, because mod_perl is loaded only during the configuration
  +phase.
  +
  +Now let's discuss each of the mentioned startup handlers and their
  +implementation in the C<MyApache::StartupLog> module in detail.
   
   =head2 PerlOpenLogsHandler
   
  @@ -107,42 +222,33 @@
   
   The handler's configuration scope is C<SRV>.
   
  -For example here is the C<MyApache::OpenLogs> handler that opens a
  -custom log file:
  +As we have seen in the C<MyApache::StartupLog::open_logs> handler, the
  +I<open_logs> phase handlers accept four arguments: the configuration
  +pool, the logging streams pool, the temporary pool and the server
  +object:
   
  -  file:MyApache/OpenLogs.pm
  -  -----------------------
  -  package MyApache::OpenLogs;
  -  
  -  use strict;
  -  use warnings;
  -  
  -  use File::Spec::Functions;
  -  
  -  my $log_file = catfile "logs", "mylog";
  -  
  -  sub handler {
  -      my ($conf_pool, $log_pool, $temp_pool, $s) = @_;
  -  
  +  sub open_logs {
  +      my($conf_pool, $log_pool, $temp_pool, $s) = @_;
         my $log_path = Apache::server_root_relative($conf_pool, $log_file);
  +  
         $s->warn("opening the log file: $log_path");
  -      open my $log, ">>$log_path" or die "can't open $log_path: $!";
  +      open $log_fh, ">>$log_path" or die "can't open $log_path: $!";
  +      my $oldfh = select($log_fh); $| = 1; select($oldfh);
     
  +      say("process $$ is born to reproduce");
         return Apache::OK;
     }
  -  1;
   
  -The I<open_logs> phase handlers accept four arguments: the
  -configuration pool, the logging streams pool, the temporary pool and
  -the server object. In our example the handler uses the function
  +In our example the handler uses the function
   C<Apache::server_root_relative()> to set the full path to the log
  -file, which is then opened. Of course in the real world handlers the
  -module needs to be extended to provide an accessor that can write to
  -this log file.
  +file, which is then opened for appending and set to unbuffered
  +mode. Finally it logs the fact that it's running in the parent
  +process.
   
  -To configure this handler add to I<httpd.conf>:
  +As you've seen in the example this handler is configured by adding to
  +I<httpd.conf>:
   
  -  PerlOpenLogsHandler MyApache::OpenLogs
  +  PerlOpenLogsHandler MyApache::StartupLog::open_logs
   
   
   
  @@ -157,33 +263,75 @@
   the I<post_config> phase you have an access to a complete
   configuration tree.
   
  +META: once mod_perl will have the API for that.
  +
   This phase is of type C<RUN_ALL>.
   
   The handler's configuration scope is C<SRV>.
   
  -Example:
  +In our C<MyApache::StartupLog> example we used the I<post_config()>
  +handler:
   
  +  sub post_config {
  +      my($conf_pool, $log_pool, $temp_pool, $s) = @_;
  +      say("configuration is completed");
  +      return Apache::OK;
  +  }
   
  +As you can see, its arguments are identical to the I<open_logs>
  +phase's handler. In this example handler we don't do much but logging
  +that the configuration was completed and returning right away.
   
  +As you've seen in the example this handler is configured by adding to
  +I<httpd.conf>:
   
  +  PerlOpenLogsHandler MyApache::StartupLog::post_config
   
   
   =head2 PerlChildInitHandler
   
  -META: PerlChildExitHandler?
  -
   The I<child_init> phase happens immediately after the child process is
  -spawned. Each child process will run the hooks of this phase only once
  -in their life-time.
  +spawned. Each child process (not a thread!) will run the hooks of this
  +phase only once in their life-time.
   
  -In the prefork MPM this phase is useful for pre-opening database
  -connections (similar to Apache::DBI in mod_perl 1.0).
  +In the prefork MPM this phase is useful for initializing any data
  +structures which should be private to each process. For example
  +C<Apache::DBI> pre-opens database connections during this phase and
  +C<Apache::Resource> sets the process' resources limits.
   
   This phase is of type C<VOID>.
   
   The handler's configuration scope is C<SRV>.
   
  -Example:
  +In our C<MyApache::StartupLog> example we used the I<child_init()>
  +handler:
  +
  +  sub child_init {
  +      my($child_pool, $s) = @_;
  +      say("process $$ is born to serve");
  +      return Apache::OK;
  +  }
  +
  +The I<child_init()> handler accepts two arguments: the child process
  +pool and the server object. The example handler logs the pid of the
  +child process it's run in and returns.
  +
  +As you've seen in the example this handler is configured by adding to
  +I<httpd.conf>:
  +
  +  PerlOpenLogsHandler MyApache::StartupLog::child_init
  +
  +
  +META: PerlChildExitHandler?
  +
  +
  +
  +
  +
  +
  +
  +
  +
   
   
   =head1 Bucket Brigades
  @@ -213,14 +361,14 @@
   previous filter. This brigade is then manipulated by the filter (e.g.,
   by modifying some buckets) and passed to the next filter in the stack.
   
  -The following diagram depicts an imaginary bucket brigade:
  +The following figure depicts an imaginary bucket brigade:
   
   =for html
   <img src="bucket_brigades.gif" width="590" height="400" 
    align="center" valign="middle" alt="bucket brigades"><br><br>
   
  -The diagram tries to show that after the presented bucket brigade has
  -passed through several filters some buckets where removed, some
  +The figure tries to show that after the presented bucket brigade has
  +passed through several filters some buckets were removed, some
   modified and some added. Of course the handler that gets the brigade
   cannot tell the history of the brigade, it can only see the existing
   buckets in the brigade.
  @@ -256,22 +404,23 @@
   C<PerlProcessConnectionHandler>, which generates the response. When
   C<PerlProcessConnectionHandler> is reading data from the client, it
   can be filtered by connection input filters. The generated response
  -can be also filtered though connection output filters. Filter are
  +can be also filtered though connection output filters. Filters are
   usually used for modifying the data flowing though them, but can be
   used for other purposes as well (e.g., logging interesting
   information).
   
  -Now let's discuss each of the mentioned handlers in detail.
  +Now let's discuss each of the C<PerlPreConnectionHandler> and
  +C<PerlProcessConnectionHandler> handlers in detail.
   
   =head2 PerlPreConnectionHandler
   
   The I<pre_connection> phase happens just after the server accepts the
   connection, but before it is handed off to a protocol module to be
   served.  It gives modules an opportunity to modify the connection as
  -soon as possible. The core server uses this phase to setup the
  -connection record based on the type of connection that is being used.
  -mod_perl itself uses this phase to register the connection input and
  -output filters.
  +soon as possible and insert filters if needed. The core server uses
  +this phase to setup the connection record based on the type of
  +connection that is being used.  mod_perl itself uses this phase to
  +register the connection input and output filters.
   
   In mod_perl 1.0 during code development C<Apache::Reload> was used to
   automatically reload modified since the last request Perl modules. It
  @@ -279,14 +428,18 @@
   phase. In mod_perl 2.0 I<pre_connection> is the earliest phase, so if
   we want to make sure that all modified Perl modules are reloaded for
   any protocols and its phases, it's the best to set the scope of the
  -Perl interpreter to the lifetime of the connection and invoke the
  -C<Apache::Reload> handler during the I<pre_connection> phase. However
  -this development-time advantage can become a disadvantage in
  -production--for example if a connection, handled by HTTP protocol, is
  -configured as C<KeepAlive> and there are several requests coming on
  -the same connection and only one handled by mod_perl and the others by
  -the default images handler, the Perl interpreter won't be available to
  -other threads while the images are being served.
  +Perl interpreter to the lifetime of the connection via:
  +
  +  PerlInterpScope connection
  +
  +and invoke the C<Apache::Reload> handler during the I<pre_connection>
  +phase. However this development-time advantage can become a
  +disadvantage in production--for example if a connection, handled by
  +HTTP protocol, is configured as C<KeepAlive> and there are several
  +requests coming on the same connection and only one handled by
  +mod_perl and the others by the default images handler, the Perl
  +interpreter won't be available to other threads while the images are
  +being served.
   
   This phase is of type C<RUN_ALL>.
   
  @@ -310,11 +463,11 @@
   
   =head2 PerlProcessConnectionHandler
   
  -The I<process_connection> phase is used to actually process the
  -connection that was received.  Only protocol modules should assign
  -handlers for this phase, as it gives them an opportunity to replace
  -the standard HTTP processing with processing for some other protocols
  -(e.g., POP3, FTP, etc.).
  +The I<process_connection> phase is used to process incoming
  +connections.  Only protocol modules should assign handlers for this
  +phase, as it gives them an opportunity to replace the standard HTTP
  +processing with processing for some other protocols (e.g., POP3, FTP,
  +etc.).
   
   This phase is of type C<RUN_FIRST>.
   
  @@ -322,10 +475,6 @@
   run protocol servers different than the core HTTP is inside dedicated
   virtual hosts.
   
  -Example:
  -
  -META: echo example comes here
  -
   A I<process_connection> handler accepts a connection record object as
   its only argument, a socket object can be retrieved from the
   connection record object.
  @@ -337,13 +486,155 @@
         return Apache::OK;
     }
   
  -META: the echo example doesn't work with filter, because it reads and
  -writes directly from/to the socket. Here comes the echo_filter
  -example.  But may be echo is not so good, use something like
  -eliza/'lc' to show the retrieval of the data, here is some eliza
  -protocol code plus an output lc filter.
   
  -  package Apache::Eliza2;
  +
  +Now let's look at the following two examples of connection
  +handlers. The first using the connection socket to read and write the
  +data and the second using bucket brigades to accomplish the same and
  +allow for connection filters to do their work.
  +
  +=head4 Socket-based Protocol Module
  +
  +To demonstrate the workings of a protocol module, we'll take a look at
  +the C<MyApache::EchoSocket> module, which simply echoes the data read
  +back to the client. In this module we will use the implementation that
  +works directly with the connection socket and therefore bypasses
  +connection filters if any.
  +
  +A protocol handler is configured using the
  +C<PerlProcessConnectionHandler> directive and we will use the
  +C<Listen> and C<E<lt>VirtualHostE<gt>> directives to bind to the
  +non-standard port B<8010>:
  +
  +  Listen 8010
  +  <VirtualHost _default_:8010>
  +      PerlModule                   MyApache::EchoSocket
  +      PerlProcessConnectionHandler MyApache::EchoSocket
  +  </VirtualHost>
  +
  +C<MyApache::EchoSocket> is then enabled when starting Apache:
  +
  +  panic% httpd
  +
  +And we give it a whirl:
  +
  +  panic% telnet localhost 8010
  +  Trying 127.0.0.1...
  +  Connected to localhost (127.0.0.1).
  +  Escape character is '^]'.
  +  Hello
  +  Hello
  +  
  +  fOo BaR
  +  fOo BaR
  +
  +  Connection closed by foreign host.
  +
  +Here is the code:
  +
  +  file:MyApache/EchoSocket.pm
  +  ------------------
  +  package MyApache::EchoSocket;
  +  
  +  use strict;
  +  use warnings FATAL => 'all';
  +  
  +  use Apache::Connection ();
  +  use APR::Socket ();
  +  
  +  use Apache::Const -compile => 'OK';
  +  
  +  use constant BUFF_LEN => 1024;
  +  
  +  sub handler {
  +      my $c = shift;
  +      my $socket = $c->client_socket;
  +  
  +      my $buff;
  +      while (1) {
  +          my($rlen, $wlen);
  +          $rlen = BUFF_LEN;
  +          $socket->recv($buff, $rlen);
  +          last if $rlen <= 0 or $buff =~ /^[\r\n]+$/;
  +          $wlen = $rlen;
  +          $socket->send($buff, $wlen);
  +          last if $wlen != $rlen;
  +      }
  +  
  +      Apache::OK;
  +  }
  +  1;
  +
  +The example handler starts with the standard I<package> declaration
  +and of course, C<use strict;>.  As with all C<Perl*Handler>s, the
  +subroutine name defaults to I<handler>.  However, in the case of a
  +protocol handler, the first argument is not a C<request_rec>, but a
  +C<conn_rec> blessed into the C<Apache::Connection> class.  We have
  +direct access to the client socket via C<Apache::Connection>'s
  +I<client_socket> method.  This returns an object blessed into the
  +C<APR::Socket> class.
  +
  +Inside the read/send loop, the handler attempts to read C<BUFF_LEN>
  +bytes from the client socket into the C<$buff> buffer.  The C<$rlen>
  +parameter will be set to the number of bytes actually read.  The
  +C<APR::Socket::recv()> method returns an APR status value, be we need
  +only check the read length to break out of the loop if it is less than
  +or equal to C<0> bytes. The handler also breaks the loop after
  +processing an input including nothing but new lines characters, which
  +is how we abort the connection in the interactive mode.
  +
  +If the handler receives some data, it sends it unmodified back to the
  +client with the C<APR::Socket::send()> method. When the loop is
  +finished the handler returns C<Apache::OK>, telling Apache to
  +terminate the connection. As mentioned earlier since this handler is
  +working directly with the connection socket, no filters can be
  +applied.
  +
  +
  +
  +=head4 Bucket Brigades-based Protocol Module
  +
  +Now let's look at the same module, but this time implemented by
  +manipulating bucket brigades, and which runs its output through a
  +connection output filter that turns all uppercase characters into
  +their lowercase equivalents.
  +
  +The following configuration defines a virtual host listening on port
  +8011 and which enables the C<MyApache::EchoBB> connection handler, which
  +will run its output through C<MyApache::EchoBB::lowercase_filter> filter:
  +
  +  Listen 8011
  +  <VirtualHost _default_:8011>
  +      PerlModule                   MyApache::EchoBB
  +      PerlProcessConnectionHandler MyApache::EchoBB
  +      PerlOutputFilterHandler      MyApache::EchoBB::lowercase_filter
  +  </VirtualHost>
  +
  +As before we start the httpd server:
  +
  +  panic% httpd
  +
  +And try the new connection handler in action:
  +
  +  panic% telnet localhost 8010
  +  Trying 127.0.0.1...
  +  Connected to localhost (127.0.0.1).
  +  Escape character is '^]'.
  +  Hello
  +  hello
  +  
  +  fOo BaR
  +  foo bar
  +
  +  Connection closed by foreign host.
  +
  +As you can see the response which is now was all in lower case,
  +because of the output filter. And here is the implementation of the
  +connection and the filter handlers.
  +
  +  file:MyApache/EchoBB.pm
  +  -------------------
  +  package MyApache::EchoBB;
     
     use strict;
     use warnings FATAL => 'all';
  @@ -353,15 +644,11 @@
     use APR::Brigade ();
     use APR::Util ();
     
  -  require Chatbot::Eliza;
  -  
     use APR::Const -compile => qw(SUCCESS EOF);
     use Apache::Const -compile => qw(OK MODE_GETLINE);
     
  -  my $eliza = new Chatbot::Eliza;
  -  
     sub handler {
  -      my Apache::Connection $c = shift;
  +      my $c = shift;
     
         my $bb_in  = APR::Brigade->new($c->pool, $c->bucket_alloc);
         my $bb_out = APR::Brigade->new($c->pool, $c->bucket_alloc);
  @@ -373,7 +660,7 @@
             if ($rv != APR::SUCCESS or $bb_in->empty) {
                 my $error = APR::strerror($rv);
                 unless ($rv == APR::EOF) {
  -                  warn "[eliza] get_brigade: $error\n";
  +                  warn "get_brigade: $error\n";
                 }
                 $bb_in->destroy;
                 last;
  @@ -394,9 +681,8 @@
                 return $status unless $status == APR::SUCCESS;
     
                 if ($data) {
  -                  $data =~ s/[\r\n]*$//;
  -                  $last++ if $data =~ /good bye/i;
  -                  $data = $eliza->transform( $data ) . "\n\n";
  +                  $last++ if $data =~ /^[\r\n]+$/;
  +                  # could do something with the data here
                     $bucket = APR::Bucket->new($data);
                 }
     
  @@ -415,7 +701,7 @@
     use base qw(Apache::Filter);
     use constant BUFF_LEN => 1024;
     
  -  sub lowercase : FilterConnectionHandler {
  +  sub lowercase_filter : FilterConnectionHandler {
         my $filter = shift;
       
         while ($filter->read(my $buffer, BUFF_LEN)) {
  @@ -427,8 +713,52 @@
     
     1;
   
  -XXX: mention that we will talk about filters later, just show that it
  -works here.
  +For the purpose of explaining how this connection handler works, we
  +are going to simplify the handler. The whole handler can be
  +represented by the following pseudo-code:
  +
  +  while ($bb_in = get_brigade()) {
  +      while ($bucket_in = $bb_in->get_bucket()) {
  +          my $data = $bucket_in->read();
  +          # do something with data
  +          $bucket_out = new_bucket($data);
  +  
  +          $bb_out->insert_tail($bucket_out);
  +      }
  +      $bb_out->insert_tail($flush_bucket);
  +      pass_brigade($bb_out);
  +  }
  +
  +The handler receives the incoming data via bucket bridages, one at a
  +time in a loop. It then process each bridage, by retrieving the
  +buckets contained in it, reading the data in, then creating new
  +buckets using the received data, and attaching them to the outgoing
  +brigade. When all the buckets from the incoming bucket brigade were
  +transformed and attached to the outgoing bucket brigade, a flush
  +bucket is created and added as the last bucket, so when the outgoing
  +bucket brigade is passed out to the outgoing connection filters, it
  +won't be buffered but sent to the client right away.
  +
  +If you look at the complete handler, the loop is terminated when one
  +of the following conditions occurs: an error happens, the end of
  +stream bucket has been seen (no more input at the connection) or when
  +the received data contains nothing but new line characters which we
  +used to to tell the server to terminate the connection.
  +
  +Notice that this handler could be much simpler, since we don't modify
  +the data. We could simply pass the whole brigade unmodified without
  +even looking at the buckets. But from this example you can see how to
  +write a connection handler where you actually want to read and/or
  +modify the data. To accomplish that modification simply add a code
  +that transforms the data which has been read from the bucket before
  +it's inserted to the outgoing brigade.
  +
  +We will skip the filter discussion here, since we are going to talk in
  +depth about filters in the dedicated to filters sections. But all you
  +need to know at this stage is that the data sent from the connection
  +handler is filtered by the outgoing filter and which transforms it to
  +be all lowercase.
  +
   
   
   
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to