File: PUT.pl

package info (click to toggle)
libnet-async-http-perl 0.50-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 436 kB
  • sloc: perl: 5,029; sh: 2; makefile: 2
file content (125 lines) | stat: -rwxr-xr-x 3,040 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/perl

use v5.14;
use warnings;

use URI;

use IO::Async::Loop;
use Net::Async::HTTP;

use POSIX qw( floor );
use Time::HiRes qw( time );
use Getopt::Long;

sub usage
{
   my ( $exitcode ) = @_;

   print STDERR <<"EOF";
Net::Async::HTTP PUT client example.

Usage:

 $0 [-u user:pass] https://example.com/file-to-put.bin /tmp/file-to-read.bin

If -u options are given, these will be sent as Basic auth credentials.
Different ports can be specified in the URL, e.g.

  http://example.com:12314/file.txt

EOF
}

# Basic commandline parameter support - -u user:password
my $userpass;
my $url;
my $src;
my $contenttype = "application/octet-stream";

GetOptions(
   'userpass|u=s' => \$userpass,
   'src=s'        => \$src,
   'type|t=s'     => \$contenttype,

   'help|h' => sub { usage(0) },
) or usage(1);

my $loop = IO::Async::Loop->new;

$url = shift @ARGV or usage(1);
$src = shift @ARGV or usage(1) if !defined $src;

my $ua = Net::Async::HTTP->new;
$loop->add( $ua );

# We'll send the size as the Content-Length, and get the filehandle ready for reading
my $size = (stat $src)[7];
open my $fh, '<', $src or die "Failed to open source file $src - $!\n";
binmode $fh;

# Prepare our request object
my $uri = URI->new($url) or die "Invalid URL?\n";
my $req = HTTP::Request->new(
   PUT => $uri->path, [
      'Host'         => $uri->host,
      'Content-Type' => $contenttype,
   ]
);

# Default is no protocol, we insist on HTTP/1.1 here, PUT probably requires that as a minimum anyway
$req->protocol( 'HTTP/1.1' );
$req->authorization_basic( split m/:/, $userpass, 2 ) if defined $userpass;
$req->content_length( $size );

# For stats
my $total = 0;
my $last = -1;
my $start;

$ua->do_request(
   request    => $req,
   host       => $uri->host,
   port       => $uri->port,
   SSL        => $uri->scheme eq 'https' ? 1 : 0,

   # We override the default behaviour (pulling content from HTTP::Request) by passing a callback explicitly
   # Originall had "content_callback", not really sure what the best thing to call this would be though.
   request_body => sub {
      my ($stream) = @_;
      unless (defined $start) {
         $start = time;
         $| = 1;
      }

      # This part is the important one - read some data, and eventually return it
      my $read = sysread $fh, my $buffer, 1048576;

      # Just for stats display, update every mbyte
      $total += $read;
      my $step = floor($total / 1048576);
      if($step > $last) {
         $last = $step;
         my $elapsed = (time - $start) || 1;
         printf("Total: %14d of %14d bytes, %5.2f%% complete, %9.3fkbyte/s   \r", $total, $size, (100 * $total) / $size, ($total) / ($elapsed * 1024));
      }

      return $buffer if $read;

      # Return undef when we're done
      print "\n\nComplete.\n";
      return;
   },
   on_response => sub {
      my ( $response ) = @_;

      close $fh or die $!;
      print $response->as_string;
   },

   on_error => sub {
      my ( $message ) = @_;

      print STDERR "Failed - $message\n";
   }
)->get;