Skip to content

Instantly share code, notes, and snippets.

@miyagawa
Created January 7, 2011 01:47
Show Gist options
  • Save miyagawa/768978 to your computer and use it in GitHub Desktop.
Save miyagawa/768978 to your computer and use it in GitHub Desktop.
package Nexus;
use strict;
use warnings;
use Carp;
use Nginx::Engine;
use HTTP::Parser::XS qw(parse_http_request);
use HTTP::Status qw(status_message);
use Plack::Util;
our $VERSION = 0.01;
sub new {
bless {}, shift;
}
sub register_service {
my($self, $app, $opts) = @_;
ngxe_init("", 512);
ngxe_server(
$opts->{host} || '*',
$opts->{port},
sub {
my($conn, $remote_host) = @_;
ngxe_writer($conn, 0, 1000, '', sub {
return if $_[1];;
ngxe_close($_[0]);
});
ngxe_reader($conn, NGXE_START, 5000, sub {
return if $_[1];
my $env = {
REMOTE_ADDR => $remote_host,
REMOTE_HOST => $remote_host,
SERVER_NAME => $opts->{host} || '127.0.0.1', # XXX: needs to be resolved?
SERVER_PORT => $opts->{port},
SCRIPT_NAME => '',
'psgi.version' => [ 1, 1 ],
'psgi.errors' => *STDERR,
'psgi.url_scheme' => 'http',
'psgi.nonblocking' => Plack::Util::TRUE, # but not AE compatible
'psgi.streaming' => Plack::Util::TRUE,
'psgi.run_once' => Plack::Util::FALSE,
'psgi.multithread' => Plack::Util::FALSE,
'psgi.multiprocess' => Plack::Util::FALSE,
'psgix.input.buffered' => Plack::Util::TRUE,
};
my $rv = parse_http_request($_[2], $env);
if ($rv == -2) {
# request incomplete and less than 4k
return;
} elsif ($rv == -1) {
# request larger than 4k and incomplete or incorrect
my $content = "Bad Request";
$_[3] = "HTTP/1.0 400 Bad Request\x0d\x0a".
"Connection: close\x0d\x0a".
"Content-Length: ".length($content)."\x0d\x0a".
"Content-Type: text/html\x0d\x0a".
"\x0d\x0a".
$content;
return;
}
# FIXME POST request doesn't work correctly
if ($env->{CONTENT_LENGTH}) {
if ($env->{CONTENT_LENGTH} > length($_[2])) {
# remove header from the read buffer
substr($_[2], 0, $rv, '');
# setting minimum length of the data in the buffer to call handle with
$_[4] = $env->{CONTENT_LENGTH};
return;
}
}
open my $input, "<", \$_[2];
$env->{'psgi.input'} = $input;
my $res = $app->($env);
my $wbuf_r = \$_[3];
if (ref $res eq 'ARRAY') {
$self->_handle_response($res, $conn, $wbuf_r);
} elsif (ref $res eq 'CODE') {
$res->(sub {
$self->_handle_response($_[0], $conn, $wbuf_r);
});
} else {
die "Bad response $res";
}
});
},
);
}
sub _handle_response {
my($self, $res, $conn, $wbuf_r) = @_;
my $hdrs = "HTTP/1.0 $res->[0] " . status_message($res->[0]) . "\015\012";
my $headers = $res->[1];
while (my($k, $v) = splice(@$headers, 0, 2)) {
$hdrs .= "$k: $v\015\012";
}
$hdrs .= "\015\012";
$$wbuf_r .= $hdrs;
my $body = $res->[2];
my $cb = sub { $$wbuf_r .= $_[0] };
if (defined $body) {
Plack::Util::foreach($body, $cb);
} else {
return Plack::Util::inline_object
write => sub { $$wbuf_r .= $_[0] },
close => sub { ngxe_close($conn) };
}
}
sub run {
my $self = shift;
$self->register_service(@_);
ngxe_loop();
}
1;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment