Furl

 view release on metacpan or  search on metacpan

t/HTTPServer.pm  view on Meta::CPAN

}

sub add_trigger {
    my ($self, $name, $code) = @_;
    push @{$self->{triggers}->{$name}}, $code;
    return $self;
}

sub call_trigger {
    my ($self, $name, @args) = @_;
    for my $code (@{ $self->{triggers}->{$name} || +[] }) {
        $code->($self, @args);
    }
}

sub run {
    my ( $self, $app ) = @_;

    $app = $self->fill_content_length($app);

    local $SIG{PIPE} = "IGNORE";
    my $sock = IO::Socket::INET->new(
        Listen    => SOMAXCONN,
        Proto     => 'tcp',
        ReuseAddr => 1,
        LocalAddr => '127.0.0.1',
        LocalPort => $self->{port},
        Timeout   => 3,
    ) or die $!;
    $sock->autoflush(1);
    while ( my $csock = $sock->accept ) {
        $csock->setsockopt( IPPROTO_TCP, TCP_NODELAY, 1 )
          or die "setsockopt(TCP_NODELAY) failed:$!";
        eval {
            $self->handle_connection($csock => $app);
        };
        print STDERR "# $@" if $@;
    }
}

sub make_header {
    my ($self, $code, $headers) = @_;
    my $msg = $STATUS_CODE{$code} || $code;
    my $ret = "$self->{protocol} $code $msg\015\012";
    for (my $i=0; $i<@$headers; $i+=2) {
        $ret .= $headers->[$i] . ': ' . $headers->[$i+1] . "\015\012";
    }
    return $ret;
}

sub handle_connection {
    my ($self, $csock, $app) = @_;

    $self->call_trigger( "BEFORE_HANDLE_CONNECTION", $csock );
    HANDLE_LOOP: while (1) {
        $self->call_trigger( "BEFORE_HANDLE_REQUEST", $csock );
        my %env;
        my $buf = '';
      PARSE_HTTP_REQUEST: while (1) {
            my $nread = sysread( $csock, $buf, $self->{bufsize}, length($buf) );
            $buf =~ s!^(\015\012)*!! if defined($buf); # for keep-alive
            if ( !defined $nread ) {
                die "cannot read HTTP request header: $!";
            }
            if ( $nread == 0 ) {
                # unexpected EOF while reading HTTP request header
                last HANDLE_LOOP;
            }
            my $ret = parse_http_request( $buf, \%env );
            if ( $ret == -2 ) {    # incomplete.
                next;
            }
            elsif ( $ret == -1 ) {    # request is broken
                die "broken HTTP header";
            }
            else {
                $buf = substr( $buf, $ret );
                last PARSE_HTTP_REQUEST;
            }
        }
        $self->call_trigger( "BEFORE_CALL_APP", $csock, \%env );
        my $res = $app->( \%env );
        $self->call_trigger( "AFTER_CALL_APP", $csock, \%env );
        my $res_header =
          $self->make_header( $res->[0], $res->[1] ) . "\015\012";
        $self->write_all( $csock, $res_header );
        for my $body (@{$res->[2]}) {
            $self->write_all( $csock, $body );
        }
        $self->call_trigger( "AFTER_HANDLE_REQUEST", $csock );
        last HANDLE_LOOP unless $csock->opened;
    }
    $self->call_trigger( "AFTER_HANDLE_CONNECTION", $csock );
}

sub fill_content_length {
    my ($self, $app) = @_;

    sub {
        my $env = shift;
        my $res = $app->($env);
        my $h = t::HTTPServer::Headers->new( $res->[1] );
        if (
            !t::HTTPServer::Util::status_with_no_entity_body( $res->[0] )
            && !$h->exists('Content-Length')
            && !$h->exists('Transfer-Encoding')
            && defined(
                my $content_length = t::HTTPServer::Util::content_length( $res->[2] )
            )
        ) {
            push @{$res->[1]}, 'Content-Length' => $content_length;
        }
        return $res;
    }
}

sub write_all {
    my ( $self, $csock, $buf ) = @_;
    my $off = 0;
    while ( my $len = length($buf) - $off ) {
        my $nwrite = $csock->syswrite( $buf, $len, $off )



( run in 0.746 second using v1.01-cache-2.11-cpan-39bf76dae61 )