[dancer-users] Dancer crashing
Hermann Calabria
hermann at ivouch.com
Fri May 17 01:42:27 BST 2019
Hi all,
Still (3 months later) struggling with this one. I’m still seeing the occasional crashes, and in addition – and this is new -- tests using Dancer::Test are failing consistency which may be yet another clue.
I’ve narrowed the potential culprit to fancy route handling being done by my app. I’m posting here functioning and well-commented code (i.e. works most of the time except when it crashes as previously described) for any comments or thoughts.
As an alternative way to fix the problem, I also welcome alternative ways to accomplish what we’re trying to accomplish (as described in comments):
use strict;
use warnings;
use Dancer;
=item
Each "instance" of the app is accessed via a URL prefix representing each "client". Examples:
Various paths within instance for client #1, called "mary":
http://www.myapp.com/mary
http://www.myapp.com/mary/foo
http://www.myapp.com/mary/foo/bar
Various paths within instance for client #2, called "susan":
http://www.myapp.com/susan
http://www.myapp.com/susan/foo
http://www.myapp.com/susan/foo/bar
The various paths are implemented as Dancer Routes:
GET '/instance/...'
Examples:
POST '/instance/bar/api/users'
GET '/instance/foo/bar'
We trick Dancer by substituting the instance URL with "/instance". All such trickery is contained within this
one file. Here's how it works:
1. Dancer will attempt to match path to anything in ./public, and return file. However, since instance-specific
paths will be along the lines of http://www.myapp.com/mary/my.css, no match will occur, since all
instance-specific assets are under ./public/instance.
2. Dancer attempts to match by route. Very first route is qr{.*} route (see below). However, before this route
runs, hook 'before' will be triggered...
3a. hook 'before' looks for instance URL prefixes (such as "/mary", "/susan" above). If match:
- change path to '/instance/..'
- if path points to a ./public resource [/instance/(static|cached)], return to qr{.*} immediately
- set vars->{'client'} to client id, such as "mary" or "susan" above
- return to qr{.*} route
3b. If no match under 3a, return to qr{.*}
4. Back in qr{.*}:
- if route path is an instance-specific public asset [/instance/(static|cached)], send it manually using Dancer's send_file()
- otherwise pass(), which leads to remaining default Dancer route handling:
- if route path matches a remaining Dancer route, that route will be executed
- if no matches, Dancer creates 404/not_found error (unless final route is another catch-all for special handling of 404s)
=cut
any qr{.*} => sub {
my $request_path = request->path_info;
# Manually send files in ./public/instance/(static|cached)
if ( $request_path =~ m{^/(instance/(static|cached)(/.*|\z))} ) {
send_file( $1 );
return; # per Dancer docs, route exits after send_file(). return is unnecessary; added for readability.
}
# causes Dancer to attempt to match to all remaining routes, with path as modified by hook
pass();
};
hook 'before' => sub {
my $request_path = request->path_info();
#
# /instance
# (already did all hook_before operations when first entered as /[instance_url], so skip now)
#
if ( $request_path =~ m{^/instance(/|\z)} ) {
return;
}
#
# /[instance_url]
#
# If match,
# 1. converts $request_path & request->path_info to /instance/...
# 2. sets $instance
#
# If no match, returns
#
my $instance;
if ( $request_path =~ m{^/([^/]+)(/.*|\z)} ) {
my $url = $1;
my $therest = $2;
if ( $url =~ /^(mary|susan)$/ ) { # simplified; actual code uses a database
$instance = $url;
$request_path = "/instance" . $therest;
request->path_info( $request_path );
}
}
if ( !defined($instance) ) {
return;
}
#
# /instance (converted from /[instance_url])
# with $instance
#
# hole for ./instance/(static|cached)
if ( $request_path =~ m{^/instance/(static|cached)(/|\z)} ) {
return;
}
# set instance-specific var
var instance => $instance;
#
# Only allow access to specific /instance areas, and in some cases require authentication
#
# /instance - welcome
if ( $request_path =~ m{^/instance/?\z} ) {
return;
}
# /instance/register - Register
if ( $request_path =~ m{^/instance/register(/|\z)} ) {
return;
}
# /instance/signedin - Signed-in area (requires authentication)
if ( $request_path =~ m{^/instance/signedin(/|\z)} ) {
if (!vars->{'not_signed_in'}) {
request->path_info( "/instance/register");
}
return;
}
# /instance/(everything else)
request->path_info('/instance/error/notfound');
return;
};
#
# /instance routes
#
get '/instance' => sub {
return "Welcome, ".vars->{'instance'};
};
get '/instance/register' => sub {
return "Please register, ".vars->{'instance'};
};
get '/instance/signedin' => sub {
return "This is the signed-in area, ".vars->{'instance'}.". You are authenticated";
};
get '/instance/error/not/found' => sub {
return "OOPS! 404 Error";
};
dance;
Test results:
use Dancer::Test;
…
response_status_is [GET => "/instance"], 200; # ok
response_status_is [GET => "/mary"], 200; # fails. Gets 404.
response_status_is [GET => "/susan"], 200; # fails. Gets 404.
Thoughts/ideas?
Thank you!!
Hermann
From: David Precious
Sent: Tuesday, March 19, 2019 6:17 AM
To: dancer-users at dancer.pm
Subject: Re: [dancer-users] Dancer crashing
On Wed, 13 Mar 2019 09:58:54 -0700
Chad Wallace <cwallace at lodgingcompany.com> wrote:
> Here are lines 396 and 397 of HTTP/Server/Simple.pm (version 0.52):
>
> my $remote_sockaddr = getpeername( $self->stdio_handle );
> my $family = sockaddr_family($remote_sockaddr);
>
> So $remote_sockaddr is undef. It should avoid calling
> sockaddr_family() in that case.
It looks like Chad is right - it would appear to be a problem with
HTTP::Server::Simple.
That means (a) it should be reported there, (b) it should only affect
you while running Dancer standalone.
You could, if you're keen, patch your local HTTP::Server::Simple with a
check that the call to getpeername( $self->stdio_handle ) did indeed
return something useful, before then passing that to sockaddr_family(),
and if not, emit as much potentially-useful debug info as you can, to
try to isolate the conditions under which this could happen.
Some form of stress-testing might help, too - maybe it's a timing
issue, a race condition where another request arrives while a previous
one is in a certain state, where the client (or another/previous
client) drops its connection right whne the server isn't expecting it,
or similar?
Cheers
Dave P
_______________________________________________
dancer-users mailing list
dancer-users at dancer.pm
http://lists.preshweb.co.uk/mailman/listinfo/dancer-users
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.preshweb.co.uk/pipermail/dancer-users/attachments/20190516/1342c29d/attachment.html>
More information about the dancer-users
mailing list