diff options
author | Christian Grothoff <christian@grothoff.org> | 2007-12-20 04:23:00 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2007-12-20 04:23:00 +0000 |
commit | c490e64780fd1c45592f5528bf755956f0f2eddb (patch) | |
tree | aa34ac04246134cb8e32a2d81573c73079c0d764 | |
parent | e787fde83a61e3f9290c589139b657c7c414201c (diff) | |
download | libmicrohttpd-c490e64780fd1c45592f5528bf755956f0f2eddb.tar.gz libmicrohttpd-c490e64780fd1c45592f5528bf755956f0f2eddb.zip |
new MHD with support for chunked encoding
-rw-r--r-- | ChangeLog | 9 | ||||
-rw-r--r-- | README | 11 | ||||
-rw-r--r-- | configure.ac | 4 | ||||
-rw-r--r-- | src/daemon/Makefile.am | 13 | ||||
-rw-r--r-- | src/daemon/connection.c | 1845 | ||||
-rw-r--r-- | src/daemon/connection.h | 25 | ||||
-rw-r--r-- | src/daemon/daemon.c | 169 | ||||
-rw-r--r-- | src/daemon/daemontest_get.c | 20 | ||||
-rw-r--r-- | src/daemon/daemontest_large_put.c | 37 | ||||
-rw-r--r-- | src/daemon/daemontest_postform.c | 9 | ||||
-rw-r--r-- | src/daemon/daemontest_put_chunked.c | 5 | ||||
-rw-r--r-- | src/daemon/fileserver_example.c | 10 | ||||
-rw-r--r-- | src/daemon/internal.h | 201 | ||||
-rw-r--r-- | src/daemon/memorypool.c | 29 | ||||
-rw-r--r-- | src/daemon/memorypool.h | 12 | ||||
-rw-r--r-- | src/daemon/minimal_example.c | 10 | ||||
-rw-r--r-- | src/include/microhttpd.h | 47 |
17 files changed, 1515 insertions, 941 deletions
@@ -1,3 +1,12 @@ | |||
1 | Wed Dec 19 21:12:04 MST 2007 | ||
2 | Implemented chunked (HTTP 1.1) downloads (including | ||
3 | sending of HTTP footers). Also allowed queuing of | ||
4 | a response early to suppress the otherwise automatic | ||
5 | "100 CONTINUE" response. Removed the mostly useless | ||
6 | "(un)register handler" methods from the API. Changed | ||
7 | the internal implementation to use a finite state | ||
8 | machine (cleaner code, slightly less memory consumption). - CG | ||
9 | |||
1 | Sun Dec 16 03:24:13 MST 2007 | 10 | Sun Dec 16 03:24:13 MST 2007 |
2 | Implemented handling of chunked (HTTP 1.1) uploads. | 11 | Implemented handling of chunked (HTTP 1.1) uploads. |
3 | Note that the upload callback must be able to | 12 | Note that the upload callback must be able to |
@@ -24,7 +24,7 @@ reporting (and use MHD_USE_DEBUG). Error reporting is not enabled by | |||
24 | default to reduce the size of the library (error messages take | 24 | default to reduce the size of the library (error messages take |
25 | space!). If you are concerned about space, you should set "CFLAGS" to | 25 | space!). If you are concerned about space, you should set "CFLAGS" to |
26 | "-Os --fomit-frame-pointer" to have gcc generate tight code. The | 26 | "-Os --fomit-frame-pointer" to have gcc generate tight code. The |
27 | resulting binary should be less than 25k (on x86). | 27 | resulting binary should be about 25k (on x86). |
28 | 28 | ||
29 | 29 | ||
30 | Portability | 30 | Portability |
@@ -50,11 +50,6 @@ indicates that a testcase should be written before implementing the | |||
50 | feature. | 50 | feature. |
51 | 51 | ||
52 | 52 | ||
53 | For http/1.1-compliance: | ||
54 | ======================== | ||
55 | connection.c: | ||
56 | - support sending of chunked responses (#1302, TEST, ARCH) | ||
57 | |||
58 | For POST: | 53 | For POST: |
59 | ========= | 54 | ========= |
60 | - add support to decode multipart/form-data with | 55 | - add support to decode multipart/form-data with |
@@ -71,6 +66,10 @@ Missing Testcases: | |||
71 | - add testcases for http/1.1 pipelining (need | 66 | - add testcases for http/1.1 pipelining (need |
72 | to figure out how to ensure curl pipelines) | 67 | to figure out how to ensure curl pipelines) |
73 | - add testcases for resource limit enforcement | 68 | - add testcases for resource limit enforcement |
69 | - add testcases for client queuing early response, | ||
70 | suppressing 100 CONTINUE | ||
71 | - extend testcase for chunked encoding to validate | ||
72 | handling of footers | ||
74 | 73 | ||
75 | Documentation: | 74 | Documentation: |
76 | ============== | 75 | ============== |
diff --git a/configure.ac b/configure.ac index adc1e3ca..09860619 100644 --- a/configure.ac +++ b/configure.ac | |||
@@ -21,8 +21,8 @@ | |||
21 | # | 21 | # |
22 | # | 22 | # |
23 | AC_PREREQ(2.57) | 23 | AC_PREREQ(2.57) |
24 | AC_INIT([libmicrohttpd], [0.1.2],[libmicrohttpd@gnunet.org]) | 24 | AC_INIT([libmicrohttpd], [0.2.0],[libmicrohttpd@gnunet.org]) |
25 | AM_INIT_AUTOMAKE([libmicrohttpd], [0.1.2]) | 25 | AM_INIT_AUTOMAKE([libmicrohttpd], [0.2.0]) |
26 | AM_CONFIG_HEADER([config.h]) | 26 | AM_CONFIG_HEADER([config.h]) |
27 | 27 | ||
28 | AH_TOP([#define _GNU_SOURCE 1]) | 28 | AH_TOP([#define _GNU_SOURCE 1]) |
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am index 4902dc52..30ec59f1 100644 --- a/src/daemon/Makefile.am +++ b/src/daemon/Makefile.am | |||
@@ -12,7 +12,7 @@ lib_LTLIBRARIES = \ | |||
12 | libmicrohttpd.la | 12 | libmicrohttpd.la |
13 | 13 | ||
14 | libmicrohttpd_la_LDFLAGS = \ | 14 | libmicrohttpd_la_LDFLAGS = \ |
15 | -export-dynamic -version-info 2:1:1 $(retaincommand) | 15 | -export-dynamic -version-info 3:0:0 $(retaincommand) |
16 | libmicrohttpd_la_SOURCES = \ | 16 | libmicrohttpd_la_SOURCES = \ |
17 | connection.c connection.h \ | 17 | connection.c connection.h \ |
18 | reason_phrase.c reason_phrase.h \ | 18 | reason_phrase.c reason_phrase.h \ |
@@ -48,7 +48,6 @@ check_PROGRAMS = \ | |||
48 | daemontest_postform \ | 48 | daemontest_postform \ |
49 | daemontest_post_loop \ | 49 | daemontest_post_loop \ |
50 | daemontest_put \ | 50 | daemontest_put \ |
51 | daemontest_put_chunked \ | ||
52 | daemontest_large_put \ | 51 | daemontest_large_put \ |
53 | daemontest_get11 \ | 52 | daemontest_get11 \ |
54 | daemontest_post11 \ | 53 | daemontest_post11 \ |
@@ -56,7 +55,9 @@ check_PROGRAMS = \ | |||
56 | daemontest_post_loop11 \ | 55 | daemontest_post_loop11 \ |
57 | daemontest_put11 \ | 56 | daemontest_put11 \ |
58 | daemontest_large_put11 \ | 57 | daemontest_large_put11 \ |
59 | daemontest_long_header | 58 | daemontest_long_header \ |
59 | daemontest_get_chunked \ | ||
60 | daemontest_put_chunked | ||
60 | 61 | ||
61 | TESTS = $(check_PROGRAMS) | 62 | TESTS = $(check_PROGRAMS) |
62 | 63 | ||
@@ -71,6 +72,12 @@ daemontest_get_LDADD = \ | |||
71 | $(top_builddir)/src/daemon/libmicrohttpd.la \ | 72 | $(top_builddir)/src/daemon/libmicrohttpd.la \ |
72 | @LIBCURL@ | 73 | @LIBCURL@ |
73 | 74 | ||
75 | daemontest_get_chunked_SOURCES = \ | ||
76 | daemontest_get_chunked.c | ||
77 | daemontest_get_chunked_LDADD = \ | ||
78 | $(top_builddir)/src/daemon/libmicrohttpd.la \ | ||
79 | @LIBCURL@ | ||
80 | |||
74 | daemontest_post_SOURCES = \ | 81 | daemontest_post_SOURCES = \ |
75 | daemontest_post.c | 82 | daemontest_post.c |
76 | daemontest_post_LDADD = \ | 83 | daemontest_post_LDADD = \ |
diff --git a/src/daemon/connection.c b/src/daemon/connection.c index 318439a8..d07a2544 100644 --- a/src/daemon/connection.c +++ b/src/daemon/connection.c | |||
@@ -2,7 +2,6 @@ | |||
2 | This file is part of libmicrohttpd | 2 | This file is part of libmicrohttpd |
3 | (C) 2007 Daniel Pittman and Christian Grothoff | 3 | (C) 2007 Daniel Pittman and Christian Grothoff |
4 | 4 | ||
5 | |||
6 | This library is free software; you can redistribute it and/or | 5 | This library is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU Lesser General Public | 6 | modify it under the terms of the GNU Lesser General Public |
8 | License as published by the Free Software Foundation; either | 7 | License as published by the Free Software Foundation; either |
@@ -62,6 +61,14 @@ | |||
62 | */ | 61 | */ |
63 | #define REQUEST_LACKS_HOST "" | 62 | #define REQUEST_LACKS_HOST "" |
64 | 63 | ||
64 | #define EXTRA_CHECKS MHD_YES | ||
65 | |||
66 | #if EXTRA_CHECKS | ||
67 | #define EXTRA_CHECK(a) if (!(a)) abort(); | ||
68 | #else | ||
69 | #define EXTRA_CHECK(a) | ||
70 | #endif | ||
71 | |||
65 | /** | 72 | /** |
66 | * Add extra debug messages with reasons for closing connections | 73 | * Add extra debug messages with reasons for closing connections |
67 | * (non-error reasons). | 74 | * (non-error reasons). |
@@ -73,6 +80,10 @@ | |||
73 | */ | 80 | */ |
74 | #define DEBUG_SEND_DATA MHD_NO | 81 | #define DEBUG_SEND_DATA MHD_NO |
75 | 82 | ||
83 | /** | ||
84 | * Should all state transitions be printed to stderr? | ||
85 | */ | ||
86 | #define DEBUG_STATES MHD_NO | ||
76 | 87 | ||
77 | /** | 88 | /** |
78 | * Get all of the headers from the request. | 89 | * Get all of the headers from the request. |
@@ -109,7 +120,6 @@ MHD_get_connection_values (struct MHD_Connection *connection, | |||
109 | return ret; | 120 | return ret; |
110 | } | 121 | } |
111 | 122 | ||
112 | |||
113 | /** | 123 | /** |
114 | * Get a particular header value. If multiple | 124 | * Get a particular header value. If multiple |
115 | * values match the kind, return any one of them. | 125 | * values match the kind, return any one of them. |
@@ -149,11 +159,11 @@ int | |||
149 | MHD_queue_response (struct MHD_Connection *connection, | 159 | MHD_queue_response (struct MHD_Connection *connection, |
150 | unsigned int status_code, struct MHD_Response *response) | 160 | unsigned int status_code, struct MHD_Response *response) |
151 | { | 161 | { |
152 | if ((connection == NULL) || | 162 | if ( (connection == NULL) || |
153 | (response == NULL) || | 163 | (response == NULL) || |
154 | (connection->response != NULL) || | 164 | (connection->response != NULL) || |
155 | (connection->have_received_body == MHD_NO) | 165 | ( (connection->state != MHD_CONNECTION_HEADERS_PROCESSED) && |
156 | || (connection->have_received_headers == MHD_NO)) | 166 | (connection->state != MHD_CONNECTION_FOOTERS_RECEIVED) ) ) |
157 | return MHD_NO; | 167 | return MHD_NO; |
158 | MHD_increment_response_rc (response); | 168 | MHD_increment_response_rc (response); |
159 | connection->response = response; | 169 | connection->response = response; |
@@ -165,6 +175,21 @@ MHD_queue_response (struct MHD_Connection *connection, | |||
165 | have already sent the full message body */ | 175 | have already sent the full message body */ |
166 | connection->response_write_position = response->total_size; | 176 | connection->response_write_position = response->total_size; |
167 | } | 177 | } |
178 | if ( (response->total_size == -1) && | ||
179 | (0 == strcasecmp(connection->version, | ||
180 | MHD_HTTP_VERSION_1_1)) ) | ||
181 | connection->have_chunked_response = MHD_YES; | ||
182 | else | ||
183 | connection->have_chunked_response = MHD_NO; | ||
184 | if (connection->state == MHD_CONNECTION_HEADERS_PROCESSED) | ||
185 | { | ||
186 | /* response was queued "early", | ||
187 | refuse to read body / footers or further | ||
188 | requests! */ | ||
189 | SHUTDOWN (connection->socket_fd, SHUT_RD); | ||
190 | connection->read_closed = MHD_YES; | ||
191 | connection->state = MHD_CONNECTION_FOOTERS_RECEIVED; | ||
192 | } | ||
168 | return MHD_YES; | 193 | return MHD_YES; |
169 | } | 194 | } |
170 | 195 | ||
@@ -173,14 +198,14 @@ MHD_queue_response (struct MHD_Connection *connection, | |||
173 | * message for this connection? | 198 | * message for this connection? |
174 | */ | 199 | */ |
175 | static int | 200 | static int |
176 | MHD_need_100_continue (struct MHD_Connection *connection) | 201 | need_100_continue (struct MHD_Connection *connection) |
177 | { | 202 | { |
178 | const char *expect; | 203 | const char *expect; |
179 | 204 | ||
180 | return ((connection->version != NULL) && | 205 | return ((connection->response == NULL) && |
206 | (connection->version != NULL) && | ||
181 | (0 == strcasecmp (connection->version, | 207 | (0 == strcasecmp (connection->version, |
182 | MHD_HTTP_VERSION_1_1)) && | 208 | MHD_HTTP_VERSION_1_1)) && |
183 | (connection->have_received_headers == MHD_YES) && | ||
184 | (NULL != (expect = MHD_lookup_connection_value (connection, | 209 | (NULL != (expect = MHD_lookup_connection_value (connection, |
185 | MHD_HEADER_KIND, | 210 | MHD_HEADER_KIND, |
186 | MHD_HTTP_HEADER_EXPECT))) | 211 | MHD_HTTP_HEADER_EXPECT))) |
@@ -199,6 +224,7 @@ connection_close_error (struct MHD_Connection *connection) | |||
199 | SHUTDOWN (connection->socket_fd, SHUT_RDWR); | 224 | SHUTDOWN (connection->socket_fd, SHUT_RDWR); |
200 | CLOSE (connection->socket_fd); | 225 | CLOSE (connection->socket_fd); |
201 | connection->socket_fd = -1; | 226 | connection->socket_fd = -1; |
227 | connection->state = MHD_CONNECTION_CLOSED; | ||
202 | if (connection->daemon->notify_completed != NULL) | 228 | if (connection->daemon->notify_completed != NULL) |
203 | connection->daemon->notify_completed (connection->daemon-> | 229 | connection->daemon->notify_completed (connection->daemon-> |
204 | notify_completed_cls, connection, | 230 | notify_completed_cls, connection, |
@@ -216,12 +242,14 @@ connection_close_error (struct MHD_Connection *connection) | |||
216 | * @return MHD_NO if readying the response failed | 242 | * @return MHD_NO if readying the response failed |
217 | */ | 243 | */ |
218 | static int | 244 | static int |
219 | ready_response (struct MHD_Connection *connection) | 245 | try_ready_normal_body (struct MHD_Connection *connection) |
220 | { | 246 | { |
221 | int ret; | 247 | int ret; |
222 | struct MHD_Response *response; | 248 | struct MHD_Response *response; |
223 | 249 | ||
224 | response = connection->response; | 250 | response = connection->response; |
251 | if (response->crc == NULL) | ||
252 | return MHD_YES; | ||
225 | ret = response->crc (response->crc_cls, | 253 | ret = response->crc (response->crc_cls, |
226 | connection->response_write_position, | 254 | connection->response_write_position, |
227 | response->data, | 255 | response->data, |
@@ -230,7 +258,8 @@ ready_response (struct MHD_Connection *connection) | |||
230 | connection->response_write_position)); | 258 | connection->response_write_position)); |
231 | if (ret == -1) | 259 | if (ret == -1) |
232 | { | 260 | { |
233 | /* end of message, signal other side by closing! */ | 261 | /* either error or http 1.0 transfer, close |
262 | socket! */ | ||
234 | #if DEBUG_CLOSE | 263 | #if DEBUG_CLOSE |
235 | #if HAVE_MESSAGES | 264 | #if HAVE_MESSAGES |
236 | MHD_DLOG (connection->daemon, "Closing connection (end of response)\n"); | 265 | MHD_DLOG (connection->daemon, "Closing connection (end of response)\n"); |
@@ -239,94 +268,281 @@ ready_response (struct MHD_Connection *connection) | |||
239 | response->total_size = connection->response_write_position; | 268 | response->total_size = connection->response_write_position; |
240 | connection_close_error (connection); | 269 | connection_close_error (connection); |
241 | return MHD_NO; | 270 | return MHD_NO; |
242 | } | 271 | } |
243 | response->data_start = connection->response_write_position; | 272 | response->data_start = connection->response_write_position; |
244 | response->data_size = ret; | 273 | response->data_size = ret; |
245 | if (ret == 0) | 274 | if (ret == 0) |
275 | return MHD_NO; | ||
276 | return MHD_YES; | ||
277 | } | ||
278 | |||
279 | /** | ||
280 | * Prepare the response buffer of this connection for | ||
281 | * sending. Assumes that the response mutex is | ||
282 | * already held. If the transmission is complete, | ||
283 | * this function may close the socket (and return | ||
284 | * MHD_NO). | ||
285 | * | ||
286 | * @return MHD_NO if readying the response failed | ||
287 | */ | ||
288 | static int | ||
289 | try_ready_chunked_body (struct MHD_Connection *connection) | ||
290 | { | ||
291 | int ret; | ||
292 | char * buf; | ||
293 | struct MHD_Response *response; | ||
294 | unsigned int size; | ||
295 | char cbuf[9]; | ||
296 | |||
297 | response = connection->response; | ||
298 | if (connection->write_buffer_size == 0) | ||
246 | { | 299 | { |
247 | /* avoid busy-waiting when using external select | 300 | size = connection->daemon->pool_size; |
248 | (we assume that the main application will | 301 | do |
249 | wake up the external select once more data | 302 | { |
250 | is ready). With internal selects, we | 303 | size /= 2; |
251 | have no choice; if the app uses a thread | 304 | if (size < 128) |
252 | per connection, ret==0 is likely a bug -- | 305 | { |
253 | the application should block until data | 306 | /* not enough memory */ |
254 | is ready! */ | 307 | #if DEBUG_CLOSE |
255 | if ((0 == | 308 | #if HAVE_MESSAGES |
256 | (connection->daemon-> | 309 | MHD_DLOG (connection->daemon, "Closing connection (out of memory)\n"); |
257 | options & (MHD_USE_SELECT_INTERNALLY | | 310 | #endif |
258 | MHD_USE_THREAD_PER_CONNECTION)))) | 311 | #endif |
259 | connection->response_unready = MHD_YES; | 312 | connection_close_error (connection); |
313 | return MHD_NO; | ||
314 | } | ||
315 | buf = MHD_pool_allocate (connection->pool, | ||
316 | size, | ||
317 | MHD_NO); | ||
318 | } | ||
319 | while (buf == NULL); | ||
320 | connection->write_buffer_size = size; | ||
321 | connection->write_buffer = buf; | ||
322 | } | ||
323 | |||
324 | ret = response->crc (response->crc_cls, | ||
325 | connection->response_write_position, | ||
326 | &connection->write_buffer[8], | ||
327 | connection->write_buffer_size - 8 - 2); | ||
328 | if (ret == -1) | ||
329 | { | ||
330 | /* end of message, signal other side! */ | ||
331 | strcpy(connection->write_buffer, | ||
332 | "0\r\n"); | ||
333 | connection->write_buffer_append_offset = 3; | ||
334 | connection->write_buffer_send_offset = 0; | ||
335 | response->total_size = connection->response_write_position; | ||
336 | return MHD_YES; | ||
337 | } | ||
338 | if (ret == 0) | ||
339 | { | ||
340 | connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY; | ||
260 | return MHD_NO; | 341 | return MHD_NO; |
261 | } | 342 | } |
262 | connection->response_unready = MHD_NO; | 343 | if (ret > 0xFFFFFF) |
344 | ret = 0xFFFFFF; | ||
345 | snprintf(cbuf, | ||
346 | 8, | ||
347 | "%X\r\n", | ||
348 | ret); | ||
349 | memcpy(&connection->write_buffer[8 - strlen(cbuf)], | ||
350 | cbuf, | ||
351 | strlen(cbuf)); | ||
352 | memcpy(&connection->write_buffer[8 + ret], | ||
353 | "\r\n", | ||
354 | 2); | ||
355 | connection->response_write_position += ret; | ||
356 | connection->write_buffer_send_offset = 8 - strlen(cbuf); | ||
357 | connection->write_buffer_append_offset = 8 + ret + 2; | ||
263 | return MHD_YES; | 358 | return MHD_YES; |
264 | } | 359 | } |
265 | 360 | ||
266 | /** | 361 | /** |
267 | * Obtain the select sets for this connection | 362 | * Check if we need to set some additional headers |
268 | * | 363 | * for http-compiliance. |
269 | * @return MHD_YES on success | ||
270 | */ | 364 | */ |
271 | int | 365 | static void |
272 | MHD_connection_get_fdset (struct MHD_Connection *connection, | 366 | add_extra_headers (struct MHD_Connection *connection) |
273 | fd_set * read_fd_set, | ||
274 | fd_set * write_fd_set, | ||
275 | fd_set * except_fd_set, int *max_fd) | ||
276 | { | 367 | { |
277 | int fd; | 368 | const char *have; |
278 | void *buf; | 369 | char buf[128]; |
279 | 370 | ||
280 | fd = connection->socket_fd; | 371 | connection->have_chunked_upload = MHD_NO; |
281 | if (fd == -1) | 372 | if (connection->response->total_size == -1) |
282 | return MHD_YES; | 373 | { |
283 | if ((connection->read_close == MHD_NO) && | 374 | have = MHD_get_response_header (connection->response, |
284 | ((connection->have_received_headers == MHD_NO) || | 375 | MHD_HTTP_HEADER_CONNECTION); |
285 | (connection->read_buffer_offset < connection->read_buffer_size))) | 376 | if ( (have == NULL) || |
377 | (0 != strcasecmp(have, "close")) ) | ||
378 | { | ||
379 | if ( (connection->version != NULL) && | ||
380 | (0 == strcasecmp(connection->version, | ||
381 | MHD_HTTP_VERSION_1_1)) ) | ||
382 | { | ||
383 | connection->have_chunked_upload = MHD_YES; | ||
384 | have = MHD_get_response_header (connection->response, | ||
385 | MHD_HTTP_HEADER_TRANSFER_ENCODING); | ||
386 | if (have == NULL) | ||
387 | MHD_add_response_header (connection->response, | ||
388 | MHD_HTTP_HEADER_TRANSFER_ENCODING, | ||
389 | "chunked"); | ||
390 | } | ||
391 | else | ||
392 | { | ||
393 | MHD_add_response_header (connection->response, | ||
394 | MHD_HTTP_HEADER_CONNECTION, "close"); | ||
395 | } | ||
396 | } | ||
397 | } | ||
398 | else if (NULL == MHD_get_response_header (connection->response, | ||
399 | MHD_HTTP_HEADER_CONTENT_LENGTH)) | ||
286 | { | 400 | { |
287 | FD_SET (fd, read_fd_set); | 401 | _REAL_SNPRINTF (buf, |
288 | if (fd > *max_fd) | 402 | 128, |
289 | *max_fd = fd; | 403 | "%llu", |
404 | (unsigned long long) connection->response->total_size); | ||
405 | MHD_add_response_header (connection->response, | ||
406 | MHD_HTTP_HEADER_CONTENT_LENGTH, buf); | ||
407 | } | ||
408 | } | ||
409 | |||
410 | /** | ||
411 | * Produce HTTP "Date:" header. | ||
412 | * @param date where to write the header | ||
413 | * @param max maximum number of characters to write | ||
414 | */ | ||
415 | static void | ||
416 | get_date_string (char *date, unsigned int max) | ||
417 | { | ||
418 | static const char *days[] = | ||
419 | { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; | ||
420 | static const char *mons[] = | ||
421 | { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", | ||
422 | "Nov", "Dec" | ||
423 | }; | ||
424 | struct tm now; | ||
425 | time_t t; | ||
426 | |||
427 | time (&t); | ||
428 | gmtime_r (&t, &now); | ||
429 | snprintf (date, | ||
430 | max - 1, | ||
431 | "Date: %3s, %02u %3s %04u %02u:%02u:%02u GMT\r\n", | ||
432 | days[now.tm_wday % 7], | ||
433 | now.tm_mday, | ||
434 | mons[now.tm_mon % 12], | ||
435 | now.tm_year, now.tm_hour, now.tm_min, now.tm_sec); | ||
436 | } | ||
437 | |||
438 | /** | ||
439 | * try growing the read buffer | ||
440 | * @return MHD_YES on success, MHD_NO on failure | ||
441 | */ | ||
442 | static int | ||
443 | try_grow_read_buffer(struct MHD_Connection * connection) | ||
444 | { | ||
445 | void * buf; | ||
446 | |||
447 | buf = MHD_pool_reallocate (connection->pool, | ||
448 | connection->read_buffer, | ||
449 | connection->read_buffer_size, | ||
450 | connection->read_buffer_size * 2 + | ||
451 | MHD_BUF_INC_SIZE + 1); | ||
452 | if (buf == NULL) | ||
453 | return MHD_NO; | ||
454 | /* we can actually grow the buffer, do it! */ | ||
455 | connection->read_buffer = buf; | ||
456 | connection->read_buffer_size = | ||
457 | connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE; | ||
458 | return MHD_YES; | ||
459 | } | ||
460 | |||
461 | /** | ||
462 | * Allocate the connection's write buffer and | ||
463 | * fill it with all of the headers (or footers, | ||
464 | * if we have already sent the body) from the | ||
465 | * HTTPd's response. | ||
466 | */ | ||
467 | static int | ||
468 | build_header_response (struct MHD_Connection *connection) | ||
469 | { | ||
470 | size_t size; | ||
471 | size_t off; | ||
472 | struct MHD_HTTP_Header *pos; | ||
473 | char code[128]; | ||
474 | char date[128]; | ||
475 | char *data; | ||
476 | enum MHD_ValueKind kind; | ||
477 | const char *reason_phrase; | ||
478 | |||
479 | if (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED) | ||
480 | { | ||
481 | add_extra_headers (connection); | ||
482 | reason_phrase = | ||
483 | MHD_get_reason_phrase_for (connection->responseCode); | ||
484 | _REAL_SNPRINTF (code, 128, "%s %u %s\r\n", MHD_HTTP_VERSION_1_1, | ||
485 | connection->responseCode, reason_phrase); | ||
486 | off = strlen (code); | ||
487 | /* estimate size */ | ||
488 | size = off + 2; /* extra \r\n at the end */ | ||
489 | kind = MHD_HEADER_KIND; | ||
490 | if (NULL == MHD_get_response_header (connection->response, | ||
491 | MHD_HTTP_HEADER_DATE)) | ||
492 | get_date_string (date, sizeof (date)); | ||
493 | else | ||
494 | date[0] = '\0'; | ||
495 | size += strlen (date); | ||
290 | } | 496 | } |
291 | else | 497 | else |
292 | { | 498 | { |
293 | if ((connection->read_close == MHD_NO) && | 499 | size = 2; |
294 | ((connection->have_received_headers == MHD_YES) && | 500 | kind = MHD_FOOTER_KIND; |
295 | (connection->read_buffer_offset == connection->read_buffer_size))) | 501 | off = 0; |
296 | { | 502 | } |
297 | /* try growing the read buffer, just in case */ | 503 | pos = connection->response->first_header; |
298 | buf = MHD_pool_reallocate (connection->pool, | 504 | while (pos != NULL) |
299 | connection->read_buffer, | 505 | { |
300 | connection->read_buffer_size, | 506 | if (pos->kind == kind) |
301 | connection->read_buffer_size * 2 + | 507 | size += strlen (pos->header) + strlen (pos->value) + 4; /* colon, space, linefeeds */ |
302 | MHD_BUF_INC_SIZE); | 508 | pos = pos->next; |
303 | if (buf != NULL) | 509 | } |
304 | { | 510 | /* produce data */ |
305 | /* we can actually grow the buffer, do it! */ | 511 | data = MHD_pool_allocate (connection->pool, size + 1, MHD_YES); |
306 | connection->read_buffer = buf; | 512 | if (data == NULL) |
307 | connection->read_buffer_size = | 513 | { |
308 | connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE; | 514 | #if HAVE_MESSAGES |
309 | FD_SET (fd, read_fd_set); | 515 | MHD_DLOG (connection->daemon, "Not enough memory for write!\n"); |
310 | if (fd > *max_fd) | 516 | #endif |
311 | *max_fd = fd; | 517 | return MHD_NO; |
312 | } | 518 | } |
313 | } | 519 | if (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED) |
520 | { | ||
521 | memcpy (data, code, off); | ||
314 | } | 522 | } |
315 | if ((connection->response != NULL) && | 523 | pos = connection->response->first_header; |
316 | (connection->response_unready == MHD_YES)) | 524 | while (pos != NULL) |
317 | { | 525 | { |
318 | pthread_mutex_lock (&connection->response->mutex); | 526 | if (pos->kind == kind) |
319 | ready_response (connection); | 527 | { |
320 | pthread_mutex_unlock (&connection->response->mutex); | 528 | SPRINTF (&data[off], "%s: %s\r\n", pos->header, pos->value); |
529 | off += strlen (pos->header) + strlen (pos->value) + 4; | ||
530 | } | ||
531 | pos = pos->next; | ||
321 | } | 532 | } |
322 | if (((connection->response != NULL) && | 533 | if (connection->state == MHD_CONNECTION_FOOTERS_RECEIVED) |
323 | (connection->response_unready == MHD_NO)) || | ||
324 | MHD_need_100_continue (connection)) | ||
325 | { | 534 | { |
326 | FD_SET (fd, write_fd_set); | 535 | strcpy (&data[off], date); |
327 | if (fd > *max_fd) | 536 | off += strlen (date); |
328 | *max_fd = fd; | ||
329 | } | 537 | } |
538 | sprintf (&data[off], "\r\n"); | ||
539 | off += 2; | ||
540 | if (off != size) | ||
541 | abort (); | ||
542 | connection->write_buffer = data; | ||
543 | connection->write_buffer_append_offset = size; | ||
544 | connection->write_buffer_send_offset = 0; | ||
545 | connection->write_buffer_size = size + 1; | ||
330 | return MHD_YES; | 546 | return MHD_YES; |
331 | } | 547 | } |
332 | 548 | ||
@@ -338,15 +554,14 @@ MHD_connection_get_fdset (struct MHD_Connection *connection, | |||
338 | * @param status_code the response code to send (413 or 414) | 554 | * @param status_code the response code to send (413 or 414) |
339 | */ | 555 | */ |
340 | static void | 556 | static void |
341 | MHD_excessive_data_handler (struct MHD_Connection *connection, | 557 | excessive_data_handler (struct MHD_Connection *connection, |
342 | unsigned int status_code) | 558 | unsigned int status_code) |
343 | { | 559 | { |
344 | struct MHD_Response *response; | 560 | struct MHD_Response *response; |
345 | 561 | ||
346 | /* die, header far too long to be reasonable */ | 562 | /* die, header far too long to be reasonable */ |
347 | connection->read_close = MHD_YES; | 563 | connection->state = MHD_CONNECTION_FOOTERS_RECEIVED; |
348 | connection->have_received_headers = MHD_YES; | 564 | connection->read_closed = MHD_YES; |
349 | connection->have_received_body = MHD_YES; | ||
350 | #if HAVE_MESSAGES | 565 | #if HAVE_MESSAGES |
351 | MHD_DLOG (connection->daemon, | 566 | MHD_DLOG (connection->daemon, |
352 | "Received excessively long header, closing connection.\n"); | 567 | "Received excessively long header, closing connection.\n"); |
@@ -354,7 +569,166 @@ MHD_excessive_data_handler (struct MHD_Connection *connection, | |||
354 | response = MHD_create_response_from_data (strlen (REQUEST_TOO_BIG), | 569 | response = MHD_create_response_from_data (strlen (REQUEST_TOO_BIG), |
355 | REQUEST_TOO_BIG, MHD_NO, MHD_NO); | 570 | REQUEST_TOO_BIG, MHD_NO, MHD_NO); |
356 | MHD_queue_response (connection, status_code, response); | 571 | MHD_queue_response (connection, status_code, response); |
572 | EXTRA_CHECK(connection->response != NULL); | ||
357 | MHD_destroy_response (response); | 573 | MHD_destroy_response (response); |
574 | if (MHD_NO == build_header_response (connection)) | ||
575 | { | ||
576 | /* oops - close! */ | ||
577 | #if HAVE_MESSAGES | ||
578 | MHD_DLOG (connection->daemon, | ||
579 | "Closing connection (failed to create response header)\n"); | ||
580 | #endif | ||
581 | connection->state = MHD_CONNECTION_CLOSED; | ||
582 | } | ||
583 | else | ||
584 | { | ||
585 | connection->state = MHD_CONNECTION_HEADERS_SENDING; | ||
586 | } | ||
587 | } | ||
588 | |||
589 | /** | ||
590 | * Add "fd" to the "fd_set". If "fd" is | ||
591 | * greater than "*max", set "*max" to fd. | ||
592 | */ | ||
593 | static void | ||
594 | do_fd_set(int fd, | ||
595 | fd_set * set, | ||
596 | int * max_fd) | ||
597 | { | ||
598 | FD_SET (fd, set); | ||
599 | if (fd > *max_fd) | ||
600 | *max_fd = fd; | ||
601 | } | ||
602 | |||
603 | /** | ||
604 | * Obtain the select sets for this connection | ||
605 | * | ||
606 | * @return MHD_YES on success | ||
607 | */ | ||
608 | int | ||
609 | MHD_connection_get_fdset (struct MHD_Connection *connection, | ||
610 | fd_set * read_fd_set, | ||
611 | fd_set * write_fd_set, | ||
612 | fd_set * except_fd_set, int *max_fd) | ||
613 | { | ||
614 | int fd; | ||
615 | |||
616 | if (connection->pool == NULL) | ||
617 | connection->pool = MHD_pool_create (connection->daemon->pool_size); | ||
618 | if (connection->pool == NULL) | ||
619 | { | ||
620 | #if HAVE_MESSAGES | ||
621 | MHD_DLOG (connection->daemon, "Failed to create memory pool!\n"); | ||
622 | #endif | ||
623 | connection_close_error (connection); | ||
624 | return MHD_NO; | ||
625 | } | ||
626 | fd = connection->socket_fd; | ||
627 | if (fd == -1) | ||
628 | return MHD_YES; | ||
629 | while (1) { | ||
630 | #if DEBUG_STATES | ||
631 | fprintf(stderr, | ||
632 | "`%s' in state %u\n", | ||
633 | __FUNCTION__, | ||
634 | connection->state); | ||
635 | #endif | ||
636 | switch (connection->state) | ||
637 | { | ||
638 | case MHD_CONNECTION_INIT: | ||
639 | case MHD_CONNECTION_URL_RECEIVED: | ||
640 | case MHD_CONNECTION_HEADER_PART_RECEIVED: | ||
641 | /* while reading headers, we always grow the | ||
642 | read buffer if needed, no size-check required */ | ||
643 | if ( (connection->read_closed) && | ||
644 | (connection->read_buffer_offset == 0) ) | ||
645 | { | ||
646 | connection->state = MHD_CONNECTION_CLOSED; | ||
647 | continue; | ||
648 | } | ||
649 | if ( (connection->read_buffer_offset == connection->read_buffer_size) && | ||
650 | (MHD_NO == try_grow_read_buffer(connection)) ) | ||
651 | { | ||
652 | excessive_data_handler (connection, | ||
653 | (connection->url != NULL) | ||
654 | ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE | ||
655 | : MHD_HTTP_REQUEST_URI_TOO_LONG); | ||
656 | continue; | ||
657 | } | ||
658 | if (MHD_NO == connection->read_closed) | ||
659 | do_fd_set (fd, read_fd_set, max_fd); | ||
660 | break; | ||
661 | case MHD_CONNECTION_HEADERS_RECEIVED: | ||
662 | /* we should never get here */ | ||
663 | EXTRA_CHECK(0); | ||
664 | break; | ||
665 | case MHD_CONNECTION_HEADERS_PROCESSED: | ||
666 | EXTRA_CHECK(0); | ||
667 | break; | ||
668 | case MHD_CONNECTION_CONTINUE_SENDING: | ||
669 | do_fd_set (fd, write_fd_set, max_fd); | ||
670 | break; | ||
671 | case MHD_CONNECTION_CONTINUE_SENT: | ||
672 | if (connection->read_buffer_offset == connection->read_buffer_size) | ||
673 | try_grow_read_buffer(connection); | ||
674 | if (connection->read_buffer_offset < connection->read_buffer_size) | ||
675 | do_fd_set (fd, read_fd_set, max_fd); | ||
676 | break; | ||
677 | case MHD_CONNECTION_BODY_RECEIVED: | ||
678 | case MHD_CONNECTION_FOOTER_PART_RECEIVED: | ||
679 | /* while reading footers, we always grow the | ||
680 | read buffer if needed, no size-check required */ | ||
681 | if (MHD_YES == connection->read_closed) | ||
682 | { | ||
683 | connection->state = MHD_CONNECTION_CLOSED; | ||
684 | continue; | ||
685 | } | ||
686 | do_fd_set (fd, read_fd_set, max_fd); | ||
687 | /* transition to FOOTERS_RECEIVED | ||
688 | happens in read handler */ | ||
689 | break; | ||
690 | case MHD_CONNECTION_FOOTERS_RECEIVED: | ||
691 | /* no socket action, wait for client | ||
692 | to provide response */ | ||
693 | break; | ||
694 | case MHD_CONNECTION_HEADERS_SENDING: | ||
695 | /* headers in buffer, keep writing */ | ||
696 | do_fd_set(fd, write_fd_set, max_fd); | ||
697 | break; | ||
698 | case MHD_CONNECTION_HEADERS_SENT: | ||
699 | EXTRA_CHECK(0); | ||
700 | break; | ||
701 | case MHD_CONNECTION_NORMAL_BODY_READY: | ||
702 | do_fd_set (fd, write_fd_set, max_fd); | ||
703 | break; | ||
704 | case MHD_CONNECTION_NORMAL_BODY_UNREADY: | ||
705 | /* not ready, no socket action */ | ||
706 | break; | ||
707 | case MHD_CONNECTION_CHUNKED_BODY_READY: | ||
708 | do_fd_set (fd, write_fd_set, max_fd); | ||
709 | break; | ||
710 | case MHD_CONNECTION_CHUNKED_BODY_UNREADY: | ||
711 | /* not ready, no socket action */ | ||
712 | break; | ||
713 | case MHD_CONNECTION_BODY_SENT: | ||
714 | EXTRA_CHECK(0); | ||
715 | break; | ||
716 | case MHD_CONNECTION_FOOTERS_SENDING: | ||
717 | do_fd_set (fd, write_fd_set, max_fd); | ||
718 | break; | ||
719 | case MHD_CONNECTION_FOOTERS_SENT: | ||
720 | EXTRA_CHECK(0); | ||
721 | break; | ||
722 | case MHD_CONNECTION_CLOSED: | ||
723 | if (connection->socket_fd != -1) | ||
724 | connection_close_error(connection); | ||
725 | return MHD_YES; /* do nothing, not even reading */ | ||
726 | default: | ||
727 | EXTRA_CHECK(0); | ||
728 | } | ||
729 | break; | ||
730 | } | ||
731 | return MHD_YES; | ||
358 | } | 732 | } |
359 | 733 | ||
360 | /** | 734 | /** |
@@ -366,7 +740,7 @@ MHD_excessive_data_handler (struct MHD_Connection *connection, | |||
366 | * return NULL. Otherwise return a pointer to the line. | 740 | * return NULL. Otherwise return a pointer to the line. |
367 | */ | 741 | */ |
368 | static char * | 742 | static char * |
369 | MHD_get_next_header_line (struct MHD_Connection *connection) | 743 | get_next_header_line (struct MHD_Connection *connection) |
370 | { | 744 | { |
371 | char *rbuf; | 745 | char *rbuf; |
372 | size_t pos; | 746 | size_t pos; |
@@ -390,10 +764,10 @@ MHD_get_next_header_line (struct MHD_Connection *connection) | |||
390 | MHD_BUF_INC_SIZE); | 764 | MHD_BUF_INC_SIZE); |
391 | if (rbuf == NULL) | 765 | if (rbuf == NULL) |
392 | { | 766 | { |
393 | MHD_excessive_data_handler (connection, | 767 | excessive_data_handler (connection, |
394 | (connection->url != NULL) | 768 | (connection->url != NULL) |
395 | ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE | 769 | ? MHD_HTTP_REQUEST_ENTITY_TOO_LARGE |
396 | : MHD_HTTP_REQUEST_URI_TOO_LONG); | 770 | : MHD_HTTP_REQUEST_URI_TOO_LONG); |
397 | } | 771 | } |
398 | else | 772 | else |
399 | { | 773 | { |
@@ -418,8 +792,8 @@ MHD_get_next_header_line (struct MHD_Connection *connection) | |||
418 | * @return MHD_NO on failure (out of memory), MHD_YES for success | 792 | * @return MHD_NO on failure (out of memory), MHD_YES for success |
419 | */ | 793 | */ |
420 | static int | 794 | static int |
421 | MHD_connection_add_header (struct MHD_Connection *connection, | 795 | connection_add_header (struct MHD_Connection *connection, |
422 | char *key, char *value, enum MHD_ValueKind kind) | 796 | char *key, char *value, enum MHD_ValueKind kind) |
423 | { | 797 | { |
424 | struct MHD_HTTP_Header *hdr; | 798 | struct MHD_HTTP_Header *hdr; |
425 | 799 | ||
@@ -431,8 +805,8 @@ MHD_connection_add_header (struct MHD_Connection *connection, | |||
431 | MHD_DLOG (connection->daemon, | 805 | MHD_DLOG (connection->daemon, |
432 | "Not enough memory to allocate header record!\n"); | 806 | "Not enough memory to allocate header record!\n"); |
433 | #endif | 807 | #endif |
434 | MHD_excessive_data_handler (connection, | 808 | excessive_data_handler (connection, |
435 | MHD_HTTP_REQUEST_ENTITY_TOO_LARGE); | 809 | MHD_HTTP_REQUEST_ENTITY_TOO_LARGE); |
436 | return MHD_NO; | 810 | return MHD_NO; |
437 | } | 811 | } |
438 | hdr->next = connection->headers_received; | 812 | hdr->next = connection->headers_received; |
@@ -468,8 +842,8 @@ parse_arguments (enum MHD_ValueKind kind, | |||
468 | } | 842 | } |
469 | MHD_http_unescape (args); | 843 | MHD_http_unescape (args); |
470 | MHD_http_unescape (equals); | 844 | MHD_http_unescape (equals); |
471 | if (MHD_NO == MHD_connection_add_header (connection, | 845 | if (MHD_NO == connection_add_header (connection, |
472 | args, equals, kind)) | 846 | args, equals, kind)) |
473 | return MHD_NO; | 847 | return MHD_NO; |
474 | args = amper; | 848 | args = amper; |
475 | } | 849 | } |
@@ -482,7 +856,7 @@ parse_arguments (enum MHD_ValueKind kind, | |||
482 | * @return MHD_YES for success, MHD_NO for failure (malformed, out of memory) | 856 | * @return MHD_YES for success, MHD_NO for failure (malformed, out of memory) |
483 | */ | 857 | */ |
484 | static int | 858 | static int |
485 | MHD_parse_cookie_header (struct MHD_Connection *connection) | 859 | parse_cookie_header (struct MHD_Connection *connection) |
486 | { | 860 | { |
487 | const char *hdr; | 861 | const char *hdr; |
488 | char *cpy; | 862 | char *cpy; |
@@ -500,8 +874,8 @@ MHD_parse_cookie_header (struct MHD_Connection *connection) | |||
500 | #if HAVE_MESSAGES | 874 | #if HAVE_MESSAGES |
501 | MHD_DLOG (connection->daemon, "Not enough memory to parse cookies!\n"); | 875 | MHD_DLOG (connection->daemon, "Not enough memory to parse cookies!\n"); |
502 | #endif | 876 | #endif |
503 | MHD_excessive_data_handler (connection, | 877 | excessive_data_handler (connection, |
504 | MHD_HTTP_REQUEST_ENTITY_TOO_LARGE); | 878 | MHD_HTTP_REQUEST_ENTITY_TOO_LARGE); |
505 | return MHD_NO; | 879 | return MHD_NO; |
506 | } | 880 | } |
507 | memcpy (cpy, hdr, strlen (hdr) + 1); | 881 | memcpy (cpy, hdr, strlen (hdr) + 1); |
@@ -536,8 +910,8 @@ MHD_parse_cookie_header (struct MHD_Connection *connection) | |||
536 | equals[strlen (equals) - 1] = '\0'; | 910 | equals[strlen (equals) - 1] = '\0'; |
537 | equals++; | 911 | equals++; |
538 | } | 912 | } |
539 | if (MHD_NO == MHD_connection_add_header (connection, | 913 | if (MHD_NO == connection_add_header (connection, |
540 | pos, equals, MHD_COOKIE_KIND)) | 914 | pos, equals, MHD_COOKIE_KIND)) |
541 | return MHD_NO; | 915 | return MHD_NO; |
542 | pos = semicolon; | 916 | pos = semicolon; |
543 | } | 917 | } |
@@ -587,235 +961,29 @@ parse_initial_message_line (struct MHD_Connection *connection, char *line) | |||
587 | return MHD_YES; | 961 | return MHD_YES; |
588 | } | 962 | } |
589 | 963 | ||
590 | |||
591 | /** | ||
592 | * This function is designed to parse the input buffer of a given | ||
593 | * connection for HTTP headers -- and in the case of chunked encoding, | ||
594 | * also for HTTP "footers". | ||
595 | * | ||
596 | * Once the header is complete, it should have set the | ||
597 | * headers_received, url and method values and set | ||
598 | * have_received_headers to MHD_YES. If no body is expected, it | ||
599 | * should also set "have_received_body" to MHD_YES. Otherwise, it | ||
600 | * should set "remaining_upload_size" to the expected size of the | ||
601 | * body. If the size of the body is unknown, it should be set to -1. | ||
602 | */ | ||
603 | static void | ||
604 | MHD_parse_connection_headers (struct MHD_Connection *connection) | ||
605 | { | ||
606 | char *last; | ||
607 | char *line; | ||
608 | char *colon; | ||
609 | char *tmp; | ||
610 | const char *clen; | ||
611 | unsigned long long cval; | ||
612 | struct MHD_Response *response; | ||
613 | |||
614 | if (((connection->have_received_body == MHD_YES) && | ||
615 | (connection->have_chunked_upload == MHD_NO)) || | ||
616 | (connection->have_received_headers == MHD_YES)) | ||
617 | abort (); | ||
618 | colon = NULL; /* make gcc happy */ | ||
619 | last = NULL; | ||
620 | while (NULL != (line = MHD_get_next_header_line (connection))) | ||
621 | { | ||
622 | if (last != NULL) | ||
623 | { | ||
624 | if ((line[0] == ' ') || (line[0] == '\t')) | ||
625 | { | ||
626 | /* value was continued on the next line, see | ||
627 | http://www.jmarshall.com/easy/http/ */ | ||
628 | last = MHD_pool_reallocate (connection->pool, | ||
629 | last, | ||
630 | strlen (last) + 1, | ||
631 | strlen (line) + strlen (last) + 1); | ||
632 | if (last == NULL) | ||
633 | { | ||
634 | MHD_excessive_data_handler (connection, | ||
635 | MHD_HTTP_REQUEST_ENTITY_TOO_LARGE); | ||
636 | break; | ||
637 | } | ||
638 | tmp = line; | ||
639 | while ((tmp[0] == ' ') || (tmp[0] == '\t')) | ||
640 | tmp++; /* skip whitespace at start of 2nd line */ | ||
641 | strcat (last, tmp); | ||
642 | continue; /* possibly more than 2 lines... */ | ||
643 | } | ||
644 | else | ||
645 | { | ||
646 | if (MHD_NO == MHD_connection_add_header (connection, | ||
647 | last, | ||
648 | colon, | ||
649 | MHD_HEADER_KIND)) | ||
650 | return; | ||
651 | last = NULL; | ||
652 | } | ||
653 | } | ||
654 | if (connection->url == NULL) | ||
655 | { | ||
656 | /* line must be request line (first line of header) */ | ||
657 | if (MHD_NO == parse_initial_message_line (connection, line)) | ||
658 | goto DIE; | ||
659 | continue; | ||
660 | } | ||
661 | /* check if this is the end of the header */ | ||
662 | if (strlen (line) == 0) | ||
663 | { | ||
664 | /* end of header */ | ||
665 | connection->have_received_headers = MHD_YES; | ||
666 | clen = MHD_lookup_connection_value (connection, | ||
667 | MHD_HEADER_KIND, | ||
668 | MHD_HTTP_HEADER_CONTENT_LENGTH); | ||
669 | if (clen != NULL) | ||
670 | { | ||
671 | if (1 != sscanf (clen, "%llu", &cval)) | ||
672 | { | ||
673 | #if HAVE_MESSAGES | ||
674 | MHD_DLOG (connection->daemon, | ||
675 | "Failed to parse `%s' header `%s', closing connection.\n", | ||
676 | MHD_HTTP_HEADER_CONTENT_LENGTH, clen); | ||
677 | #endif | ||
678 | goto DIE; | ||
679 | } | ||
680 | connection->remaining_upload_size = cval; | ||
681 | connection->have_received_body = cval == 0 ? MHD_YES : MHD_NO; | ||
682 | } | ||
683 | else | ||
684 | { | ||
685 | if (NULL == MHD_lookup_connection_value (connection, | ||
686 | MHD_HEADER_KIND, | ||
687 | MHD_HTTP_HEADER_TRANSFER_ENCODING)) | ||
688 | { | ||
689 | /* this request does not have a body */ | ||
690 | connection->remaining_upload_size = 0; | ||
691 | connection->have_received_body = MHD_YES; | ||
692 | } | ||
693 | else | ||
694 | { | ||
695 | if (connection->have_chunked_upload == MHD_NO) | ||
696 | { | ||
697 | connection->remaining_upload_size = -1; /* unknown size */ | ||
698 | if (0 == | ||
699 | strcasecmp (MHD_lookup_connection_value | ||
700 | (connection, MHD_HEADER_KIND, | ||
701 | MHD_HTTP_HEADER_TRANSFER_ENCODING), | ||
702 | "chunked")) | ||
703 | connection->have_chunked_upload = MHD_YES; | ||
704 | } | ||
705 | else | ||
706 | { | ||
707 | /* we were actually processing the footers at the | ||
708 | END of a chunked encoding; give connection | ||
709 | handler an extra chance... */ | ||
710 | connection->have_chunked_upload = MHD_NO; /* no more! */ | ||
711 | MHD_call_connection_handler (connection); | ||
712 | } | ||
713 | } | ||
714 | } | ||
715 | if ((0 != (MHD_USE_PEDANTIC_CHECKS & connection->daemon->options)) | ||
716 | && (NULL != connection->version) | ||
717 | && (0 == strcasecmp (MHD_HTTP_VERSION_1_1, connection->version)) | ||
718 | && (NULL == | ||
719 | MHD_lookup_connection_value (connection, MHD_HEADER_KIND, | ||
720 | MHD_HTTP_HEADER_HOST))) | ||
721 | { | ||
722 | /* die, http 1.1 request without host and we are pedantic */ | ||
723 | connection->have_received_body = MHD_YES; | ||
724 | connection->read_close = MHD_YES; | ||
725 | #if HAVE_MESSAGES | ||
726 | MHD_DLOG (connection->daemon, | ||
727 | "Received `%s' request without `%s' header.\n", | ||
728 | MHD_HTTP_VERSION_1_1, MHD_HTTP_HEADER_HOST); | ||
729 | #endif | ||
730 | response = | ||
731 | MHD_create_response_from_data (strlen (REQUEST_LACKS_HOST), | ||
732 | REQUEST_LACKS_HOST, MHD_NO, | ||
733 | MHD_NO); | ||
734 | MHD_queue_response (connection, MHD_HTTP_BAD_REQUEST, response); | ||
735 | MHD_destroy_response (response); | ||
736 | } | ||
737 | break; | ||
738 | } | ||
739 | /* line should be normal header line, find colon */ | ||
740 | colon = strstr (line, ":"); | ||
741 | if (colon == NULL) | ||
742 | { | ||
743 | /* error in header line, die hard */ | ||
744 | #if HAVE_MESSAGES | ||
745 | MHD_DLOG (connection->daemon, | ||
746 | "Received malformed line (no colon), closing connection.\n"); | ||
747 | #endif | ||
748 | goto DIE; | ||
749 | } | ||
750 | /* zero-terminate header */ | ||
751 | colon[0] = '\0'; | ||
752 | colon++; /* advance to value */ | ||
753 | while ((colon[0] != '\0') && ((colon[0] == ' ') || (colon[0] == '\t'))) | ||
754 | colon++; | ||
755 | /* we do the actual adding of the connection | ||
756 | header at the beginning of the while | ||
757 | loop since we need to be able to inspect | ||
758 | the *next* header line (in case it starts | ||
759 | with a space...) */ | ||
760 | last = line; | ||
761 | } | ||
762 | if ((last != NULL) && | ||
763 | (MHD_NO == MHD_connection_add_header (connection, | ||
764 | last, colon, MHD_HEADER_KIND))) | ||
765 | return; /* error */ | ||
766 | MHD_parse_cookie_header (connection); | ||
767 | return; | ||
768 | DIE: | ||
769 | #if HAVE_MESSAGES | ||
770 | MHD_DLOG (connection->daemon, | ||
771 | "Closing connection (problem parsing headers)\n"); | ||
772 | #endif | ||
773 | connection_close_error (connection); | ||
774 | } | ||
775 | |||
776 | |||
777 | /** | ||
778 | * Find the handler responsible for this request. | ||
779 | */ | ||
780 | static struct MHD_Access_Handler * | ||
781 | MHD_find_access_handler (struct MHD_Connection *connection) | ||
782 | { | ||
783 | struct MHD_Access_Handler *pos; | ||
784 | |||
785 | pos = connection->daemon->handlers; | ||
786 | while (pos != NULL) | ||
787 | { | ||
788 | if (0 == strcmp (connection->url, pos->uri_prefix)) | ||
789 | return pos; | ||
790 | pos = pos->next; | ||
791 | } | ||
792 | return &connection->daemon->default_handler; | ||
793 | } | ||
794 | |||
795 | |||
796 | /** | 964 | /** |
797 | * Call the handler of the application for this | 965 | * Call the handler of the application for this |
798 | * connection. | 966 | * connection. Handles chunking of the upload |
967 | * as well as normal uploads. | ||
799 | */ | 968 | */ |
800 | void | 969 | static void |
801 | MHD_call_connection_handler (struct MHD_Connection *connection) | 970 | call_connection_handler (struct MHD_Connection *connection) |
802 | { | 971 | { |
803 | struct MHD_Access_Handler *ah; | ||
804 | unsigned int processed; | 972 | unsigned int processed; |
805 | unsigned int available; | 973 | unsigned int available; |
806 | unsigned int used; | 974 | unsigned int used; |
807 | int instant_retry; | 975 | int instant_retry; |
808 | unsigned int i; | 976 | unsigned int i; |
977 | int malformed; | ||
809 | 978 | ||
810 | if (connection->response != NULL) | 979 | if (connection->response != NULL) |
811 | return; /* already queued a response */ | 980 | return; /* already queued a response */ |
812 | if (connection->have_received_headers == MHD_NO) | ||
813 | abort (); /* bad timing... */ | ||
814 | do | 981 | do |
815 | { | 982 | { |
816 | instant_retry = MHD_NO; | 983 | instant_retry = MHD_NO; |
817 | available = connection->read_buffer_offset; | 984 | available = connection->read_buffer_offset; |
818 | if (connection->have_chunked_upload == MHD_YES) | 985 | if ( (connection->have_chunked_upload == MHD_YES) && |
986 | (connection->remaining_upload_size == -1) ) | ||
819 | { | 987 | { |
820 | if ((connection->current_chunk_offset == | 988 | if ((connection->current_chunk_offset == |
821 | connection->current_chunk_size) | 989 | connection->current_chunk_size) |
@@ -876,18 +1044,17 @@ MHD_call_connection_handler (struct MHD_Connection *connection) | |||
876 | } | 1044 | } |
877 | if (i >= available) | 1045 | if (i >= available) |
878 | return; /* need more data... */ | 1046 | return; /* need more data... */ |
879 | /* The following if-statement is a bit crazy -- we | 1047 | malformed = (i >= 6); |
880 | use the second clause only for the side-effect, | 1048 | if (! malformed) |
881 | 0-terminating the buffer for the following sscanf | 1049 | { |
882 | attempts; yes, there should be only a single | 1050 | connection->read_buffer[i] = '\0'; |
883 | "="-sign (assignment!) in the read_buffer[i]-line. */ | 1051 | malformed = (1 != sscanf (connection->read_buffer, |
884 | if ((i >= 6) || | 1052 | "%X", |
885 | ((connection->read_buffer[i] = '\0')) || | 1053 | &connection->current_chunk_size)) && |
886 | ((1 != sscanf (connection->read_buffer, | 1054 | (1 != sscanf (connection->read_buffer, |
887 | "%X", | 1055 | "%x", &connection->current_chunk_size)); |
888 | &connection->current_chunk_size)) && | 1056 | } |
889 | (1 != sscanf (connection->read_buffer, | 1057 | if (malformed) |
890 | "%x", &connection->current_chunk_size)))) | ||
891 | { | 1058 | { |
892 | /* malformed encoding */ | 1059 | /* malformed encoding */ |
893 | #if HAVE_MESSAGES | 1060 | #if HAVE_MESSAGES |
@@ -908,11 +1075,7 @@ MHD_call_connection_handler (struct MHD_Connection *connection) | |||
908 | instant_retry = MHD_YES; | 1075 | instant_retry = MHD_YES; |
909 | if (connection->current_chunk_size == 0) | 1076 | if (connection->current_chunk_size == 0) |
910 | { | 1077 | { |
911 | /* we're back to reading HEADERS (footers!) */ | ||
912 | connection->have_received_body = MHD_YES; | ||
913 | connection->remaining_upload_size = 0; | 1078 | connection->remaining_upload_size = 0; |
914 | connection->have_received_headers = MHD_NO; | ||
915 | MHD_parse_connection_headers (connection); | ||
916 | return; | 1079 | return; |
917 | } | 1080 | } |
918 | continue; | 1081 | continue; |
@@ -925,8 +1088,7 @@ MHD_call_connection_handler (struct MHD_Connection *connection) | |||
925 | available = 0; | 1088 | available = 0; |
926 | } | 1089 | } |
927 | used = processed; | 1090 | used = processed; |
928 | ah = MHD_find_access_handler (connection); | 1091 | if (MHD_NO == connection->daemon->default_handler (connection->daemon->default_handler_cls, |
929 | if (MHD_NO == ah->dh (ah->dh_cls, | ||
930 | connection, | 1092 | connection, |
931 | connection->url, | 1093 | connection->url, |
932 | connection->method, | 1094 | connection->method, |
@@ -942,6 +1104,8 @@ MHD_call_connection_handler (struct MHD_Connection *connection) | |||
942 | connection_close_error (connection); | 1104 | connection_close_error (connection); |
943 | return; | 1105 | return; |
944 | } | 1106 | } |
1107 | if (processed > used) | ||
1108 | abort(); /* fatal client API violation! */ | ||
945 | if (processed != 0) | 1109 | if (processed != 0) |
946 | instant_retry = MHD_NO; /* client did not process everything */ | 1110 | instant_retry = MHD_NO; /* client did not process everything */ |
947 | used -= processed; | 1111 | used -= processed; |
@@ -953,79 +1117,26 @@ MHD_call_connection_handler (struct MHD_Connection *connection) | |||
953 | &connection->read_buffer[used], processed + available); | 1117 | &connection->read_buffer[used], processed + available); |
954 | if (connection->remaining_upload_size != -1) | 1118 | if (connection->remaining_upload_size != -1) |
955 | connection->remaining_upload_size -= used; | 1119 | connection->remaining_upload_size -= used; |
956 | connection->read_buffer_offset = processed + available; | 1120 | connection->read_buffer_offset = processed + available; |
957 | if ((connection->remaining_upload_size == 0) || | ||
958 | ((connection->read_buffer_offset == 0) && | ||
959 | (connection->remaining_upload_size == -1) | ||
960 | && (connection->socket_fd == -1))) | ||
961 | { | ||
962 | connection->have_received_body = MHD_YES; | ||
963 | if (connection->read_buffer != NULL) | ||
964 | MHD_pool_reallocate (connection->pool, | ||
965 | connection->read_buffer, | ||
966 | (connection->read_buffer == | ||
967 | NULL) ? 0 : connection->read_buffer_size + | ||
968 | 1, 0); | ||
969 | connection->read_buffer_offset = 0; | ||
970 | connection->read_buffer_size = 0; | ||
971 | connection->read_buffer = NULL; | ||
972 | } | ||
973 | } | 1121 | } |
974 | while (instant_retry == MHD_YES); | 1122 | while (instant_retry == MHD_YES); |
975 | } | 1123 | } |
976 | 1124 | ||
977 | /** | 1125 | /** |
978 | * This function handles a particular connection when it has been | 1126 | * Try reading data from the socket into the |
979 | * determined that there is data to be read off a socket. All implementations | 1127 | * read buffer of the connection. |
980 | * (multithreaded, external select, internal select) call this function | 1128 | * |
981 | * to handle reads. | 1129 | * @return MHD_YES if something changed, |
1130 | * MHD_NO if we were interrupted or if | ||
1131 | * no space was available | ||
982 | */ | 1132 | */ |
983 | int | 1133 | static int |
984 | MHD_connection_handle_read (struct MHD_Connection *connection) | 1134 | do_read(struct MHD_Connection * connection) |
985 | { | 1135 | { |
986 | int bytes_read; | 1136 | int bytes_read; |
987 | void *tmp; | 1137 | |
988 | 1138 | if (connection->read_buffer_size == connection->read_buffer_offset) | |
989 | if (connection->pool == NULL) | 1139 | return MHD_NO; |
990 | connection->pool = MHD_pool_create (connection->daemon->pool_size); | ||
991 | if (connection->pool == NULL) | ||
992 | { | ||
993 | #if HAVE_MESSAGES | ||
994 | MHD_DLOG (connection->daemon, "Failed to create memory pool!\n"); | ||
995 | #endif | ||
996 | connection_close_error (connection); | ||
997 | return MHD_NO; | ||
998 | } | ||
999 | if ((connection->read_buffer_offset >= connection->read_buffer_size) && | ||
1000 | (connection->have_received_headers == MHD_NO)) | ||
1001 | { | ||
1002 | /* need to grow read buffer */ | ||
1003 | tmp = MHD_pool_reallocate (connection->pool, | ||
1004 | connection->read_buffer, | ||
1005 | connection->read_buffer_size, | ||
1006 | connection->read_buffer_size * 2 + | ||
1007 | MHD_BUF_INC_SIZE + 1); | ||
1008 | if (tmp == NULL) | ||
1009 | { | ||
1010 | #if HAVE_MESSAGES | ||
1011 | MHD_DLOG (connection->daemon, | ||
1012 | "Not enough memory for reading headers!\n"); | ||
1013 | #endif | ||
1014 | MHD_excessive_data_handler (connection, | ||
1015 | MHD_HTTP_REQUEST_ENTITY_TOO_LARGE); | ||
1016 | return MHD_NO; | ||
1017 | } | ||
1018 | connection->read_buffer = tmp; | ||
1019 | connection->read_buffer_size = | ||
1020 | connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE; | ||
1021 | } | ||
1022 | if (connection->read_buffer_offset >= connection->read_buffer_size) | ||
1023 | { | ||
1024 | #if HAVE_MESSAGES | ||
1025 | MHD_DLOG (connection->daemon, "Unexpected call to %s.\n", __FUNCTION__); | ||
1026 | #endif | ||
1027 | return MHD_NO; | ||
1028 | } | ||
1029 | bytes_read = RECV (connection->socket_fd, | 1140 | bytes_read = RECV (connection->socket_fd, |
1030 | &connection->read_buffer[connection->read_buffer_offset], | 1141 | &connection->read_buffer[connection->read_buffer_offset], |
1031 | connection->read_buffer_size - | 1142 | connection->read_buffer_size - |
@@ -1044,150 +1155,297 @@ MHD_connection_handle_read (struct MHD_Connection *connection) | |||
1044 | if (bytes_read == 0) | 1155 | if (bytes_read == 0) |
1045 | { | 1156 | { |
1046 | /* other side closed connection */ | 1157 | /* other side closed connection */ |
1047 | connection->read_close = MHD_YES; | 1158 | connection->read_closed = MHD_YES; |
1048 | if ((connection->have_received_headers == MHD_YES) | 1159 | return MHD_NO; |
1049 | && (connection->read_buffer_offset > 0)) | ||
1050 | MHD_call_connection_handler (connection); | ||
1051 | #if DEBUG_CLOSE | ||
1052 | #if HAVE_MESSAGES | ||
1053 | MHD_DLOG (connection->daemon, | ||
1054 | "Shutting down connection for reading (other side closed connection)\n"); | ||
1055 | #endif | ||
1056 | #endif | ||
1057 | shutdown (connection->socket_fd, SHUT_RD); | ||
1058 | if ((connection->have_received_headers == MHD_NO) || | ||
1059 | (connection->have_received_body == MHD_NO)) | ||
1060 | { | ||
1061 | /* no request => no response! */ | ||
1062 | CLOSE (connection->socket_fd); | ||
1063 | connection->socket_fd = -1; | ||
1064 | } | ||
1065 | return MHD_YES; | ||
1066 | } | 1160 | } |
1067 | connection->read_buffer_offset += bytes_read; | 1161 | connection->read_buffer_offset += bytes_read; |
1068 | if (connection->have_received_headers == MHD_NO) | ||
1069 | MHD_parse_connection_headers (connection); | ||
1070 | if ((connection->have_received_headers == MHD_YES) | ||
1071 | && (connection->method != NULL)) | ||
1072 | MHD_call_connection_handler (connection); | ||
1073 | return MHD_YES; | 1162 | return MHD_YES; |
1074 | } | 1163 | } |
1075 | 1164 | ||
1076 | /** | 1165 | /** |
1077 | * Check if we need to set some additional headers | 1166 | * We have received (possibly the beginning of) a line in the |
1078 | * for http-compiliance. | 1167 | * header (or footer). Validate (check for ":") and prepare |
1168 | * to process. | ||
1079 | */ | 1169 | */ |
1080 | static void | 1170 | static void |
1081 | MHD_add_extra_headers (struct MHD_Connection *connection) | 1171 | process_header_line(struct MHD_Connection * connection, |
1172 | char * line) | ||
1082 | { | 1173 | { |
1083 | const char *have; | 1174 | char *colon; |
1084 | char buf[128]; | ||
1085 | 1175 | ||
1086 | if (connection->response->total_size == -1) | 1176 | /* line should be normal header line, find colon */ |
1177 | colon = strstr (line, ":"); | ||
1178 | if (colon == NULL) | ||
1087 | { | 1179 | { |
1088 | have = MHD_get_response_header (connection->response, | 1180 | /* error in header line, die hard */ |
1089 | MHD_HTTP_HEADER_CONNECTION); | 1181 | #if HAVE_MESSAGES |
1090 | if (have == NULL) | 1182 | MHD_DLOG (connection->daemon, |
1091 | MHD_add_response_header (connection->response, | 1183 | "Received malformed line (no colon), closing connection.\n"); |
1092 | MHD_HTTP_HEADER_CONNECTION, "close"); | 1184 | #endif |
1093 | } | 1185 | connection->state = MHD_CONNECTION_CLOSED; |
1094 | else if (NULL == MHD_get_response_header (connection->response, | 1186 | return; |
1095 | MHD_HTTP_HEADER_CONTENT_LENGTH)) | ||
1096 | { | ||
1097 | _REAL_SNPRINTF (buf, | ||
1098 | 128, | ||
1099 | "%llu", | ||
1100 | (unsigned long long) connection->response->total_size); | ||
1101 | MHD_add_response_header (connection->response, | ||
1102 | MHD_HTTP_HEADER_CONTENT_LENGTH, buf); | ||
1103 | } | 1187 | } |
1188 | /* zero-terminate header */ | ||
1189 | colon[0] = '\0'; | ||
1190 | colon++; /* advance to value */ | ||
1191 | while ((colon[0] != '\0') && ((colon[0] == ' ') || (colon[0] == '\t'))) | ||
1192 | colon++; | ||
1193 | /* we do the actual adding of the connection | ||
1194 | header at the beginning of the while | ||
1195 | loop since we need to be able to inspect | ||
1196 | the *next* header line (in case it starts | ||
1197 | with a space...) */ | ||
1198 | connection->last = line; | ||
1199 | connection->colon = colon; | ||
1104 | } | 1200 | } |
1105 | 1201 | ||
1202 | /** | ||
1203 | * Process a header value that spans multiple lines. | ||
1204 | * The previous line(s) are in connection->last. | ||
1205 | * | ||
1206 | * @param line the current input line | ||
1207 | * @param kind if the line is complete, add a header | ||
1208 | * of the given kind | ||
1209 | */ | ||
1106 | static void | 1210 | static void |
1107 | get_date_string (char *date, unsigned int max) | 1211 | process_broken_line(struct MHD_Connection * connection, |
1212 | char * line, | ||
1213 | enum MHD_ValueKind kind) | ||
1108 | { | 1214 | { |
1109 | static const char *days[] = | 1215 | char * last; |
1110 | { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; | 1216 | char * tmp; |
1111 | static const char *mons[] = | ||
1112 | { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", | ||
1113 | "Nov", "Dec" | ||
1114 | }; | ||
1115 | struct tm now; | ||
1116 | time_t t; | ||
1117 | 1217 | ||
1118 | time (&t); | 1218 | last = connection->last; |
1119 | gmtime_r (&t, &now); | 1219 | if ((line[0] == ' ') || (line[0] == '\t')) |
1120 | snprintf (date, | 1220 | { |
1121 | max - 1, | 1221 | /* value was continued on the next line, see |
1122 | "Date: %3s, %02u %3s %04u %02u:%02u:%02u GMT\r\n", | 1222 | http://www.jmarshall.com/easy/http/ */ |
1123 | days[now.tm_wday % 7], | 1223 | last = MHD_pool_reallocate (connection->pool, |
1124 | now.tm_mday, | 1224 | last, |
1125 | mons[now.tm_mon % 12], | 1225 | strlen (last) + 1, |
1126 | now.tm_year, now.tm_hour, now.tm_min, now.tm_sec); | 1226 | strlen (line) + strlen (last) + 1); |
1227 | if (last == NULL) | ||
1228 | { | ||
1229 | excessive_data_handler (connection, | ||
1230 | MHD_HTTP_REQUEST_ENTITY_TOO_LARGE); | ||
1231 | return; | ||
1232 | } | ||
1233 | tmp = line; | ||
1234 | while ((tmp[0] == ' ') || (tmp[0] == '\t')) | ||
1235 | tmp++; /* skip whitespace at start of 2nd line */ | ||
1236 | strcat (last, tmp); | ||
1237 | connection->last = last; | ||
1238 | return; /* possibly more than 2 lines... */ | ||
1239 | } | ||
1240 | if (MHD_NO == connection_add_header (connection, | ||
1241 | last, | ||
1242 | connection->colon, | ||
1243 | kind)) | ||
1244 | { | ||
1245 | excessive_data_handler (connection, | ||
1246 | MHD_HTTP_REQUEST_ENTITY_TOO_LARGE); | ||
1247 | return; | ||
1248 | } | ||
1249 | /* we still have the current line to deal with... */ | ||
1250 | if (strlen(line) != 0) | ||
1251 | process_header_line(connection, | ||
1252 | line); | ||
1127 | } | 1253 | } |
1128 | 1254 | ||
1129 | /** | 1255 | /** |
1130 | * Allocate the connection's write buffer and | 1256 | * Parse the various headers; figure out the size |
1131 | * fill it with all of the headers from the | 1257 | * of the upload and make sure the headers follow |
1132 | * HTTPd's response. | 1258 | * the protocol. Advance to the appropriate state. |
1133 | */ | 1259 | */ |
1134 | static int | 1260 | static void |
1135 | MHD_build_header_response (struct MHD_Connection *connection) | 1261 | parse_connection_headers(struct MHD_Connection * connection) |
1136 | { | 1262 | { |
1137 | size_t size; | 1263 | const char *clen; |
1138 | size_t off; | 1264 | unsigned long long cval; |
1139 | struct MHD_HTTP_Header *pos; | 1265 | struct MHD_Response *response; |
1140 | char code[128]; | ||
1141 | char date[128]; | ||
1142 | char *data; | ||
1143 | 1266 | ||
1144 | MHD_add_extra_headers (connection); | 1267 | parse_cookie_header (connection); |
1145 | const char *reason_phrase = | 1268 | if ((0 != (MHD_USE_PEDANTIC_CHECKS & connection->daemon->options)) |
1146 | MHD_get_reason_phrase_for (connection->responseCode); | 1269 | && (NULL != connection->version) |
1147 | _REAL_SNPRINTF (code, 128, "%s %u %s\r\n", MHD_HTTP_VERSION_1_1, | 1270 | && (0 == strcasecmp (MHD_HTTP_VERSION_1_1, connection->version)) |
1148 | connection->responseCode, reason_phrase); | 1271 | && (NULL == |
1149 | off = strlen (code); | 1272 | MHD_lookup_connection_value (connection, MHD_HEADER_KIND, |
1150 | /* estimate size */ | 1273 | MHD_HTTP_HEADER_HOST))) |
1151 | size = off + 2; /* extra \r\n at the end */ | ||
1152 | pos = connection->response->first_header; | ||
1153 | while (pos != NULL) | ||
1154 | { | 1274 | { |
1155 | size += strlen (pos->header) + strlen (pos->value) + 4; /* colon, space, linefeeds */ | 1275 | /* die, http 1.1 request without host and we are pedantic */ |
1156 | pos = pos->next; | 1276 | connection->state = MHD_CONNECTION_FOOTERS_RECEIVED; |
1277 | connection->read_closed = MHD_YES; | ||
1278 | #if HAVE_MESSAGES | ||
1279 | MHD_DLOG (connection->daemon, | ||
1280 | "Received `%s' request without `%s' header.\n", | ||
1281 | MHD_HTTP_VERSION_1_1, MHD_HTTP_HEADER_HOST); | ||
1282 | #endif | ||
1283 | response = | ||
1284 | MHD_create_response_from_data (strlen (REQUEST_LACKS_HOST), | ||
1285 | REQUEST_LACKS_HOST, MHD_NO, | ||
1286 | MHD_NO); | ||
1287 | MHD_queue_response (connection, MHD_HTTP_BAD_REQUEST, response); | ||
1288 | MHD_destroy_response (response); | ||
1289 | return; | ||
1157 | } | 1290 | } |
1158 | if (NULL == MHD_get_response_header (connection->response, | 1291 | |
1159 | MHD_HTTP_HEADER_DATE)) | 1292 | clen = MHD_lookup_connection_value (connection, |
1160 | get_date_string (date, sizeof (date)); | 1293 | MHD_HEADER_KIND, |
1161 | else | 1294 | MHD_HTTP_HEADER_CONTENT_LENGTH); |
1162 | date[0] = '\0'; | 1295 | if (clen != NULL) |
1163 | size += strlen (date); | ||
1164 | /* produce data */ | ||
1165 | data = MHD_pool_allocate (connection->pool, size + 1, MHD_YES); | ||
1166 | if (data == NULL) | ||
1167 | { | 1296 | { |
1297 | if (1 != sscanf (clen, "%llu", &cval)) | ||
1298 | { | ||
1168 | #if HAVE_MESSAGES | 1299 | #if HAVE_MESSAGES |
1169 | MHD_DLOG (connection->daemon, "Not enough memory for write!\n"); | 1300 | MHD_DLOG (connection->daemon, |
1301 | "Failed to parse `%s' header `%s', closing connection.\n", | ||
1302 | MHD_HTTP_HEADER_CONTENT_LENGTH, clen); | ||
1170 | #endif | 1303 | #endif |
1171 | return MHD_NO; | 1304 | connection->state = MHD_CONNECTION_CLOSED; |
1305 | return; | ||
1306 | } | ||
1307 | connection->remaining_upload_size = cval; | ||
1172 | } | 1308 | } |
1173 | memcpy (data, code, off); | 1309 | else |
1174 | pos = connection->response->first_header; | ||
1175 | while (pos != NULL) | ||
1176 | { | 1310 | { |
1177 | SPRINTF (&data[off], "%s: %s\r\n", pos->header, pos->value); | 1311 | if (NULL == MHD_lookup_connection_value (connection, |
1178 | off += strlen (pos->header) + strlen (pos->value) + 4; | 1312 | MHD_HEADER_KIND, |
1179 | pos = pos->next; | 1313 | MHD_HTTP_HEADER_TRANSFER_ENCODING)) |
1314 | { | ||
1315 | /* this request does not have a body */ | ||
1316 | connection->remaining_upload_size = 0; | ||
1317 | } | ||
1318 | else | ||
1319 | { | ||
1320 | connection->remaining_upload_size = -1; /* unknown size */ | ||
1321 | if (0 == | ||
1322 | strcasecmp (MHD_lookup_connection_value | ||
1323 | (connection, MHD_HEADER_KIND, | ||
1324 | MHD_HTTP_HEADER_TRANSFER_ENCODING), | ||
1325 | "chunked")) | ||
1326 | connection->have_chunked_upload = MHD_YES; | ||
1327 | } | ||
1328 | } | ||
1329 | } | ||
1330 | |||
1331 | /** | ||
1332 | * This function handles a particular connection when it has been | ||
1333 | * determined that there is data to be read off a socket. All | ||
1334 | * implementations (multithreaded, external select, internal select) | ||
1335 | * call this function to handle reads. | ||
1336 | * | ||
1337 | * @return MHD_YES if we should continue to process the | ||
1338 | * connection (not dead yet), MHD_NO if it died | ||
1339 | */ | ||
1340 | int | ||
1341 | MHD_connection_handle_read (struct MHD_Connection *connection) | ||
1342 | { | ||
1343 | connection->last_activity = time(NULL); | ||
1344 | if (connection->state == MHD_CONNECTION_CLOSED) | ||
1345 | return MHD_NO; | ||
1346 | if (MHD_NO == do_read(connection)) | ||
1347 | return MHD_YES; | ||
1348 | while (1) { | ||
1349 | #if DEBUG_STATES | ||
1350 | fprintf(stderr, | ||
1351 | "`%s' in state %u\n", | ||
1352 | __FUNCTION__, | ||
1353 | connection->state); | ||
1354 | #endif | ||
1355 | switch (connection->state) | ||
1356 | { | ||
1357 | case MHD_CONNECTION_INIT: | ||
1358 | case MHD_CONNECTION_URL_RECEIVED: | ||
1359 | case MHD_CONNECTION_HEADER_PART_RECEIVED: | ||
1360 | case MHD_CONNECTION_HEADERS_RECEIVED: | ||
1361 | case MHD_CONNECTION_HEADERS_PROCESSED: | ||
1362 | case MHD_CONNECTION_CONTINUE_SENDING: | ||
1363 | case MHD_CONNECTION_CONTINUE_SENT: | ||
1364 | case MHD_CONNECTION_BODY_RECEIVED: | ||
1365 | case MHD_CONNECTION_FOOTER_PART_RECEIVED: | ||
1366 | /* nothing to do but default action */ | ||
1367 | if (MHD_YES == connection->read_closed) | ||
1368 | { | ||
1369 | connection->state = MHD_CONNECTION_CLOSED; | ||
1370 | continue; | ||
1371 | } | ||
1372 | break; | ||
1373 | case MHD_CONNECTION_CLOSED: | ||
1374 | if (connection->socket_fd != -1) | ||
1375 | connection_close_error (connection); | ||
1376 | return MHD_NO; | ||
1377 | default: | ||
1378 | /* shrink read buffer to how much is actually used */ | ||
1379 | MHD_pool_reallocate (connection->pool, | ||
1380 | connection->read_buffer, | ||
1381 | connection->read_buffer_size + 1, | ||
1382 | connection->read_buffer_offset); | ||
1383 | break; | ||
1384 | } | ||
1385 | break; | ||
1386 | } | ||
1387 | return MHD_YES; | ||
1388 | } | ||
1389 | |||
1390 | /** | ||
1391 | * Try writing data to the socket from the | ||
1392 | * write buffer of the connection. | ||
1393 | * | ||
1394 | * @return MHD_YES if something changed, | ||
1395 | * MHD_NO if we were interrupted | ||
1396 | */ | ||
1397 | static int | ||
1398 | do_write(struct MHD_Connection * connection) | ||
1399 | { | ||
1400 | int ret; | ||
1401 | |||
1402 | ret = SEND (connection->socket_fd, | ||
1403 | &connection->write_buffer[connection-> | ||
1404 | write_buffer_send_offset], | ||
1405 | connection->write_buffer_append_offset - | ||
1406 | connection->write_buffer_send_offset, MSG_NOSIGNAL); | ||
1407 | if (ret < 0) | ||
1408 | { | ||
1409 | if (errno == EINTR) | ||
1410 | return MHD_NO; | ||
1411 | #if HAVE_MESSAGES | ||
1412 | MHD_DLOG (connection->daemon, | ||
1413 | "Failed to send data: %s\n", STRERROR (errno)); | ||
1414 | #endif | ||
1415 | connection_close_error (connection); | ||
1416 | return MHD_YES; | ||
1180 | } | 1417 | } |
1181 | strcpy (&data[off], date); | 1418 | #if DEBUG_SEND_DATA |
1182 | off += strlen (date); | 1419 | fprintf (stderr, |
1183 | sprintf (&data[off], "\r\n"); | 1420 | "Sent HEADER response: `%.*s'\n", |
1184 | off += 2; | 1421 | ret, |
1185 | if (off != size) | 1422 | &connection->write_buffer[connection-> |
1186 | abort (); | 1423 | write_buffer_send_offset]); |
1187 | connection->write_buffer = data; | 1424 | #endif |
1188 | connection->write_buffer_append_offset = size; | 1425 | connection->write_buffer_send_offset += ret; |
1426 | return MHD_YES; | ||
1427 | } | ||
1428 | |||
1429 | /** | ||
1430 | * Check if we are done sending the write-buffer. | ||
1431 | * If so, transition into "next_state". | ||
1432 | * @return MHY_NO if we are not done, MHD_YES if we are | ||
1433 | */ | ||
1434 | static int | ||
1435 | check_write_done(struct MHD_Connection * connection, | ||
1436 | enum MHD_CONNECTION_STATE next_state) | ||
1437 | { | ||
1438 | if (connection->write_buffer_append_offset != | ||
1439 | connection->write_buffer_send_offset) | ||
1440 | return MHD_NO; | ||
1441 | connection->write_buffer_append_offset = 0; | ||
1189 | connection->write_buffer_send_offset = 0; | 1442 | connection->write_buffer_send_offset = 0; |
1190 | connection->write_buffer_size = size + 1; | 1443 | connection->state = next_state; |
1444 | MHD_pool_reallocate (connection->pool, | ||
1445 | connection->write_buffer, | ||
1446 | connection->write_buffer_size, 0); | ||
1447 | connection->write_buffer = NULL; | ||
1448 | connection->write_buffer_size = 0; | ||
1191 | return MHD_YES; | 1449 | return MHD_YES; |
1192 | } | 1450 | } |
1193 | 1451 | ||
@@ -1196,207 +1454,414 @@ MHD_build_header_response (struct MHD_Connection *connection) | |||
1196 | * been determined that the socket can be written to. All | 1454 | * been determined that the socket can be written to. All |
1197 | * implementations (multithreaded, external select, internal select) | 1455 | * implementations (multithreaded, external select, internal select) |
1198 | * call this function | 1456 | * call this function |
1457 | * | ||
1458 | * @return MHD_YES if we should continue to process the | ||
1459 | * connection (not dead yet), MHD_NO if it died | ||
1199 | */ | 1460 | */ |
1200 | int | 1461 | int |
1201 | MHD_connection_handle_write (struct MHD_Connection *connection) | 1462 | MHD_connection_handle_write (struct MHD_Connection *connection) |
1202 | { | 1463 | { |
1203 | struct MHD_Response *response; | 1464 | struct MHD_Response *response; |
1204 | int ret; | 1465 | int ret; |
1205 | const char *end; | ||
1206 | 1466 | ||
1207 | if (MHD_need_100_continue (connection)) | 1467 | connection->last_activity = time(NULL); |
1208 | { | 1468 | while (1) { |
1209 | ret = SEND (connection->socket_fd, | 1469 | #if DEBUG_STATES |
1210 | &HTTP_100_CONTINUE[connection-> | 1470 | fprintf(stderr, |
1211 | continue_message_write_offset], | 1471 | "`%s' in state %u\n", |
1212 | strlen (HTTP_100_CONTINUE) - | 1472 | __FUNCTION__, |
1213 | connection->continue_message_write_offset, MSG_NOSIGNAL); | 1473 | connection->state); |
1214 | if (ret < 0) | 1474 | #endif |
1215 | { | 1475 | switch (connection->state) |
1216 | if (errno == EINTR) | 1476 | { |
1217 | return MHD_YES; | 1477 | case MHD_CONNECTION_INIT: |
1478 | case MHD_CONNECTION_URL_RECEIVED: | ||
1479 | case MHD_CONNECTION_HEADER_PART_RECEIVED: | ||
1480 | case MHD_CONNECTION_HEADERS_RECEIVED: | ||
1481 | EXTRA_CHECK(0); | ||
1482 | break; | ||
1483 | case MHD_CONNECTION_HEADERS_PROCESSED: | ||
1484 | break; | ||
1485 | case MHD_CONNECTION_CONTINUE_SENDING: | ||
1486 | ret = SEND (connection->socket_fd, | ||
1487 | &HTTP_100_CONTINUE[connection-> | ||
1488 | continue_message_write_offset], | ||
1489 | strlen (HTTP_100_CONTINUE) - | ||
1490 | connection->continue_message_write_offset, MSG_NOSIGNAL); | ||
1491 | if (ret < 0) | ||
1492 | { | ||
1493 | if (errno == EINTR) | ||
1494 | break; | ||
1218 | #if HAVE_MESSAGES | 1495 | #if HAVE_MESSAGES |
1219 | MHD_DLOG (connection->daemon, | 1496 | MHD_DLOG (connection->daemon, |
1220 | "Failed to send data: %s\n", STRERROR (errno)); | 1497 | "Failed to send data: %s\n", STRERROR (errno)); |
1221 | #endif | 1498 | #endif |
1222 | connection_close_error (connection); | 1499 | connection_close_error (connection); |
1223 | return MHD_YES; | 1500 | return MHD_NO; |
1224 | } | 1501 | } |
1225 | #if DEBUG_SEND_DATA | 1502 | #if DEBUG_SEND_DATA |
1226 | fprintf (stderr, | 1503 | fprintf (stderr, |
1227 | "Sent 100 continue response: `%.*s'\n", | 1504 | "Sent 100 continue response: `%.*s'\n", |
1228 | ret, | 1505 | ret, |
1229 | &HTTP_100_CONTINUE[connection->continue_message_write_offset]); | 1506 | &HTTP_100_CONTINUE[connection->continue_message_write_offset]); |
1230 | #endif | 1507 | #endif |
1231 | connection->continue_message_write_offset += ret; | 1508 | connection->continue_message_write_offset += ret; |
1232 | return MHD_YES; | 1509 | break; |
1233 | } | 1510 | case MHD_CONNECTION_CONTINUE_SENT: |
1234 | response = connection->response; | 1511 | case MHD_CONNECTION_BODY_RECEIVED: |
1235 | if (response == NULL) | 1512 | case MHD_CONNECTION_FOOTER_PART_RECEIVED: |
1236 | { | 1513 | case MHD_CONNECTION_FOOTERS_RECEIVED: |
1237 | #if HAVE_MESSAGES | 1514 | EXTRA_CHECK(0); |
1238 | MHD_DLOG (connection->daemon, "Unexpected call to %s.\n", __FUNCTION__); | 1515 | break; |
1239 | #endif | 1516 | case MHD_CONNECTION_HEADERS_SENDING: |
1240 | return MHD_NO; | 1517 | do_write(connection); |
1241 | } | 1518 | check_write_done(connection, |
1242 | if (MHD_NO == connection->have_sent_headers) | 1519 | MHD_CONNECTION_HEADERS_SENT); |
1243 | { | 1520 | break; |
1244 | if ((connection->write_buffer == NULL) && | 1521 | case MHD_CONNECTION_HEADERS_SENT: |
1245 | (MHD_NO == MHD_build_header_response (connection))) | 1522 | EXTRA_CHECK(0); |
1246 | { | 1523 | break; |
1247 | /* oops - close! */ | 1524 | case MHD_CONNECTION_NORMAL_BODY_READY: |
1525 | response = connection->response; | ||
1526 | if (response->crc != NULL) | ||
1527 | pthread_mutex_lock (&response->mutex); | ||
1528 | if (MHD_YES != try_ready_normal_body(connection)) | ||
1529 | { | ||
1530 | if (response->crc != NULL) | ||
1531 | pthread_mutex_unlock (&response->mutex); | ||
1532 | connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY; | ||
1533 | break; | ||
1534 | } | ||
1535 | ret = SEND (connection->socket_fd, | ||
1536 | &response->data[connection->response_write_position - | ||
1537 | response->data_start], | ||
1538 | response->data_size - (connection->response_write_position - | ||
1539 | response->data_start), MSG_NOSIGNAL); | ||
1540 | #if DEBUG_SEND_DATA | ||
1541 | if (ret > 0) | ||
1542 | fprintf (stderr, | ||
1543 | "Sent DATA response: `%.*s'\n", | ||
1544 | ret, | ||
1545 | &response->data[connection->response_write_position - | ||
1546 | response->data_start]); | ||
1547 | #endif | ||
1548 | if (response->crc != NULL) | ||
1549 | pthread_mutex_unlock (&response->mutex); | ||
1550 | if (ret < 0) | ||
1551 | { | ||
1552 | if (errno == EINTR) | ||
1553 | return MHD_YES; | ||
1248 | #if HAVE_MESSAGES | 1554 | #if HAVE_MESSAGES |
1249 | MHD_DLOG (connection->daemon, | 1555 | MHD_DLOG (connection->daemon, |
1250 | "Closing connection (failed to create response header)\n"); | 1556 | "Failed to send data: %s\n", STRERROR (errno)); |
1251 | #endif | 1557 | #endif |
1252 | connection_close_error (connection); | 1558 | connection_close_error (connection); |
1253 | return MHD_NO; | 1559 | return MHD_NO; |
1254 | } | 1560 | } |
1255 | ret = SEND (connection->socket_fd, | 1561 | connection->response_write_position += ret; |
1256 | &connection->write_buffer[connection-> | 1562 | if (connection->response_write_position == connection->response->total_size) |
1257 | write_buffer_send_offset], | 1563 | connection->state = MHD_CONNECTION_FOOTERS_SENT; /* have no footers... */ |
1258 | connection->write_buffer_append_offset - | 1564 | break; |
1259 | connection->write_buffer_send_offset, MSG_NOSIGNAL); | 1565 | case MHD_CONNECTION_NORMAL_BODY_UNREADY: |
1260 | if (ret < 0) | 1566 | EXTRA_CHECK(0); |
1261 | { | 1567 | break; |
1262 | if (errno == EINTR) | 1568 | case MHD_CONNECTION_CHUNKED_BODY_READY: |
1263 | return MHD_YES; | 1569 | do_write(connection); |
1570 | check_write_done(connection, | ||
1571 | (connection->response->total_size == connection->response_write_position) | ||
1572 | ? MHD_CONNECTION_BODY_SENT | ||
1573 | : MHD_CONNECTION_CHUNKED_BODY_UNREADY); | ||
1574 | break; | ||
1575 | case MHD_CONNECTION_CHUNKED_BODY_UNREADY: | ||
1576 | case MHD_CONNECTION_BODY_SENT: | ||
1577 | EXTRA_CHECK(0); | ||
1578 | break; | ||
1579 | case MHD_CONNECTION_FOOTERS_SENDING: | ||
1580 | do_write(connection); | ||
1581 | check_write_done(connection, | ||
1582 | MHD_CONNECTION_FOOTERS_SENT); | ||
1583 | break; | ||
1584 | case MHD_CONNECTION_FOOTERS_SENT: | ||
1585 | EXTRA_CHECK(0); | ||
1586 | break; | ||
1587 | case MHD_CONNECTION_CLOSED: | ||
1588 | if (connection->socket_fd != -1) | ||
1589 | connection_close_error(connection); | ||
1590 | return MHD_NO; | ||
1591 | } | ||
1592 | break; | ||
1593 | } | ||
1594 | return MHD_YES; | ||
1595 | } | ||
1596 | |||
1597 | /** | ||
1598 | * This function was created to handle per-connection processing that | ||
1599 | * has to happen even if the socket cannot be read or written to. All | ||
1600 | * implementations (multithreaded, external select, internal select) | ||
1601 | * call this function. | ||
1602 | * | ||
1603 | * @return MHD_YES if we should continue to process the | ||
1604 | * connection (not dead yet), MHD_NO if it died | ||
1605 | */ | ||
1606 | int | ||
1607 | MHD_connection_handle_idle (struct MHD_Connection *connection) | ||
1608 | { | ||
1609 | unsigned int timeout; | ||
1610 | const char * end; | ||
1611 | char * line; | ||
1612 | |||
1613 | while (1) { | ||
1614 | #if DEBUG_STATES | ||
1615 | fprintf(stderr, | ||
1616 | "`%s' in state %u\n", | ||
1617 | __FUNCTION__, | ||
1618 | connection->state); | ||
1619 | #endif | ||
1620 | switch (connection->state) | ||
1621 | { | ||
1622 | case MHD_CONNECTION_INIT: | ||
1623 | line = get_next_header_line(connection); | ||
1624 | if (line == NULL) | ||
1625 | break; | ||
1626 | if (MHD_NO == parse_initial_message_line (connection, line)) | ||
1627 | connection->state = MHD_CONNECTION_CLOSED; | ||
1628 | else | ||
1629 | connection->state = MHD_CONNECTION_URL_RECEIVED; | ||
1630 | continue; | ||
1631 | case MHD_CONNECTION_URL_RECEIVED: | ||
1632 | line = get_next_header_line(connection); | ||
1633 | if (line == NULL) | ||
1634 | break; | ||
1635 | if (strlen(line) == 0) | ||
1636 | { | ||
1637 | connection->state = MHD_CONNECTION_HEADERS_RECEIVED; | ||
1638 | continue; | ||
1639 | } | ||
1640 | process_header_line(connection, line); | ||
1641 | connection->state = MHD_CONNECTION_HEADER_PART_RECEIVED; | ||
1642 | continue; | ||
1643 | case MHD_CONNECTION_HEADER_PART_RECEIVED: | ||
1644 | line = get_next_header_line(connection); | ||
1645 | if (line == NULL) | ||
1646 | break; | ||
1647 | process_broken_line(connection, | ||
1648 | line, | ||
1649 | MHD_HEADER_KIND); | ||
1650 | if (strlen(line) == 0) | ||
1651 | { | ||
1652 | connection->state = MHD_CONNECTION_HEADERS_RECEIVED; | ||
1653 | continue; | ||
1654 | } | ||
1655 | continue; | ||
1656 | case MHD_CONNECTION_HEADERS_RECEIVED: | ||
1657 | parse_connection_headers (connection); | ||
1658 | if (connection->state == MHD_CONNECTION_CLOSED) | ||
1659 | continue; | ||
1660 | connection->state = MHD_CONNECTION_HEADERS_PROCESSED; | ||
1661 | continue; | ||
1662 | case MHD_CONNECTION_HEADERS_PROCESSED: | ||
1663 | call_connection_handler (connection); /* first call */ | ||
1664 | if (need_100_continue(connection)) | ||
1665 | { | ||
1666 | connection->state = MHD_CONNECTION_CONTINUE_SENDING; | ||
1667 | break; | ||
1668 | } | ||
1669 | connection->state = (connection->remaining_upload_size == 0) | ||
1670 | ? MHD_CONNECTION_FOOTERS_RECEIVED | ||
1671 | : MHD_CONNECTION_CONTINUE_SENT; | ||
1672 | continue; | ||
1673 | case MHD_CONNECTION_CONTINUE_SENDING: | ||
1674 | if (connection->continue_message_write_offset == | ||
1675 | strlen (HTTP_100_CONTINUE)) | ||
1676 | { | ||
1677 | connection->state = MHD_CONNECTION_CONTINUE_SENT; | ||
1678 | continue; | ||
1679 | } | ||
1680 | break; | ||
1681 | case MHD_CONNECTION_CONTINUE_SENT: | ||
1682 | if (connection->read_buffer_offset != 0) { | ||
1683 | call_connection_handler(connection); /* loop call */ | ||
1684 | if (connection->state == MHD_CONNECTION_CLOSED) | ||
1685 | continue; | ||
1686 | } | ||
1687 | if ( (connection->remaining_upload_size == 0) || | ||
1688 | ( (connection->remaining_upload_size == -1) && | ||
1689 | (connection->read_buffer_offset == 0) && | ||
1690 | (MHD_YES == connection->read_closed) ) ) | ||
1691 | { | ||
1692 | if ( (MHD_YES == connection->have_chunked_upload) && | ||
1693 | (MHD_NO == connection->read_closed) ) | ||
1694 | connection->state = MHD_CONNECTION_BODY_RECEIVED; | ||
1695 | else | ||
1696 | connection->state = MHD_CONNECTION_FOOTERS_RECEIVED; | ||
1697 | continue; | ||
1698 | } | ||
1699 | break; | ||
1700 | case MHD_CONNECTION_BODY_RECEIVED: | ||
1701 | line = get_next_header_line(connection); | ||
1702 | if (line == NULL) | ||
1703 | break; | ||
1704 | if (strlen(line) == 0) | ||
1705 | { | ||
1706 | connection->state = MHD_CONNECTION_FOOTERS_RECEIVED; | ||
1707 | continue; | ||
1708 | } | ||
1709 | process_header_line(connection, line); | ||
1710 | connection->state = MHD_CONNECTION_FOOTER_PART_RECEIVED; | ||
1711 | continue; | ||
1712 | case MHD_CONNECTION_FOOTER_PART_RECEIVED: | ||
1713 | line = get_next_header_line(connection); | ||
1714 | if (line == NULL) | ||
1715 | break; | ||
1716 | process_broken_line(connection, | ||
1717 | line, | ||
1718 | MHD_FOOTER_KIND); | ||
1719 | if (strlen(line) == 0) | ||
1720 | { | ||
1721 | connection->state = MHD_CONNECTION_FOOTERS_RECEIVED; | ||
1722 | continue; | ||
1723 | } | ||
1724 | continue; | ||
1725 | case MHD_CONNECTION_FOOTERS_RECEIVED: | ||
1726 | call_connection_handler (connection); /* "final" call */ | ||
1727 | if (connection->state == MHD_CONNECTION_CLOSED) | ||
1728 | continue; | ||
1729 | if (connection->response == NULL) | ||
1730 | break; /* try again next time */ | ||
1731 | if (MHD_NO == build_header_response (connection)) | ||
1732 | { | ||
1733 | /* oops - close! */ | ||
1264 | #if HAVE_MESSAGES | 1734 | #if HAVE_MESSAGES |
1265 | MHD_DLOG (connection->daemon, | 1735 | MHD_DLOG (connection->daemon, |
1266 | "Failed to send data: %s\n", STRERROR (errno)); | 1736 | "Closing connection (failed to create response header)\n"); |
1267 | #endif | 1737 | #endif |
1268 | connection_close_error (connection); | 1738 | connection->state = MHD_CONNECTION_CLOSED; |
1269 | return MHD_YES; | 1739 | continue; |
1270 | } | 1740 | } |
1271 | #if DEBUG_SEND_DATA | 1741 | connection->state = MHD_CONNECTION_HEADERS_SENDING; |
1272 | fprintf (stderr, | 1742 | break; |
1273 | "Sent HEADER response: `%.*s'\n", | 1743 | case MHD_CONNECTION_HEADERS_SENDING: |
1274 | ret, | 1744 | /* no default action */ |
1275 | &connection->write_buffer[connection-> | 1745 | break; |
1276 | write_buffer_send_offset]); | 1746 | case MHD_CONNECTION_HEADERS_SENT: |
1277 | #endif | 1747 | if (connection->have_chunked_upload) |
1278 | connection->write_buffer_send_offset += ret; | 1748 | connection->state = MHD_CONNECTION_CHUNKED_BODY_UNREADY; |
1279 | if (connection->write_buffer_append_offset == | 1749 | else |
1280 | connection->write_buffer_send_offset) | 1750 | connection->state = MHD_CONNECTION_NORMAL_BODY_UNREADY; |
1281 | { | 1751 | continue; |
1282 | connection->write_buffer_append_offset = 0; | 1752 | case MHD_CONNECTION_NORMAL_BODY_READY: |
1283 | connection->write_buffer_send_offset = 0; | 1753 | /* nothing to do here */ |
1284 | connection->have_sent_headers = MHD_YES; | 1754 | break; |
1285 | MHD_pool_reallocate (connection->pool, | 1755 | case MHD_CONNECTION_NORMAL_BODY_UNREADY: |
1286 | connection->write_buffer, | 1756 | if (connection->response->crc != NULL) |
1287 | connection->write_buffer_size, 0); | 1757 | pthread_mutex_lock (&connection->response->mutex); |
1288 | connection->write_buffer = NULL; | 1758 | if (MHD_YES == try_ready_normal_body(connection)) |
1289 | connection->write_buffer_size = 0; | 1759 | { |
1290 | } | 1760 | if (connection->response->crc != NULL) |
1291 | return MHD_YES; | 1761 | pthread_mutex_unlock (&connection->response->mutex); |
1292 | } | 1762 | connection->state = MHD_CONNECTION_NORMAL_BODY_READY; |
1293 | if (response->total_size < connection->response_write_position) | 1763 | break; |
1294 | abort (); /* internal error */ | 1764 | } |
1295 | if (response->crc != NULL) | 1765 | if (connection->response->crc != NULL) |
1296 | pthread_mutex_lock (&response->mutex); | 1766 | pthread_mutex_unlock (&connection->response->mutex); |
1297 | 1767 | /* not ready, no socket action */ | |
1298 | /* prepare send buffer */ | 1768 | break; |
1299 | if ((response->crc != NULL) && | 1769 | case MHD_CONNECTION_CHUNKED_BODY_READY: |
1300 | ((response->data_start > connection->response_write_position) || | 1770 | /* nothing to do here */ |
1301 | (response->data_start + response->data_size <= | 1771 | break; |
1302 | connection->response_write_position)) | 1772 | case MHD_CONNECTION_CHUNKED_BODY_UNREADY: |
1303 | && (MHD_YES != ready_response (connection))) | 1773 | if (connection->response->crc != NULL) |
1304 | { | 1774 | pthread_mutex_lock (&connection->response->mutex); |
1305 | pthread_mutex_unlock (&response->mutex); | 1775 | if (MHD_YES == try_ready_chunked_body(connection)) |
1306 | return MHD_YES; | 1776 | { |
1307 | } | 1777 | if (connection->response->crc != NULL) |
1308 | /* transmit */ | 1778 | pthread_mutex_unlock (&connection->response->mutex); |
1309 | ret = SEND (connection->socket_fd, | 1779 | connection->state = MHD_CONNECTION_CHUNKED_BODY_READY; |
1310 | &response->data[connection->response_write_position - | 1780 | continue; |
1311 | response->data_start], | 1781 | } |
1312 | response->data_size - (connection->response_write_position - | 1782 | if (connection->response->crc != NULL) |
1313 | response->data_start), MSG_NOSIGNAL); | 1783 | pthread_mutex_unlock (&connection->response->mutex); |
1314 | if (response->crc != NULL) | 1784 | break; |
1315 | pthread_mutex_unlock (&response->mutex); | 1785 | case MHD_CONNECTION_BODY_SENT: |
1316 | if (ret < 0) | 1786 | build_header_response(connection); |
1787 | if (connection->write_buffer_send_offset == connection->write_buffer_append_offset) | ||
1788 | connection->state = MHD_CONNECTION_FOOTERS_SENT; | ||
1789 | else | ||
1790 | connection->state = MHD_CONNECTION_FOOTERS_SENDING; | ||
1791 | continue; | ||
1792 | case MHD_CONNECTION_FOOTERS_SENDING: | ||
1793 | /* no default action */ | ||
1794 | break; | ||
1795 | case MHD_CONNECTION_FOOTERS_SENT: | ||
1796 | MHD_destroy_response (connection->response); | ||
1797 | if (connection->daemon->notify_completed != NULL) | ||
1798 | connection->daemon->notify_completed (connection->daemon-> | ||
1799 | notify_completed_cls, | ||
1800 | connection, | ||
1801 | &connection->client_context, | ||
1802 | MHD_REQUEST_TERMINATED_COMPLETED_OK); | ||
1803 | end = MHD_lookup_connection_value (connection, | ||
1804 | MHD_HEADER_KIND, | ||
1805 | MHD_HTTP_HEADER_CONNECTION); | ||
1806 | connection->client_context = NULL; | ||
1807 | connection->continue_message_write_offset = 0; | ||
1808 | connection->responseCode = 0; | ||
1809 | connection->response = NULL; | ||
1810 | connection->headers_received = NULL; | ||
1811 | connection->response_write_position = 0; | ||
1812 | connection->have_chunked_upload = MHD_NO; | ||
1813 | connection->method = NULL; | ||
1814 | connection->url = NULL; | ||
1815 | connection->write_buffer = NULL; | ||
1816 | connection->write_buffer_size = 0; | ||
1817 | connection->write_buffer_send_offset = 0; | ||
1818 | connection->write_buffer_append_offset = 0; | ||
1819 | if ((end != NULL) && (0 == strcasecmp (end, "close"))) | ||
1820 | { | ||
1821 | connection->read_closed = MHD_YES; | ||
1822 | connection->read_buffer_offset = 0; | ||
1823 | } | ||
1824 | if ( ( (MHD_YES == connection->read_closed) && | ||
1825 | (0 == connection->read_buffer_offset) ) || | ||
1826 | (connection->version == NULL) || | ||
1827 | (0 != strcasecmp (MHD_HTTP_VERSION_1_1, connection->version)) ) | ||
1828 | { | ||
1829 | connection->state = MHD_CONNECTION_CLOSED; | ||
1830 | MHD_pool_destroy (connection->pool); | ||
1831 | connection->pool = NULL; | ||
1832 | connection->read_buffer = NULL; | ||
1833 | connection->read_buffer_size = 0; | ||
1834 | connection->read_buffer_offset = 0; | ||
1835 | } | ||
1836 | else | ||
1837 | { | ||
1838 | connection->version = NULL; | ||
1839 | connection->state = MHD_CONNECTION_INIT; | ||
1840 | connection->read_buffer | ||
1841 | = MHD_pool_reset(connection->pool, | ||
1842 | connection->read_buffer, | ||
1843 | connection->read_buffer_size); | ||
1844 | } | ||
1845 | continue; | ||
1846 | case MHD_CONNECTION_CLOSED: | ||
1847 | if (connection->socket_fd != -1) | ||
1848 | connection_close_error (connection); | ||
1849 | break; | ||
1850 | default: | ||
1851 | EXTRA_CHECK(0); | ||
1852 | break; | ||
1853 | } | ||
1854 | break; | ||
1855 | } | ||
1856 | timeout = connection->daemon->connection_timeout; | ||
1857 | if ( (timeout != 0) && | ||
1858 | (time(NULL) - timeout > connection->last_activity) ) | ||
1317 | { | 1859 | { |
1318 | if (errno == EINTR) | ||
1319 | return MHD_YES; | ||
1320 | #if HAVE_MESSAGES | ||
1321 | MHD_DLOG (connection->daemon, | ||
1322 | "Failed to send data: %s\n", STRERROR (errno)); | ||
1323 | #endif | ||
1324 | connection_close_error (connection); | 1860 | connection_close_error (connection); |
1325 | return MHD_YES; | 1861 | return MHD_NO; |
1326 | } | ||
1327 | #if DEBUG_SEND_DATA | ||
1328 | fprintf (stderr, | ||
1329 | "Sent DATA response: `%.*s'\n", | ||
1330 | ret, | ||
1331 | &response->data[connection->response_write_position - | ||
1332 | response->data_start]); | ||
1333 | #endif | ||
1334 | connection->response_write_position += ret; | ||
1335 | if (connection->response_write_position > response->total_size) | ||
1336 | abort (); /* internal error */ | ||
1337 | if (connection->response_write_position == response->total_size) | ||
1338 | { | ||
1339 | if ((connection->have_received_body == MHD_NO) || | ||
1340 | (connection->have_received_headers == MHD_NO)) | ||
1341 | abort (); /* internal error */ | ||
1342 | MHD_destroy_response (response); | ||
1343 | if (connection->daemon->notify_completed != NULL) | ||
1344 | connection->daemon->notify_completed (connection->daemon-> | ||
1345 | notify_completed_cls, | ||
1346 | connection, | ||
1347 | &connection->client_context, | ||
1348 | MHD_REQUEST_TERMINATED_COMPLETED_OK); | ||
1349 | end = MHD_lookup_connection_value (connection, | ||
1350 | MHD_HEADER_KIND, | ||
1351 | MHD_HTTP_HEADER_CONNECTION); | ||
1352 | connection->client_context = NULL; | ||
1353 | connection->continue_message_write_offset = 0; | ||
1354 | connection->responseCode = 0; | ||
1355 | connection->response = NULL; | ||
1356 | connection->headers_received = NULL; | ||
1357 | connection->have_received_headers = MHD_NO; | ||
1358 | connection->have_sent_headers = MHD_NO; | ||
1359 | connection->have_received_body = MHD_NO; | ||
1360 | connection->response_write_position = 0; | ||
1361 | connection->have_chunked_upload = MHD_NO; | ||
1362 | connection->method = NULL; | ||
1363 | connection->url = NULL; | ||
1364 | if ((end != NULL) && (0 == strcasecmp (end, "close"))) | ||
1365 | { | ||
1366 | /* other side explicitly requested | ||
1367 | that we close the connection after | ||
1368 | this request */ | ||
1369 | connection->read_close = MHD_YES; | ||
1370 | } | ||
1371 | if ((connection->read_close == MHD_YES) || | ||
1372 | (0 != strcasecmp (MHD_HTTP_VERSION_1_1, connection->version))) | ||
1373 | { | ||
1374 | /* closed for reading => close for good! */ | ||
1375 | if (connection->socket_fd != -1) | ||
1376 | { | ||
1377 | #if DEBUG_CLOSE | ||
1378 | #if HAVE_MESSAGES | ||
1379 | MHD_DLOG (connection->daemon, | ||
1380 | "Closing connection (http 1.0 or end-of-stream for unknown content length)\n"); | ||
1381 | #endif | ||
1382 | #endif | ||
1383 | SHUTDOWN (connection->socket_fd, SHUT_RDWR); | ||
1384 | CLOSE (connection->socket_fd); | ||
1385 | } | ||
1386 | connection->socket_fd = -1; | ||
1387 | } | ||
1388 | connection->version = NULL; | ||
1389 | connection->read_buffer = NULL; | ||
1390 | connection->write_buffer = NULL; | ||
1391 | connection->read_buffer_size = 0; | ||
1392 | connection->read_buffer_offset = 0; | ||
1393 | connection->write_buffer_size = 0; | ||
1394 | connection->write_buffer_send_offset = 0; | ||
1395 | connection->write_buffer_append_offset = 0; | ||
1396 | MHD_pool_destroy (connection->pool); | ||
1397 | connection->pool = NULL; | ||
1398 | } | 1862 | } |
1399 | return MHD_YES; | 1863 | return MHD_YES; |
1864 | |||
1400 | } | 1865 | } |
1401 | 1866 | ||
1402 | /* end of connection.c */ | 1867 | /* end of connection.c */ |
diff --git a/src/daemon/connection.h b/src/daemon/connection.h index e00a83e1..a1763b55 100644 --- a/src/daemon/connection.h +++ b/src/daemon/connection.h | |||
@@ -39,18 +39,14 @@ MHD_connection_get_fdset (struct MHD_Connection *connection, | |||
39 | fd_set * write_fd_set, | 39 | fd_set * write_fd_set, |
40 | fd_set * except_fd_set, int *max_fd); | 40 | fd_set * except_fd_set, int *max_fd); |
41 | 41 | ||
42 | |||
43 | /** | ||
44 | * Call the handler of the application for this | ||
45 | * connection. | ||
46 | */ | ||
47 | void MHD_call_connection_handler (struct MHD_Connection *connection); | ||
48 | |||
49 | /** | 42 | /** |
50 | * This function handles a particular connection when it has been | 43 | * This function handles a particular connection when it has been |
51 | * determined that there is data to be read off a socket. All implementations | 44 | * determined that there is data to be read off a socket. All implementations |
52 | * (multithreaded, external select, internal select) call this function | 45 | * (multithreaded, external select, internal select) call this function |
53 | * to handle reads. | 46 | * to handle reads. |
47 | * | ||
48 | * @return MHD_YES if we should continue to process the | ||
49 | * connection (not dead yet), MHD_NO if it died | ||
54 | */ | 50 | */ |
55 | int MHD_connection_handle_read (struct MHD_Connection *connection); | 51 | int MHD_connection_handle_read (struct MHD_Connection *connection); |
56 | 52 | ||
@@ -60,8 +56,23 @@ int MHD_connection_handle_read (struct MHD_Connection *connection); | |||
60 | * determined that the socket can be written to. If there is no data | 56 | * determined that the socket can be written to. If there is no data |
61 | * to be written, however, the function call does nothing. All implementations | 57 | * to be written, however, the function call does nothing. All implementations |
62 | * (multithreaded, external select, internal select) call this function | 58 | * (multithreaded, external select, internal select) call this function |
59 | * | ||
60 | * @return MHD_YES if we should continue to process the | ||
61 | * connection (not dead yet), MHD_NO if it died | ||
63 | */ | 62 | */ |
64 | int MHD_connection_handle_write (struct MHD_Connection *connection); | 63 | int MHD_connection_handle_write (struct MHD_Connection *connection); |
65 | 64 | ||
66 | 65 | ||
66 | /** | ||
67 | * This function was created to handle per-connection processing that | ||
68 | * has to happen even if the socket cannot be read or written to. All | ||
69 | * implementations (multithreaded, external select, internal select) | ||
70 | * call this function. | ||
71 | * | ||
72 | * @return MHD_YES if we should continue to process the | ||
73 | * connection (not dead yet), MHD_NO if it died | ||
74 | */ | ||
75 | int | ||
76 | MHD_connection_handle_idle (struct MHD_Connection *connection); | ||
77 | |||
67 | #endif | 78 | #endif |
diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c index 19c614a1..b6f1e9f3 100644 --- a/src/daemon/daemon.c +++ b/src/daemon/daemon.c | |||
@@ -52,85 +52,6 @@ | |||
52 | */ | 52 | */ |
53 | #define DEBUG_CONNECT MHD_NO | 53 | #define DEBUG_CONNECT MHD_NO |
54 | 54 | ||
55 | /** | ||
56 | * Register an access handler for all URIs beginning with uri_prefix. | ||
57 | * | ||
58 | * @param uri_prefix | ||
59 | * @return MRI_NO if a handler for this exact prefix | ||
60 | * already exists | ||
61 | */ | ||
62 | int | ||
63 | MHD_register_handler (struct MHD_Daemon *daemon, | ||
64 | const char *uri_prefix, | ||
65 | MHD_AccessHandlerCallback dh, void *dh_cls) | ||
66 | { | ||
67 | struct MHD_Access_Handler *ah; | ||
68 | |||
69 | if ((daemon == NULL) || (uri_prefix == NULL) || (dh == NULL)) | ||
70 | return MHD_NO; | ||
71 | ah = daemon->handlers; | ||
72 | while (ah != NULL) | ||
73 | { | ||
74 | if (0 == strcmp (uri_prefix, ah->uri_prefix)) | ||
75 | return MHD_NO; | ||
76 | ah = ah->next; | ||
77 | } | ||
78 | ah = malloc (sizeof (struct MHD_Access_Handler)); | ||
79 | if (ah == NULL) | ||
80 | { | ||
81 | #if HAVE_MESSAGES | ||
82 | MHD_DLOG (daemon, "Error allocating memory: %s\n", STRERROR (errno)); | ||
83 | #endif | ||
84 | return MHD_NO; | ||
85 | } | ||
86 | |||
87 | ah->next = daemon->handlers; | ||
88 | ah->uri_prefix = strdup (uri_prefix); | ||
89 | ah->dh = dh; | ||
90 | ah->dh_cls = dh_cls; | ||
91 | daemon->handlers = ah; | ||
92 | return MHD_YES; | ||
93 | } | ||
94 | |||
95 | |||
96 | /** | ||
97 | * Unregister an access handler for the URIs beginning with | ||
98 | * uri_prefix. | ||
99 | * | ||
100 | * @param uri_prefix | ||
101 | * @return MHD_NO if a handler for this exact prefix | ||
102 | * is not known for this daemon | ||
103 | */ | ||
104 | int | ||
105 | MHD_unregister_handler (struct MHD_Daemon *daemon, | ||
106 | const char *uri_prefix, | ||
107 | MHD_AccessHandlerCallback dh, void *dh_cls) | ||
108 | { | ||
109 | struct MHD_Access_Handler *prev; | ||
110 | struct MHD_Access_Handler *pos; | ||
111 | |||
112 | if ((daemon == NULL) || (uri_prefix == NULL) || (dh == NULL)) | ||
113 | return MHD_NO; | ||
114 | pos = daemon->handlers; | ||
115 | prev = NULL; | ||
116 | while (pos != NULL) | ||
117 | { | ||
118 | if ((dh == pos->dh) && | ||
119 | (dh_cls == pos->dh_cls) && | ||
120 | (0 == strcmp (uri_prefix, pos->uri_prefix))) | ||
121 | { | ||
122 | if (prev == NULL) | ||
123 | daemon->handlers = pos->next; | ||
124 | else | ||
125 | prev->next = pos->next; | ||
126 | free (pos); | ||
127 | return MHD_YES; | ||
128 | } | ||
129 | prev = pos; | ||
130 | pos = pos->next; | ||
131 | } | ||
132 | return MHD_NO; | ||
133 | } | ||
134 | 55 | ||
135 | /** | 56 | /** |
136 | * Obtain the select sets for this daemon. | 57 | * Obtain the select sets for this daemon. |
@@ -175,7 +96,6 @@ MHD_get_fdset (struct MHD_Daemon *daemon, | |||
175 | return MHD_YES; | 96 | return MHD_YES; |
176 | } | 97 | } |
177 | 98 | ||
178 | |||
179 | /** | 99 | /** |
180 | * Main function of the thread that handles an individual | 100 | * Main function of the thread that handles an individual |
181 | * connection. | 101 | * connection. |
@@ -191,26 +111,27 @@ MHD_handle_connection (void *data) | |||
191 | int max; | 111 | int max; |
192 | struct timeval tv; | 112 | struct timeval tv; |
193 | unsigned int timeout; | 113 | unsigned int timeout; |
194 | time_t now; | 114 | unsigned int now; |
195 | 115 | ||
196 | if (con == NULL) | 116 | if (con == NULL) |
197 | abort (); | 117 | abort (); |
198 | timeout = con->daemon->connection_timeout; | 118 | timeout = con->daemon->connection_timeout; |
199 | now = time (NULL); | 119 | while ( (!con->daemon->shutdown) && |
200 | while ((!con->daemon->shutdown) && | 120 | (con->socket_fd != -1) ) |
201 | (con->socket_fd != -1) && | ||
202 | ((timeout == 0) || (now - timeout > con->last_activity))) | ||
203 | { | 121 | { |
204 | FD_ZERO (&rs); | 122 | FD_ZERO (&rs); |
205 | FD_ZERO (&ws); | 123 | FD_ZERO (&ws); |
206 | FD_ZERO (&es); | 124 | FD_ZERO (&es); |
207 | max = 0; | 125 | max = 0; |
208 | MHD_connection_get_fdset (con, &rs, &ws, &es, &max); | 126 | MHD_connection_get_fdset (con, &rs, &ws, &es, &max); |
127 | now = time(NULL); | ||
209 | tv.tv_usec = 0; | 128 | tv.tv_usec = 0; |
210 | tv.tv_sec = timeout - (now - con->last_activity); | 129 | if ( timeout > (now - con->last_activity) ) |
130 | tv.tv_sec = timeout - (now - con->last_activity); | ||
131 | else | ||
132 | tv.tv_sec = 0; | ||
211 | num_ready = SELECT (max + 1, | 133 | num_ready = SELECT (max + 1, |
212 | &rs, &ws, &es, (timeout != 0) ? &tv : NULL); | 134 | &rs, &ws, &es, (timeout != 0) ? &tv : NULL); |
213 | now = time (NULL); | ||
214 | if (num_ready < 0) | 135 | if (num_ready < 0) |
215 | { | 136 | { |
216 | if (errno == EINTR) | 137 | if (errno == EINTR) |
@@ -221,18 +142,13 @@ MHD_handle_connection (void *data) | |||
221 | #endif | 142 | #endif |
222 | break; | 143 | break; |
223 | } | 144 | } |
224 | if (((FD_ISSET (con->socket_fd, &rs)) && | 145 | if (FD_ISSET (con->socket_fd, &rs)) |
225 | (MHD_YES != MHD_connection_handle_read (con))) || | 146 | MHD_connection_handle_read (con); |
226 | ((con->socket_fd != -1) && | ||
227 | (FD_ISSET (con->socket_fd, &ws)) && | ||
228 | (MHD_YES != MHD_connection_handle_write (con)))) | ||
229 | break; | ||
230 | if ((con->have_received_headers == MHD_YES) && (con->response == NULL)) | ||
231 | MHD_call_connection_handler (con); | ||
232 | if ((con->socket_fd != -1) && | 147 | if ((con->socket_fd != -1) && |
233 | ((FD_ISSET (con->socket_fd, &rs)) || | 148 | (FD_ISSET (con->socket_fd, &ws)) ) |
234 | (FD_ISSET (con->socket_fd, &ws)))) | 149 | MHD_connection_handle_write (con); |
235 | con->last_activity = now; | 150 | if (con->socket_fd != -1) |
151 | MHD_connection_handle_idle (con); | ||
236 | } | 152 | } |
237 | if (con->socket_fd != -1) | 153 | if (con->socket_fd != -1) |
238 | { | 154 | { |
@@ -370,11 +286,6 @@ MHD_accept_connection (struct MHD_Daemon *daemon) | |||
370 | * Free resources associated with all closed connections. | 286 | * Free resources associated with all closed connections. |
371 | * (destroy responses, free buffers, etc.). A connection | 287 | * (destroy responses, free buffers, etc.). A connection |
372 | * is known to be closed if the socket_fd is -1. | 288 | * is known to be closed if the socket_fd is -1. |
373 | * | ||
374 | * Also performs connection actions that need to be run | ||
375 | * even if the connection is not selectable (such as | ||
376 | * calling the application again with upload data when | ||
377 | * the upload data buffer is full). | ||
378 | */ | 289 | */ |
379 | static void | 290 | static void |
380 | MHD_cleanup_connections (struct MHD_Daemon *daemon) | 291 | MHD_cleanup_connections (struct MHD_Daemon *daemon) |
@@ -382,33 +293,11 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon) | |||
382 | struct MHD_Connection *pos; | 293 | struct MHD_Connection *pos; |
383 | struct MHD_Connection *prev; | 294 | struct MHD_Connection *prev; |
384 | void *unused; | 295 | void *unused; |
385 | time_t timeout; | ||
386 | 296 | ||
387 | timeout = time (NULL); | ||
388 | if (daemon->connection_timeout != 0) | ||
389 | timeout -= daemon->connection_timeout; | ||
390 | else | ||
391 | timeout = 0; | ||
392 | pos = daemon->connections; | 297 | pos = daemon->connections; |
393 | prev = NULL; | 298 | prev = NULL; |
394 | while (pos != NULL) | 299 | while (pos != NULL) |
395 | { | 300 | { |
396 | if ((pos->last_activity < timeout) && (pos->socket_fd != -1)) | ||
397 | { | ||
398 | #if DEBUG_CLOSE | ||
399 | #if HAVE_MESSAGES | ||
400 | MHD_DLOG (daemon, "Connection timed out, closing connection\n"); | ||
401 | #endif | ||
402 | #endif | ||
403 | SHUTDOWN (pos->socket_fd, SHUT_RDWR); | ||
404 | CLOSE (pos->socket_fd); | ||
405 | pos->socket_fd = -1; | ||
406 | if (pos->daemon->notify_completed != NULL) | ||
407 | pos->daemon->notify_completed (pos->daemon->notify_completed_cls, | ||
408 | pos, | ||
409 | &pos->client_context, | ||
410 | MHD_REQUEST_TERMINATED_TIMEOUT_REACHED); | ||
411 | } | ||
412 | if (pos->socket_fd == -1) | 301 | if (pos->socket_fd == -1) |
413 | { | 302 | { |
414 | if (prev == NULL) | 303 | if (prev == NULL) |
@@ -420,8 +309,7 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon) | |||
420 | pthread_kill (pos->pid, SIGALRM); | 309 | pthread_kill (pos->pid, SIGALRM); |
421 | pthread_join (pos->pid, &unused); | 310 | pthread_join (pos->pid, &unused); |
422 | } | 311 | } |
423 | if (pos->response != NULL) | 312 | MHD_destroy_response (pos->response); |
424 | MHD_destroy_response (pos->response); | ||
425 | MHD_pool_destroy (pos->pool); | 313 | MHD_pool_destroy (pos->pool); |
426 | free (pos->addr); | 314 | free (pos->addr); |
427 | free (pos); | 315 | free (pos); |
@@ -432,12 +320,6 @@ MHD_cleanup_connections (struct MHD_Daemon *daemon) | |||
432 | pos = prev->next; | 320 | pos = prev->next; |
433 | continue; | 321 | continue; |
434 | } | 322 | } |
435 | |||
436 | if ((0 == (daemon->options & MHD_USE_THREAD_PER_CONNECTION)) && | ||
437 | ((pos->have_received_headers == MHD_YES) | ||
438 | && (pos->response == NULL))) | ||
439 | MHD_call_connection_handler (pos); | ||
440 | |||
441 | prev = pos; | 323 | prev = pos; |
442 | pos = pos->next; | 324 | pos = pos->next; |
443 | } | 325 | } |
@@ -573,15 +455,12 @@ MHD_select (struct MHD_Daemon *daemon, int may_block) | |||
573 | if (ds != -1) | 455 | if (ds != -1) |
574 | { | 456 | { |
575 | if (FD_ISSET (ds, &rs)) | 457 | if (FD_ISSET (ds, &rs)) |
576 | { | 458 | MHD_connection_handle_read (pos); |
577 | pos->last_activity = now; | 459 | if ( (pos->socket_fd != -1) && |
578 | MHD_connection_handle_read (pos); | 460 | (FD_ISSET (ds, &ws)) ) |
579 | } | 461 | MHD_connection_handle_write (pos); |
580 | if (FD_ISSET (ds, &ws)) | 462 | if (pos->socket_fd != -1) |
581 | { | 463 | MHD_connection_handle_idle(pos); |
582 | pos->last_activity = now; | ||
583 | MHD_connection_handle_write (pos); | ||
584 | } | ||
585 | } | 464 | } |
586 | pos = pos->next; | 465 | pos = pos->next; |
587 | } | 466 | } |
@@ -726,10 +605,8 @@ MHD_start_daemon (unsigned int options, | |||
726 | retVal->apc = apc; | 605 | retVal->apc = apc; |
727 | retVal->apc_cls = apc_cls; | 606 | retVal->apc_cls = apc_cls; |
728 | retVal->socket_fd = socket_fd; | 607 | retVal->socket_fd = socket_fd; |
729 | retVal->default_handler.dh = dh; | 608 | retVal->default_handler = dh; |
730 | retVal->default_handler.dh_cls = dh_cls; | 609 | retVal->default_handler_cls = dh_cls; |
731 | retVal->default_handler.uri_prefix = ""; | ||
732 | retVal->default_handler.next = NULL; | ||
733 | retVal->max_connections = MHD_MAX_CONNECTIONS_DEFAULT; | 610 | retVal->max_connections = MHD_MAX_CONNECTIONS_DEFAULT; |
734 | retVal->pool_size = MHD_POOL_SIZE_DEFAULT; | 611 | retVal->pool_size = MHD_POOL_SIZE_DEFAULT; |
735 | retVal->connection_timeout = 0; /* no timeout */ | 612 | retVal->connection_timeout = 0; /* no timeout */ |
diff --git a/src/daemon/daemontest_get.c b/src/daemon/daemontest_get.c index c6c63fd8..1a6ca12a 100644 --- a/src/daemon/daemontest_get.c +++ b/src/daemon/daemontest_get.c | |||
@@ -66,16 +66,24 @@ ahc_echo (void *cls, | |||
66 | const char *upload_data, unsigned int *upload_data_size, | 66 | const char *upload_data, unsigned int *upload_data_size, |
67 | void **unused) | 67 | void **unused) |
68 | { | 68 | { |
69 | static int ptr; | ||
69 | const char *me = cls; | 70 | const char *me = cls; |
70 | struct MHD_Response *response; | 71 | struct MHD_Response *response; |
71 | int ret; | 72 | int ret; |
72 | 73 | ||
73 | if (0 != strcmp (me, method)) | 74 | if (0 != strcmp (me, method)) |
74 | return MHD_NO; /* unexpected method */ | 75 | return MHD_NO; /* unexpected method */ |
76 | if (&ptr != *unused) { | ||
77 | *unused = &ptr; | ||
78 | return MHD_YES; | ||
79 | } | ||
80 | *unused = NULL; | ||
75 | response = MHD_create_response_from_data (strlen (url), | 81 | response = MHD_create_response_from_data (strlen (url), |
76 | (void *) url, MHD_NO, MHD_YES); | 82 | (void *) url, MHD_NO, MHD_YES); |
77 | ret = MHD_queue_response (connection, MHD_HTTP_OK, response); | 83 | ret = MHD_queue_response (connection, MHD_HTTP_OK, response); |
78 | MHD_destroy_response (response); | 84 | MHD_destroy_response (response); |
85 | if (ret == MHD_NO) | ||
86 | abort(); | ||
79 | return ret; | 87 | return ret; |
80 | } | 88 | } |
81 | 89 | ||
@@ -93,11 +101,11 @@ testInternalGet () | |||
93 | cbc.size = 2048; | 101 | cbc.size = 2048; |
94 | cbc.pos = 0; | 102 | cbc.pos = 0; |
95 | d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, | 103 | d = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_DEBUG, |
96 | 1080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); | 104 | 11080, NULL, NULL, &ahc_echo, "GET", MHD_OPTION_END); |
97 | if (d == NULL) | 105 | if (d == NULL) |
98 | return 1; | 106 | return 1; |
99 | c = curl_easy_init (); | 107 | c = curl_easy_init (); |
100 | curl_easy_setopt (c, CURLOPT_URL, "http://localhost:1080/hello_world"); | 108 | curl_easy_setopt (c, CURLOPT_URL, "http://localhost:11080/hello_world"); |
101 | curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); | 109 | curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); |
102 | curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); | 110 | curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); |
103 | curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); | 111 | curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); |
@@ -150,12 +158,12 @@ testMultithreadedGet () | |||
150 | curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); | 158 | curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, ©Buffer); |
151 | curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); | 159 | curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc); |
152 | curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); | 160 | curl_easy_setopt (c, CURLOPT_FAILONERROR, 1); |
153 | curl_easy_setopt (c, CURLOPT_TIMEOUT, 2L); | 161 | curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); |
154 | if (oneone) | 162 | if (oneone) |
155 | curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); | 163 | curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); |
156 | else | 164 | else |
157 | curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); | 165 | curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); |
158 | curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 2L); | 166 | curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); |
159 | // NOTE: use of CONNECTTIMEOUT without also | 167 | // NOTE: use of CONNECTTIMEOUT without also |
160 | // setting NOSIGNAL results in really weird | 168 | // setting NOSIGNAL results in really weird |
161 | // crashes on my system! | 169 | // crashes on my system! |
@@ -214,8 +222,8 @@ testExternalGet () | |||
214 | curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); | 222 | curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); |
215 | else | 223 | else |
216 | curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); | 224 | curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); |
217 | curl_easy_setopt (c, CURLOPT_TIMEOUT, 5L); | 225 | curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L); |
218 | curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 5L); | 226 | curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 15L); |
219 | // NOTE: use of CONNECTTIMEOUT without also | 227 | // NOTE: use of CONNECTTIMEOUT without also |
220 | // setting NOSIGNAL results in really weird | 228 | // setting NOSIGNAL results in really weird |
221 | // crashes on my system! | 229 | // crashes on my system! |
diff --git a/src/daemon/daemontest_large_put.c b/src/daemon/daemontest_large_put.c index 2a9ad4fa..3bbf707e 100644 --- a/src/daemon/daemontest_large_put.c +++ b/src/daemon/daemontest_large_put.c | |||
@@ -96,8 +96,16 @@ ahc_echo (void *cls, | |||
96 | return MHD_NO; /* unexpected method */ | 96 | return MHD_NO; /* unexpected method */ |
97 | if ((*done) == 0) | 97 | if ((*done) == 0) |
98 | { | 98 | { |
99 | if (*upload_data_size != PUT_SIZE) | 99 | if (*upload_data_size != PUT_SIZE) |
100 | return MHD_YES; /* not yet ready */ | 100 | { |
101 | #if 0 | ||
102 | fprintf(stderr, | ||
103 | "Waiting for more data (%u/%u)...\n", | ||
104 | *upload_data_size, | ||
105 | PUT_SIZE); | ||
106 | #endif | ||
107 | return MHD_YES; /* not yet ready */ | ||
108 | } | ||
101 | if (0 == memcmp (upload_data, put_buffer, PUT_SIZE)) | 109 | if (0 == memcmp (upload_data, put_buffer, PUT_SIZE)) |
102 | { | 110 | { |
103 | *upload_data_size = 0; | 111 | *upload_data_size = 0; |
@@ -227,7 +235,13 @@ testMultithreadedPut () | |||
227 | curl_easy_cleanup (c); | 235 | curl_easy_cleanup (c); |
228 | MHD_stop_daemon (d); | 236 | MHD_stop_daemon (d); |
229 | if (cbc.pos != strlen ("/hello_world")) | 237 | if (cbc.pos != strlen ("/hello_world")) |
230 | return 64; | 238 | { |
239 | fprintf(stderr, | ||
240 | "Got invalid response `%.*s'\n", | ||
241 | cbc.pos, | ||
242 | cbc.buf); | ||
243 | return 64; | ||
244 | } | ||
231 | if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) | 245 | if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) |
232 | return 128; | 246 | return 128; |
233 | return 0; | 247 | return 0; |
@@ -260,7 +274,10 @@ testExternalPut () | |||
260 | multi = NULL; | 274 | multi = NULL; |
261 | d = MHD_start_daemon (MHD_USE_DEBUG, | 275 | d = MHD_start_daemon (MHD_USE_DEBUG, |
262 | 1082, | 276 | 1082, |
263 | NULL, NULL, &ahc_echo, &done_flag, MHD_OPTION_END); | 277 | NULL, NULL, &ahc_echo, &done_flag, |
278 | MHD_OPTION_CONNECTION_MEMORY_LIMIT, | ||
279 | PUT_SIZE * 4, | ||
280 | MHD_OPTION_END); | ||
264 | if (d == NULL) | 281 | if (d == NULL) |
265 | return 256; | 282 | return 256; |
266 | c = curl_easy_init (); | 283 | c = curl_easy_init (); |
@@ -357,9 +374,15 @@ testExternalPut () | |||
357 | } | 374 | } |
358 | MHD_stop_daemon (d); | 375 | MHD_stop_daemon (d); |
359 | if (cbc.pos != strlen ("/hello_world")) | 376 | if (cbc.pos != strlen ("/hello_world")) |
360 | return 64; | 377 | { |
378 | fprintf(stderr, | ||
379 | "Got invalid response `%.*s'\n", | ||
380 | cbc.pos, | ||
381 | cbc.buf); | ||
382 | return 8192; | ||
383 | } | ||
361 | if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) | 384 | if (0 != strncmp ("/hello_world", cbc.buf, strlen ("/hello_world"))) |
362 | return 128; | 385 | return 16384; |
363 | return 0; | 386 | return 0; |
364 | } | 387 | } |
365 | 388 | ||
@@ -380,7 +403,7 @@ main (int argc, char *const *argv) | |||
380 | errorCount += testInternalPut (); | 403 | errorCount += testInternalPut (); |
381 | errorCount += testMultithreadedPut (); | 404 | errorCount += testMultithreadedPut (); |
382 | } | 405 | } |
383 | errorCount += testExternalPut (); | 406 | errorCount += testExternalPut (); |
384 | free (put_buffer); | 407 | free (put_buffer); |
385 | if (errorCount != 0) | 408 | if (errorCount != 0) |
386 | fprintf (stderr, "Error (code: %u)\n", errorCount); | 409 | fprintf (stderr, "Error (code: %u)\n", errorCount); |
diff --git a/src/daemon/daemontest_postform.c b/src/daemon/daemontest_postform.c index 75df1eb0..00734df6 100644 --- a/src/daemon/daemontest_postform.c +++ b/src/daemon/daemontest_postform.c | |||
@@ -72,6 +72,13 @@ post_iterator (void *cls, | |||
72 | { | 72 | { |
73 | int *eok = cls; | 73 | int *eok = cls; |
74 | 74 | ||
75 | #if 0 | ||
76 | fprintf(stderr, | ||
77 | "PI sees %s-%.*s\n", | ||
78 | key, | ||
79 | size, | ||
80 | value); | ||
81 | #endif | ||
75 | if ((0 == strcmp (key, "name")) && | 82 | if ((0 == strcmp (key, "name")) && |
76 | (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size))) | 83 | (size == strlen ("daniel")) && (0 == strncmp (value, "daniel", size))) |
77 | (*eok) |= 1; | 84 | (*eok) |= 1; |
@@ -95,7 +102,7 @@ ahc_echo (void *cls, | |||
95 | struct MHD_PostProcessor *pp; | 102 | struct MHD_PostProcessor *pp; |
96 | int ret; | 103 | int ret; |
97 | 104 | ||
98 | if (0 != strcmp ("POST", method)) | 105 | if (0 != strcmp ("POST", method)) |
99 | { | 106 | { |
100 | printf ("METHOD: %s\n", method); | 107 | printf ("METHOD: %s\n", method); |
101 | return MHD_NO; /* unexpected method */ | 108 | return MHD_NO; /* unexpected method */ |
diff --git a/src/daemon/daemontest_put_chunked.c b/src/daemon/daemontest_put_chunked.c index 9f00dbb3..84afc444 100644 --- a/src/daemon/daemontest_put_chunked.c +++ b/src/daemon/daemontest_put_chunked.c | |||
@@ -105,6 +105,11 @@ ahc_echo (void *cls, | |||
105 | printf ("Invalid upload data `%8s'!\n", upload_data); | 105 | printf ("Invalid upload data `%8s'!\n", upload_data); |
106 | return MHD_NO; | 106 | return MHD_NO; |
107 | } | 107 | } |
108 | #if 0 | ||
109 | fprintf(stderr, | ||
110 | "Not ready for response: %u/%u\n", | ||
111 | *done, 8); | ||
112 | #endif | ||
108 | return MHD_YES; | 113 | return MHD_YES; |
109 | } | 114 | } |
110 | response = MHD_create_response_from_data (strlen (url), | 115 | response = MHD_create_response_from_data (strlen (url), |
diff --git a/src/daemon/fileserver_example.c b/src/daemon/fileserver_example.c index c5bf762b..9f86fd2c 100644 --- a/src/daemon/fileserver_example.c +++ b/src/daemon/fileserver_example.c | |||
@@ -52,8 +52,9 @@ ahc_echo (void *cls, | |||
52 | const char *url, | 52 | const char *url, |
53 | const char *method, | 53 | const char *method, |
54 | const char *upload_data, | 54 | const char *upload_data, |
55 | const char *version, unsigned int *upload_data_size, void **unused) | 55 | const char *version, unsigned int *upload_data_size, void **ptr) |
56 | { | 56 | { |
57 | static int aptr; | ||
57 | struct MHD_Response *response; | 58 | struct MHD_Response *response; |
58 | int ret; | 59 | int ret; |
59 | FILE *file; | 60 | FILE *file; |
@@ -61,6 +62,13 @@ ahc_echo (void *cls, | |||
61 | 62 | ||
62 | if (0 != strcmp (method, "GET")) | 63 | if (0 != strcmp (method, "GET")) |
63 | return MHD_NO; /* unexpected method */ | 64 | return MHD_NO; /* unexpected method */ |
65 | if (&aptr != *ptr) | ||
66 | { | ||
67 | /* do never respond on first call */ | ||
68 | *ptr = &aptr; | ||
69 | return MHD_YES; | ||
70 | } | ||
71 | *ptr = NULL; /* reset when done */ | ||
64 | file = fopen (&url[1], "r"); | 72 | file = fopen (&url[1], "r"); |
65 | if (file == NULL) | 73 | if (file == NULL) |
66 | { | 74 | { |
diff --git a/src/daemon/internal.h b/src/daemon/internal.h index de4ae725..4749d10d 100644 --- a/src/daemon/internal.h +++ b/src/daemon/internal.h | |||
@@ -85,21 +85,9 @@ struct MHD_HTTP_Header | |||
85 | char *value; | 85 | char *value; |
86 | 86 | ||
87 | enum MHD_ValueKind kind; | 87 | enum MHD_ValueKind kind; |
88 | }; | ||
89 | |||
90 | |||
91 | struct MHD_Access_Handler | ||
92 | { | ||
93 | struct MHD_Access_Handler *next; | ||
94 | |||
95 | char *uri_prefix; | ||
96 | |||
97 | MHD_AccessHandlerCallback dh; | ||
98 | 88 | ||
99 | void *dh_cls; | ||
100 | }; | 89 | }; |
101 | 90 | ||
102 | |||
103 | /** | 91 | /** |
104 | * Representation of a response. | 92 | * Representation of a response. |
105 | */ | 93 | */ |
@@ -172,7 +160,129 @@ struct MHD_Response | |||
172 | 160 | ||
173 | }; | 161 | }; |
174 | 162 | ||
163 | /** | ||
164 | * States in a state machine for a connection. | ||
165 | * | ||
166 | * Transitions are any-state to CLOSED, any state to state+1, | ||
167 | * FOOTERS_SENT to INIT. CLOSED is the terminal state and | ||
168 | * INIT the initial state. | ||
169 | * | ||
170 | * Note that transitions for *reading* happen only after | ||
171 | * the input has been processed; transitions for | ||
172 | * *writing* happen after the respective data has been | ||
173 | * put into the write buffer (the write does not have | ||
174 | * to be completed yet). A transition to CLOSED or INIT | ||
175 | * requires the write to be complete. | ||
176 | */ | ||
177 | enum MHD_CONNECTION_STATE | ||
178 | { | ||
179 | /** | ||
180 | * Connection just started (no headers received). | ||
181 | * Waiting for the line with the request type, URL and version. | ||
182 | */ | ||
183 | MHD_CONNECTION_INIT = 0, | ||
184 | |||
185 | /** | ||
186 | * 1: We got the URL (and request type and version). Wait for a header line. | ||
187 | */ | ||
188 | MHD_CONNECTION_URL_RECEIVED = MHD_CONNECTION_INIT + 1, | ||
189 | |||
190 | /** | ||
191 | * 2: We got part of a multi-line request header. Wait for the rest. | ||
192 | */ | ||
193 | MHD_CONNECTION_HEADER_PART_RECEIVED = MHD_CONNECTION_URL_RECEIVED + 1, | ||
194 | |||
195 | /** | ||
196 | * 3: We got the request headers. Process them. | ||
197 | */ | ||
198 | MHD_CONNECTION_HEADERS_RECEIVED = MHD_CONNECTION_HEADER_PART_RECEIVED + 1, | ||
199 | |||
200 | /** | ||
201 | * 4: We have processed the request headers. Send 100 continue. | ||
202 | */ | ||
203 | MHD_CONNECTION_HEADERS_PROCESSED = MHD_CONNECTION_HEADERS_RECEIVED + 1, | ||
204 | |||
205 | /** | ||
206 | * 5: We have processed the headers and need to send 100 CONTINUE. | ||
207 | */ | ||
208 | MHD_CONNECTION_CONTINUE_SENDING = MHD_CONNECTION_HEADERS_PROCESSED + 1, | ||
209 | |||
210 | /** | ||
211 | * 6: We have sent 100 CONTINUE (or do not need to). Read the message body. | ||
212 | */ | ||
213 | MHD_CONNECTION_CONTINUE_SENT = MHD_CONNECTION_CONTINUE_SENDING + 1, | ||
214 | |||
215 | /** | ||
216 | * 7: We got the request body. Wait for a line of the footer. | ||
217 | */ | ||
218 | MHD_CONNECTION_BODY_RECEIVED = MHD_CONNECTION_CONTINUE_SENT + 1, | ||
175 | 219 | ||
220 | /** | ||
221 | * 8: We got part of a line of the footer. Wait for the | ||
222 | * rest. | ||
223 | */ | ||
224 | MHD_CONNECTION_FOOTER_PART_RECEIVED = MHD_CONNECTION_BODY_RECEIVED + 1, | ||
225 | |||
226 | /** | ||
227 | * 9: We received the entire footer. Wait for a response to be queued | ||
228 | * and prepare the response headers. | ||
229 | */ | ||
230 | MHD_CONNECTION_FOOTERS_RECEIVED = MHD_CONNECTION_FOOTER_PART_RECEIVED + 1, | ||
231 | |||
232 | /** | ||
233 | * 10: We have prepared the response headers in the writ buffer. | ||
234 | * Send the response headers. | ||
235 | */ | ||
236 | MHD_CONNECTION_HEADERS_SENDING = MHD_CONNECTION_FOOTERS_RECEIVED + 1, | ||
237 | |||
238 | /** | ||
239 | * 11: We have sent the response headers. Get ready to send the body. | ||
240 | */ | ||
241 | MHD_CONNECTION_HEADERS_SENT = MHD_CONNECTION_HEADERS_SENDING + 1, | ||
242 | |||
243 | /** | ||
244 | * 12: We are ready to send a part of a non-chunked body. Send it. | ||
245 | */ | ||
246 | MHD_CONNECTION_NORMAL_BODY_READY = MHD_CONNECTION_HEADERS_SENT + 1, | ||
247 | |||
248 | /** | ||
249 | * 13: We are waiting for the client to provide more | ||
250 | * data of a non-chunked body. | ||
251 | */ | ||
252 | MHD_CONNECTION_NORMAL_BODY_UNREADY = MHD_CONNECTION_NORMAL_BODY_READY + 1, | ||
253 | |||
254 | /** | ||
255 | * 14: We are ready to send a chunk. | ||
256 | */ | ||
257 | MHD_CONNECTION_CHUNKED_BODY_READY = MHD_CONNECTION_NORMAL_BODY_UNREADY + 1, | ||
258 | |||
259 | /** | ||
260 | * 15: We are waiting for the client to provide a chunk of the body. | ||
261 | */ | ||
262 | MHD_CONNECTION_CHUNKED_BODY_UNREADY = MHD_CONNECTION_CHUNKED_BODY_READY + 1, | ||
263 | |||
264 | /** | ||
265 | * 16: We have sent the response body. Prepare the footers. | ||
266 | */ | ||
267 | MHD_CONNECTION_BODY_SENT = MHD_CONNECTION_CHUNKED_BODY_UNREADY + 1, | ||
268 | |||
269 | /** | ||
270 | * 17: We have prepared the response footer. Send it. | ||
271 | */ | ||
272 | MHD_CONNECTION_FOOTERS_SENDING = MHD_CONNECTION_BODY_SENT + 1, | ||
273 | |||
274 | /** | ||
275 | * 18: We have sent the response footer. Shutdown or restart. | ||
276 | */ | ||
277 | MHD_CONNECTION_FOOTERS_SENT = MHD_CONNECTION_FOOTERS_SENDING + 1, | ||
278 | |||
279 | /** | ||
280 | * 19: This connection is closed (no more activity | ||
281 | * allowed). | ||
282 | */ | ||
283 | MHD_CONNECTION_CLOSED = MHD_CONNECTION_FOOTERS_SENT + 1, | ||
284 | |||
285 | }; | ||
176 | 286 | ||
177 | struct MHD_Connection | 287 | struct MHD_Connection |
178 | { | 288 | { |
@@ -250,6 +360,21 @@ struct MHD_Connection | |||
250 | char *write_buffer; | 360 | char *write_buffer; |
251 | 361 | ||
252 | /** | 362 | /** |
363 | * Last incomplete header line during parsing of headers. | ||
364 | * Allocated in pool. Only valid if state is | ||
365 | * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED. | ||
366 | */ | ||
367 | char *last; | ||
368 | |||
369 | /** | ||
370 | * Position after the colon on the last incomplete header | ||
371 | * line during parsing of headers. | ||
372 | * Allocated in pool. Only valid if state is | ||
373 | * either HEADER_PART_RECEIVED or FOOTER_PART_RECEIVED. | ||
374 | */ | ||
375 | char *colon; | ||
376 | |||
377 | /** | ||
253 | * Foreign address (of length addr_len). MALLOCED (not | 378 | * Foreign address (of length addr_len). MALLOCED (not |
254 | * in pool!). | 379 | * in pool!). |
255 | */ | 380 | */ |
@@ -292,6 +417,12 @@ struct MHD_Connection | |||
292 | size_t write_buffer_append_offset; | 417 | size_t write_buffer_append_offset; |
293 | 418 | ||
294 | /** | 419 | /** |
420 | * How many more bytes of the body do we expect | ||
421 | * to read? "-1" for unknown. | ||
422 | */ | ||
423 | size_t remaining_upload_size; | ||
424 | |||
425 | /** | ||
295 | * Current write position in the actual response | 426 | * Current write position in the actual response |
296 | * (excluding headers, content only; should be 0 | 427 | * (excluding headers, content only; should be 0 |
297 | * while sending headers). | 428 | * while sending headers). |
@@ -299,13 +430,6 @@ struct MHD_Connection | |||
299 | size_t response_write_position; | 430 | size_t response_write_position; |
300 | 431 | ||
301 | /** | 432 | /** |
302 | * Remaining (!) number of bytes in the upload. | ||
303 | * Set to -1 for unknown (connection will close | ||
304 | * to indicate end of upload). | ||
305 | */ | ||
306 | size_t remaining_upload_size; | ||
307 | |||
308 | /** | ||
309 | * Position in the 100 CONTINUE message that | 433 | * Position in the 100 CONTINUE message that |
310 | * we need to send when receiving http 1.1 requests. | 434 | * we need to send when receiving http 1.1 requests. |
311 | */ | 435 | */ |
@@ -333,29 +457,15 @@ struct MHD_Connection | |||
333 | * Has this socket been closed for reading (i.e. | 457 | * Has this socket been closed for reading (i.e. |
334 | * other side closed the connection)? If so, | 458 | * other side closed the connection)? If so, |
335 | * we must completely close the connection once | 459 | * we must completely close the connection once |
336 | * we are done sending our response. | 460 | * we are done sending our response (and stop |
337 | */ | 461 | * trying to read from this socket). |
338 | int read_close; | ||
339 | |||
340 | /** | ||
341 | * Have we finished receiving all of the headers yet? | ||
342 | * Set to 1 once we are done processing all of the | ||
343 | * headers. Note that due to pipelining, it is | ||
344 | * possible that the NEXT request is already | ||
345 | * (partially) waiting in the read buffer. | ||
346 | */ | ||
347 | int have_received_headers; | ||
348 | |||
349 | /** | ||
350 | * Have we finished receiving the data from a | ||
351 | * potential file-upload? | ||
352 | */ | 462 | */ |
353 | int have_received_body; | 463 | int read_closed; |
354 | 464 | ||
355 | /** | 465 | /** |
356 | * Have we finished sending all of the headers yet? | 466 | * State in the FSM for this connection. |
357 | */ | 467 | */ |
358 | int have_sent_headers; | 468 | enum MHD_CONNECTION_STATE state; |
359 | 469 | ||
360 | /** | 470 | /** |
361 | * HTTP response code. Only valid if response object | 471 | * HTTP response code. Only valid if response object |
@@ -373,6 +483,11 @@ struct MHD_Connection | |||
373 | int response_unready; | 483 | int response_unready; |
374 | 484 | ||
375 | /** | 485 | /** |
486 | * Are we sending with chunked encoding? | ||
487 | */ | ||
488 | int have_chunked_response; | ||
489 | |||
490 | /** | ||
376 | * Are we receiving with chunked encoding? This will be set to | 491 | * Are we receiving with chunked encoding? This will be set to |
377 | * MHD_YES after we parse the headers and are processing the body | 492 | * MHD_YES after we parse the headers and are processing the body |
378 | * with chunks. After we are done with the body and we are | 493 | * with chunks. After we are done with the body and we are |
@@ -402,9 +517,15 @@ struct MHD_Connection | |||
402 | struct MHD_Daemon | 517 | struct MHD_Daemon |
403 | { | 518 | { |
404 | 519 | ||
405 | struct MHD_Access_Handler *handlers; | 520 | /** |
521 | * Callback function for all requests. | ||
522 | */ | ||
523 | MHD_AccessHandlerCallback default_handler; | ||
406 | 524 | ||
407 | struct MHD_Access_Handler default_handler; | 525 | /** |
526 | * Closure argument to default_handler. | ||
527 | */ | ||
528 | void * default_handler_cls; | ||
408 | 529 | ||
409 | /** | 530 | /** |
410 | * Linked list of our current connections. | 531 | * Linked list of our current connections. |
diff --git a/src/daemon/memorypool.c b/src/daemon/memorypool.c index 6310d7d7..1bb12bae 100644 --- a/src/daemon/memorypool.c +++ b/src/daemon/memorypool.c | |||
@@ -188,4 +188,33 @@ MHD_pool_reallocate (struct MemoryPool *pool, | |||
188 | return NULL; | 188 | return NULL; |
189 | } | 189 | } |
190 | 190 | ||
191 | /** | ||
192 | * Clear all entries from the memory pool except | ||
193 | * for "keep" of the given "size". | ||
194 | * | ||
195 | * @param keep pointer to the entry to keep (maybe NULL) | ||
196 | * @param size how many bytes need to be kept at this address | ||
197 | * @return addr new address of "keep" (if it had to change) | ||
198 | */ | ||
199 | void *MHD_pool_reset(struct MemoryPool * pool, | ||
200 | void * keep, | ||
201 | unsigned int size) | ||
202 | { | ||
203 | if (keep != NULL) | ||
204 | { | ||
205 | if (keep != pool->memory) | ||
206 | { | ||
207 | memmove(pool->memory, | ||
208 | keep, | ||
209 | size); | ||
210 | keep = pool->memory; | ||
211 | } | ||
212 | pool->pos = size; | ||
213 | } | ||
214 | pool->end = pool->size; | ||
215 | return keep; | ||
216 | } | ||
217 | |||
218 | |||
219 | |||
191 | /* end of memorypool.c */ | 220 | /* end of memorypool.c */ |
diff --git a/src/daemon/memorypool.h b/src/daemon/memorypool.h index 7f53cba5..1d8ffb32 100644 --- a/src/daemon/memorypool.h +++ b/src/daemon/memorypool.h | |||
@@ -81,4 +81,16 @@ void *MHD_pool_reallocate (struct MemoryPool *pool, | |||
81 | void *old, | 81 | void *old, |
82 | unsigned int old_size, unsigned int new_size); | 82 | unsigned int old_size, unsigned int new_size); |
83 | 83 | ||
84 | /** | ||
85 | * Clear all entries from the memory pool except | ||
86 | * for "keep" of the given "size". | ||
87 | * | ||
88 | * @param keep pointer to the entry to keep (maybe NULL) | ||
89 | * @param size how many bytes need to be kept at this address | ||
90 | * @return addr new address of "keep" (if it had to change) | ||
91 | */ | ||
92 | void *MHD_pool_reset(struct MemoryPool * pool, | ||
93 | void * keep, | ||
94 | unsigned int size); | ||
95 | |||
84 | #endif | 96 | #endif |
diff --git a/src/daemon/minimal_example.c b/src/daemon/minimal_example.c index 62ee5ae5..badff7a2 100644 --- a/src/daemon/minimal_example.c +++ b/src/daemon/minimal_example.c | |||
@@ -41,14 +41,22 @@ ahc_echo (void *cls, | |||
41 | const char *url, | 41 | const char *url, |
42 | const char *method, | 42 | const char *method, |
43 | const char *upload_data, | 43 | const char *upload_data, |
44 | const char *version, unsigned int *upload_data_size, void **unused) | 44 | const char *version, unsigned int *upload_data_size, void **ptr) |
45 | { | 45 | { |
46 | static int aptr; | ||
46 | const char *me = cls; | 47 | const char *me = cls; |
47 | struct MHD_Response *response; | 48 | struct MHD_Response *response; |
48 | int ret; | 49 | int ret; |
49 | 50 | ||
50 | if (0 != strcmp (method, "GET")) | 51 | if (0 != strcmp (method, "GET")) |
51 | return MHD_NO; /* unexpected method */ | 52 | return MHD_NO; /* unexpected method */ |
53 | if (&aptr != *ptr) | ||
54 | { | ||
55 | /* do never respond on first call */ | ||
56 | *ptr = &aptr; | ||
57 | return MHD_YES; | ||
58 | } | ||
59 | *ptr = NULL; /* reset when done */ | ||
52 | response = MHD_create_response_from_data (strlen (me), | 60 | response = MHD_create_response_from_data (strlen (me), |
53 | (void *) me, MHD_NO, MHD_NO); | 61 | (void *) me, MHD_NO, MHD_NO); |
54 | ret = MHD_queue_response (connection, MHD_HTTP_OK, response); | 62 | ret = MHD_queue_response (connection, MHD_HTTP_OK, response); |
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h index 680e6d23..2197d9d9 100644 --- a/src/include/microhttpd.h +++ b/src/include/microhttpd.h | |||
@@ -84,7 +84,7 @@ extern "C" | |||
84 | /** | 84 | /** |
85 | * Current version of the library. | 85 | * Current version of the library. |
86 | */ | 86 | */ |
87 | #define MHD_VERSION 0x00000100 | 87 | #define MHD_VERSION 0x00000200 |
88 | 88 | ||
89 | /** | 89 | /** |
90 | * MHD-internal return codes. | 90 | * MHD-internal return codes. |
@@ -373,6 +373,11 @@ enum MHD_ValueKind | |||
373 | */ | 373 | */ |
374 | MHD_GET_ARGUMENT_KIND = 8, | 374 | MHD_GET_ARGUMENT_KIND = 8, |
375 | 375 | ||
376 | /** | ||
377 | * HTTP footer (only for http 1.1 chunked encodings). | ||
378 | */ | ||
379 | MHD_FOOTER_KIND = 16, | ||
380 | |||
376 | }; | 381 | }; |
377 | 382 | ||
378 | /** | 383 | /** |
@@ -544,9 +549,15 @@ typedef int | |||
544 | * libmicrohttpd guarantees that "pos" will be | 549 | * libmicrohttpd guarantees that "pos" will be |
545 | * the sum of all non-negative return values | 550 | * the sum of all non-negative return values |
546 | * obtained from the content reader so far. | 551 | * obtained from the content reader so far. |
547 | * @return -1 on error (libmicrohttpd will no longer | 552 | * @return -1 for the end of transmission (or on error); |
548 | * try to read content and instead close the connection | 553 | * if a content transfer size was pre-set and the callback |
549 | * with the client). | 554 | * has provided fewer than that amount of data, |
555 | * MHD will close the connection with the client; | ||
556 | * if no content size was specified and this is an | ||
557 | * http 1.1 connection using chunked encoding, MHD will | ||
558 | * interpret "-1" as the normal end of the transfer | ||
559 | * (possibly allowing the client to perform additional | ||
560 | * requests using the same TCP connection). | ||
550 | */ | 561 | */ |
551 | typedef int | 562 | typedef int |
552 | (*MHD_ContentReaderCallback) (void *cls, size_t pos, char *buf, int max); | 563 | (*MHD_ContentReaderCallback) (void *cls, size_t pos, char *buf, int max); |
@@ -596,7 +607,7 @@ typedef int | |||
596 | * in which case connections from any IP will be | 607 | * in which case connections from any IP will be |
597 | * accepted | 608 | * accepted |
598 | * @param apc_cls extra argument to apc | 609 | * @param apc_cls extra argument to apc |
599 | * @param dh default handler for all URIs | 610 | * @param dh handler called for all requests (repeatedly) |
600 | * @param dh_cls extra argument to dh | 611 | * @param dh_cls extra argument to dh |
601 | * @param ... list of options (type-value pairs, | 612 | * @param ... list of options (type-value pairs, |
602 | * terminated with MHD_OPTION_END). | 613 | * terminated with MHD_OPTION_END). |
@@ -655,32 +666,6 @@ int MHD_get_timeout (struct MHD_Daemon *daemon, unsigned long long *timeout); | |||
655 | */ | 666 | */ |
656 | int MHD_run (struct MHD_Daemon *daemon); | 667 | int MHD_run (struct MHD_Daemon *daemon); |
657 | 668 | ||
658 | |||
659 | /** | ||
660 | * Register an access handler for all URIs beginning with uri_prefix. | ||
661 | * | ||
662 | * @param uri_prefix | ||
663 | * @return MRI_NO if a handler for this exact prefix | ||
664 | * already exists | ||
665 | */ | ||
666 | int | ||
667 | MHD_register_handler (struct MHD_Daemon *daemon, | ||
668 | const char *uri_prefix, | ||
669 | MHD_AccessHandlerCallback dh, void *dh_cls); | ||
670 | |||
671 | /** | ||
672 | * Unregister an access handler for the URIs beginning with | ||
673 | * uri_prefix. | ||
674 | * | ||
675 | * @param uri_prefix | ||
676 | * @return MHD_NO if a handler for this exact prefix | ||
677 | * is not known for this daemon | ||
678 | */ | ||
679 | int | ||
680 | MHD_unregister_handler (struct MHD_Daemon *daemon, | ||
681 | const char *uri_prefix, | ||
682 | MHD_AccessHandlerCallback dh, void *dh_cls); | ||
683 | |||
684 | /** | 669 | /** |
685 | * Get all of the headers from the request. | 670 | * Get all of the headers from the request. |
686 | * | 671 | * |