Catalyst-Plugin-ServeFile

 view release on metacpan or  search on metacpan

lib/Catalyst/Plugin/ServeFile.pm  view on Meta::CPAN

package Catalyst::Plugin::ServeFile;

use Moo::Role;
use Plack::Util ();
use Plack::MIME ();
use HTTP::Date ();
use File::Spec ();
use Cwd ();

our $VERSION = '0.004';

# We break this out since its the first step in making this plugin 'pluggable'
# in terms of where the static assets are found.  Maybe we might have a 
# $get_asset_from_database someday 

my $get_asset_from_filesystem = sub {
  my ($c, $full_path) = @_;

  # Does it exist?
  unless(-f $full_path) {
    $c->log->debug("Static File at '$full_path' not found!") if $c->debug;
    return;
  }
  open(my $fh, '<', $full_path) || do {
    $c->log->debug("Static file at '$full_path' cannot be open'd for read with error: $@")
      if $c->debug;
    return;
  };

  my @stat = stat($full_path);
  
  unless(@stat) {
    $c->log->debug("Static file at '$full_path' cannot be stat'd with error: $@")
      if $c->debug;
    return;
  }

  Plack::Util::set_io_path($fh, Cwd::realpath($full_path)); # Support Xsendfile

  return ($fh, @stat);
};

sub serve_file {
  my $c = shift;
  my $options = ref($_[-1]) ? pop @_ : +{};
  my $config = $c->config->{'Plugin::ServeFile'} || +{};
  my %settings = (%$config, %$options);

  $c->log->abort(1) unless $settings{show_log};

  my $root = $settings{root} || $c->config->{root};

  my $file_proto = File::Spec->catdir(
    File::Spec->no_upwards(
      map { File::Spec->splitdir($_) } @_ ));

  my $full_path = File::Spec->catfile($root, $file_proto);
  my $content_type = Plack::MIME->mime_type($full_path) || 'application/octet';

  if(my $allowed_content_types = $settings{allowed_content_types}) {
    unless(scalar( grep { lc($content_type) eq lc($_) } @$allowed_content_types)) {
      $c->log->debug("Static file has disallowed content-type of '$content_type'") if $c->debug;
      return;
    }
  }

  my ($fh, @stat) = $c->$get_asset_from_filesystem($full_path);
  return unless $fh;

  if ($content_type =~ m!^text/!) {
    my $encoding =  $settings{encoding} || "utf-8";
    $content_type .= "; charset=$encoding";
  }

  $c->log->debug("Serving Static File: $full_path") if $c->debug;

  my $status = $settings{status} || $settings{code} || 200;

  $c->res->status($status);
  $c->res->headers->header(
    'Content-Type'   => $content_type,
    'Content-Length' => $stat[7],
    'Last-Modified'  => HTTP::Date::time2str( $stat[9] ),
    'Cache-control' => 'public');
  $c->res->body($fh);

  return $fh;
}

1;



( run in 0.489 second using v1.01-cache-2.11-cpan-39bf76dae61 )