So there is a lot here and there's definitely easier ways to do it but just
to start, Mojo::UserAgent can handle the streaming response in a more async
manner so you may want to start by replacing the shell out to curl with
that. Then see
https://metacpan.org/pod/Mojolicious::Guides::Cookbook#Streaming-response for
a way to deal with the response as it is received, and
https://metacpan.org/pod/Mojolicious::Guides::Rendering#Streaming for how
to write it to the response.

-Dan

On Mon, May 4, 2020 at 3:05 AM Joseph Fridy <jmfr...@gmail.com> wrote:

> I am attempting to stream the output of a curl command with Mojolicious.
> The curl command looks (roughly) like this:
>
> curl -H "x-amz-server-side-encryption-customer-algorithm:AES256" -H
> "x-amz-server-side-encryption-cust\
>
> omer-key:secretKey=" -H "x-amz-server-side-encryption-customer-key-M\
>
> D5:HashOfASecret==" 
> "https://mybucket.s3.amazonaws.com/obscureFileLocation?AWSAccessKeyId=secretStuff&Expires=1588568911&Signature=moreSecrets";
> --\
>
> dump-header header.461 --silent
>
>
> The curl command references a pre-signed URL of a file in AWS stored with
> Server Side Encryption with Client Side Keys (SSE-C), and supplies the
> necessary key information via HTTP headers (the -H command options).  The
> curl command works - but I don't want my users to have to have a system
> with curl on it to access their files.  The plan is to open the curl
> command as input to a pipe, and stream its output to the user's browser
> with Mojolicious.  The curl command also dumps out the HTTP headers from
> Amazon, so they can be used by Mojolicious.  They look like this:
>
>
> x-amz-id-2:
> sgMzHD2FJEGJrcbvzQwdhZK6mxUW+ePd6xdghTfgSlV45lMhliIw4prfk4cZMTHbS4fJN8N7xio=
>
> x-amz-request-id: 99B9CA56083DD9ED
>
> Date: Mon, 04 May 2020 04:57:22 GMT
>
> Last-Modified: Sat, 02 May 2020 03:47:35 GMT
>
> ETag: "b3a11409be2705e4581119fa59af79d3-1025"
>
> x-amz-server-side-encryption-customer-algorithm: AES256
>
> x-amz-server-side-encryption-customer-key-MD5: HashOfSecretKey==
>
> Content-Disposition: attachment; filename = "fiveGigFile"
>
> Accept-Ranges: bytes
>
> Content-Type: application/octet-stream; charset=UTF-8
>
> Content-Length: 5368709125
>
> Server: AmazonS3
>
>
> Note that the file is 5Gig.
>
>
>
> This is my stab at streaming with Mojolicious:
>
>
> use strict;
> use Mojolicious::Lite;
> use FileHandle;
> use Digest::MD5;
>
> any '/' => sub {
>   my $c = shift;
>   $c->render(template => "test");
> };
>
> any 'pickup' => sub {
>   my $c = shift;
>   my $nGigs = 0;
>   my $nMegs = 0;
>   $| = 1;
>   open(CURLCMD,"curlCmd");
>   my $curlCmd = <CURLCMD>;
>   if ($curlCmd =~ /dump-header\s*(\S+)\s+/) {
>
>     my $headerFile = $1;
>     open(my $curl,"$curlCmd |");
>     binmode $curl;
>     my $initialized = 0;
>     my $digester = Digest::MD5->new;
>
>   my $transferLength = 0;
>
>   my $drain;
>
>   $drain = sub {
>
>       my $c = shift;
>
>       my $chunk;
>
>       sysread($curl,$chunk,1024*1024);
>
>       if (!$initialized) {
>       # read the headers, and set up the transfer...
>
>        open(HEADERS,$headerFile);
>
>        while(my $line = <HEADERS>) {
>
>          $c->res->headers->parse($line);
>
>        }
>        close(HEADERS);
>
>        $initialized = 1;
>
>        print "header initialization completed for the following
> headers\n";
>
>        print join("\n",@{$c->res->headers->names}),"\n";
>
>       }
>
>       if ($initialized) {
>
>          while (length($chunk)) {
>
>            $digester->add($chunk);
>
>            $transferLength += length($chunk);
>
>            $c->write($chunk,$drain);
>
>            my $currentMegs = int($transferLength/(1024*1024));
>
>            if (($currentMegs > $nMegs) && ($currentMegs < 1024)) {
>
>              print "TransferLength: $transferLength\n";
>
>              $nMegs = $currentMegs;
>
>            }
>
>            my $currentGigs = int($transferLength/(1024*1024*1024));
>
>            if ($currentGigs > $nGigs) {
>
>              print "TransferLength: $transferLength\n";
>
>              $nGigs = $currentGigs;
>
>            }
>
>          }
>
>          if (length($chunk) <= 1) {
>
>            if ($chunk == 0) {
>
>             print "End of file found on curl pipe.";
>
>             print "$transferLength bytes transmitted\n";
>
>             print "with an MD5 hash of ",$digester->hexdigest,"\n";
>
>             $drain = undef;
>
>            }
>
>            if (!defined $chunk) {
>
>             print "Transfer error encountered on curl pipe.\n";
>
>             print "Error:",$!,"\n";
>
>             $drain = undef;
>
>           }
>
>         }
>
>       }
>
>     };
>
>     $c->$drain;
>
>   }
>
> };
>
> app->start;
>
> __DATA__
>
>
> @@ test.html.ep
>
> <!DOCTYPE html>
>
> <html>
>
> <body>
>
> <a href="/pickup" >Test of curl streaming... </a>
>
> </body>
>
> </html>
>
>
>
> When I ran this the first time, it read about 606MB of data, and the
> server crashed with an "Out of memory!".  Subsequent runs failed at about
> 139MB, with a server crash and no "Out of memory!" message.
>
>
> Obviously, I am an idiot.  Some guidance in the precise way I am being an
> idiot would be greatly appreciated.
>
>
> Regards,
>
>
> Joe Fridy
>
> --
> You received this message because you are subscribed to the Google Groups
> "Mojolicious" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to mojolicious+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/mojolicious/86c878ad-8bd2-4a40-94fd-cc16fbe39201%40googlegroups.com
> <https://groups.google.com/d/msgid/mojolicious/86c878ad-8bd2-4a40-94fd-cc16fbe39201%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
>

-- 
You received this message because you are subscribed to the Google Groups 
"Mojolicious" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to mojolicious+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/mojolicious/CABMkAVWxcEGpd1ZHDNUWZJoRddvb7iE18RAZ%2BTCx7uqkhMj4KQ%40mail.gmail.com.

Reply via email to