Home | History | Annotate | Download | only in chapters
      1 Now that we are able to inspect the incoming request in great detail,
      2 this chapter discusses the means to enrich the outgoing responses likewise.
      3 
      4 As you have learned in the @emph{Hello, Browser} chapter, some obligatory 
      5 header fields are added and set automatically for simple responses by the library
      6 itself but if more advanced features are desired, additional fields have to be created.
      7 One of the possible fields is the content type field and an example will be developed around it.
      8 This will lead to an application capable of correctly serving different types of files.
      9 
     10 
     11 When we responded with HTML page packed in the static string previously, the client had no choice
     12 but guessing about how to handle the response, because the server had not told him. 
     13 What if we had sent a picture or a sound file?  Would the message have been understood
     14 or merely been displayed as an endless stream of random characters in the browser?
     15 This is what the mime content types are for. The header of the response is extended
     16 by certain information about how the data is to be interpreted. 
     17 
     18 To introduce the concept, a picture of the format @emph{PNG} will be sent to the client 
     19 and labeled accordingly with @code{image/png}.
     20 Once again, we can base the new example on the @code{hellobrowser} program.
     21 
     22 @verbatim
     23 #define FILENAME "picture.png"
     24 #define MIMETYPE "image/png"
     25 
     26 static int 
     27 answer_to_connection (void *cls, struct MHD_Connection *connection, 
     28 		      const char *url, 
     29                       const char *method, const char *version, 
     30 		      const char *upload_data, 
     31               	      size_t *upload_data_size, void **con_cls)
     32 {
     33   unsigned char *buffer = NULL;
     34   struct MHD_Response *response;
     35 @end verbatim
     36 @noindent
     37  
     38 We want the program to open the file for reading and determine its size:
     39 @verbatim
     40   int fd;
     41   int ret;
     42   struct stat sbuf;
     43 
     44   if (0 != strcmp (method, "GET"))
     45     return MHD_NO;
     46   if ( (-1 == (fd = open (FILENAME, O_RDONLY))) ||
     47        (0 != fstat (fd, &sbuf)) )
     48     {
     49      /* error accessing file */
     50       /* ... (see below) */
     51     }
     52  /* ... (see below) */
     53 @end verbatim
     54 @noindent
     55 
     56 When dealing with files, there is a lot that could go wrong on the
     57 server side and if so, the client should be informed with @code{MHD_HTTP_INTERNAL_SERVER_ERROR}.
     58 
     59 @verbatim 
     60       /* error accessing file */
     61      if (fd != -1) close (fd);
     62       const char *errorstr =
     63         "<html><body>An internal server error has occured!\
     64                               </body></html>";
     65       response =
     66 	MHD_create_response_from_buffer (strlen (errorstr), 
     67 				         (void *) errorstr, 
     68 				         MHD_RESPMEM_PERSISTENT);
     69       if (response)
     70         {
     71           ret =
     72             MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR,
     73                                 response);
     74           MHD_destroy_response (response);
     75 
     76           return MHD_YES;
     77         }
     78       else
     79         return MHD_NO;
     80   if (!ret) 
     81     {
     82       const char *errorstr = "<html><body>An internal server error has occured!\
     83                               </body></html>";
     84 
     85       if (buffer) free(buffer);
     86     
     87       response = MHD_create_response_from_buffer (strlen(errorstr), (void*) errorstr,
     88                                                   MHD_RESPMEM_PERSISTENT);
     89 
     90       if (response)
     91         {     
     92           ret = MHD_queue_response (connection, 
     93 	      			    MHD_HTTP_INTERNAL_SERVER_ERROR, 
     94 				    response);
     95           MHD_destroy_response (response);
     96 
     97           return MHD_YES;    
     98         } 
     99       else return MHD_NO;
    100     }
    101 @end verbatim
    102 @noindent
    103 
    104 Note that we nevertheless have to create a response object even for sending a simple error code.
    105 Otherwise, the connection would just be closed without comment, leaving the client curious about
    106 what has happened.
    107 
    108 But in the case of success a response will be constructed directly from the file descriptor:
    109 
    110 @verbatim
    111      /* error accessing file */
    112      /* ... (see above) */
    113     }
    114 
    115   response =
    116     MHD_create_response_from_fd_at_offset (sbuf.st_size, fd, 0);
    117   MHD_add_response_header (response, "Content-Type", MIMETYPE);
    118   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    119   MHD_destroy_response (response);
    120 @end verbatim
    121 @noindent
    122 
    123 Note that the response object will take care of closing the file desciptor for us.
    124 
    125 Up to this point, there was little new. The actual novelty is that we enhance the header with the
    126 meta data about the content. Aware of the field's name we want to add, it is as easy as that:
    127 @verbatim
    128 MHD_add_response_header(response, "Content-Type", MIMETYPE);
    129 @end verbatim
    130 @noindent
    131 We do not have to append a colon expected by the protocol behind the first 
    132 field---@emph{GNU libhttpdmicro} will take care of this. 
    133 
    134 The function finishes with the well-known lines
    135 @verbatim
    136   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    137   MHD_destroy_response (response);
    138   return ret;
    139 }
    140 @end verbatim
    141 @noindent
    142 
    143 The complete program @code{responseheaders.c} is in the @code{examples} section as usual.
    144 Find a @emph{PNG} file you like and save it to the directory the example is run from under the name
    145 @code{picture.png}. You should find the image displayed on your browser if everything worked well.
    146 
    147 @heading Remarks
    148 The include file of the @emph{MHD} library comes with the header types mentioned in @emph{RFC 2616}
    149 already defined as macros. Thus, we could have written @code{MHD_HTTP_HEADER_CONTENT_TYPE} instead
    150 of @code{"Content-Type"} as well. However, one is not limited to these standard headers and could
    151 add custom response headers without violating the protocol. Whether, and how, the client would react
    152 to these custom header is up to the receiver. Likewise, the client is allowed to send custom request
    153 headers to the server as well, opening up yet more possibilities how client and server could 
    154 communicate with each other.
    155 
    156 The method of creating the response from a file on disk only works for static content.
    157 Serving dynamically created responses will be a topic of a future chapter.
    158 
    159 @heading Exercises
    160 @itemize @bullet
    161 
    162 @item
    163 Remember that the original program was written under a few assumptions---a static response
    164 using a local file being one of them. In order to simulate a very large or hard to reach file that cannot be provided
    165 instantly, postpone the queuing in the callback with the @code{sleep} function for 30 seconds 
    166 @emph{if} the file @code{/big.png} is requested (but deliver the same as above). A request for
    167 @code{/picture.png} should provide just the same but without any artificial delays.
    168 
    169 Now start two instances of your browser (or even use two machines) and see how the second client
    170 is put on hold while the first waits for his request on the slow file to be fulfilled.
    171 
    172 Finally, change the sourcecode to use @code{MHD_USE_THREAD_PER_CONNECTION} when the daemon is 
    173 started and try again.
    174 
    175 
    176 @item
    177 Did you succeed in implementing the clock exercise yet? This time, let the server save the 
    178 program's start time @code{t} and implement a response simulating a countdown that reaches 0 at
    179 @code{t+60}. Returning a message saying on which point the countdown is, the response should
    180 ultimately be to reply "Done" if the program has been running long enough,
    181 
    182 An unofficial, but widely understood, response header line is @code{Refresh: DELAY; url=URL} with
    183 the uppercase words substituted to tell the client it should request the given resource after 
    184 the given delay again. Improve your program in that the browser (any modern browser should work)
    185 automatically reconnects and asks for the status again every 5 seconds or so. The URL would have
    186 to be composed so that it begins with "http://", followed by the @emph{URI} the server is reachable
    187 from the client's point of view.
    188 
    189 Maybe you want also to visualize the countdown as a status bar by creating a 
    190 @code{<table>} consisting of one row and @code{n} columns whose fields contain small images of either
    191 a red or a green light.
    192 
    193 @end itemize
    194