In this checkpoint, you will have to start parsing the HTTP messages and building responses for the client of your library. The image to the right is what I call a (B)roken UML (BUML) diagram to show you the sequence of events that occur between the daemon and the client (in this case, a web server). Note that the dark arrow heads indicate the direction of the function call (i.e., who made the function call) and time increases from the top of the figure down towards the bottom of the figure.
Initially, the Web Server calls MHD_start_daemon
followed by MHD_run
. At some point, a TCP connection is
established with the daemon, which calls
the AcceptPolicyCallback
function suplied by the
daemon. Assuming this returns MHD_YES
, the connection is
established and an HTTP request is received over the TCP connection.
When the request is received, we read it into our internal buffers and we have to parse the headers because the header is terminated by two CRLFs (i.e., "\r\n\r\n" indicates the end of the header). The first line of a request begins with GET, HEAD, PUT, POST, TRACE, OPTIONS, or DELETE. Note that HTTP 1.1 allows other methods to be defined. For our project, you will only have to deal with GETs (though you can get extra credit for implementing other methods).
Once we have determined that we have the entire body of an HTTP
request, we call the AccessHandlerCallback, which passes the session
to the Web Server. The Web Server will not return from this
function call until it has optionally
called the functions listed in the figure including
MHD_queue_response
. In essence, a single request will
cause a single response to be
created. After MHD_queue_response
is called, the daemon
will send the response to the network. Note that if the server created
the response object by MHD_create_response_from_callback
,
our daemon will need to begin writing the response object to the
network and continually call
the MHD_ContentReaderCallback
function until the Web
Server has given it all of its data. Additionally, if we used
MHD_ContentReaderCallback
, we must
call MHD_ContentReaderFreeCallback
when we are done
receiving data from the callback.
GET requests have the format:
GET /somelocation/myfile.html HTTP/1.1 Host: www.mysite.com Accept: *Note that the second field in the first line is the URL of the file or object you are trying to get. The third field is always HTTP/1.1, to indicate the protocol version we are implementing.
Header lines begin after the first line. These always begin with a keyword, followed by a colon (:), followed by a characters that ends with a CRLF ("\r\n"). For example, in this GET, the two header lines have keys of Host and Accept while the values of each line are www.mysite.com and * respectively.
Because the client will call MHD_get_session_values
or
MHD_lookup_session_values
, you will need to parse these
from your input buffer so that you can give them (or locate) key,
value pairs.
From the daemon's perspective, the only header field you are
interested in is one that looks like Connection: Close
This pair indicates that a session should be terminated after the
response is sent back.
In order to decide how to handle a request, the Web Server can
register Handlers. When we get a request, it will specify a
URI on the first line, as noted with the GET above. URIs on this line
always being with '/'. The Web Server registers prefixes
which determine which handler is called. To register a prefix, the
server calls MHD_register_handler
(and to unregister a
prefix it calls MHD_unregister_handler
).
For example, a Web Server might register "/" and "/images" as the first and second handlers respectively. Anything beginning with "/images" would call the second handler while anything begining with just "/" would call the first handler.
To store this, your MHD_daemon struct must contain a list (array, whatever) of prefix to AccessHandler mappings. You'll check incoming URIs against this list and then call the correct AccessHandler.
As with headers, our daemon does not determine what kind of response
is generated, but leaves it up to the web server to decide this. The
web server will call MHD_create_response_from_data
or
MHD_create_response_from_callback
. In both cases, the
data you receive from the client of your code (the Web Server, in this
case) fills in the data portion of your response. An HTTP
response looks like the following:
HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 54 <html>...<html>The first field in the first line is always HTTP/1.1, and the response code (the second field) is given to you when the Web Server calls MHD_queue_response. The third field is a reason code. This field can actually say whatever you want, regardless of whether it matches the response code or not. Here is a table of common response phrases. If you receive one of these response codes, you should simply match it with a default response phrase:
Status Code | Reason Phrase |
---|---|
100 | Continue |
101 | Switching Protocols |
200 | OK |
201 | Created |
202 | Accepted |
203 | Non-Authoritative Information |
204 | No Content |
205 | Reset Content |
206 | Partial Content |
300 | Multiple Choices |
301 | Moved Permanently |
302 | Found |
303 | See Other |
304 | Not Modified |
305 | Use Proxy |
306 | (Unused) |
307 | Temporary Redirect |
400 | Bad Request |
401 | Unauthorized |
402 | Payment Required |
403 | Forbidden |
404 | Not Found |
405 | Method Not Allowed |
406 | Not Acceptable |
407 | Proxy Authentication Required |
408 | Request Timeout |
409 | Conflict |
410 | Gone |
411 | Length Required |
412 | Precondition Failed |
413 | Request Entity Too Large |
414 | Request URI Too Long |
415 | Unsupported Media Type |
416 | Requested Range Not Satisfiable |
417 | Expectation Failed |
500 | Internal Server Error |
501 | Not Implemented |
502 | Bad Gateway |
503 | Service Unavailable |
504 | Gateway Timeout |
505 | HTTP Version Not Supported |
The additional headers for the reponse are generated by the Web
Server. To add a header, the Web Server
calls MHD_add_response_header
and to delete a header the
Web Server calls MHD_del_response_header
. The Web Server
can also iterate through the response headers by
calling MHD_get_response_headers
to which the daemon will
call the MHD_KeyValueIterator
for each header already in
the response object.
You can build the web server side of things to test your code by using
the daemontest.c code. This code can allow you to start and stop a web
server, create the callbacks, and even read files and pass them as
responses to GET requests (MHD_create_response_from_data
can take a
pointer to a buffer that holds the file in it). You can simply do this
by registering a prefix handler for "/" which will handle all URI
requests. This would be an excellent way to understand how
your daemon works and to test it to make sure it actually works the
way as intended!