Simple HTTP Server

 < Day Day Up > 



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

Listing 18.9 Python simple HTTP server source.

start example
  1   import socket   2   import string   3   import os   4   import re   5   import time   6      7   class Simple_http_server:   8      9     # Define the class variables (host and server socket)  10     host = 'localhost'  11     sock = 0  12  13     #  14     # Define the content type  15     #  16     def define_content_type( self, filename ):  17  18       fileparts = filename.split('.')  19  20       extension = string.lower( fileparts[1] )  21  22       # Based upon the extension, return the content-type string  23       if re.search( 'htm', extension ):  24         return 'text/html'  25       if re.search( 'txt', extension ):  26         return 'text/plain'  27       if re.search( 'py', extension ):  28         return 'text/plain'  29       else:  30         return 'application/octet-stream'  31  32  33     #  34     # Emit the response header  35     #  36     def emit_response_header( self, sock, content_type ):  37  38       # Emit the HTTP response message header  39       sock.send( 'HTTP/1.1 200 OK\n' )  40       sock.send( 'Server: Python shttp\n' )  41       sock.send( 'Connection: close\n' )  42       sock.send( 'Content-Type: ' )  43       sock.send( content_type )  44       sock.send( '\n\n' )  45  46  47     #  48     # Handle an HTTP GET method  49     #  50     def handle_get_method( self, sock, filename ):  51  52       # Remove the leading '/' from the filename   53       if filename[0] == '/':  54         filename = filename[1:len(filename)]  55  56       # If the filename is only '/' or a null string, then  57       # convert this to the index file.  58       if filename == '/' or filename.__len__() == 0:  59         filename = 'index.html'  60  61       print 'file is', filename  62  63       # Ensure the file is readable  64       if os.access( filename, os.R_OK ):  65  66         # Determine the content type  67         content_type = self.define_content_type( filename )  68  69         print 'content type ', content_type  70  71         self.emit_response_header( sock, content_type )  72  73         # Open the defined file and emit the contents through  74         # the socket.  75         file = open( filename, 'r' )  76         sock.send( file.read() )  77         file.close()  78         sock.send( "\n" )  79  80       else:  81  82         print 'File not found\n'  83  84         sock.send( 'HTTP/1.1 404\n\nFile not found\n\n' )  85  86  87     #  88     # Handle an individual HTTP session (callback).  89     #  90     def handle_connection( self, conn ):  91  92       print 'Handling new connection'  93  94       # Read the HTTP request message  95       line = conn.recv( 200 )  96  97       # Split the HTTP request message into a   98       # space-delimited array  99       elements = string.splitfields( line, ' ' ) 100 101       if elements[0] == "GET": 102 103         # If the GET method was requested, call  104         # handle_get_method 105         self.handle_get_method( conn, elements[1] ) 106 107       else: 108 109         print 'Unknown Method ', elements[0], '\n' 110 111         # All other HTTP methods are unimplemented... 112         conn.send( 'HTTP/1.0 501 Method Unimplemented\n' ) 113 114 115     # 116     # Initialize an HTTP Server Instance. 117     # 118     def __init__( self, port = 80 ): 119 120       # Create a new server socket and bind it with the host 121       # and port 122       self.sock = socket.socket( socket.AF_INET,  123                                  socket.SOCK_STREAM ) 124       self.sock.setsockopt( socket.SOL_SOCKET,  125                             socket.SO_REUSEADDR, 1 ) 126       self.sock.bind( (self.host, port) ) 127       self.sock.listen( 1 ) 128 129       # The big loop 130       while 1: 131 132         # Await a new client connection 133         conn, (remotehost, remoteport) = self.sock.accept() 134 135         print 'client at ', remotehost, remoteport 136 137         # Call handle_connection with the new client socket 138         self.handle_connection( conn ) 139 140         # Bug alert -- close method shuts the socket down  141         # and aborts remaining outgoing data. 142         conn.shutdown(1)
end example

This implementation of the simple HTTP server is based upon a simple class that provides the HTTP server functionality. There are no visible methods, but the __init__ method maps directly to the initialization of the creation of a new HTTP server instance. This Python example is different from the others that we’ve investigated in this chapter because we’re creating a simple HTTP server class here, rather than a script. Later in this section, we’ll see how to create an instance of the simple HTTP server class.

Lines 118–142 of Listing 18.9 provides the __init__ method for the Simple_http_ server class. We use the def statement to define a new method, in this case the special method called __init__ that is invoked when a new instance of the class is created (line 118). Note also that the first argument, self, is provided here and in all other methods defined in this class. The self argument represents the instance of this class. Note at line 11, we declare the class variable sock. When referenced for the instance of this class, we denote it as self.sock (as in line 122). The final item to note at line 118 is the port argument, which is defaulted here as the value 80. If a value is not provided when the instance is created, the default value of 80 is used. Otherwise, whatever value the caller provides is used.

The purpose of the __init__ method is to create the HTTP socket server object and serve as the big loop for the server. We begin socket creation at line 122 where we create our socket using the socket method. Note the use of self to denote the instance variable. We use the SOCK_STREAM to create a TCP socket (as it’s the Transport layer used by HTTP). At lines 124–125, we make the local address information reusable with the setsockopt method. At line 126, we bind our server socket to the local address tuple. The local address is made up of the instance variable host (defined at line 10) and the port argument passed in by the caller. Note that port is not prefixed by self, as port isn’t an instance variable but instead a local variable on the stack. Finally, at line 127, we invoke the listen method to permit the server to accept incoming connections.

The HTTP server loop starts at line 130 with the while statement. This infinite loop begins with a call to the accept method to await a new incoming client connection. The accept method returns two items, the client socket and an address tuple representing the peer that connected (line 133). We emit this for debug at line 135 using the print statement. At line 138, we invoke the local handle_connection method that is used to handle the current client connection, satisfying its HTTP request. Upon return, we close the client socket. In the current release of Python, using the close method when data is waiting to be sent causes this data to be discarded. The workaround is to use the shutdown method (specifying the how argument as 1), which permits enqueued data to be sent to the peer. Control then resumes at the accept method, awaiting a new client connection.

The handle_connection method (lines 90–112) services the HTTP request method received by the simple server. The client socket is passed into the method as the conn variable (line 90) along with the self variable representing the object instance. We emit some debugging information at line 92 to identify that we’re servicing a new client connection. We then receive the HTTP request through the client socket using the recv method (line 95). We specify a maximum of 200 bytes to be received (which gives us at least the first line of the HTTP request, which contains the actual file request). The string result at line 95 is returned to the line variable. At line 99, we split the HTTP request line into a string array using the splitfields method of the string module. Recall that the HTTP request line is made up of the request method (such as GET), the filename for the request, and the HTTP version, all delimited by a space. We test the HTTP request method at line 101 to see if the client has requested a GET. If so, we call method handle_get_method at line 105 and include the client socket and the filename from the request (element[1]). Otherwise, if the request method was something other than GET, we emit a debugging message at line 109 and then send back an unimplemented error message to the client at line 112 using the send method.

Method handle_get_method exists solely to satisfy HTTP GET method requests (lines 50–84 of Listing 18.9). In addition to the self variable, the method also accepts the client socket (sock) and the filename being requested. Any leading ‘/’ character is removed in lines 53–54, by first testing the filename for the character and, if present, removing the first character using the substring of the filename excluding the first character. If the filename consisted only of the ‘/’ character, or is now empty, the filename is set to the default ‘index.html’ (lines 58–59). Next, debugging information is emitted at line 61 to identify the requested file.

After the file being requested has been isolated, we begin the process of serving this file to the client. We begin by identifying whether the filename is an actual readable file using the access method of the os module (line 64). If so, we invoke the define_content_type method to identify the content type being returned. This string (content_type) is emitted for debugging purposes at line 69 using the print statement. The HTTP response message header is then emitted using the emit_response_header method (line 71). Finally, the actual file is sent to the client. The file is first opened at line 75 using the open method. At line 76, the entire file is read using the read method, and immediately sent to the client using the send method. After closing the file using the close method, we emit a final carriage-return through the client socket and the client request is complete.

If the file had not been accessible on the file system (as determined at line 64), a debugging message would have been emitted at line 82 and an HTTP error message emitted to the client using the send method at line 84.

The emit_response_header method (lines 36–44) sends the HTTP response message header to the client through the socket. The response message header consists of two items, the status line (the first line emitted at line 39) and then a set of optional headers (lines 40–43). The response message header is emitted through to the client socket using the send method. At the end of the response message header is a blank line (line 44), which is required to separate the optional headers from the message body (emitted in method handle_get_method).

The final method in the Simple_http_server class is the define_content_type method. This method is used to identify the content type based upon the suffix of the filename to be served. For example, if an HTML file is to be served (such as ‘index.html’), then the content type is ‘text/html’. If a text file is to be served (such as ‘file.txt’), then the content type is ‘text/plain’. The only other content type that is served by the server is ‘application/octet-stream’. This type is commonly stored by the HTTP client in a file, because it does not know how to deal with the content.

Method define_content_type is shown in lines 16–30 of Listing 18.9. The filename is split into two parts (the filename and the suffix) using the split method. The split occurs on the ‘.’ character, which is used to separate the actual filename from the suffix. The extension is then converted to lowercase using the lower method of the string module, storing the result in variable extension. We then use the search method of the re (regular-expression) module to identify which particular suffix appears, and based upon a successful match, the content-type is returned. If the suffix is not matched, the ‘application/octet-stream’ content type is returned to the caller. This content type is then transferred to the client in the HTTP response message header as the value of the ‘Content-Type’ key.

The final item to discuss with the Simple_http_server class is how an application can create a new Simple_http_server object. As shown in Listing 18.10, a new Simple_http_server object is created by using the class name as a method and providing any arguments that may be necessary. In this case, we provide the port (80), though for this default value, we could have passed nothing. The result of line 5 is actually the invocation of the __init__ method, as shown in Listing 18.9. If the __init__ method returned, the next step (at line 7) is destroying the object using the del statement.

Listing 18.10 Instantiating a new Python HTTP server.

start example
 1   # 2   # Create a new HTTP server and start it on port 80 3   # 4 5   shttp = Simple_http_server( 80 ) 6 7   del shttp
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