Simple HTTP Server

 < Day Day Up > 



Now, let’s look at a simple HTTP server written in Perl. The entire source listing for the Perl simple HTTP server is provided in Listing 19.9. Let’s walk through this listing to understand the sample implementation.

The simple HTTP server in Perl is made up of five subroutines, as shown in Listing 19.9. These subroutines range from the main subroutine, Simple_Http_Server (lines 124–163), handle_connection (lines 92–118), handle_get_method (lines 46–86), emit_response_header (lines 29–40), and finally define_content_type (lines 6–23). We’ll walk through each of these subroutines, in their order of use.

The first subroutine is Simple_Http_Server, which is the initialization routine for the server (lines 124–163). This subroutine is responsible for setting up the server socket and awaiting incoming client connections. Upon receiving a new connection, the handle_connection subroutine is called to service it. At line 124, we declare our new subroutine and then assign our incoming arguments using the shift function. Note the similarity here to standard shell languages. The shift operator is used to shift out the contents of a list (starting from the left). The default parameter to shift (if no argument is specified) is @ARGV, the standard function call parameter list. Therefore, the first shift gets the first argument (in this case the $addr) and the second shift extracts the second argument ($port). If no argument was present for $port, the die operator would be called, causing the script to exit.

At line 131, we create our server socket of type SOCK_STREAM. To ensure reusability of the local address, we call setsockopt with the SO_REUSEADDR option. We then construct the local address to which we’ll bind. At line 140, we take the address passed in by the caller ($addr) and convert this to a numeric address (from dotted-string notation). Next, this numeric address and the user-defined $port are used with sockaddr_in to create our packed address (line 143). This parameter is then used at line 146 to bind our server socket to the local address defined by $paddr. The final setup element is shown at line 149, where we permit incoming client connections using the listen call.

The final portion of Simple_Http_Server is the server loop (lines 152–161). Using the accept call (line 154), we accept new incoming client connections, storing the new client socket in CLISOCK. We then call handle_connection with the new client socket to service its request (line 156). Upon return at line 157, we close the client socket using the close function.

The handle_connection subroutine is used to service an individual client connection (lines 92–118). We first shift out the client socket passed in by the caller ($sock) and then call the recv function to get the HTTP request from the client (line 98). Using the split function, we extract the first three elements of the $line string. These three elements are space delimited and represent the HTTP method (GET, HEAD, etc.), the object requested (filename), and the HTTP version. At line 104, we check the request, and if it’s the GET method, then we call the handle_get_method subroutine (line 107). Otherwise, we emit an error to the client (unimplemented method) using the print function (line 114).

As the simple HTTP server supports only the GET method, the meat of the HTTP server is the handle_get_method subroutine (lines 46–86). At lines 48–49, we shift out the $sock and $fname (filename) arguments and then use a regular expression at line 53 to remove any ‘/’ characters from the filename. The ‘=~’ operator forces the regular expression to be applied to the lefthand side of the expression (here $fname). This regular expression consists of a command (s/, for substitution), the character we want to substitute (\/, which means the ‘/’ character), and, finally, the character that we want to replace it with (because nothing comes between the / separator and the final / terminator, this means no character). Finally, the ‘g’ means globally replace all instances. After this operation is complete, we test the resulting filename with an empty string at line 56, and if an empty string did result from the regular expression operation, we replace the filename with the default (index.html).

At line 60, we call the define_content_type subroutine with the filename to identify the type of content being returned to the client. Note that the my statement here simply means that the following variable ($content_type) is scoped to the current block (this subroutine). The subroutine define_content_type returns a string representing the content-type string, which is then stored into $content_type. We next call emit_ response_header at line 62, providing the $content_type variable (because it will use this to declare our content type for our response message).

We then test the existence of the file being requested using ‘-e’ at line 65. If not present, we report an error to the client at line 82 (error code 404, file not found). Otherwise, we open the file at line 70, and read the contents of the file into the list @thefile. This list is then written to the client socket at line 72 using the print function. A final newline is also written to the client socket at line 73 before the file is closed at line 75 using the close function.

The emit_response_header subroutine (lines 29–40) is used to send a standard HTTP message response back to the client. It sends an HTTP status line and then an optional set of headers. The subroutine first shifts out the client socket handle (argument 1) and then the content type string (stored into $ct) at lines 31–32. The success status line is emitted to the client at line 34 using the print function and then a set of optional headers (lines 35–37). Note the specification of content type to the client at line 37. This uses the previously created content_type string returned from the define_content_type subroutine. Finally, two newlines are emitted at line 38 to the client to separate the HTTP message response header from the body.

The final subroutine in the simple HTTP server is the define_content_type subroutine. This is used to identify the type of content being returned to the HTTP client. Although there are a variety of ways to do this, the simplest involves looking at the file extension and defining the content type based upon it. For example, the content type “text/html” is required when returning HTML content. Therefore, if we look at the file extension and find “.htm”, then we can assume it’s an HTML file and set the content type accordingly. After shifting the filename out of the argument list, we test it against a list of prospective extensions (lines 11–19). We first check (using a regular expression) to see if the string ‘.htm’ is present in the filename. If so, we return the content type ‘text/html’ to the caller. For ‘.txt’ and ‘.pl’ extensions, we return ‘text/plain’. Finally, for extensions that are unknown, we return ‘application/octet-stream’. This is the default content type for unknown types. At line 21, we return the content type to the caller.

Finally, to start the simple HTTP server (line 169), we call the Simple_Http_Server subroutine and provide it with the host and port on which we want to bind. At this point, connections to the server are possible.

Listing 19.9 Perl simple HTTP server source.

start example
  1   use Socket;   2   3   #   4   #  Determine the content type based upon the filename   5   #   6   sub define_content_type {   7   8     my $name = shift   9       or die 'usage is define_content_type( $filename )';  10  11     if      ($name =~ /.htm/) {   12       $string = "text/html";  13     } elsif ($name =~ /.txt/) {   14       $string = "text/plain";  15     } elsif ($name =~ /.pl/) {  16       $string = "text/plain";  17     } else {  18       $string = "application/octet-stream";  19     }  20  21     return( $string );  22  23   }  24  25  26   #  27   #  Emit the standard HTTP response message header  28   #  29   sub emit_response_header {  30  31     my $sock = shift;  32     my $ct = shift;  33  34     print $sock "HTTP/1.1 200 OK\n";  35     print $sock "Server: Perl shttp\n";  36     print $sock "Connection: close\n";  37     print $sock "Content-Type: ", $ct;  38     print $sock "\n\n";  39  40   }  41  42  43   #  44   #  HTTP 'GET' Method Handler  45   #  46   sub handle_get_method {  47  48     my $sock = shift;  49     my $fname = shift or  50       die 'usage is handle_get_method( $sock, $filename )';  51  52     # Remove any '/' characters  53     $fname =~ s/\///g;  54  55     # If filename is now empty, convert it to the default  56     if ($fname eq "") {  57       $fname = "index.html";  58     }  59  60     my ($content_type) =  define_content_type( $fname );  61  62     emit_response_header( $sock, $content_type );  63  64     # Test the extistence of the requested file  65     if (-e $fname) {  66  67       # Open the file and emit it through the socket  68       open( INFILE, $fname );  69  70       @theFile = <INFILE>;  71  72       print $sock @theFile;  73       print $sock "\n";  74  75       close( INFILE );  76  77     } else {  78  79       print "File not found.\n";  80  81       # Unknown file -- notify client  82       print $sock "HTTP/1.1 404\n\nFile not found.\n\n";  83  84     }  85  86   }  87  88  89   #  90   #  HTTP Connection Handler  91   #  92   sub handle_connection {  93  94     my $sock = shift or  95       die 'usage is handle_connection( $sock )';  96  97     # Get the request line from the client  98     recv( $sock, $line, 1024, 0 );  99 100     # Split the GET request into its parts 101     ($Request, $Filename, $Version) = split( ' ', $line, 3 ); 102 103     # Check the request — we handle only GET requests 104     if ( $Request eq "GET" ) { 105 106       # Call our GET method handler 107       handle_get_method( $sock, $Filename ); 108 109     } else { 110 111       print "Unknown Method ", $Filename, "\n"; 112 113       # Unknown method — notify client 114       print $sock, "HTTP/1.1 501 Unimplemented Method\n\n"; 115 116     } 117 118   } 119 120 121   # 122   #  Initialization function for the simple HTTP server 123   # 124   sub Simple_Http_Server { 125 126     my $addr = shift; 127     my $port = shift or 128       die 'usage is Simple_HTTP_Server( $addr, $port )'; 129 130     # Create a new TCP Server socket 131     socket( SRVSOCK, Socket::AF_INET,  132              Socket::SOCK_STREAM, 0 ) 133       or die "socket: $!"; 134 135     # Make the local address reusable 136     setsockopt( SRVSOCK, Socket::SOL_SOCKET, 137                  Socket::SO_REUSEADDR, 1 ); 138 139     # Conver the dotted IP string to a binary address 140     my $baddr = inet_aton( $addr ); 141 142     # Create a packed address for the server 143     my $paddr = sockaddr_in( $port, $baddr ); 144 145     # Bind the local address to the server 146     bind( SRVSOCK, $paddr ) or die "bind: $!"; 147 148     # Enable incoming connections 149     listen( SRVSOCK, Socket::SOMAXCONN ) or  150       die "listen: $!"; 151 152     while (1) { 153 154       if ( accept( CLISOCK, SRVSOCK ) ) { 155 156         handle_connection( CLISOCK ); 157         close( CLISOCK ); 158 159       } 160 161     } 162 163   } 164 165 166   # 167   #  Create a new HTTP server and start it on port 80 168   # 169   Simple_Http_Server( '127.0.0.1', 80 );
end example



 < Day Day Up > 



BSD Sockets Programming from a Multi-Language Perspective
Network Programming for Microsoft Windows , Second Edition (Microsoft Programming Series)
ISBN: 1584502681
EAN: 2147483647
Year: 2003
Pages: 225
Authors: Jim Ohlund

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net