Simple HTTP Server

 < Day Day Up > 



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

Listing 20.10 Ruby simple HTTP server source.

start example
  1   require ‘socket’   2   3   class Simple_http_server   4   5     #   6     #  Determine the content type based upon the filename   7     #   8     def define_content_type( filename )   9  10       fileparts = filename.split(".")  11  12       case fileparts[1]  13         when "html","htm", "HTML", "HTM"  14           return "text/html"  15         when "txt", "TXT"  16           return "text/plain"  17         when "rb"  18           return "text/plain"  19         else  20           return "application/octet-stream"  21       end  22  23     end  24     protected:define_content_type  25  26  27     #  28     #  Emit the standard HTTP response message header  29     #  30     def emit_response_header( sock, content_type )  31  32       sock.write( "HTTP/1.1 200 OK\n" )  33       sock.write( "Server: Ruby shttp\n" )  34       sock.write( "Connection: close\n" )  35       sock.write( "Content-Type: " )  36       sock.write( content_type )  37       sock.write( "\n\n" )  38  39     end  40     protected:emit_response_header  41  42  43     #  44     # HTTP ‘GET’ Method Handler  45     #  46     def handle_get_method( sock, filename )  47  48       filename = filename.delete("/")  49  50       if File::exist? filename then  51  52         print "Requested filename is "  53         print filename + "\n"  54  55         content_type = define_content_type( filename )  56  57         emit_response_header( sock, content_type )  58  59         # Emit the file through the socket  60         file = open( filename, "r" )  61         sock.write( file.read )  62         file.close  63         sock.write( "\n" )  64  65       else  66  67         print "File not found.\n"  68  69         # Unknown file -- notify client  70         sock.write( "HTTP/1.0 404\n\n File not found\n\n" )  71  72       end  73  74     end  75     protected:handle_get_method  76  77  78     #  79     # HTTP Connection Handler  80     #  81     def handle_connection( sock )  82  83       print "Handling new connection\n"  84  85       # Grab a line from the client  86       line = sock.gets  87  88       # Removing any leading/trailing whitespace and  89       # trailing newline  90       line.strip!.chomp!  91  92       # Split the line into three elements  93       # (command, filename, version)  94       elements = line.split  95  96       # Check the request -- we handle only GET requests  97       if elements[0] =~ "GET"  98  99         # Call our GET method handler 100         handle_get_method( sock, elements[1] ) 101 102       else 103 104         print "Unknown Method " + elements[0] + "\n" 105 106         # Unknown method -- notify client 107         sock.write("HTTP/1.0 501 Unimplemented Method\n\n") 108 109       end 110 111     end # handle_connection 112     protected:handle_connection 113 114 115     # 116     # Initialization function for the simple HTTP server 117     # 118     def start( port = 80 ) 119 120       # Create a new TCP Server using port 80 121       servsock = TCPserver::new( port ) 122 123       # Make the port immediately reusable after this 124       # socket is closed 125       servsock.setsockopt( Socket::SOL_SOCKET, 126                            Socket::SO_REUSEADDR, 1 ) 127 128       # Debug data -- emit the server socket info 129       print("server address : ", 130              servsock.addr::join(":"), "\n\n") 131 132       # The big loop 133       while true 134 135         # Await a connection from a client socket 136         clisock = servsock.accept 137 138         # Emit some debugging data on the peer 139         print("Accepted new connection", 140                clisock.peeraddr::join(":"), "\n") 141 142         # handle the new connection 143         self::handle_connection( clisock ) 144 145         print "\n" 146 147         # Close the client connection 148         clisock.close 149 150       end 151 152     end # start 153 154   end
end example

This implementation of the simple HTTP server is based upon a simple class that provides the HTTP server functionality. The only visible method is the start method, which starts a new HTTP server using a given port. Because the start method is the entry point to our simple HTTP server class, we’ll start here first.

The start method accepts a single argument, the port number to use for the HTTP server (line 118). If the caller provides no port number, the default port number of 80 is used. Next, as with all stream servers, we create a new TCP server socket using the TCPServer::new method. We provide our port variable, which would have been defaulted if not specified. With our new server socket, stored in servsock, we make the address/port combination reusable using the SO_REUSEADDR socket option (lines 125 and 126). Our server socket is now ready to accept connections, so we emit some debugging data using the addr method to identify some information about the server.

At line 133, we start our server loop. This infinite loop awaits and then handles incoming HTTP sessions. At line 136, we accept new client connections using the accept method and the server socket (servsock). When a client connects, a new socket will be returned representing the socket to use for this session. This will be stored in clisock. To identify that a new client connection has been received, we use the peeraddr function. This returns information identifying the peer end of the new client socket, which we emit to the display. Next, at line 143, we call the handle_connection method to service the given HTTP request. This method is a protected method within our Simple_http_server class. After the session has been serviced using handle_connection, we close the client socket at line 148 using the close method and start the server loop again awaiting a client connection with the accept method.

The handle_connection method of our class is responsible for handling the client’s HTTP request (lines 81–112). We begin getting a line of text from the socket, which represents the actual request (the first line defines the HTTP message type and file of interest). The gets method is used on the client socket and results in a string being stored in line. We use the strip! to remove leading and trailing whitespace from the line (line 90). We then use the chomp! method on line to remove the trailing record separator (in this case, ‘\n’). Certain Ruby methods utilize the ‘!’ modifier, which operates directly on the particular variable. In the case of strip and chomp, the given variable is modified instead of returning a new variable.

At line 94, we use the split method to split the line into N separate strings. The new string array is stored in variable elements. At line 97, we check the first element to see that it’s the ‘GET’ string (representing the HTTP GET method). In this simple HTTP server, we handle only the GET method. If we have received a GET request from the client, we use the method handle_get_method, to service the actual client request. If the server has received some other type of request, we specify an error to the client using the write method at line 107. We emit a standard HTTP error, which is split into three parts, the HTTP version number, the error code, and a human-readable string identifying the error. The particular error we emit here is the unimplemented error, or 501.

One final point to note about the method is line 112. The protected:handle_connection specifies that the handle_connection method is protected to this class (Simple_http_server). Therefore, the method can only be invoked by methods within this class, and not by external means. The only method that isn’t protected within the Simple_http_server class is the start method, which is used to start the server.

Method handle_get_method, called from handle_connection, is invoked only when a request is received containing the HTTP GET method (lines 46–75). Method handle_get_method is called with two arguments representing the client socket and the filename (parsed from the GET request). We begin by stripping the ‘/’ character, which will be present at the beginning of the filename (line 48). This character specifies the root file system (directory portion of the filename), but isn’t useful when we’re trying to locate the file using file access methods. We test whether the file actually exists using the File::exist? method at line 50. If the exist? method returns false, we emit another HTTP response method using the write method. This method is error code 404, or file-not-found (line 70).

If the file is found (in the directory where the server was started), we identify the content type of the file using the internal method define_content_type (at line 55). The string response from define_content_type is stored in the local variable content_type. We next emit an HTTP response header using emit_response_header, specifying our client socket and the content type that we’re serving (line 57).

We emit the file following the HTTP response header in lines 60–63. We open the file for read, storing a file descriptor in the file variable. Next, we use the socket write method to emit the contents of the file returned by file.read (line 61). The entire file will be read and emitted by this single line. Next, we close the file using the close method and then write a final newline to the socket using the write method at line 63.

Let’s now look at the final two methods that are used by the Simple_http_server. At lines 30–40, the HTTP response message header is served through the socket. This consists of a basic number of message headers, including the status response (first line) and the content type being served (through the previously created content_type variable). The write method is used in each case to emit the headers. Note that in the end, two newlines are emitted, which are required by HTTP to separate the HTTP message header from the message body (a blank line must separate them).

The define_content_type method is used to identify the content type of the content requested based upon the suffix of the filename (lines 8–24). We split the filename into two parts at line 10 using the split method. The first element of the array will contain the filename and the second element will contain the file extension (suffix). Lines 12–21 provide a case statement to return a string based upon the file extension found. The string that’s returned is used solely in the construction of the response header. The content_type informs the client how it should treat the response message body.

That completes the source for the Simple_http_server class. Now, let’s look at how a user might use the class. Listing 20.11 provides a sample illustration of its use. At line 5, we create a new HTTP server using the new method (on the Simple_http_server class). We then call the start method of our new class instance and specify that it should occupy port 80 (line 9). That’s it! In a threaded design, we could specify another HTTP server by simply instantiating the Simple_http_server to another class instance, and start it on another port.

Listing 20.11 Instantiating a new HTTP server.

start example
 1   # 2   # Create a new HTTP server and start it on port 80 3   # 4 5   server = Simple_http_server::new 6 7   print "Starting new HTTP server on port 80\n" 8 9   server.start 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