230 likes | 397 Views
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’. Mozilla Firefox. Our ‘mini’ server. Request-message (to server). HTTP request line. Request ‘headers’. Optional ‘body’.
E N D
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’ Mozilla Firefox Our ‘mini’ server 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
‘iterative’ server-design ‘accept’ a connection-request from a client ‘receive’ the HTTP request-message We use some new ‘helper’ functions for these steps validate and process the HTTP command ‘send’ the HTTP response-message ‘close’ the connection with this client
‘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’: • We associate an input-stream with ‘conn’: • And we associate an output-stream with a ‘duplicate’ of our connection-socket ‘conn’: struct sockaddr_in caddr = { 0 }; socklen_t salen = sizeof( caddr ); int conn = accept( sock, (sockaddr*)&caddr, &calen ); FILE *rx = fdopen( conn, “r” ); 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: • The remaining lines (request headers and the empty line) can be received like this: char request[ BUFSIZ ] = { 0 }; fgets( request, BUFSIZ, rx ); 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 • We can use the formatted string-scan function ‘sscanf()’ to parse the request <COMMAND> <RESOURCE> <VERSION> ”\r\n” 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; }
‘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 directory-listing 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”