# perldoc > POE::Filter::HTTPD

## NAME
    [POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown) - parse simple HTTP requests, and serialize [HTTP::Response](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3AResponse/markdown)

## SYNOPSIS
      #!perl

      use warnings;
      use strict;

      use POE qw([Component::Server::TCP](https://www.chedong.com/phpMan.php/perldoc/Component%3A%3AServer%3A%3ATCP/markdown) [Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/Filter%3A%3AHTTPD/markdown));
      use [HTTP::Response](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3AResponse/markdown);

      [POE::Component::Server::TCP](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AComponent%3A%3AServer%3A%3ATCP/markdown)->new(
        Port         => 8088,
        ClientFilter => '[POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown)',  ### <-- HERE WE ARE!

        ClientInput => sub {
          my $request = $_[ARG0];

          # It's a response for the client if there was a problem.
          if ($request->isa("[HTTP::Response](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3AResponse/markdown)")) {
            my $response = $request;

            $request = $response->request;
            warn "ERROR: ", $request->message if $request;

            $_[HEAP]{client}->put($response);
            $_[KERNEL]->yield("shutdown");
            return;
          }

          my $request_fields = '';
          $request->headers()->scan(
            sub {
              my ($header, $value) = @_;
              $request_fields .= (
                "<tr><td>$header</td><td>$value</td></tr>"
              );
            }
          );

          my $response = [HTTP::Response](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3AResponse/markdown)->[new(200)](https://www.chedong.com/phpMan.php/man/new/200/markdown);
          $response->push_header( 'Content-type', 'text/html' );
          $response->content(
            "<html><head><title>Your Request</title></head>" .
            "<body>Details about your request:" .
            "<table border='1'>$request_fields</table>" .
            "</body></html>"
          );

          $_[HEAP]{client}->put($response);
          $_[KERNEL]->yield("shutdown");
        }
      );

      print "Aim your browser at port 8088 of this host.\n";
      [POE::Kernel](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AKernel/markdown)->run();
      exit;

## DESCRIPTION
    [POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown) interprets input streams as HTTP 0.9, 1.0 or 1.1 requests. It returns a
    [HTTP::Request](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3ARequest/markdown) objects upon successfully parsing a request.

    On failure, it returns an [HTTP::Response](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3AResponse/markdown) object describing the failure. The intention is that
    application code will notice the [HTTP::Response](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3AResponse/markdown) and send it back without further processing. The
    erroneous request object is sometimes available via the "$r->request" in [HTTP::Response](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3AResponse/markdown) method.
    This is illustrated in the "SYNOPSIS".

    For output, [POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown) accepts [HTTP::Response](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3AResponse/markdown) objects and returns their corresponding
    streams.

    Please see [HTTP::Request](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3ARequest/markdown) and [HTTP::Response](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3AResponse/markdown) for details about how to use these objects.

    HTTP headers are not allowed to have UTF-8 characters; they must be ISO-8859-1.
    [POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown) will convert all UTF-8 into the MIME encoded equivalent. It uses
    "[utf8::is_utf8](https://www.chedong.com/phpMan.php/perldoc/utf8%3A%3Aisutf8/markdown)" for detection-8 and [Email::MIME::RFC2047::Encoder](https://www.chedong.com/phpMan.php/perldoc/Email%3A%3AMIME%3A%3ARFC2047%3A%3AEncoder/markdown) for convertion. If utf8 is not
    installed, no conversion happens. If [Email::MIME::RFC2047::Encoder](https://www.chedong.com/phpMan.php/perldoc/Email%3A%3AMIME%3A%3ARFC2047%3A%3AEncoder/markdown) is not installed,
    "[utf8::downgrade](https://www.chedong.com/phpMan.php/perldoc/utf8%3A%3Adowngrade/markdown)" is used instead. In this last case, you will see a warning if you try to send
    UTF-8 headers.

## PUBLIC FILTER METHODS
    [POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown) implements the basic [POE::Filter](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter/markdown) interface.

  new
### new

    "MaxBuffer" sets the maximum amount of data the filter will hold in memory. Defaults to 512 MB
    (536870912 octets). Because [POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown) copies all data into memory, setting this number
    to high would allow a malicious HTTPD client to fill all server memory and swap.

    "MaxContent" sets the maximum size of the content of an HTTP request. Defaults to 1 MB (1038336
    octets). Because [POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown) copies all data into memory, setting this number to high
    would allow a malicious HTTPD client to fill all server memory and swap. Ignored if "Streaming"
    is set.

    "Streaming" turns on request streaming mode. Defaults to off. In streaming mode this filter will
    return either an [HTTP::Request](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3ARequest/markdown) object or a block of content. The [HTTP::Request](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3ARequest/markdown) object's content
    will return empty. The blocks of content will be parts of the request's body, up to
    Content-Length in size. You distinguish between request objects and content blocks using
    "[Scalar::Util](https://www.chedong.com/phpMan.php/perldoc/Scalar%3A%3AUtil/markdown)/bless" (See "Streaming Request" below). This option supersedes "MaxContent".

## CAVEATS
    Some versions of libwww are known to generate invalid HTTP. For example, this code (adapted from
    the [HTTP::Request::Common](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3ARequest%3A%3ACommon/markdown) documentation) will cause an error in a [POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown) daemon:

    NOTE: Using this test with libwww-perl/5.834 showed that it added the proper HTTP/1.1 data!
    We're not sure which version of LWP fixed this. This example is valid for older LWP
    installations, beware!

      use [HTTP::Request::Common](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3ARequest%3A%3ACommon/markdown);
      use [LWP::UserAgent](https://www.chedong.com/phpMan.php/perldoc/LWP%3A%3AUserAgent/markdown);

      my $ua = [LWP::UserAgent](https://www.chedong.com/phpMan.php/perldoc/LWP%3A%3AUserAgent/markdown)->new();
      $ua->request(POST '<http://example.com>', [ foo => 'bar' ]);

    By default, [HTTP::Request](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3ARequest/markdown) is HTTP version agnostic. It makes no attempt to add an HTTP version
    header unless you specifically declare a protocol using "$request->protocol('HTTP/1.0')".

    According to the HTTP 1.0 RFC (1945), when faced with no HTTP version header, the parser is to
    default to HTTP/0.9. [POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown) follows this convention. In the transaction detailed
    above, the [Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/Filter%3A%3AHTTPD/markdown) based daemon will return a 400 error since POST is not a valid HTTP/0.9
    request type.

    Upon handling a request error, it is most expedient and reliable to respond with the error and
    shut down the connection. Invalid HTTP requests may corrupt the request stream. For example, the
    absence of a Content-Length header signals that a request has no content. Requests with content
    but without that header will be broken into a content-less request and invalid data. The invalid
    data may also appear to be a request! Hilarity will ensue, possibly repeatedly, until the filter
    can find the next valid request. By shutting down the connection on the first sign of error, the
    client can retry its request with a clean connection and filter.

## Streaming Request
    Normally [POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown) reads the entire request content into memory before returning the
    [HTTP::Request](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3ARequest/markdown) to your code. In streaming mode, it will return the content separately, as
    unblessed scalars. The content may be split up into blocks of varying sizes, depending on OS and
    transport constraints. Your code can distinguish the request object from the content blocks
    using "blessed" in [Scalar::Util](https://www.chedong.com/phpMan.php/perldoc/Scalar%3A%3AUtil/markdown).

        use [Scalar::Util](https://www.chedong.com/phpMan.php/perldoc/Scalar%3A%3AUtil/markdown);
        use [POE::Wheel::ReadWrite](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AWheel%3A%3AReadWrite/markdown);
        use [POE::Filter](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter/markdown):HTTPD;

        $heap->{wheel} = [POE::Wheel::ReadWrite](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AWheel%3A%3AReadWrite/markdown)->new(
                            InputEvent => 'http_input',
                            Filter => [POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown)->new( Streaming => 1 ),
                            # ....
                    );

        sub http_input_handler
        {
            my( $heap, $req_or_data ) = @_[ HEAP, ARG0 ];
            if( blessed $req_or_data ) {
                my $request = $req_or_data;
                if( $request->isa( '[HTTP::Response](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3AResponse/markdown)') ) {
                    # HTTP error
                    $heap->{wheel}->put( $request );
                }
                else {
                    # HTTP request
                    # ....
                }
            }
            else {
                my $data = $req_or_data;
                # ....
            }
        }

    You may trivally create a DoS bug if you hold all content in memory but do not impose a maximum
    Content-Length. An attacker could send "Content-Length: 1099511627776" (aka 1 TB) and keep
    sending data until all your system's memory and swap is filled.

    Content-Length has been sanitized by [POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown) so checking it is trivial :

        if( $request->headers( 'Content-Length' ) > 1024*1024 ) {
            my $resp = [HTTP::Response](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3AResponse/markdown)->new( RC_REQUEST_ENTITY_TOO_LARGE ),
                                                 "So much content!" )
            $heap->{wheel}->put( $resp );
            return;
        }

    If you want to handle large amounts of data, you should save the content to a file before
    processing it. You still need to check Content-Length or an attacker might fill up the
    partition.

        use [File::Temp](https://www.chedong.com/phpMan.php/perldoc/File%3A%3ATemp/markdown) qw(tempfile);

        if( blessed $_[ARG0] ) {
            $heap->{request} = $_[ARG0];
            if( $heap->{request}->method eq 'GET' ) {
                handle_get( $heap );
                delete $heap->{request};
                return;
            }
            my( $fh, $file ) = tempfile( "httpd-XXXXXXXX", TMPDIR=>1 );
            $heap->{content_file} = $file;
            $heap->{content_fh} = $fh;
            $heap->{content_size} = 0;
        }
        else {
            return unless $heap->{request};

            $heap->{content_size} += length( $_[ARG0] );
            $heap->{content_fh}->print( $_[ARG0] );
            if( $heap->{content_size} >= $heap->{request}->headers( 'content-length' ) ) {
                delete $heap->{content_fh};
                delete $heap->{content_size};

                # Now we can parse $heap->{content_file}
                if( $heap->{request}->method eq 'POST' ) {
                    handle_post( $heap );
                }
                else {
                    # error ...
                }
            }
        }

        sub handle_post
        {
            my( $heap ) = @_;
            # Now we have to load and parse $heap->{content_file}

            # Next 6 lines make the data available to CGI->init
            local $ENV{REQUEST_METHOD} = 'POST';
            local $[CGI::PERLEX](https://www.chedong.com/phpMan.php/perldoc/CGI%3A%3APERLEX/markdown) = $[CGI::PERLEX](https://www.chedong.com/phpMan.php/perldoc/CGI%3A%3APERLEX/markdown) = "CGI-PerlEx/Fake";
            local $ENV{CONTENT_TYPE} = $heap->{req}->header( 'content-type' );
            local $ENV{CONTENT_LENGTH} = $heap->{req}->header( 'content-length' );
            my $keep = [IO::File](https://www.chedong.com/phpMan.php/perldoc/IO%3A%3AFile/markdown)->new( "<&STDIN" ) or die "Unable to reopen STDIN: $!";
            open STDIN, "<$heap->{content_file}" or die "Reopening STDIN failed: $!";

            my $qcgi = CGI->new();

            # cleanup
            open STDIN, "<&".$keep->fileno or die "Unable to reopen $keep: $!";
            undef $keep;
            unlink delete $heap->{content_file};

            # now use $q as you would normaly
            my $file = $q->upload( 'field_name' );

            # ....
        }

        sub handle_get
        {
            my( $heap ) = @_;

            # 4 lines to get data into CGI->init
            local $ENV{REQUEST_METHOD} = 'GET';
            local $[CGI::PERLEX](https://www.chedong.com/phpMan.php/perldoc/CGI%3A%3APERLEX/markdown) = $[CGI::PERLEX](https://www.chedong.com/phpMan.php/perldoc/CGI%3A%3APERLEX/markdown) = "CGI-PerlEx/Fake";
            local $ENV{CONTENT_TYPE} = $heap->{req}->header( 'content-type' );
            local $ENV{'QUERY_STRING'} = $heap->{req}->uri->query;

            my $q = CGI->new();

            # now use $q as you would normaly
            # ....
        }

## Streaming Response
    It is possible to use [POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown) for streaming content, but an application can use it to
    send headers and then switch to [POE::Filter::Stream](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AStream/markdown).

    From the input handler (the InputEvent handler if you're using wheels, or the ClientInput
    handler for [POE::Component::Server::TCP](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AComponent%3A%3AServer%3A%3ATCP/markdown)):

      my $response = [HTTP::Response](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3AResponse/markdown)->[new(200)](https://www.chedong.com/phpMan.php/man/new/200/markdown);
      $response->push_header('Content-type', 'audio/x-mpeg');
      $_[HEAP]{client}->put($response);
      $_[HEAP]{client}->set_output_filter([POE::Filter::Stream](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AStream/markdown)->new());

    Then the output-flushed handler (FlushEvent for [POE::Wheel::ReadWrite](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AWheel%3A%3AReadWrite/markdown), or ClientFlushed for
    [POE::Component::Server::TCP](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AComponent%3A%3AServer%3A%3ATCP/markdown)) can put() chunks of the stream as needed.

      my $bytes_read = sysread(
        $_[HEAP]{file_to_stream}, my $buffer = '', 4096
      );

      if ($bytes_read) {
        $_[HEAP]{client}->put($buffer);
      }
      else {
        delete $_[HEAP]{file_to_stream};
        $_[KERNEL]->yield("shutdown");
      }

## SEE ALSO
    Please see [POE::Filter](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter/markdown) for documentation regarding the base interface.

    The SEE ALSO section in POE contains a table of contents covering the entire POE distribution.

    [HTTP::Request](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3ARequest/markdown) and [HTTP::Response](https://www.chedong.com/phpMan.php/perldoc/HTTP%3A%3AResponse/markdown) explain all the wonderful things you can do with these classes.

## BUGS
    Many aspects of HTTP 1.0 and higher are not supported, such as keep-alive. A simple I/O filter
    can't support keep-alive, for example. A number of more feature-rich POE HTTP servers are on the
    CPAN. See <<http://search.cpan.org/search?query=POE>+http+server&mode=dist>

AUTHORS & COPYRIGHTS
    [POE::Filter::HTTPD](https://www.chedong.com/phpMan.php/perldoc/POE%3A%3AFilter%3A%3AHTTPD/markdown) was contributed by Artur Bergman. Documentation is provided by Rocco Caputo.

    Please see POE for more information about authors and contributors.

