📄 daemon.pm
字号:
elsif ($res->request && $res->request->method eq "HEAD") { # probably OK } elsif (ref($content) eq "CODE") { if ($self->proto_ge("HTTP/1.1")) { $res->push_header("Transfer-Encoding" => "chunked"); $chunked++; } else { $self->force_last_request; } } elsif (length($content)) { $res->header("Content-Length" => length($content)); } else { $self->force_last_request; $res->header('connection','close'); } print $self $res->headers_as_string($CRLF); print $self $CRLF; # separates headers and content } if ($self->head_request) { # no content } elsif (ref($content) eq "CODE") { while (1) { my $chunk = &$content(); last unless defined($chunk) && length($chunk); if ($chunked) { printf $self "%x%s%s%s", length($chunk), $CRLF, $chunk, $CRLF; } else { print $self $chunk; } } print $self "0$CRLF$CRLF" if $chunked; # no trailers either } elsif (length $content) { print $self $content; }}sub send_redirect{ my($self, $loc, $status, $content) = @_; $status ||= RC_MOVED_PERMANENTLY; Carp::croak("Status '$status' is not redirect") unless is_redirect($status); $self->send_basic_header($status); my $base = $self->daemon->url; $loc = $HTTP::URI_CLASS->new($loc, $base) unless ref($loc); $loc = $loc->abs($base); print $self "Location: $loc$CRLF"; if ($content) { my $ct = $content =~ /^\s*</ ? "text/html" : "text/plain"; print $self "Content-Type: $ct$CRLF"; } print $self $CRLF; print $self $content if $content && !$self->head_request; $self->force_last_request; # no use keeping the connection open}sub send_error{ my($self, $status, $error) = @_; $status ||= RC_BAD_REQUEST; Carp::croak("Status '$status' is not an error") unless is_error($status); my $mess = status_message($status); $error ||= ""; $mess = <<EOT;<title>$status $mess</title><h1>$status $mess</h1>$errorEOT unless ($self->antique_client) { $self->send_basic_header($status); print $self "Content-Type: text/html$CRLF"; print $self "Content-Length: " . length($mess) . $CRLF; print $self $CRLF; } print $self $mess unless $self->head_request; $status;}sub send_file_response{ my($self, $file) = @_; if (-d $file) { $self->send_dir($file); } elsif (-f _) { # plain file local(*F); sysopen(F, $file, 0) or return $self->send_error(RC_FORBIDDEN); binmode(F); my($ct,$ce) = guess_media_type($file); my($size,$mtime) = (stat _)[7,9]; unless ($self->antique_client) { $self->send_basic_header; print $self "Content-Type: $ct$CRLF"; print $self "Content-Encoding: $ce$CRLF" if $ce; print $self "Content-Length: $size$CRLF" if $size; print $self "Last-Modified: ", time2str($mtime), "$CRLF" if $mtime; print $self $CRLF; } $self->send_file(\*F) unless $self->head_request; return RC_OK; } else { $self->send_error(RC_NOT_FOUND); }}sub send_dir{ my($self, $dir) = @_; $self->send_error(RC_NOT_FOUND) unless -d $dir; $self->send_error(RC_NOT_IMPLEMENTED);}sub send_file{ my($self, $file) = @_; my $opened = 0; local(*FILE); if (!ref($file)) { open(FILE, $file) || return undef; binmode(FILE); $file = \*FILE; $opened++; } my $cnt = 0; my $buf = ""; my $n; while ($n = sysread($file, $buf, 8*1024)) { last if !$n; $cnt += $n; print $self $buf; } close($file) if $opened; $cnt;}sub daemon{ my $self = shift; ${*$self}{'httpd_daemon'};}1;__END__=head1 NAMEHTTP::Daemon - a simple http server class=head1 SYNOPSIS use HTTP::Daemon; use HTTP::Status; my $d = HTTP::Daemon->new || die; print "Please contact me at: <URL:", $d->url, ">\n"; while (my $c = $d->accept) { while (my $r = $c->get_request) { if ($r->method eq 'GET' and $r->url->path eq "/xyzzy") { # remember, this is *not* recommended practice :-) $c->send_file_response("/etc/passwd"); } else { $c->send_error(RC_FORBIDDEN) } } $c->close; undef($c); }=head1 DESCRIPTIONInstances of the C<HTTP::Daemon> class are HTTP/1.1 servers thatlisten on a socket for incoming requests. The C<HTTP::Daemon> is asubclass of C<IO::Socket::INET>, so you can perform socket operationsdirectly on it too.The accept() method will return when a connection from a client isavailable. The returned value will be an C<HTTP::Daemon::ClientConn>object which is another C<IO::Socket::INET> subclass. Calling theget_request() method on this object will read data from the client andreturn an C<HTTP::Request> object. The ClientConn object also providemethods to send back various responses.This HTTP daemon does not fork(2) for you. Your application, i.e. theuser of the C<HTTP::Daemon> is responsible for forking if that isdesirable. Also note that the user is responsible for generatingresponses that conform to the HTTP/1.1 protocol.The following methods of C<HTTP::Daemon> are new (or enhanced) relativeto the C<IO::Socket::INET> base class:=over 4=item $d = HTTP::Daemon->new=item $d = HTTP::Daemon->new( %opts )The constructor method takes the same arguments as theC<IO::Socket::INET> constructor, but unlike its base class it can alsobe called without any arguments. The daemon will then set up a listenqueue of 5 connections and allocate some random port number.A server that wants to bind to some specific address on the standardHTTP port will be constructed like this: $d = HTTP::Daemon->new( LocalAddr => 'www.thisplace.com', LocalPort => 80, );See L<IO::Socket::INET> for a description of other arguments that canbe used configure the daemon during construction.=item $c = $d->accept=item $c = $d->accept( $pkg )=item ($c, $peer_addr) = $d->acceptThis method works the same the one provided by the base class, but itreturns an C<HTTP::Daemon::ClientConn> reference by default. If apackage name is provided as argument, then the returned object will beblessed into the given class. It is probably a good idea to make thatclass a subclass of C<HTTP::Daemon::ClientConn>.The accept method will return C<undef> if timeouts have been enabledand no connection is made within the given time. The timeout() methodis described in L<IO::Socket>.In list context both the client object and the peer address will bereturned; see the description of the accept method L<IO::Socket> fordetails.=item $d->urlReturns a URL string that can be used to access the server root.=item $d->product_tokensReturns the name that this server will use to identify itself. Thisis the string that is sent with the C<Server> response header. Themain reason to have this method is that subclasses can override it ifthey want to use another product name.The default is the string "libwww-perl-daemon/#.##" where "#.##" isreplaced with the version number of this module.=backThe C<HTTP::Daemon::ClientConn> is a C<IO::Socket::INET>subclass. Instances of this class are returned by the accept() methodof C<HTTP::Daemon>. The following methods are provided:=over 4=item $c->get_request=item $c->get_request( $headers_only )This method read data from the client and turns it into anC<HTTP::Request> object which is returned. It returns C<undef>if reading fails. If it fails, then the C<HTTP::Daemon::ClientConn>object ($c) should be discarded, and you should not try call thismethod again on it. The $c->reason method might give you someinformation about why $c->get_request failed.The get_request() method will normally not return until the wholerequest has been received from the client. This might not be what youwant if the request is an upload of a large file (and with chunkedtransfer encoding HTTP can even support infinite request messages -uploading live audio for instance). If you pass a TRUE value as the$headers_only argument, then get_request() will return immediatelyafter parsing the request headers and you are responsible for readingthe rest of the request content. If you are going to call$c->get_request again on the same connection you better read thecorrect number of bytes.=item $c->read_buffer=item $c->read_buffer( $new_value )Bytes read by $c->get_request, but not used are placed in the I<readbuffer>. The next time $c->get_request is called it will consume thebytes in this buffer before reading more data from the networkconnection itself. The read buffer is invalid after $c->get_requesthas failed.If you handle the reading of the request content yourself you need toempty this buffer before you read more and you need to placeunconsumed bytes here. You also need this buffer if you implementservices like I<101 Switching Protocols>.This method always return the old buffer content and can optionallyreplace the buffer content if you pass it an argument.=item $c->reasonWhen $c->get_request returns C<undef> you can obtain a short stringdescribing why it happened by calling $c->reason.=item $c->proto_ge( $proto )Return TRUE if the client announced a protocol with version numbergreater or equal to the given argument. The $proto argument can be astring like "HTTP/1.1" or just "1.1".=item $c->antique_clientReturn TRUE if the client speaks the HTTP/0.9 protocol. No statuscode and no headers should be returned to such a client. This shouldbe the same as !$c->proto_ge("HTTP/1.0").=item $c->head_requestReturn TRUE if the last request was a C<HEAD> request. No contentbody must be generated for these requests.=item $c->force_last_requestMake sure that $c->get_request will not try to read more requests offthis connection. If you generate a response that is not selfdelimiting, then you should signal this fact by calling this method.This attribute is turned on automatically if the client announcesprotocol HTTP/1.0 or worse and does not include a "Connection:Keep-Alive" header. It is also turned on automatically when HTTP/1.1or better clients send the "Connection: close" request header.=item $c->send_status_line=item $c->send_status_line( $code )=item $c->send_status_line( $code, $mess )=item $c->send_status_line( $code, $mess, $proto )Send the status line back to the client. If $code is omitted 200 isassumed. If $mess is omitted, then a message corresponding to $codeis inserted. If $proto is missing the content of the$HTTP::Daemon::PROTO variable is used.=item $c->send_crlfSend the CRLF sequence to the client.=item $c->send_basic_header=item $c->send_basic_header( $code )=item $c->send_basic_header( $code, $mess )=item $c->send_basic_header( $code, $mess, $proto )Send the status line and the "Date:" and "Server:" headers back tothe client. This header is assumed to be continued and does not endwith an empty CRLF line.See the description of send_status_line() for the description of theaccepted arguments.=item $c->send_response( $res )Write a C<HTTP::Response> object to theclient as a response. We try hard to make sure that the response isself delimiting so that the connection can stay persistent for furtherrequest/response exchanges.The content attribute of the C<HTTP::Response> object can be a normalstring or a subroutine reference. If it is a subroutine, thenwhatever this callback routine returns is written back to theclient as the response content. The routine will be called until itreturn an undefined or empty value. If the client is HTTP/1.1 awarethen we will use chunked transfer encoding for the response.=item $c->send_redirect( $loc )=item $c->send_redirect( $loc, $code )=item $c->send_redirect( $loc, $code, $entity_body )Send a redirect response back to the client. The location ($loc) canbe an absolute or relative URL. The $code must be one the redirectstatus codes, and defaults to "301 Moved Permanently"=item $c->send_error=item $c->send_error( $code )=item $c->send_error( $code, $error_message )Send an error response back to the client. If the $code is missing a"Bad Request" error is reported. The $error_message is a string thatis incorporated in the body of the HTML entity body.=item $c->send_file_response( $filename )Send back a response with the specified $filename as content. If thefile is a directory we try to generate an HTML index of it.=item $c->send_file( $filename )=item $c->send_file( $fd )Copy the file to the client. The file can be a string (whichwill be interpreted as a filename) or a reference to an C<IO::Handle>or glob.=item $c->daemonReturn a reference to the corresponding C<HTTP::Daemon> object.=back=head1 SEE ALSORFC 2616L<IO::Socket::INET>, L<IO::Socket>=head1 COPYRIGHTCopyright 1996-2003, Gisle AasThis library is free software; you can redistribute it and/ormodify it under the same terms as Perl itself.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -