Small web server in Perl: miniserver.pl

http://www.wellho.net/resources/ex.php4?item=p402/miniserver.pl

#!/usr/bin/perl

use strict;
use Socket;
use IO::Socket;

# Simple web server in Perl
# Serves out .html files, echos form data

sub parse_form {
    my $data = $_[0];
    my %data;
    foreach (split /&/, $data) {
        my ($key, $val) = split /=/;
        $val =~ s/\+/ /g;
        $val =~ s/%(..)/chr(hex($1))/eg;
        $data{$key} = $val;}
    return %data; }

# Setup and create socket

my $port = shift;
defined($port) or die "Usage: $0 portno\n";

my $DOCUMENT_ROOT = $ENV{'HOME'} . "/public_html";
my $server = new IO::Socket::INET(Proto => 'tcp',
                                  LocalPort => $port,
                                  Listen => SOMAXCONN,
                                  Reuse => 1);
$server or die "Unable to create server socket: $!" ;

# Await requests and handle them as they arrive

while (my $client = $server->accept()) {
    $client->autoflush(1);
    my %request = ();
    my %data;

    {

#-------- Read Request ---------------

        local $/ = Socket::CRLF;
        while (<$client>) {
            chomp; # Main http request
            if (/\s*(\w+)\s*([^\s]+)\s*HTTP\/(\d.\d)/) {
                $request{METHOD} = uc $1;
                $request{URL} = $2;
                $request{HTTP_VERSION} = $3;
            } # Standard headers
            elsif (/:/) {
                (my $type, my $val) = split /:/, $_, 2;
                $type =~ s/^\s+//;
                foreach ($type, $val) {
                        s/^\s+//;
                        s/\s+$//;
                }
                $request{lc $type} = $val;
            } # POST data
            elsif (/^$/) {
                read($client, $request{CONTENT}, $request{'content-length'})
                    if defined $request{'content-length'};
                last;
            }
        }
    }

#-------- SORT OUT METHOD  ---------------

    if ($request{METHOD} eq 'GET') {
        if ($request{URL} =~ /(.*)\?(.*)/) {
                $request{URL} = $1;
                $request{CONTENT} = $2;
                %data = parse_form($request{CONTENT});
        } else {
                %data = ();
        }
        $data{"_method"} = "GET";
    } elsif ($request{METHOD} eq 'POST') {
                %data = parse_form($request{CONTENT});
                $data{"_method"} = "POST";
    } else {
        $data{"_method"} = "ERROR";
    }

#------- Serve file ----------------------

        my $localfile = $DOCUMENT_ROOT.$request{URL};

# Send Response
        if (open(FILE, "<$localfile")) {
            print $client "HTTP/1.0 200 OK", Socket::CRLF;
            print $client "Content-type: text/html", Socket::CRLF;
            print $client Socket::CRLF;
            my $buffer;
            while (read(FILE, $buffer, 4096)) {
                print $client $buffer;
            }
            $data{"_status"} = "200";
        }
        else {
            print $client "HTTP/1.0 404 Not Found", Socket::CRLF;
            print $client Socket::CRLF;
            print $client "<html><body>404 Not Found</body></html>";
            $data{"_status"} = "404";
        }
        close(FILE);

# Log Request
        print ($DOCUMENT_ROOT.$request{URL},"\n");
        foreach (keys(%data)) {
                print (" $_ = $data{$_}\n"); }

# ----------- Close Connection and loop ------------------

    close $client;
}

__END__

Notes on this Web server in Perl ...

Reports all files served as being Text/HTML
Uses HTTP/1.0 (so no virtual hosting support)
Does not process "home page" requests through to index.html
No support for 300 redirects
Only GET and POST method supported
Standard URL encoding only
Single thread / process only

If you want a proper web server, use Apache httpd ... but this
is great as the basis of an Intranet specialised http handler -
for example to act as a data logger with acknowledgements.