File Coverage

blib/lib/IO/AsyncX/Sendfile.pm
Criterion Covered Total %
statement 41 45 91.1
branch 12 22 54.5
condition n/a
subroutine 9 9 100.0
pod n/a
total 62 76 81.5


line stmt bran cond sub pod time code
1             package IO::AsyncX::Sendfile;
2             # ABSTRACT: sendfile support for IO::Async
3 1     1   71885 use strict;
  1         2  
  1         30  
4 1     1   4 use warnings;
  1         1  
  1         41  
5              
6             our $VERSION = '0.001';
7              
8             =head1 NAME
9              
10             IO::AsyncX::Sendfile - adds support for L to L
11              
12             =head1 VERSION
13              
14             version 0.001
15              
16             =head1 SYNOPSIS
17              
18             $stream->sendfile(
19             file => 'somefile',
20             )->on_done(sub {
21             $stream->close;
22             });
23              
24             =head1 DESCRIPTION
25              
26             B: This is currently a proof-of-concept, the actual API may vary in later
27             versions. Eventually this functionality will be incorporated into the generic async
28             filehandling API, so this module is provided as a workaround in the interim.
29              
30             Provides a L method on L.
31              
32             =cut
33              
34 1     1   578 use Sys::Sendfile;
  1         427  
  1         46  
35 1     1   5 use Fcntl qw(SEEK_SET SEEK_END);
  1         1  
  1         37  
36 1     1   568 use Future;
  1         6123  
  1         26  
37 1     1   598 use IO::Async::Stream;
  1         24056  
  1         343  
38              
39             =head1 METHODS
40              
41             Note that these methods are injected directly into L.
42              
43             =cut
44              
45             =head2 sendfile
46              
47             Write the contents of the file directly to the socket without reading it
48             into memory first (using the kernel's sendfile call if available).
49              
50             Called with the following named parameters:
51              
52             =over 4
53              
54             =item * file - if defined, this will be used as the filename to open
55              
56             =item * fh - if defined, we'll use this as the filehandle
57              
58             =item * length - if defined, send this much data from the file (default is
59             'everything from current position to end')
60              
61             =back
62              
63             Returns a L which will be resolved with the number of bytes written
64             when successful.
65              
66             Example usage:
67              
68             my $listener = $loop->listen(
69             addr => {
70             family => 'unix',
71             socktype => 'stream',
72             path => 'sendfile.sock',
73             },
74             on_stream => sub {
75             my $stream = shift;
76             $stream->configure(
77             on_read => sub {
78             my ($self, $buffref, $eof) = @_;
79             $$buffref = '';
80             return 0;
81             },
82             );
83             if('send one file') {
84             $stream->sendfile(
85             file => 'test.dat',
86             )->on_done(sub {
87             warn "File send complete: @_\n";
88             $stream->close;
89             });
90             } else {
91             $stream->sendfile(file => 'first.dat');
92             $stream->sendfile(file => 'second.dat');
93             $stream->write('EOF', on_flush => sub { shift->close });
94             }
95             $loop->add($stream);
96             }
97             );
98              
99             =cut
100              
101             *IO::Async::Stream::sendfile = sub {
102 32     32   155401 my $self = shift;
103 32         86 my %args = @_;
104 32 50       110 die "Stream must be added to loop first" unless $self->loop;
105              
106 32 50       196 if(defined $args{file}) {
107 32 50       1300 open $args{fh}, '<', $args{file} or die "Could not open " . $args{file} . " for input - $!";
108 32         100 binmode $args{fh};
109             }
110 32 50       102 die 'No file?' unless my $fh = delete $args{fh};
111              
112             # Work out how much we need to write
113 32         62 my $total = my $remaining = $args{length};
114 32 50       67 unless(defined $total) {
115 32         54 my $pos = tell $fh;
116 32 50       99 seek $fh, 0, SEEK_END or die "Unable to seek - $!";
117 32         52 $total = $remaining = tell $fh;
118 32 50       93 seek $fh, $pos, SEEK_SET or die "Unable to seek - $!";
119             }
120 32         92 my $f = $self->loop->new_future;
121              
122             $self->write(sub {
123 480     480   1191958 my $stream = shift;
124 480 100       1285 return unless $remaining;
125              
126 448 50       797 unless($remaining > 0) {
127 0         0 $f->fail(EOF => "Attempt to write past EOF, remaining bytes: " . $remaining);
128 0         0 return;
129             }
130              
131 448 50       996 if(my $written = sendfile $stream->write_handle, $fh, $remaining) {
132 448         38861 $remaining -= $written;
133 448         1339 return ''; # empty string => call us again please
134             }
135              
136 0         0 $f->fail(sendfile => $!, $remaining);
137 0         0 return;
138             }, on_flush => sub {
139 32 50   32   282 $f->done($total) unless $f->is_ready;
140 32         873 });
141 32         3881 return $f;
142             };
143              
144              
145             1;
146              
147             __END__