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 <cwallace@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