Perlbal-Plugin-PSGI

 view release on metacpan or  search on metacpan

lib/Perlbal/Plugin/PSGI.pm  view on Meta::CPAN

    my $backend = Perlbal::Plugin::PSGI::Backend->new;
    $backend->assign_client($self);
}

package Perlbal::Plugin::PSGI::Backend;

use strict;
use warnings;

use Perlbal::ClientHTTPBase;
use Perlbal::Service;

use Plack::Util;
use Plack::HTTPParser qw(parse_http_request);
use HTTP::Status;

sub new {
    my $class = shift;
    my $self = bless {}, (ref $class || $class);
    $self->{input} = [];
    $self->{remaining} = 0;
    return $self;
}

sub close {
    # Do we need to do any cleanup?
}

sub forget_client {
    # Do we need to do any cleanup?
}

sub write {
    my $self = shift;
    my $bufref = shift;
    my $input = $self->{input};
    push @$input, $bufref;
    $self->{remaining} -= length($$bufref);
    return if $self->{remaining};
    $self->run_request;
}

sub assign_client {
    my $self = shift;
    my Perlbal::ClientHTTPBase $pb = shift;
    my Perlbal::Service $svc = $pb->{service};
    $self->{client} = $pb;
    $pb->backend($self);

    my $hdr = $pb->{req_headers} or return 0;
    my ($server_name, $server_port) = split /:/, ($pb->{selector_svc} ? $pb->{selector_svc}->{listen} : $svc->{listen});

    my $env = $self->{env} = {
        'psgi.version'      => [ 1, 0 ],
        'psgi.errors'       => Plack::Util::inline_object(print => sub { Perlbal::log('error', @_) }),
        'psgi.url_scheme'   => 'http',
        'psgi.nonblocking'  => Plack::Util::TRUE,
        'psgi.run_once'     => Plack::Util::FALSE,
        'psgi.multithread'  => Plack::Util::FALSE,
        'psgi.multiprocess' => Plack::Util::FALSE,
        'psgi.streaming'    => Plack::Util::TRUE,
        REMOTE_ADDR         => $pb->{peer_ip},
        SERVER_NAME         => $server_name,
        SERVER_PORT         => $server_port,
    };

    parse_http_request($pb->{headers_string}, $env);

    if ($env->{CONTENT_LENGTH}) {
        $self->{remaining} = $env->{CONTENT_LENGTH};
    } else {
        $self->run_request;
    }
}

sub run_request {
    my $self = shift;

    my Perlbal::ClientHTTPBase $pb = $self->{client};
    my Perlbal::Service $svc = $pb->{service};
    my $app = $svc->{extra_config}->{_psgi_app};
    my $env = $self->{env};
    my $buf_ref = \join('', map { $$_ } @{$self->{input}});
    open my $input, "<", $buf_ref;
    $env->{'psgi.input'} = $input;

    my $responder = sub {
        my $res = shift;

        my $hd = $pb->{res_headers} = Perlbal::HTTPHeaders->new_response($res->[0]);
        my %seen;
        while (my($k, $v) = splice @{$res->[1]}, 0, 2) {
            if ($seen{lc($k)}++) {
                my $newvalue = $hd->header($k) . "\015\012$k: $v";
                $hd->header($k, $newvalue);
            } else {
                $hd->header($k, $v);
            }
        }

        $pb->setup_keepalive($hd);

        $pb->state('xfer_resp');
        $pb->tcp_cork(1);  # cork writes to self
        $pb->write($hd->to_string_ref);

        if (!defined $res->[2]) {
            return Plack::Util::inline_object
                write => sub { $pb->write(@_) },
                close => sub { $pb->write(sub { $pb->http_response_sent}) };
        } elsif (Plack::Util::is_real_fh($res->[2])) {
            $pb->reproxy_fh($res->[2], -s $res->[2]);
        } else {
            Plack::Util::foreach($res->[2], sub { $pb->write(@_) });
            $pb->write(sub { $pb->http_response_sent });
        }
    };

    my $res = Plack::Util::run_app $app, $env;
    ref $res eq 'CODE' ? $res->($responder) : $responder->($res);
}



( run in 2.207 seconds using v1.01-cache-2.11-cpan-140bd7fdf52 )