libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

exploringrequests.inc (5447B)


      1 This chapter will deal with the information which the client sends to the
      2 server at every request. We are going to examine the most useful fields of such a request
      3 and print them out in a readable manner. This could be useful for logging facilities.
      4 
      5 The starting point is the @emph{hellobrowser} program with the former response removed.
      6 
      7 This time, we just want to collect information in the callback function, thus we will
      8 just return MHD_NO after we have probed the request. This way, the connection is closed
      9 without much ado by the server.
     10 
     11 @verbatim
     12 static enum MHD_Result
     13 answer_to_connection (void *cls, struct MHD_Connection *connection,
     14                       const char *url,
     15 		      const char *method, const char *version, 
     16 		      const char *upload_data, 
     17                       size_t *upload_data_size, void **req_cls)
     18 {
     19   ...  
     20   return MHD_NO;
     21 }
     22 @end verbatim
     23 @noindent
     24 The ellipsis marks the position where the following instructions shall be inserted.
     25 
     26 
     27 We begin with the most obvious information available to the server, the request line. You should
     28 already have noted that a request consists of a command (or "HTTP method") and a URI (e.g. a filename).
     29 It also contains a string for the version of the protocol which can be found in @code{version}.
     30 To call it a "new request" is justified because we return only @code{MHD_NO}, thus ensuring the 
     31 function will not be called again for this connection. 
     32 @verbatim
     33 printf ("New %s request for %s using version %s\n", method, url, version);
     34 @end verbatim
     35 @noindent
     36 
     37 The rest of the information is a bit more hidden. Nevertheless, there is a lot of it sent from common
     38 Internet browsers. It is stored in "key-value" pairs and we want to list what we find in the header. 
     39 As there is no mandatory set of keys a client has to send, each key-value pair is printed out one by
     40 one until there are no more left. We do this by writing a separate function which will be called for
     41 each pair just like the above function is called for each HTTP request. 
     42 It can then print out the content of this pair.
     43 @verbatim
     44 static enum MHD_Result
     45 print_out_key (void *cls, enum MHD_ValueKind kind,
     46                const char *key, const char *value)
     47 {
     48   printf ("%s: %s\n", key, value);
     49   return MHD_YES;
     50 }
     51 @end verbatim
     52 @noindent
     53 
     54 To start the iteration process that calls our new function for every key, the line
     55 @verbatim
     56 MHD_get_connection_values (connection, MHD_HEADER_KIND, &print_out_key, NULL);
     57 @end verbatim
     58 @noindent
     59 needs to be inserted in the connection callback function too. The second parameter tells the function 
     60 that we are only interested in keys from the general HTTP header of the request. Our iterating
     61 function @code{print_out_key} does not rely on any additional information to fulfill its duties
     62 so the last parameter can be NULL.
     63 
     64 All in all, this constitutes the complete @code{logging.c} program for this chapter which can be 
     65 found in the @code{examples} section.
     66 
     67 Connecting with any modern Internet browser should yield a handful of keys. You should try to
     68 interpret them with the aid of @emph{RFC 9110}.
     69 Especially worth mentioning is the "Host" key which is often used to serve several different websites
     70 hosted under one single IP address but reachable by different domain names (this is called virtual hosting).
     71 
     72 @heading Conclusion
     73 The introduced capabilities to itemize the content of a simple GET request---especially the
     74 URI---should already allow the server to satisfy clients' requests for small specific resources
     75 (e.g. files) or even induce alteration of server state. However, the latter is not
     76 recommended as the GET method (including its header data) is by convention considered a "safe"
     77 operation, which should not change the server's state in a significant way.  By convention,
     78 GET operations can thus be performed by crawlers and other automatic software.  Naturally
     79 actions like searching for a passed string are fine.
     80 
     81 Of course, no transmission can occur while the return value is still set to @code{MHD_NO} in the
     82 callback function.
     83 
     84 @heading Exercises
     85 @itemize @bullet
     86 @item
     87 By parsing the @code{url} string and delivering responses accordingly, implement a small server for
     88 "virtual" files. When asked for @code{/index.htm@{l@}}, let the response consist of a HTML page
     89 containing a link to @code{/another.html} page which is also to be created "on the fly" in case of
     90 being requested. If neither of these two pages are requested, @code{MHD_HTTP_NOT_FOUND} shall be
     91 returned accompanied by an informative message.
     92 
     93 @item
     94 A very interesting information has still been ignored by our logger---the client's IP address. 
     95 Implement a callback function 
     96 @verbatim
     97 static enum MHD_Result
     98 on_client_connect (void *cls,
     99                    const struct sockaddr *addr,
    100                    socklen_t addrlen)
    101 @end verbatim
    102 @noindent
    103 that prints out the IP address in an appropriate format. You might want to use the POSIX function
    104 @code{inet_ntoa} but bear in mind that @code{addr} is actually just a structure containing other
    105 substructures and is @emph{not} the variable this function expects. 
    106 Make sure to return @code{MHD_YES} so that the library knows the client is allowed to connect
    107 (and to then process the request). If one wanted to limit access basing on IP addresses, this would be the place
    108 to do it. The address of your @code{on_client_connect} function must be passed as the third parameter to the
    109 @code{MHD_start_daemon} call.
    110 
    111 @end itemize