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@dancer.pm Subject: Re: [dancer-users] Dancer crashing On Wed, 13 Mar 2019 09:58:54 -0700 Chad Wallace <cwall...@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@dancer.pm http://lists.preshweb.co.uk/mailman/listinfo/dancer-users
_______________________________________________ dancer-users mailing list dancer-users@dancer.pm http://lists.preshweb.co.uk/mailman/listinfo/dancer-users