1 package AnyEvent
::HTTPD
::HTTPConnection
;
9 our @ISA = qw
/Object::Event/;
13 AnyEvent::HTTPD::HTTPConnection - A simple HTTP connection for request and response handling
17 This class is a helper class for L<AnyEvent:HTTPD::HTTPServer> and L<AnyEvent::HTTPD>,
18 it handles TCP reading and writing as well as parsing and serializing
21 It has no public interface yet.
23 =head1 COPYRIGHT & LICENSE
25 Copyright 2008 Robin Redeker, all rights reserved.
27 This program is free software; you can redistribute it and/or modify it
28 under the same terms as Perl itself.
34 my $class = ref($this) || $this;
39 AnyEvent
::Handle
->new (
42 $self->event ('disconnect');
43 delete $self->{handles
}->{$_[0]}
46 $self->event ('disconnect', "Error: $!");
47 delete $self->{handles
}->{$_[0]}
50 $self->{rbuf
} .= $_[0]->rbuf;
52 $self->handle_data (\
$self->{rbuf
});
62 my ($self, $code, $msg, $hdr, $content) = @_;
64 if ($code !~ /^(1\d\d|204|304)$/) {
65 unless (defined $content) { $content = "$code $msg" }
66 $hdr->{'Content-Type'} = 'text/plain';
69 $self->response ($code, $msg, $hdr, $content);
73 my ($self, $code, $msg, $hdr, $content) = @_;
74 my $res = "HTTP/1.0 $code $msg\015\012";
75 $hdr->{'Expires'} = $hdr->{'Date'} = time2str
time;
76 $hdr->{'Cache-Control'} = "max-age=0";
77 $hdr->{'Content-Length'} = length $content;
79 while (my ($h, $v) = each %$hdr) {
80 $res .= "$h: $v\015\012";
84 $self->{hdl
}->push_write ($res);
85 $self->{hdl
}->on_drain (sub { $self->do_disconnect; });
90 if ($str =~ /^"(.*?)"$/) {
93 while ($str =~ s/^(?:([^"]+)|\\(.))//s) {
102 # FIXME: (or test if this is the case)
103 # 09 01:21:32 <schmorp> header der art
104 # 09 01:21:40 <schmorp> " xxx :bbbb" werden nicht geparsed oder?
105 # 09 01:21:56 <schmorp> also,d as istd er header xxx
110 while ($header =~ /\G
111 ([^:\000-\040]+) : [\011\040]*
112 ((?
:[^\015\012]+|\015\012[\011\040])* )
118 for (keys %$hdr) { $hdr->{$_} = substr $hdr->{$_}, 1; }
123 my ($self, $hdr, $cont) = @_;
125 $hdr = _parse_headers
($hdr);
126 if ($hdr->{'Content-Disposition'} =~ /form-data/) {
127 my ($dat, $name_para) = split /\s*;\s*/, $hdr->{'Content-Disposition'};
128 my ($name, $par) = split /\s*=\s*/, $name_para;
129 if ($par =~ /^".*"$/) { $par = _unquote
($par) }
130 return ($par, $cont, $hdr->{'Content-Type'});
135 sub decode_multipart
{
136 my ($self, $cont, $boundary) = @_;
141 ^--\Q
$boundary\E
\015\012
142 ((?
:[^\015\012]+\015\012)* ) \015\012
144 (--\Q
$boundary\E
(--)?
\015\012)
146 my ($h, $c, $e) = ($1, $2, $3);
148 if (my (@p) = $self->decode_part ($h, $c)) {
149 push @
{$parts->{$p[0]}}, [$p[1], $p[2], $p[3]];
157 # application/x-www-form-urlencoded
159 # This is the default content type. Forms submitted with this content type must
160 # be encoded as follows:
162 # 1. Control names and values are escaped. Space characters are replaced by
163 # `+', and then reserved characters are escaped as described in [RFC1738],
164 # section 2.2: Non-alphanumeric characters are replaced by `%HH', a percent
165 # sign and two hexadecimal digits representing the ASCII code of the
166 # character. Line breaks are represented as "CR LF" pairs (i.e., `%0D%0A').
168 # 2. The control names/values are listed in the order they appear in the
169 # document. The name is separated from the value by `=' and name/value pairs
170 # are separated from each other by `&'.
176 $val =~ s/%([0-9a-f][0-9a-f])/chr (hex ($1))/eg;
180 sub parse_urlencoded
{
181 my ($self, $cont) = @_;
182 my (@pars) = split /\&/, $cont;
186 my ($name, $val) = split /=/, $_;
187 $name = _url_unescape
($name);
188 $val = _url_unescape
($val);
190 push @
{$cont->{$name}}, [$val, ''];
196 my ($self, $method, $uri, $hdr, $cont) = @_;
198 my ($c, @params) = split /\s*;\s*/, $hdr->{'Content-Type'};
201 if (/^\s*boundary\s*=\s*(.*?)\s*$/) {
202 $bound = _unquote
($1);
206 if ($c eq 'multipart/form-data') {
207 $cont = $self->decode_multipart ($cont, $bound);
209 } elsif ($c =~ /x-www-form-urlencoded/) {
210 $cont = $self->parse_urlencoded ($cont);
213 $self->event (request
=> $method, $uri, $hdr, $cont);
217 my ($self, $rbuf) = @_;
219 if ($self->{content_len
}) {
220 if ($self->{content_len
} <= length $$rbuf) {
221 my $cont = substr $$rbuf, 0, $self->{content_len
};
222 $$rbuf = substr $$rbuf, $self->{content_len
};
223 $self->handle_request (@
{delete $self->{last_header
}}, $cont);
224 delete $self->{content_len
};
229 (\S
+) \040 (\S
+) \040 HTTP\
/(\d
+)\
.(\d
+) \015\012
230 ((?
:[^\015]+\015\012)* ) \015\012//sx) {
232 my ($m, $u, $vm, $vi, $h) = ($1,$2,$3,$4,$5);
235 if ($m ne 'GET' && $m ne 'HEAD' && $m ne 'POST') {
236 $self->error (405, "method not allowed", { Allow
=> "GET,HEAD,POST" });
241 $self->error (506, "http protocol version not supported");
245 $hdr = _parse_headers
($h);
247 $self->{last_header
} = [$m, $u, $hdr];
249 if (defined $hdr->{'Content-Length'}) {
250 $self->{content_len
} = $hdr->{'Content-Length'};
251 $self->handle_data ($rbuf);
253 $self->handle_request (@
{$self->{last_header
}});
260 my ($self, $err) = @_;
262 $self->event ('disconnect', $err);