use Fcntl qw(FD_CLOEXEC F_SETFD F_GETFD SEEK_SET);
use Time::HiRes qw(clock_gettime CLOCK_MONOTONIC);
use parent qw(Exporter);
-our @EXPORT_OK = qw(now msg_more write_in_full);
+our @EXPORT_OK = qw(now msg_more);
use warnings;
use 5.010_001;
sub psendfile ($$$) {
my ($sock, $fh, $off) = @_;
- sysseek($fh, $$off, SEEK_SET) or return;
- defined(my $to_write = sysread($fh, my $buf, 16384)) or return;
+ seek($fh, $$off, SEEK_SET) or return;
+ defined(my $to_write = read($fh, my $buf, 16384)) or return;
my $written = 0;
while ($to_write > 0) {
if (defined(my $w = syswrite($sock, $buf, $to_write, $written))) {
$! == EAGAIN ? $self->watch_in1 : $self->close;
}
-sub write_in_full ($$$$) {
- my ($fh, $bref, $len, $off) = @_;
- my $rv = 0;
- while ($len > 0) {
- my $w = syswrite($fh, $$bref, $len, $off);
- return ($rv ? $rv : $w) unless $w; # undef or 0
- $rv += $w;
- $len -= $w;
- $off += $w;
- }
- $rv
-}
-
+# n.b.: use ->write/->read for this buffer to allow compatibility with
+# PerlIO::mmap or PerlIO::scalar if needed
sub tmpbuf ($$) {
my ($bref, $off) = @_;
# open(my $fh, '+>>', undef) doesn't set O_APPEND
my ($fh, $path) = tempfile('wbuf-XXXXXXX', TMPDIR => 1);
open $fh, '+>>', $path or die "open: $!";
+ $fh->autoflush(1);
unlink $path;
my $to_write = bytes::length($$bref) - $off;
- my $w = write_in_full($fh, $bref, $to_write, $off);
- die "write_in_full ($to_write): $!" unless defined $w;
- $w == $to_write ? $fh : die("short write $w < $to_write");
+ $fh->write($$bref, $to_write, $off) or die "write ($to_write): $!";
+ $fh;
}
=head2 C<< $obj->write( $data ) >>
} else {
my $last = $wbuf->[-1];
if (ref($last) eq 'GLOB') { # append to tmp file buffer
- write_in_full($last, $bref, bytes::length($$bref), 0);
+ unless ($last->print($$bref)) {
+ warn "error buffering: $!";
+ return $self->close;
+ }
} else {
push @$wbuf, tmpbuf($bref, 0);
}
return;
}
last if $r == 0;
- my $off = 0;
- while ($r > 0) {
- my $w = syswrite($in, $buf, $r, $off);
- if (defined $w) {
- $r -= $w;
- $off += $w;
- } else {
- err($env, "error writing temporary file: $!");
- return;
- }
+ unless (print $in $buf) {
+ err($env, "error writing temporary file: $!");
+ return;
}
}
+ # ensure it's visible to git-http-backend(1):
+ unless ($in->flush) {
+ err($env, "error writing temporary file: $!");
+ return;
+ }
unless (defined(sysseek($in, 0, SEEK_SET))) {
err($env, "error seeking temporary file: $!");
return;
use HTTP::Date qw(time2str);
use IO::Handle;
require PublicInbox::EvCleanup;
-PublicInbox::DS->import(qw(msg_more write_in_full));
+PublicInbox::DS->import(qw(msg_more));
use PublicInbox::Syscall qw(EPOLLIN EPOLLONESHOT);
use constant {
CHUNK_START => -1, # [a-f0-9]+\r\n
$len ? read_input($self) : app_dispatch($self);
}
+# IO::Handle::write returns boolean, this returns bytes written:
+sub xwrite ($$$) {
+ my ($fh, $rbuf, $max) = @_;
+ my $w = bytes::length($$rbuf);
+ $w = $max if $w > $max;
+ $fh->write($$rbuf, $w) or return;
+ $w;
+}
+
sub read_input ($) {
my ($self) = @_;
my $env = $self->{env};
while ($len > 0) {
if ($$rbuf ne '') {
- my $w = write_in_full($input, $rbuf, $len, 0);
+ my $w = xwrite($input, $rbuf, $len);
return write_err($self, $len) unless $w;
$len -= $w;
die "BUG: $len < 0 (w=$w)" if $len < 0;
}
}
+sub input_tmpfile ($) {
+ open($_[0], '+>', undef);
+ $_[0]->autoflush(1);
+}
+
sub input_prepare {
my ($self, $env) = @_;
my $input;
quit($self, 413);
return;
}
- open($input, '+>', undef);
+ input_tmpfile($input);
} elsif (env_chunked($env)) {
$len = CHUNK_START;
- open($input, '+>', undef);
+ input_tmpfile($input);
} else {
$input = $null_io;
}
# drain the current chunk
until ($len <= 0) {
if ($$rbuf ne '') {
- my $w = write_in_full($input, $rbuf, $len, 0);
+ my $w = xwrite($input, $rbuf, $len);
return write_err($self, "$len chunk") if !$w;
$len -= $w;
if ($len == 0) {