]> Sergey Matveev's repositories - public-inbox.git/blobdiff - lib/PublicInbox/HTTP.pm
http: get rid of anonymous subs for write/close
[public-inbox.git] / lib / PublicInbox / HTTP.pm
index 19b57d59518d87ec79e0b564bf63a81ed5935c71..ad1a2f9f5e2e850e5f9e0eec0f62ebecf0b2dca3 100644 (file)
@@ -18,9 +18,9 @@ use Plack::HTTPParser qw(parse_http_request); # XS or pure Perl
 use HTTP::Status qw(status_message);
 use HTTP::Date qw(time2str);
 use IO::Handle;
-require PublicInbox::EvCleanup;
 use PublicInbox::DS qw(msg_more);
 use PublicInbox::Syscall qw(EPOLLIN EPOLLONESHOT);
+use PublicInbox::Tmpfile;
 use constant {
        CHUNK_START => -1,   # [a-f0-9]+\r\n
        CHUNK_END => -2,     # \r\n
@@ -222,22 +222,20 @@ sub response_header_write {
 }
 
 # middlewares such as Deflater may write empty strings
-sub chunked_wcb ($) {
-       my ($self) = @_;
-       sub {
-               return if $_[0] eq '';
-               msg_more($self, sprintf("%x\r\n", bytes::length($_[0])));
-               msg_more($self, $_[0]);
-
-               # use $self->write(\"\n\n") if you care about real-time
-               # streaming responses, public-inbox WWW does not.
-               msg_more($self, "\r\n");
-       }
+sub chunked_write ($$) {
+       my $self = $_[0];
+       return if $_[1] eq '';
+       msg_more($self, sprintf("%x\r\n", bytes::length($_[1])));
+       msg_more($self, $_[1]);
+
+       # use $self->write(\"\n\n") if you care about real-time
+       # streaming responses, public-inbox WWW does not.
+       msg_more($self, "\r\n");
 }
 
-sub identity_wcb ($) {
-       my ($self) = @_;
-       sub { $self->write(\($_[0])) if $_[0] ne '' }
+sub identity_write ($$) {
+       my $self = $_[0];
+       $self->write(\($_[1])) if $_[1] ne '';
 }
 
 sub next_request ($) {
@@ -251,17 +249,16 @@ sub next_request ($) {
        }
 }
 
-sub response_done_cb ($$) {
+sub response_done {
        my ($self, $alive) = @_;
-       sub {
-               my $env = delete $self->{env};
-               $self->write(\"0\r\n\r\n") if $alive == 2;
-               $self->write($alive ? \&next_request : \&close);
-       }
+       delete $self->{env}; # we're no longer busy
+       $self->write(\"0\r\n\r\n") if $alive == 2;
+       $self->write($alive ? \&next_request : \&close);
 }
 
-sub getline_response ($$$) {
-       my ($self, $write, $close) = @_;
+sub getline_response ($$) {
+       my ($self, $alive) = @_;
+       my $write = $alive == 2 ? \&chunked_write : \&identity_write;
        my $pull; # DANGER: self-referential
        $pull = sub {
                my $forward = $self->{forward};
@@ -273,13 +270,14 @@ sub getline_response ($$$) {
                } if $forward;
 
                if (defined $buf) {
-                       $write->($buf); # may close in PublicInbox::DS::write
+                       # may close in PublicInbox::DS::write
+                       $write->($self, $buf);
 
                        if ($self->{sock}) {
                                my $wbuf = $self->{wbuf} ||= [];
                                push @$wbuf, $pull;
 
-                               # wbuf may be populated by $write->($buf),
+                               # wbuf may be populated by $write->(...$buf),
                                # no need to rearm if so:
                                $self->requeue if scalar(@$wbuf) == 1;
                                return; # likely
@@ -299,7 +297,7 @@ sub getline_response ($$$) {
                        }
                }
                $forward = undef;
-               $close->(); # call response_done_cb
+               response_done($self, $alive);
        };
 
        $pull->(); # kick-off!
@@ -308,25 +306,30 @@ sub getline_response ($$$) {
 sub response_write {
        my ($self, $env, $res) = @_;
        my $alive = response_header_write($self, $env, $res);
-       my $close = response_done_cb($self, $alive);
-       my $write = $alive == 2 ? chunked_wcb($self) : identity_wcb($self);
        if (defined(my $body = $res->[2])) {
                if (ref $body eq 'ARRAY') {
-                       $write->($_) foreach @$body;
-                       $close->();
+                       if ($alive == 2) {
+                               chunked_write($self, $_) for @$body;
+                       } else {
+                               identity_write($self, $_) for @$body;
+                       }
+                       response_done($self, $alive);
                } else {
                        $self->{forward} = $body;
-                       getline_response($self, $write, $close);
+                       getline_response($self, $alive);
                }
+       # these are returned to the calling application:
+       } elsif ($alive == 2) {
+               bless [ $self, $alive ], 'PublicInbox::HTTP::Chunked';
        } else {
-               # this is returned to the calling application:
-               Plack::Util::inline_object(write => $write, close => $close);
+               bless [ $self, $alive ], 'PublicInbox::HTTP::Identity';
        }
 }
 
 sub input_tmpfile ($) {
-       open($_[0], '+>', undef);
-       $_[0]->autoflush(1);
+       my $input = tmpfile('http.input', $_[0]->{sock}) or return;
+       $input->autoflush(1);
+       $input;
 }
 
 sub input_prepare {
@@ -338,10 +341,10 @@ sub input_prepare {
                        quit($self, 413);
                        return;
                }
-               input_tmpfile($input);
+               $input = input_tmpfile($self);
        } elsif (env_chunked($env)) {
                $len = CHUNK_START;
-               input_tmpfile($input);
+               $input = input_tmpfile($self);
        } else {
                $input = $null_io;
        }
@@ -451,9 +454,7 @@ sub quit {
 
 sub close {
        my $self = $_[0];
-       if (my $env = delete $self->{env}) {
-               delete $env->{'psgix.io'}; # prevent circular references
-       }
+       delete $self->{env}; # prevent circular references
        if (my $forward = delete $self->{forward}) {
                eval { $forward->close };
                err($self, "forward ->close error: $@") if $@;
@@ -467,4 +468,29 @@ sub busy () {
        ($self->{rbuf} || $self->{env} || $self->{wbuf});
 }
 
+# Chunked and Identity packages are used for writing responses.
+# They may be exposed to the PSGI application when the PSGI app
+# returns a CODE ref for "push"-based responses
+package PublicInbox::HTTP::Chunked;
+use strict;
+
+sub write {
+       # ([$http], $buf) = @_;
+       PublicInbox::HTTP::chunked_write($_[0]->[0], $_[1])
+}
+
+sub close {
+       # $_[0] = [$http, $alive]
+       PublicInbox::HTTP::response_done(@{$_[0]});
+}
+
+package PublicInbox::HTTP::Identity;
+use strict;
+our @ISA = qw(PublicInbox::HTTP::Chunked);
+
+sub write {
+       # ([$http], $buf) = @_;
+       PublicInbox::HTTP::identity_write($_[0]->[0], $_[1]);
+}
+
 1;