|
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
|