Blame doc/chapters/exploringrequests.inc

Packit 875988
This chapter will deal with the information which the client sends to the
Packit 875988
server at every request. We are going to examine the most useful fields of such an request
Packit 875988
and print them out in a readable manner. This could be useful for logging facilities.
Packit 875988
Packit 875988
The starting point is the @emph{hellobrowser} program with the former response removed.
Packit 875988
Packit 875988
This time, we just want to collect information in the callback function, thus we will
Packit 875988
just return MHD_NO after we have probed the request. This way, the connection is closed
Packit 875988
without much ado by the server.
Packit 875988
Packit 875988
@verbatim
Packit 875988
static int 
Packit 875988
answer_to_connection (void *cls, struct MHD_Connection *connection, 
Packit 875988
                      const char *url, 
Packit 875988
		      const char *method, const char *version, 
Packit 875988
		      const char *upload_data, 
Packit 875988
                      size_t *upload_data_size, void **con_cls)
Packit 875988
{
Packit 875988
  ...  
Packit 875988
  return MHD_NO;
Packit 875988
}
Packit 875988
@end verbatim
Packit 875988
@noindent
Packit 875988
The ellipsis marks the position where the following instructions shall be inserted.
Packit 875988
Packit 875988
Packit 875988
We begin with the most obvious information available to the server, the request line. You should
Packit 875988
already have noted that a request consists of a command (or "HTTP method") and a URI (e.g. a filename).
Packit 875988
It also contains a string for the version of the protocol which can be found in @code{version}.
Packit 875988
To call it a "new request" is justified because we return only @code{MHD_NO}, thus ensuring the 
Packit 875988
function will not be called again for this connection. 
Packit 875988
@verbatim
Packit 875988
printf ("New %s request for %s using version %s\n", method, url, version);
Packit 875988
@end verbatim
Packit 875988
@noindent
Packit 875988
Packit 875988
The rest of the information is a bit more hidden. Nevertheless, there is lot of it sent from common
Packit 875988
Internet browsers. It is stored in "key-value" pairs and we want to list what we find in the header. 
Packit 875988
As there is no mandatory set of keys a client has to send, each key-value pair is printed out one by
Packit 875988
one until there are no more left. We do this by writing a separate function which will be called for
Packit 875988
each pair just like the above function is called for each HTTP request. 
Packit 875988
It can then print out the content of this pair.
Packit 875988
@verbatim
Packit 875988
int print_out_key (void *cls, enum MHD_ValueKind kind, 
Packit 875988
                   const char *key, const char *value)
Packit 875988
{
Packit 875988
  printf ("%s: %s\n", key, value);
Packit 875988
  return MHD_YES;
Packit 875988
}
Packit 875988
@end verbatim
Packit 875988
@noindent
Packit 875988
Packit 875988
To start the iteration process that calls our new function for every key, the line
Packit 875988
@verbatim
Packit 875988
MHD_get_connection_values (connection, MHD_HEADER_KIND, &print_out_key, NULL);
Packit 875988
@end verbatim
Packit 875988
@noindent
Packit 875988
needs to be inserted in the connection callback function too. The second parameter tells the function 
Packit 875988
that we are only interested in keys from the general HTTP header of the request. Our iterating
Packit 875988
function @code{print_out_key} does not rely on any additional information to fulfill its duties
Packit 875988
so the last parameter can be NULL.
Packit 875988
Packit 875988
All in all, this constitutes the complete @code{logging.c} program for this chapter which can be 
Packit 875988
found in the @code{examples} section.
Packit 875988
Packit 875988
Connecting with any modern Internet browser should yield a handful of keys. You should try to 
Packit 875988
interpret them with the aid of @emph{RFC 2616}.
Packit 875988
Especially worth mentioning is the "Host" key which is often used to serve several different websites
Packit 875988
hosted under one single IP address but reachable by different domain names (this is called virtual hosting).
Packit 875988
Packit 875988
@heading Conclusion
Packit 875988
The introduced capabilities to itemize the content of a simple GET request---especially the
Packit 875988
URI---should already allow the server to satisfy clients' requests for small specific resources
Packit 875988
(e.g. files) or even induce alteration of server state. However, the latter is not
Packit 875988
recommended as the GET method (including its header data) is by convention considered a "safe"
Packit 875988
operation, which should not change the server's state in a significant way.  By convention,
Packit 875988
GET operations can thus be performed by crawlers and other automatic software.  Naturally
Packit 875988
actions like searching for a passed string are fine.
Packit 875988
Packit 875988
Of course, no transmission can occur while the return value is still set to @code{MHD_NO} in the
Packit 875988
callback function.
Packit 875988
Packit 875988
@heading Exercises
Packit 875988
@itemize @bullet
Packit 875988
@item
Packit 875988
By parsing the @code{url} string and delivering responses accordingly, implement a small server for
Packit 875988
"virtual" files. When asked for @code{/index.htm@{l@}}, let the response consist of a HTML page
Packit 875988
containing a link to @code{/another.html} page which is also to be created "on the fly" in case of
Packit 875988
being requested. If neither of these two pages are requested, @code{MHD_HTTP_NOT_FOUND} shall be
Packit 875988
returned accompanied by an informative message.
Packit 875988
Packit 875988
@item
Packit 875988
A very interesting information has still been ignored by our logger---the client's IP address. 
Packit 875988
Implement a callback function 
Packit 875988
@verbatim
Packit 875988
static int on_client_connect (void *cls,
Packit 875988
                              const struct sockaddr *addr,
Packit 875988
			      socklen_t addrlen)
Packit 875988
@end verbatim
Packit 875988
@noindent
Packit 875988
that prints out the IP address in an appropriate format. You might want to use the POSIX function
Packit 875988
@code{inet_ntoa} but bear in mind that @code{addr} is actually just a structure containing other
Packit 875988
substructures and is @emph{not} the variable this function expects. 
Packit 875988
Make sure to return @code{MHD_YES} so that the library knows the client is allowed to connect
Packit 875988
(and to then process the request). If one wanted to limit access basing on IP addresses, this would be the place
Packit 875988
to do it. The address of your @code{on_client_connect} function must be passed as the third parameter to the
Packit 875988
@code{MHD_start_daemon} call.
Packit 875988
Packit 875988
@end itemize