AnyEvent-HTTPD-Router
view release on metacpan or search on metacpan
lib/AnyEvent/HTTPD/Router.pm view on Meta::CPAN
package AnyEvent::HTTPD::Router;
use common::sense;
use parent 'AnyEvent::HTTPD';
use AnyEvent::HTTPD;
use Carp;
use AnyEvent::HTTPD::Router::DefaultDispatcher;
our $VERSION = '1.0.1';
sub new {
my $this = shift;
my $class = ref($this) || $this;
my %args = @_;
# todo documentation how to overwrite your dispathing
my $dispatcher = delete $args{dispatcher};
my $routes = delete $args{routes};
my $auto_respond_404 = delete $args{auto_respond_404};
my $dispatcher_class = delete $args{dispatcher_class}
|| 'AnyEvent::HTTPD::Router::DefaultDispatcher';
my $known_methods = delete $args{known_methods}
|| [ qw/GET HEAD POST PUT PATCH DELETE TRACE OPTIONS CONNECT/ ];
my $self = $class->SUPER::new(%args);
$self->{known_methods} = $known_methods;
$self->{dispatcher} = defined $dispatcher
? $dispatcher
: $dispatcher_class->new();
$self->reg_cb(
'request' => sub {
my $self = shift;
my $req = shift;
my $matched = $self->dispatcher->match( $self, $req );
unless ($matched) {
$self->event( 'no_route_found' => $req );
}
},
);
$self->reg_cb('no_route_found' => sub {
my ( $httpd, $req ) = @_;
$req->respond( [ 404, 'not found', {}, '' ] );
}) if $auto_respond_404;
if ($routes) {
$self->reg_routes( @$routes );
}
return $self;
}
sub dispatcher { shift->{dispatcher} }
sub _check_verb {
my $self = shift;
my $verb = shift;
my $methods = shift;
if ( $verb =~ m/^:/ ) {
$methods->{$_}++ for qw(GET POST); # convert ':verbs' to POST and GET
return 1;
} elsif ( grep { $verb eq $_ } @{ $self->{known_methods} } ) {
$methods->{$verb}++;
return 1;
}
return;
}
sub reg_routes {
my $self = shift;
croak 'arguments to reg_routes are required' if @_ == 0;
croak 'arguments to reg_routes are confusing' if @_ % 3 != 0;
# * mix allowed methods and new http methods together
my %methods = map { $_ => 1 } @{ $self->allowed_methods };
while (my ($verbs, $path, $cb) = splice(@_, 0, 3) ) {
$verbs = ref($verbs) eq 'ARRAY'
? $verbs
: [ $verbs ];
if ( not ref($cb) eq 'CODE' ) {
croak 'callback must be a coderef';
}
elsif ( not $path =~ m/^\// ) {
croak 'path syntax is wrong';
}
foreach my $verb (@$verbs) {
croak 'verbs or methods are wrong'
unless $self->_check_verb( $verb, \%methods );
}
$self->dispatcher->add_route($verbs, $path, $cb);
}
# set allowed methods new
# Todo: setter doesnt work in this AE::HTTPD version
# so must do push(@{$self->{allowed_methods}}
# later we can do setter if AE::HTTPD version is high enough
$self->{allowed_methods} = [ sort keys %methods ];
lib/AnyEvent/HTTPD/Router.pm view on Meta::CPAN
$httpd->stop_request;
$req->respond([
200, 'ok', { 'X-Your-Method' => $req->method }, '' ]);
},
GET => '/calendar/:year/:month/:day' => sub {
my ( $httpd, $req, $param ) = @_;
my $calendar_entries = get_cal_entries(
$param->{year}, $param->{month}, $param->{day}
);
$httpd->stop_request;
$reg->respond([
200, 'ok', { 'Content-Type' => 'application/json'},
to_json($calendar_entries)
]);
},
GET => '/static-files/*' => sub {
my ( $httpd, $req, $param ) = @_;
my $requeted_file = $param->{'*'};
my ($content, $content_type) = black_magic($requested_file);
$httpd->stop_request;
$req->respond([
200, 'ok', { 'Content-Type' => $content_type }, $content ]);
}
);
$httpd->run();
=head1 METHODS
=over
=item * C<new()>
Creates a new C<AnyEvent::HTTPD::Router> server. The constructor handles the
following parameters. All further parameters are passed to C<AnyEvent::HTTPD>.
=over
=item * C<dispatcher>
You can pass your own implementation of your router dispatcher into this module.
This expects the dispatcher to be an instance not a class name.
=item * C<dispatcher_class>
You can pass your own implementation of your router dispatcher into this module.
This expects the dispatcher to be a class name.
=item * C<routes>
You can add the routes at the constructor. This is an ArrayRef.
=item * C<known_methods>
Whenever you register a new route this modules checks if the method is either
customer method prefixed with ':' or a $known_method. You would need to change
this, if you would like to implement WebDAV, for example. This is an ArrayRef.
=item * C<auto_respond_404>
If the value for this parameter is set to true a a simple C<404> responder will
be installed that responds if not route matches. You can implement your own
handler see L<EVENTS>.
=back
=item * C<reg_routes( [$method, $path, $callback]* )>
You can add further routes with this method. Multiple routes can be added at
once. To add a route you need do add 3 parameters: <method>, <path>, <callback>.
=item * C<*>
C<AnyEvent::HTTPD::Router> subclasses C<AnyEvent::HTTPD> so you can use all
methods the parent class.
=back
=head1 EVENTS
=over
=item * no_route_found => $request
When the dispatcher can not find a route that matches on your request, the
event C<no_route_found> will be emitted.
In the case that routes and callbacks (C<reg_cb()>) for paths as used with
C<AnyEvent::HTTPD> are mixed, keep in mind that that C<no_route_found> will
happen before the other path callbacks are executed. So for a
C<404 not found> handler you could do
$httpd->reg_cb('' => sub {
my ( $httpd, $req ) = @_;
$req->respond( [ 404, 'not found', {}, '' ] );
});
If you just use C<reg_routes()> and don't mix with C<reg_cb()> for paths you
could implement the C<404 not found> handler like this:
$httpd->reg_cb('no_route_found' => sub {
my ( $httpd, $req ) = @_;
$req->respond( [ 404, 'not found', {}, '' ] );
});
This is exactly what you get if you specify C<auto_respond_404> at the
constructor.
=item * See L<AnyEvent::HTTPD/EVENTS>
=back
=head1 WRITING YOUR OWN ROUTE DISPATCHER
If you want to change the implementation of the dispatching you specify the
C<dispatcher> or C<dispatcher_class>. You need to implement the C<match()>
method.
In the case you specify the C<request_class> for C<AnyEvent::HTTPD> you might
need to make adaptions to the C<match()> method as well.
=head1 SEE ALSO
=over
=item * L<AnyEvent>
=item * L<AnyEvent::HTTPD>
=back
There are a lot of HTTP Router modules in CPAN:
=over
=item * L<HTTP::Router>
=item * L<Router::Simple>
=item * L<Router::R3>
=item * L<Router::Boom>
=back
=head1 BUILDING AND RELEASING THIS MODULE
This module uses L<https://metacpan.org/pod/Minilla>.
=head1 LICENSE
Copyright (C) Martin Barth.
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=head1 CONTRIBUTORS
=over
=item Paul Koschinski
=back
=head1 AUTHOR
( run in 1.141 second using v1.01-cache-2.11-cpan-39bf76dae61 )