lemonldap-ng/lemonldap-ng-common/lib/Lemonldap/NG/Common/PSGI/Router.pm

153 lines
4.2 KiB
Perl

package Lemonldap::NG::Common::PSGI::Router;
use Mouse;
use Lemonldap::NG::Common::PSGI;
use Lemonldap::NG::Common::PSGI::Constants;
our $VERSION = '1.9.0';
extends 'Lemonldap::NG::Common::PSGI';
# Properties
has 'routes' => (
is => 'rw',
isa => 'HashRef',
default => sub { { GET => {}, POST => {}, PUT => {}, DELETE => {} } }
);
has 'defaultRoute' => ( is => 'rw', default => 'index.html' );
# Routes initialization
sub addRoute {
my ( $self, $word, $dest, $methods ) = splice(@_);
$methods ||= [qw(GET POST PUT DELETE)];
foreach my $method (@$methods) {
$self->genRoute( $self->routes->{$method}, $word, $dest );
}
return $self;
}
sub genRoute {
my ( $self, $routes, $word, $dest ) = splice @_;
if ( ref $word eq 'ARRAY' ) {
foreach my $w (@$word) {
$self->genRoute( $routes, $w, $dest );
}
}
else {
if ( $word =~ /^:(.*)$/ ) {
$routes->{'#'} = $1;
die "Target required for $word" unless ($dest);
$word = ':';
}
else {
$dest ||= $word;
}
if ( my $t = ref $dest ) {
if ( $t eq 'CODE' ) {
$routes->{$word} = $dest;
}
elsif ( $t eq 'HASH' ) {
$routes->{$word} ||= {};
foreach my $w ( keys %$dest ) {
$self->genRoute( $routes->{$word}, $w, $dest->{$w} );
}
}
elsif ( $t eq 'ARRAY' ) {
$routes->{$word} ||= {};
foreach my $w ( @{$dest} ) {
$self->genRoute( $routes->{$word}, $w );
}
}
else {
die "Type $t unauthorizated in routes";
}
}
elsif ( $dest =~ /^(.+)\.html$/ ) {
my $tpl = $1 or die;
$routes->{$word} = sub { $self->sendHtml( $_[1], $tpl ) };
}
elsif ( $self->can($dest) ) {
$routes->{$word} = sub { shift; $self->$dest(@_) };
}
else {
die "$dest() isn't a method";
}
$self->lmLog( "route $word added", 'debug' );
}
}
sub routerAbort {
my ( $self, $path, $msg ) = splice @_;
delete $self->routes->{$path};
$self->addRoute(
$path => sub {
my ( $self, $req ) = splice @_;
return $self->sendError( $req, $msg, 500 );
}
);
}
# Methods that dispatch requests
sub router {
my ( $self, $req ) = splice @_;
#print STDERR Dumper($self->routes);use Data::Dumper;
# Reinitialize configuration message
$Lemonldap::NG::Common::Conf::msg = '';
# Launch reqInit() if exists
if ( $self->can('reqInit') ) {
$self->reqInit($req);
}
# Only words are taken in path
my @path = grep { $_ =~ /^[\.\w]+/ } split /\//, $req->path();
$self->lmLog( "Start routing " . ( $path[0] // 'default route' ), 'debug' );
unless (@path) {
push @path, $self->defaultRoute;
# TODO: E-Tag, Expires,...
#
## NB: this is not HTTP compliant: host and protocol are required !
#my $url = '/' . $self->defaultRoute;
#return [
# 302,
# [ 'Content-Type' => 'text/plain', 'Location' => $url ],
# ['Document has moved here: $url']
#];
}
return $self->followPath( $req, $self->routes->{ $req->method }, \@path );
}
sub followPath {
my ( $self, $req, $routes, $path ) = splice @_;
if ( $path->[0] and defined $routes->{ $path->[0] } ) {
my $w = shift @$path;
if ( ref( $routes->{$w} ) eq 'CODE' ) {
return $routes->{$w}->( $self, $req, @$path );
}
return $self->followPath( $req, $routes->{$w}, $path );
}
elsif ( $routes->{':'} ) {
my $v = shift @$path;
$req->params->{ $routes->{'#'} } = $v;
if ( ref( $routes->{':'} ) eq 'CODE' ) {
return $routes->{':'}->( $self, $req, @$path );
}
return $self->followPath( $req, $routes->{':'}, $path );
}
elsif ( my $sub = $routes->{'*'} ) {
return $self->$sub( $req, @$path );
}
else {
$self->lmLog( 'Bad request received (' . $req->path . ')', 'warn' );
return $self->sendError( $req, 'Bad request', 400 );
}
}
1;