1Now that we are able to inspect the incoming request in great detail, 2this chapter discusses the means to enrich the outgoing responses likewise. 3 4As you have learned in the @emph{Hello, Browser} chapter, some obligatory 5header fields are added and set automatically for simple responses by the library 6itself but if more advanced features are desired, additional fields have to be created. 7One of the possible fields is the content type field and an example will be developed around it. 8This will lead to an application capable of correctly serving different types of files. 9 10 11When we responded with HTML page packed in the static string previously, the client had no choice 12but guessing about how to handle the response, because the server had not told him. 13What if we had sent a picture or a sound file? Would the message have been understood 14or merely been displayed as an endless stream of random characters in the browser? 15This is what the mime content types are for. The header of the response is extended 16by certain information about how the data is to be interpreted. 17 18To introduce the concept, a picture of the format @emph{PNG} will be sent to the client 19and labeled accordingly with @code{image/png}. 20Once 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 26static int 27answer_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 38We 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 56When dealing with files, there is a lot that could go wrong on the 57server 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 104Note that we nevertheless have to create a response object even for sending a simple error code. 105Otherwise, the connection would just be closed without comment, leaving the client curious about 106what has happened. 107 108But 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 123Note that the response object will take care of closing the file desciptor for us. 124 125Up to this point, there was little new. The actual novelty is that we enhance the header with the 126meta data about the content. Aware of the field's name we want to add, it is as easy as that: 127@verbatim 128MHD_add_response_header(response, "Content-Type", MIMETYPE); 129@end verbatim 130@noindent 131We do not have to append a colon expected by the protocol behind the first 132field---@emph{GNU libhttpdmicro} will take care of this. 133 134The 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 143The complete program @code{responseheaders.c} is in the @code{examples} section as usual. 144Find 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 148The include file of the @emph{MHD} library comes with the header types mentioned in @emph{RFC 2616} 149already defined as macros. Thus, we could have written @code{MHD_HTTP_HEADER_CONTENT_TYPE} instead 150of @code{"Content-Type"} as well. However, one is not limited to these standard headers and could 151add custom response headers without violating the protocol. Whether, and how, the client would react 152to these custom header is up to the receiver. Likewise, the client is allowed to send custom request 153headers to the server as well, opening up yet more possibilities how client and server could 154communicate with each other. 155 156The method of creating the response from a file on disk only works for static content. 157Serving dynamically created responses will be a topic of a future chapter. 158 159@heading Exercises 160@itemize @bullet 161 162@item 163Remember that the original program was written under a few assumptions---a static response 164using a local file being one of them. In order to simulate a very large or hard to reach file that cannot be provided 165instantly, 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 169Now start two instances of your browser (or even use two machines) and see how the second client 170is put on hold while the first waits for his request on the slow file to be fulfilled. 171 172Finally, change the sourcecode to use @code{MHD_USE_THREAD_PER_CONNECTION} when the daemon is 173started and try again. 174 175 176@item 177Did you succeed in implementing the clock exercise yet? This time, let the server save the 178program'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 180ultimately be to reply "Done" if the program has been running long enough, 181 182An unofficial, but widely understood, response header line is @code{Refresh: DELAY; url=URL} with 183the uppercase words substituted to tell the client it should request the given resource after 184the given delay again. Improve your program in that the browser (any modern browser should work) 185automatically reconnects and asks for the status again every 5 seconds or so. The URL would have 186to be composed so that it begins with "http://", followed by the @emph{URI} the server is reachable 187from the client's point of view. 188 189Maybe 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 191a red or a green light. 192 193@end itemize 194