A ‘minimal’ web-server We can glean insights about the operation of the HTTP protocol if we create our own web-server A web-browser is our ‘client’ Our ‘mini’ server Mozilla Firefox Request-message (to server) HTTP request line Request ‘headers’ Optional ‘body’ Response-message (to client) HTTP status line Response ‘headers’ Optional ‘body’ TCP server ‘boilerplate’ • By now we have seen that any connection oriented ‘server’ has some standard steps to execute by way of ‘connection-setup’ – Initialize a host ‘socket-address’ structure – Create a TCP socket to use for ‘listening’ – Bind the ‘socket-address’ to this socket – Enable the socket to ‘reuse’ its port-number – Convert the socket into a ‘listening’ socket – Accept a ‘connection-request’ from a client ‘helper’ functions • We can employ standard library-functions to help setup the internet socket-address data-structure for our server’s host: – ‘gethostname()’ – ‘gethostbyname()’ – ‘inet_ntoa()’ – ‘inet_addr()’ – ‘htons()’ Our ‘sockaddr_in’ setup struct sockaddr_in sin_family sin_port sin_addr padded with zeros Setup our ‘listening’ socket ‘iterative’ server-design ‘accept’ a connection-request from a client ‘receive’ the HTTP request-message validate and process the HTTP command ‘send’ the HTTP response-message ‘close’ the connection with this client We use some new ‘helper’ functions for these steps ‘fdopen()’ • To conveniently process the incoming stream of characters in a client’s HTTP request-message (which is organized into lines of text with CRLF as the line-delimiter), and to conveniently reply with similarly organized lines of text, we would like to use the standard ‘buffered’ i/o functions • To do this requires substituting ‘streams’ for our TCP ‘connection’ socket, which is accomplished by using the ‘fdopen()’ library-function ‘fdopen()’ and ‘dup()’ • To conveniently process the incoming stream of characters in a client’s HTTP request-message (which is organized into lines of text with CRLF as the line-delimiter), and to conveniently reply with similarly organized lines of text, we would like to use the standard ‘buffered’ i/o functions • To do this will require us to associate ‘streams’ with our TCP ‘connection’ socket, one for input, one for output, which we can accomplish using the ‘fdopen()’ and ‘dup()’ library-functions Our i/o streams • Our connection-socket is named ‘conn’: struct sockaddr_in socklen_t int caddr = { 0 }; salen = sizeof( caddr ); conn = accept( sock, (sockaddr*)&caddr, &calen ); • We associate an input-stream with ‘conn’: FILE *rx = fdopen( conn, “r” ); • And we associate an output-stream with a ‘duplicate’ of our connection-socket ‘conn’: FILE *tx = fdopen( dup( conn ), “w” ); Request message’s lines • Now it’s easy to receive the first ‘line’ of an HTTP request-message sent by our client: char request[ BUFSIZ ] = { 0 }; fgets( request, BUFSIZ, rx ); • The remaining lines (request headers and the empty line) can be received like this: char header[ BUFSIZ ] = { 0 }; while ( fgets( request, BUFSIZ, rx ) ) if ( strcmp( header, “\r\n” ) == 0 ) break; Parsing the ‘request’ • The HTTP Request-line is comprised of three fields, separated by a blank-space and terminated with the CR/LF delimiter <COMMAND> <RESOURCE> <VERSION> ”\r\n” • We can use the formatted string-scan function ‘sscanf()’ to parse the request char cmd[ BUFSIZ ] = { 0 }, arg[ BUFSIZ ] = { 0 }; strcpy( arg, “./” ); // for restricting the client’s access if ( sscanf( “%s%s”, cmd, arg+2 ) != 2 ) { perror( “sscanf” ); } Validating the request • For now our ‘miniature’ web-server only accepts the basic HTTP ‘GET’ command: if ( strcmp( cmd, “GET” ) != 0 ) { // the HTTP Response status, headers, and empty line fprintf( tx, “HTTP/1.0 501 Not Implemented \r\n” ); fprintf( tx, “Content-type: text/plain \r\n” ); fprintf( tx, “\r\n” ); // the HTTP Response-message’s ‘body’ fprintf( tx, “That one is not yet implemented \r\n” fclose( rx ); fclose( tx ); continue; } ‘nonexistent’ resource? • The ‘stat()’ library-function can be used to verify that a requested resource exists struct stat info = { 0 }; if ( stat( arg, &info ) < 0 ) // the target does not exist { // the HTTP Response’s status, headers, and empty line fprintf( tx, “HTTP/1.0 404 Not Found \r\n” ); fprintf( tx, “Content-type: text/plain \r\n” ); fprintf( tx, “\r\n” ); // the HTTP Response-message’s ‘body’ fprintf( tx, “Requested item not found \r\n” fclose( rx ); fclose( tx ); continue; } ‘struct stat’ ‘statdemo()’ • We have posted a demo-program which lets you see the information about a file which the Linux operating system keeps track of (including the ‘type’ of the file) EXAMPLE: $ ./statdemo mywebsvr.cpp ‘folder’ or ‘file’? • Our server can send its client a directorylisting if that type of resource is requested if ( S_ISDIR( info.st_mode ) ) // the requested resource is a directory { // the HTTP Response’s status, headers, and empty line fprintf( tx, “HTTP/1.0 200 OK \r\n” ); fprintf( tx, “Content-type: text/plain \r\n” ); fprintf( tx, “\r\n” ); // the HTTP Response-message’s ‘body’ fflush( tx ); if ( fork() == 0 ) // child-process will deliver the directory-listing { dup2( conn, 1 ); dup2( conn, 2 ); close( conn ); execlp( “ls”, “ls”, “-l”, arg, NULL ); exit( 1 ); } wait( NULL ); // parent-process will sleep until tge child exits fclose( rx ); fclose( tx ); continue; } A sample web-page • We created a tiny webpage example for demonstrating our web-server’s ability to deliver an ‘HTML’ file to a web-browser Delivering an ‘html’ file if ( strcmp( strrchr( arg, ‘.’ ) + 1, “html” ) == 0 ) // the requested resource is an ‘html’ file { // the HTTP Response’s status, headers, and empty line fprintf( tx, “HTTP/1.0 200 OK \r\n” ); fprintf( tx, “Content-type: text/html \r\n” ); fprintf( tx, “\r\n” ); // the HTTP Response-message’s ‘body’ fflush( tx ); if ( fork() == 0 ) // child-process will deliver the file’s contents { dup2( conn, 1 ); dup2( conn, 2 ); close( conn ); execlp( “cat”, “cat”, arg, NULL ); exit( 1 ); } wait( NULL ); // parent-process will sleep until the child exits fclose( rx ); fclose( tx ); continue; } ‘mywebsvr.cpp’ • We posted this miniature web-server on our course website for you to download • You can use Linux’s ‘Firefox’ browser to see if indeed it can deliver the sample HTML-file (named ‘mywebpage.html’) • First start the web-server application, then launch ‘Firefox’ and type in this URL: <http://hrn235xx:54321/mywebpage.html> Your classroom host’s station-number In-class exercise #1 • Try using Microsoft’s Internet Explorer to request the ‘mywebpage.html’ document • Edit your copy of ‘mywebpage.html’ to add the following attributes within the <body> tag in that HTML document: <body text=“#00FF00” bgcolor=“#0000FF”> In-class exercise #2 • Copy the file named ‘warrior.jpg’ from our course website into your local directory • Then modify the ‘mywebpage.html’ file by adding this extra line in front of </body>: <img src=“warrior.jpg”> • Now extend the functionality of our ‘mini’ web-server by adding a block of new code which enables a child-process to deliver an image-file having the ‘.jpg’ file-suffix Exercise #2 (continued) • You can do this with your Editor by using ‘copy-and-paste’ applied to the code-block that delivers an ‘html’ file to a client – just change the ‘html’ file-suffix to ‘jpg’, and change the ‘Content-type’ statement so that ‘text/html’ becomes ‘image/jpeg”