The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Any::Daemon::HTTP::VirtualHost - webserver virtual hosts

SYNOPSIS

my $vhost  = Any::Daemon::HTTP::VirtualHost->new
 ( directories => ...
 , rewrite     => ...
 , handlers    => ...
 );

my $daemon = Any::Daemon::HTTP->new
  ( @other_options
  , vhosts  => $vhost  # or \@vhosts
  );

# or
my $daemon = Any::Daemon::HTTP->new(@other_opts);
$daemon->addVirtualHost($vhost);
$daemon->addVirtualHost(@vhost2_opts);

# create object which extends Any::Daemon::HTTP::VirtualHost
my $myvhost = MyVHost->new(...);
$daemon->addVirtualHost($myvhost);

DESCRIPTION

These virtual host (vhost) configuration are used by Any::Daemon::HTTP, to implement (server) name based data separation. Its features resemble those of Apache virtual hosts.

Each vhost usually has two Any::Daemon::HTTP::Directory slaves: one which describes the permissions for user directories (url paths in the form /~user/ ), the other for data outside the user space.

METHODS

Constructors

You may avoid the creation of extension classes for each virtual host, by using these options.

Any::Daemon::HTTP::VirtualHost->new(%options|\%options)
-Option     --Default
 aliases      'AUTO'
 directories  <see text>
 directory    undef
 documents    <undef>
 handler      undef
 handlers     {}
 name         <required>
 proxies      undef
 proxy        undef
 redirect     <undef>
 rewrite      <undef>
 user_dirs    undef
aliases => HOSTNAME|'AUTO'|ARRAY

[0.26] Alternative host components which indicate the same virtual host. When 'AUTO' is given (the default since [0.28]), then generateAliases() is used to produce a convenient list.

directories => OBJECT|HASH|ARRAY

Pass one or more Any::Daemon::HTTP::Directory OBJECTS, or HASHes which will be used to initialize them.

directory => OBJECT|HASH|ARRAY

[0.28] Alias for directories.

documents => DIRECTORY

An absolute DIRECTORY for the location of the source files. Creates the most free Any::Daemon::HTTP::Directory object. If you need things like access restrictions, then do not use this option but the directories option.

handler => CODE|METHOD|HASH

Alias for handlers.

handlers => CODE|METHOD|HASH

The keys are path names, part of the request URIs. The values are CODE-references, called when that URI is addressed. The access rules are taken from the directory definition which is selected by the path. Read "DETAILS" for the details.

name => HOSTNAME
proxies => OBJECT|HASH|ARRAY

Pass one or more Any::Daemon::HTTP::Proxy OBJECTS, or HASHes which will be used to initialize them.

proxy => OBJECT|HASH|ARRAY

[0.28] Alias for proxies.

redirect => CODE|METHOD|HASH

[0.21] Automatically redirect the browser to some other url, maybe to an other host. Configuration like for rewrite.

rewrite => CODE|METHOD|HASH

When a request arrives, the URI can be rewritten to become an other request. See "URI Rewrite".

[0.21] When a METHOD name is specified, that will be called on the virtual host object. An HASH as parameter is interpreted as a simple lookup table.

user_dirs => undef|OBJECT|HASH

With an (empty?) HASH which contains instantiation parameter, an Any::Daemon::HTTP::UserDirs is created for you, with standard Apache behavior. You may provide your own OBJECT. Without this parameter, there are no public user pages.

Attributes

$obj->aliases()

Returns a list of all aliases (alternative names) for this server.

$obj->generateAliases($hostname)
Any::Daemon::HTTP::VirtualHost->generateAliases($hostname)
$obj->name()

Returns the primary name for this server.

Handler

$obj->addHandler(CODE|METHOD|PAIRS|HASH)

Handlers are called to dynamically generate responses, for instance to fill-in templates. The "DETAILS" section below explains how handlers work.

When only CODE is given, then this will be the default handler for all paths (under '/', top). [0.21] CODE may also be a $method name.

Usually, you pass some PAIRS or a HASH, relating PATH names inside the virtual host into function references or method names to be used for that tree.

example:

$vhost->addHandler('/' => \&default_handler
   , '/upload' => \&upload_handler);

$vhost->addHandler(\&default_handler);

# [0.21] will call $vhost->formHandle
$vhost->addHandler('/form' => 'formHandler');
$obj->addHandlers($params)

Alias for addHandler().

$obj->findHandler($uri|$path|@segments)

Find the handler which matches the given $uri best. The $uri is the rewritten URI of the request, an URI object. It's $path is sufficient, may also be broken into path @segments already.

$obj->handleRequest( $server, $session, $request, [$uri] )

Basic daemon actions

$obj->addSource($source)

The $source objects extend Any::Daemon::HTTP::Source, for instance a ::Directory or a ::Proxy. You can find them back via sourceFor().

$obj->mustRedirect($uri)

[0.21] Returns an HTTP::Response object if the $uri needs to be redirected, according to the vhost configuration.

$obj->redirect( $uri, [$http_code] )

[0.21] Returns an HTTP::Response object of the $uri.

$obj->rewrite($uri)

Returns an $uri object as result, which may be the original in case of no rewrite was needed. See "URI Rewrite".

Directories

$obj->addDirectory($object|HASH|%options)

Either pass a Any::Daemon::HTTP::Directory $object or the %options to create the object. When %options are provided, they are passed to Any::Daemon::HTTP::Directory::new() to create the $object.

$obj->filename($uri)

Translate the $uri into a filename, without checking for existence. Returns undef is not possible.

$obj->sourceFor($path|$path_segments)

Find the best matching Any::Daemon::HTTP::Source object, which might be a ::UserDirs, a ::Directory, or a ::Proxy.

Proxies

$obj->addProxy($object|HASH|%options)

Either pass a Any::Daemon::HTTP::Proxy $object or the %options to create the object. When %options are provided, they are passed to Any::Daemon::HTTP::Proxy::new() to create the $object.

DETAILS

Handlers

Handlers are called to dynamically generate responses, for instance to fill-in templates. In other frameworks, they are called 'routes' or 'get'.

When a request for an URI is received, it is first checked whether a static file can fulfil the request. If not, a search is started for the handler with the longest path.

# /upload($|/*) goes to the upload_handler
$vhost->addHandlers
  ( '/'       => \&default_handler
  , '/upload' => \&upload_handler
  );

# Missing files go to the default_handler
# which is actually replacing the existing one
$vhost->addHandler(\&default_handler);

# [0.21] This will call $vhost->formHandle(...), especially
# useful in your virtual host sub-class.
$vhost->addHandler('/form' => 'formHandler');

The handlers are called with many arguments, and should return an HTTP::Response object:

$vhost->addHandler('/upload' => $handler);
my $resp = $hander->($vhost, $session, $req, $uri, $tree);

$vhost->addHandler('/form' => $method);
my $resp = $vhost->$method($session, $req, $uri, $tree);

In which

  • $vhost is an Any::Daemon::HTTP::VirtualHost,

  • $session is an Any::Daemon::HTTP::Session,

  • $req is an HTTP::Request,

  • $uri an URI after rewrite rules, and

  • $tree the selected Any::Daemon::HTTP::Directory.

The handler could work like this:

sub formHandler($$$$)
{   my ($vhost, $session, $req, $uri, $tree) = @_;
    # in OO extended vhosts, then $vhost => $self

    # Decode path parameters in Plack style
    # ignore two components: '/' and 'form' from the path
    my (undef, undef, $name, @more) = $uri->path_segments;

    HTTP::Response->new(HTTP_OK, ...);
}

Your virtual host as class

When your virtual host has larger configuration or many handlers --or when you like clean programming--, it may be a good choice to put your code in a separate package with the normal Object Oriented extension mechanism.

You may need to implement your own information persistence via databases or configation files. For that, extend Any::Daemon::HTTP::Session.

. Example: own virtual host

package My::Service;
use parent 'Any::Daemon::HTTP::VirtualHost';

sub init($)
{   my ($self, $args) = @_;
    $args->{session_class} = 'My::Service::Session';
    $self->SUPER::init($args);
    
    $self->addDirectory(...);
    $self->addHandler(a => 'ah');
    ... etc ...
    $self;
}

sub ah($$$$)
{   my ($self, $session, $request, $uri, $tree) = @_;
    return HTTP::Response->new(...);
}

package My::Service::Session;
use parent 'Any::Daemon::HTTP::Session';

URI Rewrite

For each request, the rewrite() method is called to see whether a rewrite of the URI is required. The method must return the original URI object (the only parameter) or a new URI object.

. Example: usage

my $vhost = Any::Daemon::HTTP::VirtualHost
  ->new(..., rewrite => \&rewrite);

my $vhost = My::Service     # see above
  ->new(..., rewrite => 'rewrite');

my $vhost = My::Service     # see above
  ->new(..., rewrite => \%lookup_table);

. Example: rewrite URI

my %lookup =
  ( '/'     => '/index-en.html'
  , '/news' => '/news/2013/index.html'
  );

sub rewrite($)
{  my ($vhost, $uri) = @_;
   # when called as method, $vhost --> $self

   # with lookup table
   $uri = URI->new_abs($lookup{$uri->path}, $uri)
       if exists $lookup{$uri->path};

   # whole directory trees
   $uri = URI->new_abs('/somewhere/else'.$1, $uri)
       if $uri->path =~ m!^/some/dir(/.*|$)!;
   
   $uri;
}

Using Template Toolkit

Connecting this server to the popular Template Toolkit web-page framework is quite simple:

# Use TT only for pages under /status
$vhost->addHandler('/status' => 'ttStatus');

sub ttStatus($$$$)
{   my ($self, $session, $request, $uri, $tree) = @_;;

    # Often, this object is global or an attribute
    my $template = Template->new(...);

    my $output;
    my $values = {...};  # collect the values
    $template->process($fn, $values, \$output)
        or die $template->error, "\n";

    HTTP::Response->new(HTTP_OK, undef
      , ['Content-Type' => 'text/html']
      , "$output"
      );
}

See Log::Report::Template if you need translations as well.

SEE ALSO

This module is part of Any-Daemon-HTTP distribution version 0.30, built on April 06, 2020. Website: http://perl.overmeer.net/any-daemon/

LICENSE

Copyrights 2013-2020 by [Mark Overmeer]. For other contributors see ChangeLog.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. See http://dev.perl.org/licenses/