my
$CRLF
=
"\x0d\x0a"
;
sub
configure
{
my
$self
=
shift
;
my
%args
=
@_
;
if
(
exists
$args
{app} ) {
$self
->{app} =
delete
$args
{app};
}
$self
->SUPER::configure(
%args
);
}
sub
on_request
{
my
$self
=
shift
;
my
(
$req
) =
@_
;
open
my
$stdin
,
"<"
, \
$req
->body;
my
$socket
=
$req
->stream->read_handle;
my
$path_info
=
$req
->path;
$path_info
=
""
if
$path_info
eq
"/"
;
my
%env
= (
SERVER_PROTOCOL
=>
$req
->protocol,
SCRIPT_NAME
=>
''
,
PATH_INFO
=>
$path_info
,
QUERY_STRING
=>
$req
->query_string //
""
,
REQUEST_METHOD
=>
$req
->method,
REQUEST_URI
=>
$req
->path,
'psgi.version'
=> [1,0],
'psgi.url_scheme'
=>
"http"
,
'psgi.input'
=>
$stdin
,
'psgi.errors'
=> \
*STDERR
,
'psgi.multithread'
=> 0,
'psgi.multiprocess'
=> 0,
'psgi.run_once'
=> 0,
'psgi.nonblocking'
=> 1,
'psgi.streaming'
=> 1,
'psgix.io'
=>
$socket
,
'psgix.input.buffered'
=> 1,
'net.async.http.server'
=>
$self
,
'net.async.http.server.req'
=>
$req
,
'io.async.loop'
=>
$self
->get_loop,
);
if
(
$socket
->can(
"sockport"
) ) {
%env
= (
%env
,
SERVER_PORT
=>
$socket
->sockport,
SERVER_NAME
=>
$socket
->sockhost,
REMOTE_ADDR
=>
$socket
->peerhost,
REMOTE_PORT
=>
$socket
->peerport,
);
}
elsif
(
$socket
->can(
"hostpath"
) ) {
%env
= (
%env
,
SERVER_PORT
=>
$socket
->hostpath,
SERVER_NAME
=>
"localhost"
,
);
}
foreach
(
$req
->headers ) {
my
(
$name
,
$value
) =
@$_
;
$name
=~ s/-/_/g;
$name
=
uc
$name
;
$name
=
"HTTP_$name"
unless
$name
=~ m/^CONTENT_(?:LENGTH|TYPE)$/;
$env
{
$name
} =
$value
;
}
my
$resp
=
$self
->{app}->( \
%env
);
my
$responder
=
sub
{
my
(
$status
,
$headers
,
$body
) = @{ +
shift
};
my
$response
= HTTP::Response->new(
$status
);
$response
->protocol(
$req
->protocol );
my
$has_content_length
= 0;
my
$use_chunked_transfer
;
while
(
my
(
$key
,
$value
) =
splice
@$headers
, 0, 2 ) {
$response
->push_header(
$key
,
$value
);
$has_content_length
= 1
if
$key
eq
"Content-Length"
;
$use_chunked_transfer
++
if
$key
eq
"Transfer-Encoding"
and
$value
eq
"chunked"
;
}
if
( !
defined
$body
) {
croak
"Responder given no body in void context"
unless
defined
wantarray
;
unless
(
$has_content_length
) {
$response
->header(
"Transfer-Encoding"
=>
"chunked"
);
$use_chunked_transfer
++;
}
$req
->
write
(
$response
->as_string(
$CRLF
) );
return
$use_chunked_transfer
?
Net::Async::HTTP::Server::PSGI::ChunkWriterStream->new(
$req
) :
Net::Async::HTTP::Server::PSGI::WriterStream->new(
$req
);
}
elsif
(
ref
$body
eq
"ARRAY"
) {
unless
(
$has_content_length
) {
my
$len
= 0;
my
$found_undef
;
$len
+=
length
(
$_
// (
$found_undef
++,
""
) )
for
@$body
;
carp
"Found undefined value in PSGI body"
if
$found_undef
;
$response
->content_length(
$len
);
}
$req
->
write
(
$response
->as_string(
$CRLF
) );
$req
->
write
(
$_
)
for
@$body
;
$req
->done;
}
else
{
unless
(
$has_content_length
) {
$response
->header(
"Transfer-Encoding"
=>
"chunked"
);
$use_chunked_transfer
++;
}
$req
->
write
(
$response
->as_string(
$CRLF
) );
if
(
$use_chunked_transfer
) {
$req
->
write
(
sub
{
return
unless
defined
$body
;
local
$/ = \8192;
my
$buffer
=
$body
->getline;
defined
$buffer
and
return
sprintf
(
"%X$CRLF%s$CRLF"
,
length
$buffer
,
$buffer
);
$body
->
close
;
undef
$body
;
return
"0$CRLF$CRLF"
;
} );
}
else
{
$req
->
write
(
sub
{
local
$/ = \8192;
my
$buffer
=
$body
->getline;
defined
$buffer
and
return
$buffer
;
$body
->
close
;
return
undef
;
} );
}
$req
->done;
}
};
if
(
ref
$resp
eq
"ARRAY"
) {
$responder
->(
$resp
);
}
elsif
(
ref
$resp
eq
"CODE"
) {
$resp
->(
$responder
);
}
}
package
Net::Async::HTTP::Server::PSGI::WriterStream;
sub
new
{
my
$class
=
shift
;
return
bless
[
@_
],
$class
;
}
sub
write
{
shift
->[0]->
write
(
$_
[0] ) }
sub
close
{
shift
->[0]->done }
package
Net::Async::HTTP::Server::PSGI::ChunkWriterStream;
sub
new
{
my
$class
=
shift
;
return
bless
[
@_
],
$class
;
}
sub
write
{
shift
->[0]->write_chunk(
$_
[0] ) }
sub
close
{
shift
->[0]->write_chunk_eof }
0x55AA;