diff options
Diffstat (limited to 'doc/chapters/responseheaders.inc')
-rw-r--r-- | doc/chapters/responseheaders.inc | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/doc/chapters/responseheaders.inc b/doc/chapters/responseheaders.inc new file mode 100644 index 00000000..19d10142 --- /dev/null +++ b/doc/chapters/responseheaders.inc | |||
@@ -0,0 +1,177 @@ | |||
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 hadn't 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 | int answer_to_connection (void *cls, struct MHD_Connection *connection, const char *url, | ||
27 | const char *method, const char *version, const char *upload_data, | ||
28 | unsigned int *upload_data_size, void **con_cls) | ||
29 | { | ||
30 | unsigned char *buffer = NULL; | ||
31 | struct MHD_Response *response; | ||
32 | @end verbatim | ||
33 | @noindent | ||
34 | |||
35 | We want the program to load the graphics file into memory: | ||
36 | @verbatim | ||
37 | long size; | ||
38 | FILE *fp; | ||
39 | int ret = 0; | ||
40 | |||
41 | if (0 != strcmp(method, "GET")) return MHD_NO; | ||
42 | |||
43 | size = get_file_size (FILENAME); | ||
44 | if (size != 0) | ||
45 | { | ||
46 | fp = fopen (FILENAME, "rb"); | ||
47 | if (fp) | ||
48 | { | ||
49 | buffer = malloc (size); | ||
50 | |||
51 | if (buffer) | ||
52 | if (size == fread (buffer, 1, size, fp)) ret = 1; | ||
53 | |||
54 | fclose(fp); | ||
55 | } | ||
56 | } | ||
57 | @end verbatim | ||
58 | @noindent | ||
59 | |||
60 | The @code{GetFileSize} function, which returns a size of zero if the file could not be opened or | ||
61 | found, is left out on this page for tidiness. | ||
62 | |||
63 | When dealing with files and allocating memory, there is a lot that could go wrong on the | ||
64 | server side and if so, the client should be informed with @code{MHD_HTTP_INTERNAL_SERVER_ERROR}. | ||
65 | |||
66 | @verbatim | ||
67 | if (!ret) | ||
68 | { | ||
69 | const char *errorstr = "<html><body>An internal server error has occured!\ | ||
70 | </body></html>"; | ||
71 | |||
72 | if (buffer) free(buffer); | ||
73 | |||
74 | response = MHD_create_response_from_data(strlen(errorstr), (void*)errorstr, | ||
75 | MHD_NO, MHD_NO); | ||
76 | |||
77 | if (response) | ||
78 | { | ||
79 | ret = MHD_queue_response (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, response); | ||
80 | MHD_destroy_response (response); | ||
81 | |||
82 | return MHD_YES; | ||
83 | } | ||
84 | else return MHD_NO; | ||
85 | } | ||
86 | @end verbatim | ||
87 | @noindent | ||
88 | |||
89 | Note that we nevertheless have to create a response object even for sending a simple error code. | ||
90 | Otherwise, the connection would just be closed without comment, leaving the client curious about | ||
91 | what has happened. | ||
92 | |||
93 | But in the case of success a response will be constructed that contains the buffer filled with the | ||
94 | file's content. | ||
95 | |||
96 | @verbatim | ||
97 | response = MHD_create_response_from_data (size, (void*)buffer, MHD_YES, MHD_NO); | ||
98 | @end verbatim | ||
99 | @noindent | ||
100 | |||
101 | Contrary to the above case where a static string will be sent, this time we have to | ||
102 | keep track of the dynamically allocated buffer. As discussed in the @ref{Hello browser example}, | ||
103 | the buffer cannot be safely freed as soon as the function call returns. Instead, we ask the function | ||
104 | to keep charge of freeing the buffer itself when it is not longer needed. Thus, no further @code{free} | ||
105 | command is invoked by us. | ||
106 | |||
107 | Up to this point, there was little new. The actual novelty is that we enhance the header with the | ||
108 | meta data about the content. Aware of the field's name we want to add, it is as easy as that: | ||
109 | @verbatim | ||
110 | MHD_add_response_header(response, "Content-Type", MIMETYPE); | ||
111 | @end verbatim | ||
112 | @noindent | ||
113 | We do not have to append a colon expected by the protocol hehind the first | ||
114 | field---@emph{GNU libhttpdmicro} will take care of this. | ||
115 | |||
116 | The function finishes with the well-known lines | ||
117 | @verbatim | ||
118 | ret = MHD_queue_response (connection, MHD_HTTP_OK, response); | ||
119 | MHD_destroy_response (response); | ||
120 | return ret; | ||
121 | } | ||
122 | @end verbatim | ||
123 | @noindent | ||
124 | |||
125 | The complete program @code{responseheaders.c} is in the @code{examples} section as usual. | ||
126 | Find a @emph{PNG} file you like and save it to the directory the example is run from under the name | ||
127 | @code{picture.png}. You should find the image displayed on your browser if everything worked well. | ||
128 | |||
129 | @heading Remarks | ||
130 | The include file of the @emph{MHD} library comes with the header types mentioned in @emph{RFC 2616} | ||
131 | already defined as macros. Thus, we could have written @code{MHD_HTTP_HEADER_CONTENT_TYPE} instead | ||
132 | of @code{"Content-Type"} as well. However, one is not limited to these standard headers and could | ||
133 | add custom response headers without violating the protocol. Whether, and how, the client would react | ||
134 | to these custom header is up to the receiver. Likewise, the client is allowed to send custom request | ||
135 | headers to the server as well, opening up yet more possibilities how client and server could | ||
136 | communicate with each other. | ||
137 | |||
138 | The method of creating the response from one big chunk of data is only feasible for smaller files. | ||
139 | A public file server satisfying many request at the same time would be choking under these high | ||
140 | demands on memory very soon. Serving responses in smaller parts would be more adequate here and | ||
141 | will be a topic of a future chapter. | ||
142 | |||
143 | @heading Exercises | ||
144 | @itemize @bullet | ||
145 | |||
146 | @item | ||
147 | Remember that the original program was written under a few assumptions---a small, static response | ||
148 | being one of them. In order to simulate a very large or hard to reach file that cannot be provided | ||
149 | instantly, postpone the queuing in the callback with the @code{sleep} function for 30 seconds | ||
150 | @emph{if} the file @code{/big.png} is requested (but deliver the same as above). A request for | ||
151 | @code{/picture.png} should provide just the same but without any artificial delays. | ||
152 | |||
153 | Now start two instances of your browser (or even use two machines) and see how the second client | ||
154 | is put on hold while the first waits for his request on the slow file to be fulfilled. | ||
155 | |||
156 | Finally, change the sourcecode to use @code{MHD_USE_THREAD_PER_CONNECTION} when the daemon is | ||
157 | started and try again. | ||
158 | |||
159 | |||
160 | @item | ||
161 | Did you succeed in implementing the clock exercise yet? This time, let the server save the | ||
162 | program's start time @code{t} and implement a response simulating a countdown that reaches 0 at | ||
163 | @code{t+60}. Returning a message saying on which point the countdown is, the response should | ||
164 | ultimately be to reply "Done" if the program has been running long enough, | ||
165 | |||
166 | A non official, but widely understood, response header line is @code{Refresh: DELAY; url=URL} with | ||
167 | the uppercase words substituted to tell the client it should request the given resource after | ||
168 | the given delay again. Improve your program in that the browser (any modern browser should work) | ||
169 | automatically reconnects and asks for the status again every 5 seconds or so. The URL would have | ||
170 | to be composed so that it begins with "http://", followed by the @emph{URI} the server is reachable | ||
171 | from the client's point of view. | ||
172 | |||
173 | Maybe you want also to visualize the countdown as a status bar by creating a | ||
174 | @code{<table>} consisting of one row and @code{n} columns whose fields contain small images of either | ||
175 | a red or a green light. | ||
176 | |||
177 | @end itemize | ||