Simple HTTP Server

 < Day Day Up > 



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

The Tcl simple HTTP server is made up of five procedures, with a general design mimicking that of all other simple HTTP servers presented in this book. In this discussion, we cover each of the procedures in their order of use, which in this case means last procedure first (from Listing 21.8).

The http_server procedure is the main entry for the simple HTTP server (lines 125-134). The http_server procedure accepts a single argument, which is the port on which the server should become resident (bind, in essence). At line 128, the socket command is used with the -server option to create the server socket. Immediately following the -server option at line 128 is the procedure that will be called upon accept of a new client socket (http_accept). The last argument for the socket command is the port, which we'll be bound to. The new server socket is stored into servsock (which is not used again here). At line 132, we allow the Tcl event loop to take over by calling vwait on an unused variable.

The http_accept procedure (lines 83-119) handles a new client connection and determines the type of request being made from the client. The http_accept procedure (a callback for the socket function) accepts the new client socket (sock) and the address and port from which the new client connection came (addr and port). We emit a debug message to standard-out at line 85 and then configure the new client socket for line buffering using fconfigure (line 87). A line of text is then read from the client using the gets command and stored in line (line 90). Recall that an HTTP request is made up of the request type (GET, etc.), the object desired (file), and the HTTP version. These are separated by spaces, so we use the split command at line 93 to split up the request into the three separate elements. Lines 97-99 extract the newly split elements from the list and store them to the individual variables using the lindex command. At line 102, we test the request against 'get' (using string compare), and, if it matches (line 105), we call the handle_get_method procedure. Otherwise, an error message is emitted to standard-out (line 112) and to the client (line 113) using the puts command. Finally, at line 117, the client socket is closed using the close command.

The handle_get_method procedure (lines 49-77) handles the connection from an HTTP GET message perspective and is the meat of the simple HTTP server. The procedure accepts as arguments the client socket (sock) and the filename being requested (filename). The first step is cleaning up the filename that was requested by the Web client. We trim the ‘/' characters from the beginning and end of the filename using the string trim command (line 52). After this is complete, we check to see if the filename is now empty (for example, if a ‘/' was requested indicating the default file). If the filename is now zero length (as identified by string compare with a null string at line 55), we set the filename to the default file (index.html).

At line 62, we open the filename defined by the client using the open command. We then call define_content_type to determine the type of content being returned to the user (line 65). We pass the filename to define_content_type, as the content type is determined here by the file extension. We then emit the HTTP response header (line 70 using a call to emit_response_header) and then emit the contents of the file through the socket using the puts command at line 73. Note that we read the file anonymously using the read command and pass the data read immediately to the socket using the puts command. Finally, we close the file opened at line 62 with the close command at line 75.

The final two procedures to be discussed are support procedures called from the handle_get_method procedure. The first is emit_response_header (lines 32-43) and define_content_type (lines 4-26).

Procedure emit_response_header is used to emit the HTTP message response header to the client. It accepts as arguments the client socket and the content type. The puts command is used to write the response header to the client, and specifies a message header with status code (line 37) followed by a number of optional elements. An important element to notice is the content-type line (line 40), which tells the Web client how to render the associated message body. A blank line is also emitted (line 41), which is used to identify the end of the response message, and the beginning of the HTTP response.

Procedure define_content_type is used to identify the type of content being returned to the client, based upon the extension of the filename (lines 4-26). The filename is split into a list at line 7 using the split command, and the extension extracted from the list using lindex at line 10. A series of if commands are then performed (lines 14-24) to identify the supported content types (returned as strings). If an unsupported content type is found, the default is returned ('application/octet-stream').

Listing 21.8 Tcl simple HTTP server source.

start example
  1   #   2   # Determine the content type based upon the file extension   3   #   4   proc define_content_type { filename } {   5   6     # Split the filename into filename / extension   7     set fileparts [split $filename {"."}]   8   9     # Grab the extension part  10     set extension [lindex $fileparts 1]  11  12     # Test the extensions and return the appropriate  13     # content-type string  14     if       { [string compare $extension "html"] == 0 } {  15       return "text/html"  16     } elseif { [string compare $extension "htm"] == 0 } {  17       return "text/html"  18     } elseif { [string compare $extension "txt"] == 0 } {  19       return "text/html"  20     } elseif { [string compare $extension "tcl"] == 0 } {  21       return "text/plain"  22     } else {  23       return "application/octet-stream"  24     }  25  26   }  27  28  29   #  30   # Emit the standard HTTP response message header  31   #  32   proc emit_response_header { sock content_type } {  33  34     # Emit the header through the socket with one  35     # blank line to identify the header / message  36     # separation.  37     puts $sock "HTTP/1.1 200 OK"  38     puts $sock "Server: TCL shttp"  39     puts $sock "Connection: close"  40     puts $sock "Content-Type: $content_type"  41     puts $sock "\n"  42  43   }  44  45  46   #  47   #  HTTP Get Method Handler  48   #  49   proc handle_get_method { sock filename } {  50  51     # Trim leading and trailing '/'  52     set newfile [string trim $filename "/"]  53  54     # If new file is blank, replace with 'index.html'  55     if { [string compare $newfile "" ] == 0 } {  56  57       set newfile "index.html"  58  59     }  60  61     # Open the requested file  62     set f [open $newfile r]  63  64     # Call define_content_type and get the content type  65     set ct [ define_content_type $filename ]  66  67     puts "The content type is $ct"  68  69     # Emit the response header  70     emit_response_header $sock $ct  71  72     # Emit the contents of the file  73     puts $sock [read $f]  74  75     close $f  76  77   }  78  79  80   #  81   #  HTTP Connection Handler  82   #  83   proc http_accept { sock addr port } {  84  85     puts "Handling new connection from $addr port $port"  86  87     fconfigure $sock -buffering line  88  89     # Grab a line from the client  90     gets $sock line  91  92     # Split the command out into request / file / version  93     set message [split $line {" "}]  94  95     # Split out the elements of the HTTP request into  96     # separate strings.  97     set request [ lindex $message 0 ]  98     set filename [ lindex $message 1 ]  99     set version [ lindex $message 2 ] 100 101     # Test the request 102     set get_test [string compare -nocase $request "get"] 103 104     # If 'GET', then call the get method handler 105     if { $get_test == 0 } { 106 107       handle_get_method $sock $filename 108 109     } else { 110 111       # Emit the unimplemented error 112       puts "Unknown method $request" 113       puts $sock "HTTP/1.0 501 Unimplemented Method" 114 115     } 116 117     close $sock 118 119   } 120 121 122   # 123   # Initialization function for the simple HTTP server 124   # 125   proc http_server { port } { 126 127     # Create the TCP Server using the user-defined port 128     set servsock [ socket -server http_accept $port ] 129 130     # Wait indefinitely (allow the event-loop to take 131     # over) 132     vwait forever 133 134   }
end example

The simple Tcl HTTP server is started as shown in Listing 21.9. In this listing, we simply call the http_server procedure (line 7) and specify the port number on which we want the server to bind.

Listing 21.9 Starting the Tcl simple HTTP server.

start example
 1   # 2   # Start the HTTP server on port 80 3   # 4 5   puts "Starting the HTTP server on port 80" 6 7   http_server 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