Dancer2

 view release on metacpan or  search on metacpan

lib/Dancer2/Core/App.pm  view on Meta::CPAN

        if ( $prefix && $prefix ne '/' ) {
            $path =~ s/^\Q$prefix\E//;
        }
        # static file dir - either system root or public_dir
        my $dir = $options{system_path}
            ? Path::Tiny->rootdir
            : $self->_public_dir_path;

        my $err_response = sub {
            my $status = shift;
            $self->response->status($status);
            $self->response->header( 'Content-Type', 'text/plain' );
            $self->response->content( Dancer2::Core::HTTP->status_message($status) );
            $self->with_return->( $self->response );
        };

        # resolve relative paths (with '../') as much as possible
        # On Windows, absolute paths (e.g., C:/foo) must not be joined with
        # the rootdir since Path::Tiny would produce an invalid /C:/foo path.
        my $pt_path = Path::Tiny::path($path);
        $file_path = ( $pt_path->is_absolute
            ? $pt_path
            : Path::Tiny::path( $dir, $path )
        )->realpath;

        # We need to check whether they are trying to access
        # a directory outside their scope
        $err_response->(403) if !$dir->realpath->subsumes($file_path);

        # other error checks
        $err_response->(403) if !$file_path->exists;
        $err_response->(404) if !$file_path->is_file;
        $err_response->(403) if !-r $file_path;

        # Read file content as bytes
        $fh = $file_path->openr_raw();
        $content_type = $self->mime_type->for_file($file_path) || 'text/plain';
        if ( $content_type =~ m!^text/! ) {
            $charset = $self->config->{charset};
        }

        # cleanup for other functions not assuming on Path::Tiny
        $file_path = $file_path->stringify;
    }

    # Now we are sure we can render the file...
    $self->execute_hook( 'core.app.before_file_render', $file_path );

    # response content type and charset
    ( exists $options{'content_type'} ) and $content_type = $options{'content_type'};
    ( exists $options{'charset'} ) and $charset = $options{'charset'};
    $content_type .= "; charset=$charset" if $content_type and $charset;
    ( defined $content_type )
      and $self->response->header('Content-Type' => $content_type );

    # content disposition
    ( exists $options{filename} )
      and $self->response->header( 'Content-Disposition' =>
          ($options{content_disposition} || "attachment") . "; filename=\"$options{filename}\"" );

    # use a delayed response unless server does not support streaming
    my $use_streaming = exists $options{streaming} ? $options{streaming} : 1;
    my $response;
    my $env = $self->request->env;
    if ( $env->{'psgi.streaming'} && $use_streaming ) {
        my $cb = sub {
            my $responder = $Dancer2::Core::Route::RESPONDER;
            my $res = $Dancer2::Core::Route::RESPONSE;
            return $responder->(
                [ $res->status, $res->headers_to_array, $fh ]
            );
        };

        Scalar::Util::weaken( my $weak_self = $self );

        $response = Dancer2::Core::Response::Delayed->new(
            error_cb => sub { $weak_self->logger_engine->log( warning => @_ ) },
            cb       => $cb,
            request  => $Dancer2::Core::Route::REQUEST,
            response => $Dancer2::Core::Route::RESPONSE,
        );
    }
    else {
        $response = $self->response;
        # direct assignment to hash element, avoids around modifier
        # trying to serialise this this content.

        # optimized slurp
        {
            ## no critic qw(Variables::RequireInitializationForLocalVars)
            local $/;
            $response->{'content'} = <$fh>;
        }

        $response->is_encoded(1);    # bytes are already encoded
    }

    $self->execute_hook( 'core.app.after_file_render', $response );
    $self->with_return->( $response );
}

sub BUILD {
    my $self = shift;
    $self->init_route_handlers();
    $self->_init_hooks();
}

sub finish {
    my $self = shift;

    # normalize some values that require calculations
    defined $self->config->{'static_handler'}
        or $self->config->{'static_handler'} = -d $self->config->{'public_dir'};

    $self->register_route_handlers;
    $self->compile_hooks;

    @{$self->plugins}
        && $self->plugins->[0]->can('_add_postponed_plugin_hooks')
        && $self->plugins->[0]->_add_postponed_plugin_hooks(
            $self->postponed_hooks
        );

    foreach my $prep_cb ( @{ $self->prep_apps } ) {
        $prep_cb->($self);



( run in 0.874 second using v1.01-cache-2.11-cpan-c966e8aa7e8 )