A ‘minimal’ web-server We can glean insights about the

advertisement
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”
Download