]> git.proxmox.com Git - pve-http-server.git/commitdiff
allow stream download from path and over pvedaemon-proxy
authorStefan Reiter <s.reiter@proxmox.com>
Thu, 22 Apr 2021 15:34:53 +0000 (17:34 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Thu, 22 Apr 2021 16:18:50 +0000 (18:18 +0200)
Allow specifying a filepath for stream=1 instead of either a path or fh
with stream=1.

With this in place, we can also just return the path to the proxy in
case we want to stream a response back, and let it read from the file
itself. This way, the pvedaemon is cut out of the transfer pipe.

Signed-off-by: Stefan Reiter <s.reiter@proxmox.com>
PVE/APIServer/AnyEvent.pm

index f3db0e37d1ab8cf82e28d3229ada23e87a5e6edc..0654bd4901fd45e0cbdf64e5a9a70d62521d0e84 100644 (file)
@@ -410,9 +410,33 @@ sub send_file_start {
            my $mime;
 
            if (ref($download) eq 'HASH') {
-               $fh = $download->{fh};
                $mime = $download->{'content-type'};
 
+               if ($download->{path} && $download->{stream} &&
+                   $reqstate->{request}->header('PVEDisableProxy'))
+               {
+                   # avoid double stream from a file, let the proxy handle it
+                   die "internal error: file proxy streaming only available for pvedaemon\n"
+                       if !$self->{trusted_env};
+                   my $header = HTTP::Headers->new(
+                       pvestreamfile => $download->{path},
+                       Content_Type => $mime,
+                   );
+                   # we need some data so Content-Length gets set correctly and
+                   # the proxy doesn't wait for more data - place a canary
+                   my $resp = HTTP::Response->new(200, "OK", $header, "error canary");
+                   $self->response($reqstate, $resp);
+                   return;
+               }
+
+               if (!($fh = $download->{fh})) {
+                   my $path = $download->{path};
+                   die "internal error: {download} returned but neither fh not path given\n"
+                       if !$path;
+                   sysopen($fh, "$path", O_NONBLOCK | O_RDONLY)
+                       or die "open stream path '$path' for reading failed: $!\n";
+               }
+
                if ($download->{stream}) {
                    my $header = HTTP::Headers->new(Content_Type => $mime);
                    my $resp = HTTP::Response->new(200, "OK", $header);
@@ -750,6 +774,7 @@ sub proxy_request {
                eval {
                    my $code = delete $hdr->{Status};
                    my $msg = delete $hdr->{Reason};
+                   my $stream = delete $hdr->{pvestreamfile};
                    delete $hdr->{URL};
                    delete $hdr->{HTTPVersion};
                    my $header = HTTP::Headers->new(%$hdr);
@@ -757,9 +782,16 @@ sub proxy_request {
                        $location =~ s|^http://localhost:85||;
                        $header->header(Location => $location);
                    }
-                   my $resp = HTTP::Response->new($code, $msg, $header, $body);
-                   # Note: disable compression, because body is already compressed
-                   $self->response($reqstate, $resp, undef, 1);
+                   if ($stream) {
+                       sysopen(my $fh, "$stream", O_NONBLOCK | O_RDONLY)
+                           or die "open stream path '$stream' for forwarding failed: $!\n";
+                       my $resp = HTTP::Response->new($code, $msg, $header, undef);
+                       $self->response($reqstate, $resp, undef, 1, 0, $fh);
+                   } else {
+                       my $resp = HTTP::Response->new($code, $msg, $header, $body);
+                       # Note: disable compression, because body is already compressed
+                       $self->response($reqstate, $resp, undef, 1);
+                   }
                };
                warn $@ if $@;
            });