has
[
qw/on_upgrade on_request/
];
has
req
=>
sub
{ Mojo::Message::Request->new };
has
res
=>
sub
{ Mojo::Message::Response->new };
sub
client_read {
my
(
$self
,
$chunk
) =
@_
;
my
$preserved
=
$self
->{state};
my
$read
=
length
$chunk
;
$self
->{state} =
'done'
if
$read
== 0;
my
$req
=
$self
->req;
my
$res
=
$self
->res;
if
(
$req
->method =~ /^head$/i) {
$res
->parse_until_body(
$chunk
);
$self
->{state} =
'done'
if
$res
->content->is_parsing_body;
}
else
{
$res
->parse(
$chunk
);
$self
->{state} =
'done'
if
$res
->is_done;
}
if
(
$self
->{state} eq
'done'
&& (
$res
->code ||
''
) eq
'100'
) {
$self
->res(
$res
->new);
$self
->{state} =
$preserved
;
}
$self
->{state} =
'done'
if
$self
->error;
return
$self
;
}
sub
client_write {
my
$self
=
shift
;
$self
->{offset} ||= 0;
$self
->{
write
} ||= 0;
my
$req
=
$self
->req;
unless
(
$self
->{state}) {
my
$headers
=
$req
->headers;
unless
(
$headers
->connection) {
if
(
$self
->keep_alive ||
$self
->kept_alive) {
$headers
->connection(
'keep-alive'
);
}
else
{
$headers
->connection(
'close'
) }
}
$self
->{state} =
'write_start_line'
;
$self
->{
write
} =
$req
->start_line_size;
}
my
$chunk
=
''
;
if
(
$self
->{state} eq
'write_start_line'
) {
my
$buffer
=
$req
->get_start_line_chunk(
$self
->{offset});
my
$written
=
defined
$buffer
?
length
$buffer
: 0;
$self
->{
write
} =
$self
->{
write
} -
$written
;
$self
->{offset} =
$self
->{offset} +
$written
;
$chunk
.=
$buffer
;
if
(
$self
->{
write
} <= 0) {
$self
->{state} =
'write_headers'
;
$self
->{offset} = 0;
$self
->{
write
} =
$req
->header_size;
}
}
if
(
$self
->{state} eq
'write_headers'
) {
my
$buffer
=
$req
->get_header_chunk(
$self
->{offset});
my
$written
=
defined
$buffer
?
length
$buffer
: 0;
$self
->{
write
} =
$self
->{
write
} -
$written
;
$self
->{offset} =
$self
->{offset} +
$written
;
$chunk
.=
$buffer
;
if
(
$self
->{
write
} <= 0) {
$self
->{state} =
'write_body'
;
$self
->{offset} = 0;
$self
->{
write
} =
$req
->body_size;
$self
->{
write
} = 1
if
$req
->is_chunked;
}
}
if
(
$self
->{state} eq
'write_body'
) {
my
$buffer
=
$req
->get_body_chunk(
$self
->{offset});
my
$written
=
defined
$buffer
?
length
$buffer
: 0;
$self
->{
write
} =
$self
->{
write
} -
$written
;
$self
->{offset} =
$self
->{offset} +
$written
;
$chunk
.=
$buffer
if
defined
$buffer
;
$self
->{state} =
'read_response'
if
defined
$buffer
&& !
length
$buffer
;
$self
->{
write
} = 1
if
$req
->is_chunked;
$self
->{state} =
'read_response'
if
$self
->{
write
} <= 0;
}
return
$chunk
;
}
sub
keep_alive {
my
(
$self
,
$keep_alive
) =
@_
;
if
(
$keep_alive
) {
$self
->{keep_alive} =
$keep_alive
;
return
$self
;
}
my
$req
=
$self
->req;
my
$version
=
$req
->version;
$self
->{keep_alive} ||= 0
if
$req
->version eq
'0.9'
||
$version
eq
'1.0'
;
my
$res
=
$self
->res;
$version
=
$res
->version;
$self
->{keep_alive} ||= 0
if
$version
eq
'0.9'
||
$version
eq
'1.0'
;
my
$req_connection
=
$req
->headers->connection ||
''
;
my
$res_connection
=
$res
->headers->connection ||
''
;
$self
->{keep_alive} = 1
if
$req_connection
=~ /^keep-alive$/i
||
$res_connection
=~ /^keep-alive$/i;
$self
->{keep_alive} = 0
if
$req_connection
=~ /^
close
$/i ||
$res_connection
=~ /^
close
$/i;
$self
->{keep_alive} = 1
unless
defined
$self
->{keep_alive};
return
$self
->{keep_alive};
}
sub
server_leftovers {
my
$self
=
shift
;
my
$req
=
$self
->req;
return
unless
$req
->content->has_leftovers;
my
$leftovers
=
$req
->leftovers;
$req
->{state} =
'done'
;
return
$leftovers
;
}
sub
server_read {
my
(
$self
,
$chunk
) =
@_
;
my
$req
=
$self
->req;
$req
->parse(
$chunk
)
unless
$req
->error;
$self
->{state} ||=
'read'
;
my
$res
=
$self
->res;
my
$handled
=
$self
->{handled};
if
(
$req
->error && !
$handled
) {
$self
->on_request->(
$self
);
$res
->headers->connection(
'close'
);
$self
->{handled} = 1;
}
elsif
((
length
$chunk
== 0) || (
$req
->is_done && !
$handled
)) {
my
$ws
;
$ws
=
$self
->on_upgrade->(
$self
)
if
$req
->headers->upgrade;
$self
->on_request->(
$ws
? (
$ws
,
$self
) :
$self
);
$self
->{handled} = 1;
}
elsif
(
$req
->content->is_parsing_body && !
defined
$self
->{continued}) {
if
((
$req
->headers->expect ||
''
) =~ /100-
continue
/i) {
$self
->{state} =
'write'
;
$res
->code(100);
$self
->{continued} = 0;
}
}
return
$self
;
}
sub
server_write {
my
$self
=
shift
;
my
$chunk
=
''
;
return
$chunk
unless
$self
->{state};
$self
->{offset} ||= 0;
$self
->{
write
} ||= 0;
my
$res
=
$self
->res;
if
(
$self
->{state} eq
'write'
) {
my
$headers
=
$res
->headers;
unless
(
$headers
->connection) {
if
(
$self
->keep_alive) {
$headers
->connection(
'keep-alive'
) }
else
{
$headers
->connection(
'close'
) }
}
$self
->{state} =
'write_start_line'
;
$self
->{
write
} =
$res
->start_line_size;
}
if
(
$self
->{state} eq
'write_start_line'
) {
my
$buffer
=
$res
->get_start_line_chunk(
$self
->{offset});
my
$written
=
defined
$buffer
?
length
$buffer
: 0;
$self
->{
write
} =
$self
->{
write
} -
$written
;
$self
->{offset} =
$self
->{offset} +
$written
;
$chunk
.=
$buffer
;
if
(
$self
->{
write
} <= 0) {
$self
->{state} =
'write_headers'
;
$self
->{offset} = 0;
$self
->{
write
} =
$res
->header_size;
}
}
if
(
$self
->{state} eq
'write_headers'
) {
my
$buffer
=
$res
->get_header_chunk(
$self
->{offset});
my
$written
=
defined
$buffer
?
length
$buffer
: 0;
$self
->{
write
} =
$self
->{
write
} -
$written
;
$self
->{offset} =
$self
->{offset} +
$written
;
$chunk
.=
$buffer
;
if
(
$self
->{
write
} <= 0) {
if
(
$self
->req->method =~ /^head$/i) {
$self
->{state} =
'done'
;
}
else
{
$self
->{state} =
'write_body'
;
$self
->{offset} = 0;
$self
->{
write
} =
$res
->body_size;
$self
->{
write
} = 1
if
$res
->is_dynamic;
}
}
}
if
(
$self
->{state} eq
'write_body'
) {
if
(
$self
->{
write
} <= 0) {
if
(
defined
$self
->{continued} &&
$self
->{continued} == 0) {
$self
->{continued} = 1;
$self
->{state} =
'read'
;
$self
->res(
$res
->new);
}
elsif
(!
defined
$self
->{continued}) {
$self
->{state} =
'done'
}
}
else
{
my
$buffer
=
$res
->get_body_chunk(
$self
->{offset});
my
$written
=
defined
$buffer
?
length
$buffer
: 0;
$self
->{
write
} =
$self
->{
write
} -
$written
;
$self
->{offset} =
$self
->{offset} +
$written
;
if
(
defined
$buffer
) {
$chunk
.=
$buffer
;
delete
$self
->{delay};
}
else
{
my
$delay
=
delete
$self
->{delay};
$self
->{state} =
'paused'
if
$delay
;
$self
->{delay} = 1
unless
$delay
;
}
$self
->{
write
} = 1
if
$res
->is_dynamic;
$self
->{state} =
'done'
if
$self
->{
write
} <= 0 || (
defined
$buffer
&& !
length
$buffer
);
}
}
return
$chunk
;
}
1;