Commits

Fabrice Gabolde committed 46bca43

Initial import.

Comments (0)

Files changed (1)

lib/Dancer/Plugin/XSendfile.pm

+package Dancer::Plugin::XSendfile;
+
+use strict;
+use warnings;
+
+use URI::Escape;
+use File::Spec;
+
+use Dancer qw/:syntax/;
+use Dancer::Plugin;
+
+register 'x_send_file' => sub {
+
+    my ($path, %options) = @_;
+
+    my $request = Dancer::SharedData->request;
+
+    # If we're given an IO::Scalar object, DTRT (abort!!)
+    if (Scalar::Util::blessed($path) && $path->isa('IO::Scalar')) {
+        die 'x_send_file does not support IO::Scalar objects, use regular send_file instead';
+    }
+
+    unless ($options{system_path}) {
+
+        $path = File::Spec->catfile(setting('public'), $path);
+        $path = File::Spec->rel2abs($path);
+
+    }
+
+    if (-f $path) {
+
+        my $size_in_bytes = -s $path;
+
+        my $resp = Dancer::Renderer->get_file_response_for_path($path);
+
+        if (my $range = $request->header('Range')) {
+
+            # for Content-Range, RFC 2616 says: start byte | dash |
+            # last byte | slash | total byte size
+
+            $resp->status(206); # Partial Content
+
+            if ($range =~ /^bytes=(\d+)-$/) {
+
+                # resuming a download
+                my $start = $1;
+                $resp->header('Content-Range' => sprintf('%d-%d/%d',
+                                                         $start,
+                                                         $size_in_bytes - 1,
+                                                         $size_in_bytes));
+
+                $resp->header('X-Sendfile2' => sprintf('%s %d-',
+                                                       URI::Escape::uri_escape($path), $start));
+
+            } elsif ($range =~ /^bytes=(\d+)-(\d+)$/) {
+
+                # requesting a range
+                my ($start, $end) = ($1, $2);
+
+                Dancer::Error->new(code => 416,
+                                   message => 'Requested Range Not Satisfiable')->render
+                    if $size_in_bytes < $end - 1;
+
+                $resp->header('Content-Range' => sprintf('%d-%d/%d',
+                                                         $start,
+                                                         $end,
+                                                         $end - $start + 1));
+
+                $resp->header('X-Sendfile2' => sprintf('%s %d-%d',
+                                                       URI::Escape::uri_escape($path), $start, $end));
+
+            } else {
+
+                # weird range?
+                Dancer::Error->new(code => 400,
+                                   message => 'Range header could not be parsed')->render;
+
+            }
+
+        } else {
+
+            # no range requested, send the regular header
+            $resp->header('X-Sendfile' => URI::Escape::uri_escape($path));
+
+        }
+
+        return $resp;
+
+    }
+
+    Dancer::Error->new(
+        code    => 404,
+        message => "No such file: `$path'"
+    )->render();
+
+};
+
+register_plugin;
+1;