Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Rewrote the server to directly use AnyEvent::HTTPD::HTTPServer to

bypass Request abstraction.

Much faster now, 1800 -> 2600 QPS on my MBP ;)
  • Loading branch information...
commit 8bfddab5e32c6edaec9e997103bdb75c5cbf38d3 1 parent ba6263c
@miyagawa authored
Showing with 116 additions and 94 deletions.
  1. +116 −94 lib/Plack/Handler/AnyEvent/HTTPD.pm
View
210 lib/Plack/Handler/AnyEvent/HTTPD.pm
@@ -4,13 +4,10 @@ use strict;
use 5.008_001;
our $VERSION = '0.01';
-use AnyEvent::HTTPD;
use Plack::Util;
use HTTP::Status;
use URI::Escape;
-my %_sockets;
-
sub new {
my($class, %args) = @_;
bless {%args}, $class;
@@ -19,111 +16,140 @@ sub new {
sub register_service {
my($self, $app) = @_;
- my $httpd = AnyEvent::HTTPD->new(
+ my $httpd = Plack::Handler::AnyEvent::HTTPD::Server->new(
port => $self->{port} || 9000,
host => $self->{host},
- connection_class => 'Plack::Handler::AnyEvent::HTTPD::Connection',
request_timeout => $self->{request_timeout},
+ app => $app,
);
- $httpd->reg_cb(client_disconnected => sub {
- my($httpd, $host, $port) = @_;
- delete $_sockets{join(":", $host, $port)};
- });
-
- $httpd->reg_cb(
- '' => sub {
- my($httpd, $req) = @_;
-
- my $env = {
- REMOTE_ADDR => $req->client_host,
- SERVER_PORT => $httpd->port,
- SERVER_NAME => $httpd->host,
- SCRIPT_NAME => '',
- REQUEST_METHOD => $req->method,
- PATH_INFO => URI::Escape::uri_unescape($req->{url}->path),
- REQUEST_URI => $req->{url}->as_string,
- QUERY_STRING => $req->{url}->query,
- SERVER_PROTOCOL => 'HTTP/1.0', # no way to get this from HTTPConnection
- 'psgi.version' => [ 1, 1 ],
- 'psgi.errors' => *STDERR,
- 'psgi.url_scheme' => 'http',
- 'psgi.nonblocking' => Plack::Util::TRUE,
- 'psgi.streaming' => Plack::Util::TRUE,
- 'psgi.run_once' => Plack::Util::FALSE,
- 'psgi.multithread' => Plack::Util::FALSE,
- 'psgi.multiprocess' => Plack::Util::FALSE,
- 'psgi.input' => do {
- open my $input, "<", \(defined $req->content ? $req->content : '');
- $input;
+ $self->{_httpd} = $httpd;
+}
+
+sub run {
+ my $self = shift;
+ $self->register_service(@_);
+
+ $self->{_httpd}->run;
+}
+
+package Plack::Handler::AnyEvent::HTTPD::Server;
+use parent qw(AnyEvent::HTTPD::HTTPServer);
+
+sub new {
+ my $class = shift;
+ my $self = $class->SUPER::new(
+ connection_class => 'Plack::Handler::AnyEvent::HTTPD::Connection',
+ @_,
+ );
+ $self->reg_cb(
+ connect => sub {
+ my($self, $con) = @_;
+ Scalar::Util::weaken($self);
+ $self->{conns}->{$con} = $con->reg_cb(
+ request => sub {
+ my($con, $meth, $url, $hdr, $cont) = @_;
+ $self->handle_psgi_request($con, $meth, $url, $hdr, $cont);
},
- 'psgix.io' => delete $_sockets{join(":", $req->client_host, $req->client_port)},
- };
+ );
+ },
+ disconnect => sub {
+ my($self, $con) = @_;
+ $con->unreg_cb(delete $self->{conns}->{$con});
+ },
+ );
- my $hdr = $req->headers;
- $env->{CONTENT_TYPE} = delete $hdr->{'content-type'};
- $env->{CONTENT_LENGTH} = delete $hdr->{'content-length'};
+ $self->{state} ||= {};
- while (my($key, $val) = each %$hdr) {
- $key =~ tr/-/_/;
- $env->{"HTTP_" . uc $key} = $val;
+ $self;
+}
+
+sub handle_psgi_request {
+ my($self, $con, $meth, $url, $hdr, $cont) = @_;
+
+ my($path_info, $query) = split /\?/, $url, 2;
+
+ my $env = {
+ REMOTE_ADDR => $con->{host},
+ SERVER_PORT => $self->port,
+ SERVER_NAME => $self->host,
+ SCRIPT_NAME => '',
+ REQUEST_METHOD => $meth,
+ PATH_INFO => URI::Escape::uri_unescape($path_info),
+ REQUEST_URI => $url,
+ QUERY_STRING => $query,
+ SERVER_PROTOCOL => 'HTTP/1.0', # no way to get this from HTTPConnection
+ 'psgi.version' => [ 1, 1 ],
+ 'psgi.errors' => *STDERR,
+ 'psgi.url_scheme' => 'http',
+ 'psgi.nonblocking' => Plack::Util::TRUE,
+ 'psgi.streaming' => Plack::Util::TRUE,
+ 'psgi.run_once' => Plack::Util::FALSE,
+ 'psgi.multithread' => Plack::Util::FALSE,
+ 'psgi.multiprocess' => Plack::Util::FALSE,
+ 'psgi.input' => do {
+ open my $input, "<", \(ref $cont ? '' : $cont);
+ $input;
+ },
+ 'psgix.io' => $con->{fh},
+ };
+
+ $env->{CONTENT_TYPE} = delete $hdr->{'content-type'};
+ $env->{CONTENT_LENGTH} = delete $hdr->{'content-length'};
+
+ while (my($key, $val) = each %$hdr) {
+ $key =~ tr/-/_/;
+ $env->{"HTTP_" . uc $key} = $val;
+ }
+
+ my $res = Plack::Util::run_app($self->{app}, $env);
+
+ Scalar::Util::weaken($con);
+ my $respond = sub {
+ my $res = shift;
+
+ my @res = ($res->[0], HTTP::Status::status_message($res->[0]), {@{$res->[1]}});
+
+ if (defined $res->[2]) {
+ my $content;
+ Plack::Util::foreach($res->[2], sub { $content .= $_[0] });
+
+ # Work around AnyEvent::HTTPD bugs that it sets
+ # Content-Length even when it's not necessary
+ if (!$content && Plack::Util::status_with_no_entity_body($res->[0])) {
+ $content = sub { $_[0]->(undef) if $_[0] };
}
- my $res = Plack::Util::run_app($app, $env);
-
- my $respond = sub {
- my $res = shift;
-
- my @res = ($res->[0], HTTP::Status::status_message($res->[0]), {@{$res->[1]}});
-
- if (defined $res->[2]) {
- my $content;
- Plack::Util::foreach($res->[2], sub { $content .= $_[0] });
-
- # Work around AnyEvent::HTTPD bugs that it sets
- # Content-Length even when it's not necessary
- if (!$content && Plack::Util::status_with_no_entity_body($res->[0])) {
- $content = sub { $_[0]->(undef) if $_[0] };
- }
-
- $req->respond([ @res, $content ]);
-
- return;
- } else {
- # Probably unnecessary, but in case ->write is
- # called before the poll callback is execute.
- my @buf;
- my $data_cb = sub { push @buf, $_[0] };
- $req->respond([
- @res,
- sub {
- # TODO $data_cb = undef -> Client Disconnect
- $data_cb = shift;
- if ($data_cb && @buf) {
- $data_cb->($_) for @buf;
- @buf = ()
- }
- }
- ]);
-
- return Plack::Util::inline_object
- write => sub { $data_cb->($_[0]) if $data_cb },
- close => sub { $data_cb->(undef) if $data_cb };
+ $con->response(@res, $content) if $con;
+
+ return;
+ } else {
+ # Probably unnecessary, but in case ->write is
+ # called before the poll callback is execute.
+ my @buf;
+ my $data_cb = sub { push @buf, $_[0] };
+ $con->response(@res, sub {
+ # TODO $data_cb = undef -> Client Disconnect
+ $data_cb = shift;
+ if ($data_cb && @buf) {
+ $data_cb->($_) for @buf;
+ @buf = ()
}
- };
+ }) if $con;
- ref $res eq 'CODE' ? $res->($respond) : $respond->($res);
+ return Plack::Util::inline_object
+ write => sub { $data_cb->($_[0]) if $data_cb },
+ close => sub { $data_cb->(undef) if $data_cb };
}
- );
+ };
- $self->{_httpd} = $httpd;
+ ref $res eq 'CODE' ? $res->($respond) : $respond->($res);
}
sub run {
my $self = shift;
- $self->register_service(@_);
-
- $self->{_httpd}->run;
+ $self->{cv} = AE::cv;
+ $self->{cv}->recv;
}
package Plack::Handler::AnyEvent::HTTPD::Connection;
@@ -133,10 +159,6 @@ use parent qw(AnyEvent::HTTPD::HTTPConnection);
sub handle_request {
my($self, $method, $uri, $hdr, $cont) = @_;
- Scalar::Util::weaken(
- $_sockets{join(":", $self->{host}, $self->{port})} = $self->{hdl}->{fh}
- );
-
$self->{keep_alive} = ($hdr->{connection} =~ /keep-alive/io);
$self->event(request => $method, $uri, $hdr, $cont);
}
Please sign in to comment.
Something went wrong with that request. Please try again.