Simple HTTP Server

 < Day Day Up > 



Now, let’s look at a simple HTTP server written in the C language. We focus only on the networking elements; the remaining functions are provided on the CD-ROM.

The first part of our simple HTTP server is the creation and configuration of our stream server socket. Listing 16.9 shows the start function that is used to start the server.

At line 7, we create our stream socket, storing it in serverFd (our server socket descriptor). To ensure that we can restart the server without having to wait two minutes before the address/port combination becomes available, the SO_REUSEADDR socket option is used. We name our server socket at lines 16–19, permitting connections from any interface (using the INADDR_ANY wildcard address) and the port provided by the caller to start. The bind call is used at lines 22–23 to performing the actual name binding.

At line 26, we identify our willingness to accept new connections using the listen call. Note that we specify that up to five connections may be outstanding while we process the current one.

At line 28, our infinite loop begins, which represents our simple HTTP server. The accept call at line 31 accepts new client connections, and a quick test of the return value identifies whether an error was returned. If an error was returned, we exit the loop using the C break keyword (line 32). Otherwise, we call the handle_connection function at line 35 and pass the client socket descriptor returned by the accept API function at line 31. Upon returning from handle_connection, we close the client socket descriptor at line 38 and continue with the loop at line 40.

If an error had resulted from the accept call (detected at line 32), control would have resumed at line 43, where the server socket would be closed and the server then ended.

Listing 16.9 Simple HTTP server function start.

start example
  1   static void start( unsigned short port )  2   {  3     int serverfd, clientfd, on=1;  4     struct sockaddr_in servaddr;  5  6     /* Create a new TCP server */  7     serverfd = socket( AF_INET, SOCK_STREAM, 0 );  8  9     /* Make the port immediately reusable after this socket 10      * is close. 11      */ 12     setsockopt( serverfd, SOL_SOCKET, SO_REUSEADDR, 13                  &on, sizeof(on) ); 14 15     /* Initialize the address structure to which we'll bind */ 16     bzero( (void *)&servaddr, sizeof(servaddr) ); 17     servaddr.sin_family = AF_INET; 18     servaddr.sin_addr.s_addr = htonl( INADDR_ANY ); 19     servaddr.sin_port = htons( port ); 20 21     /* Bind the address to the socket */ 22     bind( serverfd, 23            (struct sockaddr *)&servaddr, sizeof(servaddr) ); 24 25     /* Make the new server visible for incoming connections */ 26     listen( serverfd, 5 ); 27 28     while ( 1 ) { 29 30       /* Await a connection from a client socket */ 31       clientfd = accept( serverfd, NULL, NULL ); 32       if (clientfd <= 0) break; 33 34       /* handle the new connection */ 35       handle_connection( clientfd ); 36 37       /* Close the client connection */ 38       close( clientfd ); 39 40     } 41 42     /* Close the server socket */ 43     close( serverfd ); 44 45     return; 46   }
end example

The handle_connection function (shown in Listing 16.10) exists to satisfy the HTTP command received by the peer client. The result will be an HTTP response sent back through the same socket. The handle_connection function can be logically split into two parts, extracting the HTTP command from the socket and then generating the response.

Lines 9–26 provide the means to read the HTTP command from the socket. Recall from the HTTP server discussion in Chapter 15, Software Patterns Introduction, that an HTTP command is terminated by a single blank line. Lines 18–20 provide a means to detect this situation and break from the loop of reading from the socket.

The next part of handle_connection is providing the response. We first check the type of request at line 29 and then if it is a GET request, we call the handle_get_method function. Note that we also parse the requested filename from the HTTP request prior to calling the handle_get_method function.

If the request was not GET, we return an HTTP error message in lines 39–45. Note that in this case, a standard HTTP error code is written to the peer client using the write function.

Listing 16.10 Simple HTTP server function handle_connection.

start example
 1   static void handle_connection( int sock )  2   {  3     int len, max, ret;  4     char inbuf[1024]={0};  5  6     max = 0;  7  8     /* Read in the HTTP request message from the socket */  9     while ( 1 ) { 10 11       len = read( sock, &inbuf[max], (1024-max) ); 12       if (len <= 0) return; 13 14       /* Update the total string size */ 15       max += len; 16 17       /* HTTP request message ends with CRLF/CRLF */ 18       if ((inbuf[max-4] == 0x0d) && (inbuf[max-3] == 0x0a) && 19           (inbuf[max-2] == 0x0d) && (inbuf[max-1] == 0x0a)) { 20         break; 21 22       } 23 24       inbuf[max] = 0; 25 26     } 27 28     /* Determine the request type */ 29     if (!strncmp( inbuf, "GET", 3)) { 30 31       char filename[100]; 32 33       getFilename( inbuf, filename, 4 ); 34 35       handle_get_method( sock, filename ); 36 37     } else { 38 39       const char *notimpl= 40                    {"HTTP/1.0 501 Not Implemented.\n\n"}; 41 42       printf("Unknown Method %s\n", inbuf); 43 44       /* Unknown file — notify client */ 45       write( sock, notimpl, strlen(notimpl) ); 46 47     } 48 49   }
end example

The handle_get_method function in Listing 16.11 provides the means to satisfy an HTTP GET request. This function performs two basic functions. The first is to identify whether the file requested by the peer client exists on the local file system (lines 9–17). If the file is not found, a standard HTTP error message is written to the peer. In this case, it’s the HTTP 404 file-not-found error.

If the file was found, then we first determine the type of file we’re dealing with (line 23). After this is known, we can generate the HTTP response message header (which includes the type of content being sent back to the peer).

Finally, in lines 30–41, the contents of the file are read using the read function and then written through the client socket to the peer using the write function. To close the HTTP transaction, we send a new line representing a blank line (end-of-message) and then close the socket using the close function.

Listing 16.11 Simple HTTP server function handle_get_method.

start example
 1   static void handle_get_method( int sock, char *filename )  2   {  3     int fd, ret;  4     char buffer[255+1];  5  6     /* Try to open the file requested by the client */  7     fd = open( filename, O_RDONLY );  8  9     if (fd == -1) { 10 11       /* Can't find, emit not found error to the client */ 12       const char *notfound= 13                     {"HTTP/1.0 404\n\n File not found.\n\n"}; 14 15       write( sock, notfound, strlen( notfound ) ); 16 17       printf("File not found.\n"); 18 19     } else { 20 21       char *content_type; 22 23       content_type = define_content_type( filename ); 24 25       emit_response_header( sock, content_type ); 26 27       /* Read and emit the file contents to the client 28        * through the socket. 29        */ 30       ret = 1; 31       while ( ret > 0 ) { 32 33         ret = read( fd, buffer, 255 ); 34 35         if ( ret > 0 ) { 36 37           write( sock, buffer, ret ); 38 39         } else break; 40 41       } 42 43       /* Emit a newline */ 44       write( sock, "\015\012", 2 ); 45 46       close( fd ); 47 48     } 49 50     return; 51   }
end example

The final function to consider for the simple HTTP server is the emit_response_ header function. This function, shown in Listing 16.12, creates and then sends the HTTP response message header to the client peer through the socket.

The caller provides the client socket and the content_type to the emit_response_ header function. Recall that the content_type string may be “text/html” for an HTML page or “application/octet-stream” for a binary stream of data.

The function simply constructs the HTTP response message within the resp_header buffer (using the variable content_type) and then writes this buffer to the peer through the passed client socket (sock).

Listing 16.12 Simple HTTP server function emit_response_header.

start example
 1   static void emit_response_header( int  sock,  2                                     char *content_type )  3   {  4     char resp_header[256];  5  6     /* Construct the response header (including the variable  7      * content type) and send it to the client through the  8      * socket.  9      */ 10     sprintf( resp_header, 11              "HTTP/1.1 200 OK\n" 12              "Server: C shttp\n" 13              "Connection: close\n" 14              "Content-Type: %s\n\n", content_type ); 15 16     write( sock, resp_header, strlen(resp_header) ); 17 18     return; 19   }
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