Async-Microservice

 view release on metacpan or  search on metacpan

lib/Async/MicroserviceReq.pm  view on Meta::CPAN

    isa      => 'Ref',
    required => 0,
    lazy     => 1,
    builder  => '_build_json_content'
);
has 'params' => ( is => 'ro', isa => 'Object', required => 1 );
has 'plack_respond' => (
    is       => 'rw',
    isa      => 'CodeRef',
    required => 0,
    clearer  => 'clear_plack_respond'
);
has 'static_dir' =>
    ( is => 'ro', isa => 'Path::Class::Dir', required => 1, coerce => 1 );

has 'base_url' => (
    is       => 'ro',
    isa      => 'URI',
    required => 1,
    lazy     => 1,
    builder  => '_build_base_url'
);
has 'want_json' => (
    is       => 'ro',
    isa      => 'Bool',
    required => 1,
    lazy     => 1,
    builder  => '_build_want_json'
);
has 'jsonp' => (
    is       => 'ro',
    isa      => 'Str',
    required => 1,
);
has 'using_frontend_proxy' => (
    is      => 'ro',
    isa     => 'Bool',
    default => 0,
);
has 'pending_ref' => (
    is       => 'ro',
    isa      => 'ScalarRef[Int]',
    required => 1,
);
has 'request_start' => (
    is       => 'ro',
    isa      => 'Num',
    required => 1,
    default  => sub { time() },
);
has 'request_timeout' => ( is => 'ro', isa => 'Num', required => 1 );
has '_warn_running_too_long' => (
    is      => 'ro',
    isa     => 'ArrayRef',
    lazy    => 1,
    builder => '_build_warn_running_too_long'
);

after 'BUILD' => sub {
    my ($self) = @_;
    $self->_warn_running_too_long;    # init timer
    return;
};

sub _build_base_url {
    my ($self) = @_;
    return URI->new('/') if !$self->using_frontend_proxy;

    my $https_on = '';
    $https_on = $self->headers->header('HTTP_X_FORWARDED_HTTPS')
        if $self->headers->header('HTTP_X_FORWARDED_HTTPS');
    $https_on = 'ON'
        if $self->headers->header('HTTP_X_FORWARDED_PROTO')
        && $self->headers->header('HTTP_X_FORWARDED_PROTO') eq
        'https';    # Pound
    my $url_scheme = ( $https_on && uc $https_on eq 'ON' ? 'https' : 'http' );
    my $default_port = $url_scheme eq 'https' ? 443 : 80;

    my $redirect_host;
    my $redirect_port = $default_port;
    if ( $self->headers->header('HTTP_X_FORWARDED_HOST') ) {

        # in apache1 ServerName example.com:443
        if ( $self->headers->header('HTTP_X_FORWARDED_SERVER') ) {
            my ( $host, ) =
                $self->headers->header('HTTP_X_FORWARDED_SERVER') =~
                /([^,\s]+)$/;
            if ( $host =~ /^(.+):(\d+)$/ ) {
                $redirect_port = $2;
                $host          = $1;
            }
            $redirect_host = $host;
        }
        my ( $host, ) =
            $self->headers->header('HTTP_X_FORWARDED_HOST') =~ /([^,\s]+)$/;
        if ( $host =~ /^(.+):(\d+)$/ ) {
            $redirect_port = $2;
            $host          = $1;
        }
        elsif ( $self->headers->header('HTTP_X_FORWARDED_PORT') ) {

            # in apache2 httpd.conf (RequestHeader set X-Forwarded-Port 8443)
            $redirect_port = $self->headers->header('HTTP_X_FORWARDED_PORT');
        }
        $redirect_host = $host;
    }

    unless ($redirect_host) {
        $log->warn(
            'using front-end proxy but no host information in headers, check if your proxy is configured to send correct headers'
        );
        return URI->new('/');
    }

    my $redirect_host_port;
    if (   ( ( $redirect_port eq '80' ) && ( $url_scheme eq 'http' ) )
        || ( ( $redirect_port eq '443' ) && ( $url_scheme eq 'https' ) ) ) {
        $redirect_host_port = $redirect_host;
    }
    else {
        $redirect_host_port = $redirect_host . ':' . $redirect_port;
    }

    return URI->new( $url_scheme . '://' . $redirect_host_port . '/' );
}

sub _build_warn_running_too_long {
    my ($o_self) = @_;

    weaken( my $self = $o_self );
    return AnyEvent->timer(
        'after'    => $self->request_timeout,
        'interval' => $self->request_timeout,
        'cb'       => sub {
            $log->errorf(
                'request %s %s running too long for %d seconds',
                $self->method, $self->path, ( time - $self->request_start ),
            );
        },
    );
}

sub _build_want_json {
    my ($self) = @_;

    my $accept = $self->headers->header('Accept');
    return 0
        unless defined($accept) && length($accept);

    my $chosen =
        choose( [ [ 'json', 1.0, 'application/json' ] ], $self->headers, );
    return defined($chosen) ? 1 : 0;
}

sub _build_json_content {
    my ($self) = @_;
    return $json->decode( $self->content );
}

sub BUILD {
    ${ $_[0]->pending_ref }++;
    return;
}

sub DEMOLISH {
    ${ $_[0]->pending_ref }--;
    return;
}

sub text_plain {
    my ( $self, @text ) = @_;
    return $self->respond( 200, [], join( "\n", ( @text, q{} ) ) );
}

sub _should_wrap_payload_as_json {
    my ( $self, $headers_as_hash, $payload ) = @_;
    return (   $self->want_json
            && !ref($payload)
            && !$headers_as_hash->{'content-type'} ) ? 1 : 0;
}

sub _wrap_payload {
    my ( $self, $state ) = @_;

    return $state->{payload}
        unless $self->_should_wrap_payload_as_json( $state->{headers_as_hash},
        $state->{payload} );

    if ( $state->{status} < 400 ) {
        return { 'data' => $state->{payload} };
    }



( run in 0.819 second using v1.01-cache-2.11-cpan-2ed5026b665 )