diff options
author | Christian Grothoff <christian@grothoff.org> | 2007-08-08 08:07:49 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2007-08-08 08:07:49 +0000 |
commit | 0cacf72380222e704cff8c05b329ba6bac41f631 (patch) | |
tree | 5fb26264b53a285aece3bb0912645da2275a4715 | |
parent | 5b7eec5d2d858dd33aa7f65a7655de1410154f0a (diff) | |
download | libmicrohttpd-0cacf72380222e704cff8c05b329ba6bac41f631.tar.gz libmicrohttpd-0cacf72380222e704cff8c05b329ba6bac41f631.zip |
updates
-rw-r--r-- | ChangeLog | 4 | ||||
-rw-r--r-- | README | 54 | ||||
-rw-r--r-- | src/daemon/Makefile.am | 20 | ||||
-rw-r--r-- | src/daemon/connection.c | 483 | ||||
-rw-r--r-- | src/daemon/daemon.c | 65 | ||||
-rw-r--r-- | src/daemon/daemontest_post.c | 91 | ||||
-rw-r--r-- | src/daemon/daemontest_put.c | 36 | ||||
-rw-r--r-- | src/daemon/internal.h | 87 | ||||
-rw-r--r-- | src/daemon/memorypool.c | 180 | ||||
-rw-r--r-- | src/daemon/memorypool.h | 87 | ||||
-rw-r--r-- | src/include/microhttpd.h | 50 |
11 files changed, 898 insertions, 259 deletions
@@ -1,2 +1,6 @@ | |||
1 | Wed Aug 8 01:46:06 MDT 2007 | ||
2 | Added pool allocation and connection limitations (total | ||
3 | number and memory size). | ||
4 | |||
1 | Tue Jan 9 20:52:48 MST 2007 | 5 | Tue Jan 9 20:52:48 MST 2007 |
2 | Created project build files and updated API. - CG | 6 | Created project build files and updated API. - CG |
@@ -1,32 +1,25 @@ | |||
1 | Run "autoreconf -f -i" to create configure. | 1 | Run "autoreconf -fi" to create configure. |
2 | 2 | ||
3 | This is still pre-alpha software. The following | 3 | This is still pre-alpha software. The following things should be |
4 | things need to be implemented (in list of importance) | 4 | implemented (in order of importance) before we can claim to be |
5 | before certain features can be used at all: | 5 | reasonably complete: |
6 | 6 | ||
7 | 7 | ||
8 | For POST: | 8 | For http/1.1-compliance: |
9 | ========= | 9 | ======================== |
10 | - Decoding of POST data, testing thereof | 10 | connection.c: |
11 | - POST testcase currently fails (blocks!) | 11 | - support responding immediately with "100 CONTINUE" (http 1.1)! |
12 | |||
13 | For http-compliance: | ||
14 | ==================== | ||
15 | session.c: | ||
16 | - send proper error code back if headers are too long | 12 | - send proper error code back if headers are too long |
17 | (investigate what we should do with those headers, | 13 | (currently, we just close the connection) |
18 | read? give user control?) | 14 | - support chunked requests from clients |
19 | ALSO: should this limit be per-line or for the | 15 | - send proper error code back if client forgot the "Host" header (?) |
20 | entire header? (currently, we enforce per-line, | 16 | - automatically add MHD_HTTP_HEADER_DATE if client "forgot" to add one (?) |
21 | but the entire header might make more sense!) | 17 | - automatically drop body from responses to "HEAD" requests? |
22 | - http 1.0 compatibility (if 1.0, force connection | ||
23 | close at the end!) | ||
24 | 18 | ||
25 | For IPv6: | 19 | For POST: |
26 | ========= | 20 | ========= |
27 | daemon.c: | 21 | - find better way to handle POST data that does not fit into memory |
28 | - fix start daemon and accept handlers | 22 | - add support to decode multipart/form-data |
29 | |||
30 | 23 | ||
31 | For SSL: | 24 | For SSL: |
32 | ======== | 25 | ======== |
@@ -34,13 +27,8 @@ microhttpd.h: | |||
34 | - define appropriate MHD_OPTIONs | 27 | - define appropriate MHD_OPTIONs |
35 | - actual implementation | 28 | - actual implementation |
36 | 29 | ||
37 | 30 | Missing Testcases: | |
38 | Other: | 31 | ================== |
39 | ====== | 32 | - add testcases for http/1.1 pipelining (need |
40 | - allow client to control size of input/output | 33 | to figure out how to ensure curl pipelines) |
41 | buffers (add MHD_OPTION) | 34 | - add testcases for resource limit enforcement |
42 | - allow client to limit total number of connections | ||
43 | (add MHD_OPTION) | ||
44 | |||
45 | |||
46 | |||
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am index 5ce824cc..e0174f40 100644 --- a/src/daemon/Makefile.am +++ b/src/daemon/Makefile.am | |||
@@ -11,6 +11,7 @@ libmicrohttpd_la_SOURCES = \ | |||
11 | connection.c connection.h \ | 11 | connection.c connection.h \ |
12 | daemon.c \ | 12 | daemon.c \ |
13 | internal.c internal.h \ | 13 | internal.c internal.h \ |
14 | memorypool.c memorypool.h \ | ||
14 | plibc.h \ | 15 | plibc.h \ |
15 | response.c response.h | 16 | response.c response.h |
16 | 17 | ||
@@ -31,7 +32,9 @@ check_PROGRAMS = \ | |||
31 | daemontest \ | 32 | daemontest \ |
32 | daemontest_get \ | 33 | daemontest_get \ |
33 | daemontest_post \ | 34 | daemontest_post \ |
34 | daemontest_put | 35 | daemontest_put \ |
36 | daemontest_post11 \ | ||
37 | daemontest_put11 | ||
35 | 38 | ||
36 | TESTS = $(check_PROGRAMS) | 39 | TESTS = $(check_PROGRAMS) |
37 | 40 | ||
@@ -46,18 +49,29 @@ daemontest_get_LDADD = \ | |||
46 | $(top_builddir)/src/daemon/libmicrohttpd.la \ | 49 | $(top_builddir)/src/daemon/libmicrohttpd.la \ |
47 | @LIBCURL@ | 50 | @LIBCURL@ |
48 | 51 | ||
49 | |||
50 | daemontest_post_SOURCES = \ | 52 | daemontest_post_SOURCES = \ |
51 | daemontest_post.c | 53 | daemontest_post.c |
52 | daemontest_post_LDADD = \ | 54 | daemontest_post_LDADD = \ |
53 | $(top_builddir)/src/daemon/libmicrohttpd.la \ | 55 | $(top_builddir)/src/daemon/libmicrohttpd.la \ |
54 | @LIBCURL@ | 56 | @LIBCURL@ |
55 | 57 | ||
56 | |||
57 | daemontest_put_SOURCES = \ | 58 | daemontest_put_SOURCES = \ |
58 | daemontest_put.c | 59 | daemontest_put.c |
59 | daemontest_put_LDADD = \ | 60 | daemontest_put_LDADD = \ |
60 | $(top_builddir)/src/daemon/libmicrohttpd.la \ | 61 | $(top_builddir)/src/daemon/libmicrohttpd.la \ |
61 | @LIBCURL@ | 62 | @LIBCURL@ |
62 | 63 | ||
64 | daemontest_post11_SOURCES = \ | ||
65 | daemontest_post.c | ||
66 | daemontest_post11_LDADD = \ | ||
67 | $(top_builddir)/src/daemon/libmicrohttpd.la \ | ||
68 | @LIBCURL@ | ||
69 | |||
70 | |||
71 | daemontest_put11_SOURCES = \ | ||
72 | daemontest_put.c | ||
73 | daemontest_put11_LDADD = \ | ||
74 | $(top_builddir)/src/daemon/libmicrohttpd.la \ | ||
75 | @LIBCURL@ | ||
76 | |||
63 | endif \ No newline at end of file | 77 | endif \ No newline at end of file |
diff --git a/src/daemon/connection.c b/src/daemon/connection.c index 21b7872c..bb3d8990 100644 --- a/src/daemon/connection.c +++ b/src/daemon/connection.c | |||
@@ -27,8 +27,14 @@ | |||
27 | 27 | ||
28 | #include "internal.h" | 28 | #include "internal.h" |
29 | #include "connection.h" | 29 | #include "connection.h" |
30 | #include "memorypool.h" | ||
30 | #include "response.h" | 31 | #include "response.h" |
31 | 32 | ||
33 | /** | ||
34 | * Size by which MHD usually tries to increment read/write buffers. | ||
35 | */ | ||
36 | #define MHD_BUF_INC_SIZE 2048 | ||
37 | |||
32 | 38 | ||
33 | /** | 39 | /** |
34 | * Get all of the headers from the request. | 40 | * Get all of the headers from the request. |
@@ -40,9 +46,9 @@ | |||
40 | */ | 46 | */ |
41 | int | 47 | int |
42 | MHD_get_connection_values(struct MHD_Connection * connection, | 48 | MHD_get_connection_values(struct MHD_Connection * connection, |
43 | enum MHD_ValueKind kind, | 49 | enum MHD_ValueKind kind, |
44 | MHD_KeyValueIterator iterator, | 50 | MHD_KeyValueIterator iterator, |
45 | void * iterator_cls) { | 51 | void * iterator_cls) { |
46 | int ret; | 52 | int ret; |
47 | struct MHD_HTTP_Header * pos; | 53 | struct MHD_HTTP_Header * pos; |
48 | 54 | ||
@@ -131,41 +137,77 @@ MHD_connection_get_fdset(struct MHD_Connection * connection, | |||
131 | fd_set * except_fd_set, | 137 | fd_set * except_fd_set, |
132 | int * max_fd) { | 138 | int * max_fd) { |
133 | int fd; | 139 | int fd; |
140 | void * buf; | ||
134 | 141 | ||
135 | fd = connection->socket_fd; | 142 | fd = connection->socket_fd; |
136 | if (fd == -1) | 143 | if (fd == -1) |
137 | return MHD_YES; | 144 | return MHD_YES; |
138 | if ( (connection->read_close == 0) && | 145 | if ( (connection->read_close == 0) && |
139 | ( (connection->headersReceived == 0) || | 146 | ( (connection->headersReceived == 0) || |
140 | (connection->readLoc < connection->read_buffer_size) ) ) | 147 | (connection->readLoc < connection->read_buffer_size) ) ) { |
141 | FD_SET(fd, read_fd_set); | 148 | FD_SET(fd, read_fd_set); |
142 | if (connection->response != NULL) | 149 | if (fd > *max_fd) |
150 | *max_fd = fd; | ||
151 | } else { | ||
152 | |||
153 | |||
154 | if ( (connection->read_close == 0) && | ||
155 | ( (connection->headersReceived == 1) && | ||
156 | (connection->post_processed == MHD_NO) && | ||
157 | (connection->readLoc == connection->read_buffer_size) ) ) { | ||
158 | /* try growing the read buffer, just in case */ | ||
159 | buf = MHD_pool_reallocate(connection->pool, | ||
160 | connection->read_buffer, | ||
161 | connection->read_buffer_size, | ||
162 | connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE); | ||
163 | if (buf != NULL) { | ||
164 | /* we can actually grow the buffer, do it! */ | ||
165 | connection->read_buffer = buf; | ||
166 | connection->read_buffer_size = connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE; | ||
167 | FD_SET(fd, read_fd_set); | ||
168 | if (fd > *max_fd) | ||
169 | *max_fd = fd; | ||
170 | } | ||
171 | } | ||
172 | } | ||
173 | if (connection->response != NULL) { | ||
143 | FD_SET(fd, write_fd_set); | 174 | FD_SET(fd, write_fd_set); |
144 | if ( (fd > *max_fd) && | 175 | if (fd > *max_fd) |
145 | ( (connection->headersReceived == 0) || | 176 | *max_fd = fd; |
146 | (connection->readLoc < connection->read_buffer_size) || | 177 | } |
147 | (connection->response != NULL) ) ) | ||
148 | *max_fd = fd; | ||
149 | return MHD_YES; | 178 | return MHD_YES; |
150 | } | 179 | } |
151 | 180 | ||
152 | /** | 181 | /** |
153 | * Parse a single line of the HTTP header. Remove it | 182 | * We ran out of memory processing the |
154 | * from the read buffer. If the current line does not | 183 | * header. Handle it properly. |
184 | */ | ||
185 | static void | ||
186 | MHD_excessive_header_handler(struct MHD_Connection * connection) { | ||
187 | /* die, header far too long to be reasonable; | ||
188 | FIXME: send proper response to client | ||
189 | (stop reading, queue proper response) */ | ||
190 | MHD_DLOG(connection->daemon, | ||
191 | "Received excessively long header line, closing connection.\n"); | ||
192 | CLOSE(connection->socket_fd); | ||
193 | connection->socket_fd = -1; | ||
194 | } | ||
195 | |||
196 | /** | ||
197 | * Parse a single line of the HTTP header. Advance | ||
198 | * read_buffer (!) appropriately. If the current line does not | ||
155 | * fit, consider growing the buffer. If the line is | 199 | * fit, consider growing the buffer. If the line is |
156 | * far too long, close the connection. If no line is | 200 | * far too long, close the connection. If no line is |
157 | * found (incomplete, buffer too small, line too long), | 201 | * found (incomplete, buffer too small, line too long), |
158 | * return NULL. Otherwise return a copy of the line. | 202 | * return NULL. Otherwise return a pointer to the line. |
159 | */ | 203 | */ |
160 | static char * | 204 | static char * |
161 | MHD_get_next_header_line(struct MHD_Connection * connection) { | 205 | MHD_get_next_header_line(struct MHD_Connection * connection) { |
162 | char * rbuf; | 206 | char * rbuf; |
163 | size_t pos; | 207 | size_t pos; |
164 | size_t start; | ||
165 | 208 | ||
166 | if (connection->readLoc == 0) | 209 | if (connection->readLoc == 0) |
167 | return NULL; | 210 | return NULL; |
168 | start = 0; | ||
169 | pos = 0; | 211 | pos = 0; |
170 | rbuf = connection->read_buffer; | 212 | rbuf = connection->read_buffer; |
171 | while ( (pos < connection->readLoc - 1) && | 213 | while ( (pos < connection->readLoc - 1) && |
@@ -175,56 +217,55 @@ MHD_get_next_header_line(struct MHD_Connection * connection) { | |||
175 | if (pos == connection->readLoc - 1) { | 217 | if (pos == connection->readLoc - 1) { |
176 | /* not found, consider growing... */ | 218 | /* not found, consider growing... */ |
177 | if (connection->readLoc == connection->read_buffer_size) { | 219 | if (connection->readLoc == connection->read_buffer_size) { |
178 | /* grow buffer to read larger header or die... */ | 220 | rbuf = MHD_pool_reallocate(connection->pool, |
179 | if (connection->read_buffer_size < 4 * MHD_MAX_BUF_SIZE) { | 221 | connection->read_buffer, |
180 | rbuf = malloc(connection->read_buffer_size * 2); | 222 | connection->read_buffer_size, |
181 | memcpy(rbuf, | 223 | connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE); |
182 | connection->read_buffer, | 224 | if (rbuf == NULL) { |
183 | connection->readLoc); | 225 | MHD_excessive_header_handler(connection); |
184 | free(connection->read_buffer); | ||
185 | connection->read_buffer = rbuf; | ||
186 | connection->read_buffer_size *= 2; | ||
187 | } else { | 226 | } else { |
188 | /* die, header far too long to be reasonable */ | 227 | connection->read_buffer_size = connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE; |
189 | MHD_DLOG(connection->daemon, | 228 | connection->read_buffer = rbuf; |
190 | "Received excessively long header line (>%u), closing connection.\n", | ||
191 | 4 * MHD_MAX_BUF_SIZE); | ||
192 | CLOSE(connection->socket_fd); | ||
193 | connection->socket_fd = -1; | ||
194 | } | 229 | } |
195 | } | 230 | } |
196 | return NULL; | 231 | return NULL; |
197 | } | 232 | } |
198 | /* found, check if we have proper CRLF */ | 233 | /* found, check if we have proper CRLF */ |
199 | rbuf = malloc(pos + 1); | 234 | if ( (rbuf[pos] == '\r') && |
200 | memcpy(rbuf, | 235 | (rbuf[pos+1] == '\n') ) |
201 | connection->read_buffer, | 236 | rbuf[pos++] = '\0'; /* skip both r and n */ |
202 | pos); | 237 | rbuf[pos++] = '\0'; |
203 | rbuf[pos] = '\0'; | 238 | connection->read_buffer += pos; |
204 | if ( (connection->read_buffer[pos] == '\r') && | 239 | connection->read_buffer_size -= pos; |
205 | (connection->read_buffer[pos+1] == '\n') ) | ||
206 | pos++; /* skip both r and n */ | ||
207 | pos++; | ||
208 | memmove(connection->read_buffer, | ||
209 | &connection->read_buffer[pos], | ||
210 | connection->readLoc - pos); | ||
211 | connection->readLoc -= pos; | 240 | connection->readLoc -= pos; |
212 | return rbuf; | 241 | return rbuf; |
213 | } | 242 | } |
214 | 243 | ||
215 | static void | 244 | /** |
245 | * @return MHD_NO on failure (out of memory), MHD_YES for success | ||
246 | */ | ||
247 | static int | ||
216 | MHD_connection_add_header(struct MHD_Connection * connection, | 248 | MHD_connection_add_header(struct MHD_Connection * connection, |
217 | const char * key, | 249 | char * key, |
218 | const char * value, | 250 | char * value, |
219 | enum MHD_ValueKind kind) { | 251 | enum MHD_ValueKind kind) { |
220 | struct MHD_HTTP_Header * hdr; | 252 | struct MHD_HTTP_Header * hdr; |
221 | 253 | ||
222 | hdr = malloc(sizeof(struct MHD_HTTP_Header)); | 254 | hdr = MHD_pool_allocate(connection->pool, |
255 | sizeof(struct MHD_HTTP_Header), | ||
256 | MHD_YES); | ||
257 | if (hdr == NULL) { | ||
258 | MHD_DLOG(connection->daemon, | ||
259 | "Not enough memory to allocate header record!\n"); | ||
260 | MHD_excessive_header_handler(connection); | ||
261 | return MHD_NO; | ||
262 | } | ||
223 | hdr->next = connection->headers_received; | 263 | hdr->next = connection->headers_received; |
224 | hdr->header = strdup(key); | 264 | hdr->header = key; |
225 | hdr->value = strdup(value); | 265 | hdr->value = value; |
226 | hdr->kind = kind; | 266 | hdr->kind = kind; |
227 | connection->headers_received = hdr; | 267 | connection->headers_received = hdr; |
268 | return MHD_YES; | ||
228 | } | 269 | } |
229 | 270 | ||
230 | /** | 271 | /** |
@@ -253,7 +294,10 @@ MHD_http_unescape(char * val) { | |||
253 | } | 294 | } |
254 | } | 295 | } |
255 | 296 | ||
256 | static void | 297 | /** |
298 | * @return MHD_NO on failure (out of memory), MHD_YES for success | ||
299 | */ | ||
300 | static int | ||
257 | parse_arguments(enum MHD_ValueKind kind, | 301 | parse_arguments(enum MHD_ValueKind kind, |
258 | struct MHD_Connection * connection, | 302 | struct MHD_Connection * connection, |
259 | char * args) { | 303 | char * args) { |
@@ -263,7 +307,7 @@ parse_arguments(enum MHD_ValueKind kind, | |||
263 | while (args != NULL) { | 307 | while (args != NULL) { |
264 | equals = strstr(args, "="); | 308 | equals = strstr(args, "="); |
265 | if (equals == NULL) | 309 | if (equals == NULL) |
266 | return; /* invalid, ignore */ | 310 | return MHD_NO; /* invalid, ignore */ |
267 | equals[0] = '\0'; | 311 | equals[0] = '\0'; |
268 | equals++; | 312 | equals++; |
269 | amper = strstr(equals, "&"); | 313 | amper = strstr(equals, "&"); |
@@ -273,18 +317,22 @@ parse_arguments(enum MHD_ValueKind kind, | |||
273 | } | 317 | } |
274 | MHD_http_unescape(args); | 318 | MHD_http_unescape(args); |
275 | MHD_http_unescape(equals); | 319 | MHD_http_unescape(equals); |
276 | MHD_connection_add_header(connection, | 320 | if (MHD_NO == MHD_connection_add_header(connection, |
277 | args, | 321 | args, |
278 | equals, | 322 | equals, |
279 | kind); | 323 | kind)) |
324 | return MHD_NO; | ||
280 | args = amper; | 325 | args = amper; |
281 | } | 326 | } |
327 | return MHD_YES; | ||
282 | } | 328 | } |
283 | 329 | ||
284 | /** | 330 | /** |
285 | * Parse the cookie header (see RFC 2109). | 331 | * Parse the cookie header (see RFC 2109). |
332 | * | ||
333 | * @return MHD_YES for success, MHD_NO for failure (malformed, out of memory) | ||
286 | */ | 334 | */ |
287 | static void | 335 | static int |
288 | MHD_parse_cookie_header(struct MHD_Connection * connection) { | 336 | MHD_parse_cookie_header(struct MHD_Connection * connection) { |
289 | const char * hdr; | 337 | const char * hdr; |
290 | char * cpy; | 338 | char * cpy; |
@@ -294,11 +342,22 @@ MHD_parse_cookie_header(struct MHD_Connection * connection) { | |||
294 | int quotes; | 342 | int quotes; |
295 | 343 | ||
296 | hdr = MHD_lookup_connection_value(connection, | 344 | hdr = MHD_lookup_connection_value(connection, |
297 | MHD_HEADER_KIND, | 345 | MHD_HEADER_KIND, |
298 | "Cookie"); | 346 | "Cookie"); |
299 | if (hdr == NULL) | 347 | if (hdr == NULL) |
300 | return; | 348 | return MHD_YES; |
301 | cpy = strdup(hdr); | 349 | cpy = MHD_pool_allocate(connection->pool, |
350 | strlen(hdr)+1, | ||
351 | MHD_YES); | ||
352 | if (cpy == NULL) { | ||
353 | MHD_DLOG(connection->daemon, | ||
354 | "Not enough memory to parse cookies!\n"); | ||
355 | MHD_excessive_header_handler(connection); | ||
356 | return MHD_NO; | ||
357 | } | ||
358 | memcpy(cpy, | ||
359 | hdr, | ||
360 | strlen(hdr)+1); | ||
302 | pos = cpy; | 361 | pos = cpy; |
303 | while (pos != NULL) { | 362 | while (pos != NULL) { |
304 | equals = strstr(pos, "="); | 363 | equals = strstr(pos, "="); |
@@ -328,13 +387,14 @@ MHD_parse_cookie_header(struct MHD_Connection * connection) { | |||
328 | equals[strlen(equals)-1] = '\0'; | 387 | equals[strlen(equals)-1] = '\0'; |
329 | equals++; | 388 | equals++; |
330 | } | 389 | } |
331 | MHD_connection_add_header(connection, | 390 | if (MHD_NO == MHD_connection_add_header(connection, |
332 | pos, | 391 | pos, |
333 | equals, | 392 | equals, |
334 | MHD_COOKIE_KIND); | 393 | MHD_COOKIE_KIND)) |
394 | return MHD_NO; | ||
335 | pos = semicolon; | 395 | pos = semicolon; |
336 | } | 396 | } |
337 | free(cpy); | 397 | return MHD_YES; |
338 | } | 398 | } |
339 | 399 | ||
340 | /** | 400 | /** |
@@ -355,7 +415,7 @@ parse_initial_message_line(struct MHD_Connection * connection, | |||
355 | if (uri == NULL) | 415 | if (uri == NULL) |
356 | return MHD_NO; /* serious error */ | 416 | return MHD_NO; /* serious error */ |
357 | uri[0] = '\0'; | 417 | uri[0] = '\0'; |
358 | connection->method = strdup(line); | 418 | connection->method = line; |
359 | uri++; | 419 | uri++; |
360 | while (uri[0] == ' ') | 420 | while (uri[0] == ' ') |
361 | uri++; | 421 | uri++; |
@@ -372,11 +432,11 @@ parse_initial_message_line(struct MHD_Connection * connection, | |||
372 | connection, | 432 | connection, |
373 | args); | 433 | args); |
374 | } | 434 | } |
375 | connection->url = strdup(uri); | 435 | connection->url = uri; |
376 | if (httpVersion == NULL) | 436 | if (httpVersion == NULL) |
377 | connection->version = strdup(""); | 437 | connection->version = ""; |
378 | else | 438 | else |
379 | connection->version = strdup(httpVersion); | 439 | connection->version = httpVersion; |
380 | return MHD_YES; | 440 | return MHD_YES; |
381 | } | 441 | } |
382 | 442 | ||
@@ -398,6 +458,7 @@ MHD_parse_connection_headers(struct MHD_Connection * connection) { | |||
398 | char * colon; | 458 | char * colon; |
399 | char * tmp; | 459 | char * tmp; |
400 | const char * clen; | 460 | const char * clen; |
461 | const char * end; | ||
401 | unsigned long long cval; | 462 | unsigned long long cval; |
402 | 463 | ||
403 | if (connection->bodyReceived == 1) | 464 | if (connection->bodyReceived == 1) |
@@ -409,51 +470,38 @@ MHD_parse_connection_headers(struct MHD_Connection * connection) { | |||
409 | (line[0] == '\t') ) { | 470 | (line[0] == '\t') ) { |
410 | /* value was continued on the next line, see | 471 | /* value was continued on the next line, see |
411 | http://www.jmarshall.com/easy/http/ */ | 472 | http://www.jmarshall.com/easy/http/ */ |
412 | if ( (strlen(line) + strlen(last) > | 473 | last = MHD_pool_reallocate(connection->pool, |
413 | 4 * MHD_MAX_BUF_SIZE) ) { | 474 | last, |
414 | free(line); | 475 | strlen(last)+1, |
415 | free(last); | 476 | strlen(line) + strlen(last) + 1); |
416 | last = NULL; | 477 | if (last == NULL) { |
417 | MHD_DLOG(connection->daemon, | 478 | MHD_excessive_header_handler(connection); |
418 | "Received excessively long header line (>%u), closing connection.\n", | ||
419 | 4 * MHD_MAX_BUF_SIZE); | ||
420 | CLOSE(connection->socket_fd); | ||
421 | connection->socket_fd = -1; | ||
422 | break; | 479 | break; |
423 | } | 480 | } |
424 | tmp = malloc(strlen(line) + strlen(last) + 1); | ||
425 | strcpy(tmp, last); | ||
426 | free(last); | ||
427 | last = tmp; | ||
428 | tmp = line; | 481 | tmp = line; |
429 | while ( (tmp[0] == ' ') || | 482 | while ( (tmp[0] == ' ') || |
430 | (tmp[0] == '\t') ) | 483 | (tmp[0] == '\t') ) |
431 | tmp++; /* skip whitespace at start of 2nd line */ | 484 | tmp++; /* skip whitespace at start of 2nd line */ |
432 | strcat(last, tmp); | 485 | strcat(last, tmp); |
433 | free(line); | ||
434 | continue; /* possibly more than 2 lines... */ | 486 | continue; /* possibly more than 2 lines... */ |
435 | } else { | 487 | } else { |
436 | MHD_connection_add_header(connection, | 488 | if (MHD_NO == MHD_connection_add_header(connection, |
437 | last, | 489 | last, |
438 | colon, | 490 | colon, |
439 | MHD_HEADER_KIND); | 491 | MHD_HEADER_KIND)) |
440 | free(last); | 492 | return; |
441 | last = NULL; | 493 | last = NULL; |
442 | } | 494 | } |
443 | } | 495 | } |
444 | if (connection->url == NULL) { | 496 | if (connection->url == NULL) { |
445 | /* line must be request line (first line of header) */ | 497 | /* line must be request line (first line of header) */ |
446 | if (MHD_NO == parse_initial_message_line(connection, | 498 | if (MHD_NO == parse_initial_message_line(connection, |
447 | line)) { | 499 | line)) |
448 | free(line); | 500 | goto DIE; |
449 | goto DIE; | ||
450 | } | ||
451 | free(line); | ||
452 | continue; | 501 | continue; |
453 | } | 502 | } |
454 | /* check if this is the end of the header */ | 503 | /* check if this is the end of the header */ |
455 | if (strlen(line) == 0) { | 504 | if (strlen(line) == 0) { |
456 | free(line); | ||
457 | /* end of header */ | 505 | /* end of header */ |
458 | connection->headersReceived = 1; | 506 | connection->headersReceived = 1; |
459 | clen = MHD_lookup_connection_value(connection, | 507 | clen = MHD_lookup_connection_value(connection, |
@@ -483,6 +531,17 @@ MHD_parse_connection_headers(struct MHD_Connection * connection) { | |||
483 | connection->bodyReceived = 0; | 531 | connection->bodyReceived = 0; |
484 | } | 532 | } |
485 | } | 533 | } |
534 | end = MHD_lookup_connection_value(connection, | ||
535 | MHD_HEADER_KIND, | ||
536 | MHD_HTTP_HEADER_CONNECTION); | ||
537 | if ( (end != NULL) && | ||
538 | (0 == strcasecmp(end, | ||
539 | "close")) ) { | ||
540 | /* other side explicitly requested | ||
541 | that we close the connection after | ||
542 | this request */ | ||
543 | connection->read_close = MHD_YES; | ||
544 | } | ||
486 | break; | 545 | break; |
487 | } | 546 | } |
488 | /* line should be normal header line, find colon */ | 547 | /* line should be normal header line, find colon */ |
@@ -507,13 +566,12 @@ MHD_parse_connection_headers(struct MHD_Connection * connection) { | |||
507 | with a space...) */ | 566 | with a space...) */ |
508 | last = line; | 567 | last = line; |
509 | } | 568 | } |
510 | if (last != NULL) { | 569 | if ( (last != NULL) && |
511 | MHD_connection_add_header(connection, | 570 | (MHD_NO == MHD_connection_add_header(connection, |
512 | last, | 571 | last, |
513 | colon, | 572 | colon, |
514 | MHD_HEADER_KIND); | 573 | MHD_HEADER_KIND)) ) |
515 | free(last); | 574 | return; /* error */ |
516 | } | ||
517 | MHD_parse_cookie_header(connection); | 575 | MHD_parse_cookie_header(connection); |
518 | return; | 576 | return; |
519 | DIE: | 577 | DIE: |
@@ -540,6 +598,103 @@ MHD_find_access_handler(struct MHD_Connection * connection) { | |||
540 | } | 598 | } |
541 | 599 | ||
542 | /** | 600 | /** |
601 | * Test if we are able to process the POST data. | ||
602 | * This depends on available memory (enough to load | ||
603 | * all of the POST data into the pool) and the | ||
604 | * content encoding of the POST data. And of course, | ||
605 | * this requires that the request is actually a | ||
606 | * POST request. | ||
607 | * | ||
608 | * @return MHD_YES if so | ||
609 | */ | ||
610 | static int | ||
611 | MHD_test_post_data(struct MHD_Connection * connection) { | ||
612 | const char * encoding; | ||
613 | void * buf; | ||
614 | |||
615 | if (0 != strcasecmp(connection->method, | ||
616 | MHD_HTTP_METHOD_POST)) | ||
617 | return MHD_NO; | ||
618 | encoding = MHD_lookup_connection_value(connection, | ||
619 | MHD_HEADER_KIND, | ||
620 | MHD_HTTP_HEADER_CONTENT_TYPE); | ||
621 | if (encoding == NULL) | ||
622 | return MHD_NO; | ||
623 | if ( (0 == strcasecmp(MHD_HTTP_POST_ENCODING_FORM_URLENCODED, | ||
624 | encoding)) && | ||
625 | (connection->uploadSize != -1) ) { | ||
626 | buf = MHD_pool_reallocate(connection->pool, | ||
627 | connection->read_buffer, | ||
628 | connection->read_buffer_size, | ||
629 | connection->uploadSize + 1); | ||
630 | if (buf == NULL) | ||
631 | return MHD_NO; | ||
632 | connection->read_buffer_size = connection->uploadSize + 1; | ||
633 | connection->read_buffer = buf; | ||
634 | return MHD_YES; | ||
635 | } | ||
636 | return MHD_NO; | ||
637 | } | ||
638 | |||
639 | /** | ||
640 | * Process the POST data here (adding to headers). | ||
641 | * | ||
642 | * Needs to first check POST encoding and then do | ||
643 | * the right thing (TM). The POST data is in the | ||
644 | * connection's post_data buffer between the postPos | ||
645 | * and postLoc offsets. The POST message maybe | ||
646 | * incomplete. The existing buffer (allocated from | ||
647 | * the pool) can be used and modified but must then | ||
648 | * be properly removed from the struct. | ||
649 | * | ||
650 | * @return MHD_YES on success, MHD_NO on error (i.e. out of | ||
651 | * memory). | ||
652 | */ | ||
653 | static int | ||
654 | MHD_parse_post_data(struct MHD_Connection * connection) { | ||
655 | const char * encoding; | ||
656 | int ret; | ||
657 | |||
658 | encoding = MHD_lookup_connection_value(connection, | ||
659 | MHD_HEADER_KIND, | ||
660 | MHD_HTTP_HEADER_CONTENT_TYPE); | ||
661 | if (encoding == NULL) | ||
662 | return MHD_NO; | ||
663 | if (0 == strcasecmp(MHD_HTTP_POST_ENCODING_FORM_URLENCODED, | ||
664 | encoding)) { | ||
665 | ret = parse_arguments(MHD_POSTDATA_KIND, | ||
666 | connection, | ||
667 | connection->read_buffer); | ||
668 | /* invalidate read buffer for other uses -- | ||
669 | in particular, do not give it to the | ||
670 | client; if this were to be needed, we would | ||
671 | have to make a copy, which would double memory | ||
672 | requirements */ | ||
673 | connection->read_buffer_size = 0; | ||
674 | connection->readLoc = 0; | ||
675 | connection->uploadSize = 0; | ||
676 | connection->read_buffer = NULL; | ||
677 | return ret; | ||
678 | } | ||
679 | if (0 == strcasecmp(MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, | ||
680 | encoding)) { | ||
681 | /* this code should never been reached right now, | ||
682 | since the test_post_data function would already | ||
683 | return MHD_NO; code is here only for future | ||
684 | extensions... */ | ||
685 | /* see http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 */ | ||
686 | MHD_DLOG(connection->daemon, | ||
687 | "Unsupported multipart encoding of POST data specified, not processing POST data.\n"); | ||
688 | return MHD_NO; | ||
689 | } | ||
690 | /* this should never be reached, just here for | ||
691 | error checking */ | ||
692 | MHD_DLOG(connection->daemon, | ||
693 | "Unknown encoding of POST data specified, not processing POST data.\n"); | ||
694 | return MHD_NO; | ||
695 | } | ||
696 | |||
697 | /** | ||
543 | * Call the handler of the application for this | 698 | * Call the handler of the application for this |
544 | * connection. | 699 | * connection. |
545 | */ | 700 | */ |
@@ -552,9 +707,6 @@ MHD_call_connection_handler(struct MHD_Connection * connection) { | |||
552 | abort(); /* bad timing... */ | 707 | abort(); /* bad timing... */ |
553 | ah = MHD_find_access_handler(connection); | 708 | ah = MHD_find_access_handler(connection); |
554 | processed = connection->readLoc; | 709 | processed = connection->readLoc; |
555 | /* FIXME: in case of POST, we need to | ||
556 | process the POST data here as well | ||
557 | (adding to the header list! */ | ||
558 | if (MHD_NO == ah->dh(ah->dh_cls, | 710 | if (MHD_NO == ah->dh(ah->dh_cls, |
559 | connection, | 711 | connection, |
560 | connection->url, | 712 | connection->url, |
@@ -564,7 +716,7 @@ MHD_call_connection_handler(struct MHD_Connection * connection) { | |||
564 | &processed)) { | 716 | &processed)) { |
565 | /* serios internal error, close connection */ | 717 | /* serios internal error, close connection */ |
566 | MHD_DLOG(connection->daemon, | 718 | MHD_DLOG(connection->daemon, |
567 | "Internal application error, closing connection."); | 719 | "Internal application error, closing connection.\n"); |
568 | CLOSE(connection->socket_fd); | 720 | CLOSE(connection->socket_fd); |
569 | connection->socket_fd = -1; | 721 | connection->socket_fd = -1; |
570 | return; | 722 | return; |
@@ -583,7 +735,6 @@ MHD_call_connection_handler(struct MHD_Connection * connection) { | |||
583 | connection->bodyReceived = 1; | 735 | connection->bodyReceived = 1; |
584 | connection->readLoc = 0; | 736 | connection->readLoc = 0; |
585 | connection->read_buffer_size = 0; | 737 | connection->read_buffer_size = 0; |
586 | free(connection->read_buffer); | ||
587 | connection->read_buffer = NULL; | 738 | connection->read_buffer = NULL; |
588 | } | 739 | } |
589 | } | 740 | } |
@@ -600,17 +751,30 @@ MHD_connection_handle_read(struct MHD_Connection * connection) { | |||
600 | int bytes_read; | 751 | int bytes_read; |
601 | void * tmp; | 752 | void * tmp; |
602 | 753 | ||
754 | if (connection->pool == NULL) | ||
755 | connection->pool = MHD_pool_create(connection->daemon->pool_size); | ||
756 | if (connection->pool == NULL) { | ||
757 | MHD_DLOG(connection->daemon, | ||
758 | "Failed to create memory pool!\n"); | ||
759 | CLOSE(connection->socket_fd); | ||
760 | connection->socket_fd = -1; | ||
761 | return MHD_NO; | ||
762 | } | ||
603 | if ( (connection->readLoc >= connection->read_buffer_size) && | 763 | if ( (connection->readLoc >= connection->read_buffer_size) && |
604 | (connection->headersReceived == 0) ) { | 764 | (connection->headersReceived == 0) ) { |
605 | /* need to grow read buffer */ | 765 | /* need to grow read buffer */ |
606 | tmp = malloc(connection->read_buffer_size * 2 + MHD_MAX_BUF_SIZE); | 766 | tmp = MHD_pool_reallocate(connection->pool, |
607 | memcpy(tmp, | 767 | connection->read_buffer, |
608 | connection->read_buffer, | 768 | connection->read_buffer_size, |
609 | connection->read_buffer_size); | 769 | connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE); |
610 | connection->read_buffer_size = connection->read_buffer_size * 2 + MHD_MAX_BUF_SIZE; | 770 | if (tmp == NULL) { |
611 | if (connection->read_buffer != NULL) | 771 | MHD_DLOG(connection->daemon, |
612 | free(connection->read_buffer); | 772 | "Not enough memory for reading headers!\n"); |
773 | MHD_excessive_header_handler(connection); | ||
774 | return MHD_NO; | ||
775 | } | ||
613 | connection->read_buffer = tmp; | 776 | connection->read_buffer = tmp; |
777 | connection->read_buffer_size = connection->read_buffer_size * 2 + MHD_BUF_INC_SIZE; | ||
614 | } | 778 | } |
615 | if (connection->readLoc >= connection->read_buffer_size) { | 779 | if (connection->readLoc >= connection->read_buffer_size) { |
616 | MHD_DLOG(connection->daemon, | 780 | MHD_DLOG(connection->daemon, |
@@ -634,16 +798,28 @@ MHD_connection_handle_read(struct MHD_Connection * connection) { | |||
634 | } | 798 | } |
635 | if (bytes_read == 0) { | 799 | if (bytes_read == 0) { |
636 | /* other side closed connection */ | 800 | /* other side closed connection */ |
801 | connection->read_close = MHD_YES; | ||
637 | if (connection->readLoc > 0) | 802 | if (connection->readLoc > 0) |
638 | MHD_call_connection_handler(connection); | 803 | MHD_call_connection_handler(connection); |
639 | shutdown(connection->socket_fd, SHUT_RD); | 804 | shutdown(connection->socket_fd, SHUT_RD); |
640 | return MHD_YES; | 805 | return MHD_YES; |
641 | } | 806 | } |
642 | connection->readLoc += bytes_read; | 807 | connection->readLoc += bytes_read; |
643 | if (connection->headersReceived == 0) | 808 | if (connection->headersReceived == 0) { |
644 | MHD_parse_connection_headers(connection); | 809 | MHD_parse_connection_headers(connection); |
645 | if (connection->headersReceived == 1) | 810 | if (connection->headersReceived == 1) { |
646 | MHD_call_connection_handler(connection); | 811 | connection->post_processed = MHD_test_post_data(connection); |
812 | } | ||
813 | } | ||
814 | if (connection->headersReceived == 1) { | ||
815 | if ( (connection->post_processed == MHD_YES) && | ||
816 | (connection->uploadSize == connection->readLoc) ) | ||
817 | if (MHD_NO == MHD_parse_post_data(connection)) | ||
818 | connection->post_processed = MHD_NO; | ||
819 | if ( (connection->post_processed == MHD_NO) || | ||
820 | (connection->read_buffer_size == connection->readLoc) ) | ||
821 | MHD_call_connection_handler(connection); | ||
822 | } | ||
647 | return MHD_YES; | 823 | return MHD_YES; |
648 | } | 824 | } |
649 | 825 | ||
@@ -666,9 +842,9 @@ MHD_add_extra_headers(struct MHD_Connection * connection) { | |||
666 | } else if (NULL == MHD_get_response_header(connection->response, | 842 | } else if (NULL == MHD_get_response_header(connection->response, |
667 | MHD_HTTP_HEADER_CONTENT_LENGTH)) { | 843 | MHD_HTTP_HEADER_CONTENT_LENGTH)) { |
668 | _REAL_SNPRINTF(buf, | 844 | _REAL_SNPRINTF(buf, |
669 | 128, | 845 | 128, |
670 | "%llu", | 846 | "%llu", |
671 | (unsigned long long) connection->response->total_size); | 847 | (unsigned long long) connection->response->total_size); |
672 | MHD_add_response_header(connection->response, | 848 | MHD_add_response_header(connection->response, |
673 | MHD_HTTP_HEADER_CONTENT_LENGTH, | 849 | MHD_HTTP_HEADER_CONTENT_LENGTH, |
674 | buf); | 850 | buf); |
@@ -680,7 +856,7 @@ MHD_add_extra_headers(struct MHD_Connection * connection) { | |||
680 | * fill it with all of the headers from the | 856 | * fill it with all of the headers from the |
681 | * HTTPd's response. | 857 | * HTTPd's response. |
682 | */ | 858 | */ |
683 | static void | 859 | static int |
684 | MHD_build_header_response(struct MHD_Connection * connection) { | 860 | MHD_build_header_response(struct MHD_Connection * connection) { |
685 | size_t size; | 861 | size_t size; |
686 | size_t off; | 862 | size_t off; |
@@ -702,7 +878,14 @@ MHD_build_header_response(struct MHD_Connection * connection) { | |||
702 | pos = pos->next; | 878 | pos = pos->next; |
703 | } | 879 | } |
704 | /* produce data */ | 880 | /* produce data */ |
705 | data = malloc(size + 1); | 881 | data = MHD_pool_allocate(connection->pool, |
882 | size + 1, | ||
883 | MHD_YES); | ||
884 | if (data == NULL) { | ||
885 | MHD_DLOG(connection->daemon, | ||
886 | "Not enough memory for write!\n"); | ||
887 | return MHD_NO; | ||
888 | } | ||
706 | memcpy(data, | 889 | memcpy(data, |
707 | code, | 890 | code, |
708 | off); | 891 | off); |
@@ -721,7 +904,10 @@ MHD_build_header_response(struct MHD_Connection * connection) { | |||
721 | if (off != size) | 904 | if (off != size) |
722 | abort(); | 905 | abort(); |
723 | connection->write_buffer = data; | 906 | connection->write_buffer = data; |
724 | connection->write_buffer_size = size; | 907 | connection->writeLoc = size; |
908 | connection->writePos = 0; | ||
909 | connection->write_buffer_size = size + 1; | ||
910 | return MHD_YES; | ||
725 | } | 911 | } |
726 | 912 | ||
727 | /** | 913 | /** |
@@ -743,11 +929,16 @@ MHD_connection_handle_write(struct MHD_Connection * connection) { | |||
743 | return MHD_NO; | 929 | return MHD_NO; |
744 | } | 930 | } |
745 | if (! connection->headersSent) { | 931 | if (! connection->headersSent) { |
746 | if (connection->write_buffer == NULL) | 932 | if ( (connection->write_buffer == NULL) && |
747 | MHD_build_header_response(connection); | 933 | (MHD_NO == MHD_build_header_response(connection)) ) { |
934 | /* oops - close! */ | ||
935 | CLOSE(connection->socket_fd); | ||
936 | connection->socket_fd = -1; | ||
937 | return MHD_NO; | ||
938 | } | ||
748 | ret = SEND(connection->socket_fd, | 939 | ret = SEND(connection->socket_fd, |
749 | &connection->write_buffer[connection->writeLoc], | 940 | &connection->write_buffer[connection->writePos], |
750 | connection->write_buffer_size - connection->writeLoc, | 941 | connection->writeLoc - connection->writePos, |
751 | 0); | 942 | 0); |
752 | if (ret < 0) { | 943 | if (ret < 0) { |
753 | if (errno == EINTR) | 944 | if (errno == EINTR) |
@@ -759,13 +950,17 @@ MHD_connection_handle_write(struct MHD_Connection * connection) { | |||
759 | connection->socket_fd = -1; | 950 | connection->socket_fd = -1; |
760 | return MHD_YES; | 951 | return MHD_YES; |
761 | } | 952 | } |
762 | connection->writeLoc += ret; | 953 | connection->writePos += ret; |
763 | if (connection->writeLoc == connection->write_buffer_size) { | 954 | if (connection->writeLoc == connection->writePos) { |
764 | connection->writeLoc = 0; | 955 | connection->writeLoc = 0; |
765 | free(connection->write_buffer); | 956 | connection->writePos = 0; |
957 | connection->headersSent = 1; | ||
958 | MHD_pool_reallocate(connection->pool, | ||
959 | connection->write_buffer, | ||
960 | connection->write_buffer_size, | ||
961 | 0); | ||
766 | connection->write_buffer = NULL; | 962 | connection->write_buffer = NULL; |
767 | connection->write_buffer_size = 0; | 963 | connection->write_buffer_size = 0; |
768 | connection->headersSent = 1; | ||
769 | } | 964 | } |
770 | return MHD_YES; | 965 | return MHD_YES; |
771 | } | 966 | } |
@@ -781,13 +976,13 @@ MHD_connection_handle_write(struct MHD_Connection * connection) { | |||
781 | if (response->data_size == 0) { | 976 | if (response->data_size == 0) { |
782 | if (response->data != NULL) | 977 | if (response->data != NULL) |
783 | free(response->data); | 978 | free(response->data); |
784 | response->data = malloc(MHD_MAX_BUF_SIZE); | 979 | response->data = malloc(MHD_BUF_INC_SIZE); |
785 | response->data_size = MHD_MAX_BUF_SIZE; | 980 | response->data_size = MHD_BUF_INC_SIZE; |
786 | } | 981 | } |
787 | ret = response->crc(response->crc_cls, | 982 | ret = response->crc(response->crc_cls, |
788 | connection->messagePos, | 983 | connection->messagePos, |
789 | response->data, | 984 | response->data, |
790 | MAX(MHD_MAX_BUF_SIZE, | 985 | MAX(MHD_BUF_INC_SIZE, |
791 | response->data_size - connection->messagePos)); | 986 | response->data_size - connection->messagePos)); |
792 | if (ret == -1) { | 987 | if (ret == -1) { |
793 | /* end of message, signal other side by closing! */ | 988 | /* end of message, signal other side by closing! */ |
@@ -838,22 +1033,26 @@ MHD_connection_handle_write(struct MHD_Connection * connection) { | |||
838 | connection->headersSent = 0; | 1033 | connection->headersSent = 0; |
839 | connection->bodyReceived = 0; | 1034 | connection->bodyReceived = 0; |
840 | connection->messagePos = 0; | 1035 | connection->messagePos = 0; |
841 | free(connection->method); | ||
842 | connection->method = NULL; | 1036 | connection->method = NULL; |
843 | free(connection->url); | ||
844 | connection->url = NULL; | 1037 | connection->url = NULL; |
845 | free(connection->write_buffer); | ||
846 | connection->write_buffer = NULL; | ||
847 | connection->write_buffer_size = 0; | ||
848 | if ( (connection->read_close != 0) || | 1038 | if ( (connection->read_close != 0) || |
849 | (0 != strcasecmp(MHD_HTTP_VERSION_1_1, | 1039 | (0 != strcasecmp(MHD_HTTP_VERSION_1_1, |
850 | connection->version)) ) { | 1040 | connection->version)) ) { |
851 | /* closed for reading => close for good! */ | 1041 | /* closed for reading => close for good! */ |
852 | CLOSE(connection->socket_fd); | 1042 | if (connection->socket_fd != -1) |
1043 | CLOSE(connection->socket_fd); | ||
853 | connection->socket_fd = -1; | 1044 | connection->socket_fd = -1; |
854 | } | 1045 | } |
855 | free(connection->version); | ||
856 | connection->version = NULL; | 1046 | connection->version = NULL; |
1047 | connection->read_buffer = NULL; | ||
1048 | connection->write_buffer = NULL; | ||
1049 | connection->read_buffer_size = 0; | ||
1050 | connection->readLoc = 0; | ||
1051 | connection->write_buffer_size = 0; | ||
1052 | connection->writePos = 0; | ||
1053 | connection->writeLoc = 0; | ||
1054 | MHD_pool_destroy(connection->pool); | ||
1055 | connection->pool = NULL; | ||
857 | } | 1056 | } |
858 | return MHD_YES; | 1057 | return MHD_YES; |
859 | } | 1058 | } |
diff --git a/src/daemon/daemon.c b/src/daemon/daemon.c index 39abee6a..48ec9219 100644 --- a/src/daemon/daemon.c +++ b/src/daemon/daemon.c | |||
@@ -28,9 +28,17 @@ | |||
28 | #include "internal.h" | 28 | #include "internal.h" |
29 | #include "response.h" | 29 | #include "response.h" |
30 | #include "connection.h" | 30 | #include "connection.h" |
31 | #include "memorypool.h" | ||
31 | 32 | ||
32 | #define MHD_MAX_CONNECTIONS FD_SETSIZE -4 | 33 | /** |
34 | * Default connection limit. | ||
35 | */ | ||
36 | #define MHD_MAX_CONNECTIONS_DEFAULT FD_SETSIZE -4 | ||
33 | 37 | ||
38 | /** | ||
39 | * Default memory allowed per connection. | ||
40 | */ | ||
41 | #define MHD_POOL_SIZE_DEFAULT (1024 * 1024) | ||
34 | 42 | ||
35 | /** | 43 | /** |
36 | * Register an access handler for all URIs beginning with uri_prefix. | 44 | * Register an access handler for all URIs beginning with uri_prefix. |
@@ -229,6 +237,13 @@ MHD_accept_connection(struct MHD_Daemon * daemon) { | |||
229 | MHD_DLOG(daemon, | 237 | MHD_DLOG(daemon, |
230 | "Error accepting connection: %s\n", | 238 | "Error accepting connection: %s\n", |
231 | STRERROR(errno)); | 239 | STRERROR(errno)); |
240 | if (s != -1) | ||
241 | CLOSE(s); /* just in case */ | ||
242 | return MHD_NO; | ||
243 | } | ||
244 | if (daemon->max_connections == 0) { | ||
245 | /* above connection limit - reject */ | ||
246 | CLOSE(s); | ||
232 | return MHD_NO; | 247 | return MHD_NO; |
233 | } | 248 | } |
234 | if (MHD_NO == daemon->apc(daemon->apc_cls, | 249 | if (MHD_NO == daemon->apc(daemon->apc_cls, |
@@ -241,7 +256,13 @@ MHD_accept_connection(struct MHD_Daemon * daemon) { | |||
241 | memset(connection, | 256 | memset(connection, |
242 | 0, | 257 | 0, |
243 | sizeof(struct MHD_Connection)); | 258 | sizeof(struct MHD_Connection)); |
259 | connection->pool = NULL; | ||
244 | connection->addr = malloc(addrlen); | 260 | connection->addr = malloc(addrlen); |
261 | if (connection->addr == NULL) { | ||
262 | CLOSE(s); | ||
263 | free(connection); | ||
264 | return MHD_NO; | ||
265 | } | ||
245 | memcpy(connection->addr, | 266 | memcpy(connection->addr, |
246 | addr, | 267 | addr, |
247 | addrlen); | 268 | addrlen); |
@@ -258,11 +279,13 @@ MHD_accept_connection(struct MHD_Daemon * daemon) { | |||
258 | STRERROR(errno)); | 279 | STRERROR(errno)); |
259 | free(connection->addr); | 280 | free(connection->addr); |
260 | CLOSE(s); | 281 | CLOSE(s); |
282 | free(connection->addr); | ||
261 | free(connection); | 283 | free(connection); |
262 | return MHD_NO; | 284 | return MHD_NO; |
263 | } | 285 | } |
264 | connection->next = daemon->connections; | 286 | connection->next = daemon->connections; |
265 | daemon->connections = connection; | 287 | daemon->connections = connection; |
288 | daemon->max_connections--; | ||
266 | return MHD_YES; | 289 | return MHD_YES; |
267 | } | 290 | } |
268 | 291 | ||
@@ -281,7 +304,6 @@ static void | |||
281 | MHD_cleanup_connections(struct MHD_Daemon * daemon) { | 304 | MHD_cleanup_connections(struct MHD_Daemon * daemon) { |
282 | struct MHD_Connection * pos; | 305 | struct MHD_Connection * pos; |
283 | struct MHD_Connection * prev; | 306 | struct MHD_Connection * prev; |
284 | struct MHD_HTTP_Header * hpos; | ||
285 | void * unused; | 307 | void * unused; |
286 | 308 | ||
287 | pos = daemon->connections; | 309 | pos = daemon->connections; |
@@ -296,25 +318,12 @@ MHD_cleanup_connections(struct MHD_Daemon * daemon) { | |||
296 | pthread_kill(pos->pid, SIGALRM); | 318 | pthread_kill(pos->pid, SIGALRM); |
297 | pthread_join(pos->pid, &unused); | 319 | pthread_join(pos->pid, &unused); |
298 | } | 320 | } |
299 | free(pos->addr); | ||
300 | if (pos->url != NULL) | ||
301 | free(pos->url); | ||
302 | if (pos->method != NULL) | ||
303 | free(pos->method); | ||
304 | if (pos->write_buffer != NULL) | ||
305 | free(pos->write_buffer); | ||
306 | if (pos->read_buffer != NULL) | ||
307 | free(pos->read_buffer); | ||
308 | while (pos->headers_received != NULL) { | ||
309 | hpos = pos->headers_received; | ||
310 | pos->headers_received = hpos->next; | ||
311 | free(hpos->header); | ||
312 | free(hpos->value); | ||
313 | free(hpos); | ||
314 | } | ||
315 | if (pos->response != NULL) | 321 | if (pos->response != NULL) |
316 | MHD_destroy_response(pos->response); | 322 | MHD_destroy_response(pos->response); |
323 | MHD_pool_destroy(pos->pool); | ||
324 | free(pos->addr); | ||
317 | free(pos); | 325 | free(pos); |
326 | daemon->max_connections++; | ||
318 | if (prev == NULL) | 327 | if (prev == NULL) |
319 | pos = daemon->connections; | 328 | pos = daemon->connections; |
320 | else | 329 | else |
@@ -474,6 +483,8 @@ MHD_start_daemon(unsigned int options, | |||
474 | struct sockaddr_in6 servaddr6; | 483 | struct sockaddr_in6 servaddr6; |
475 | const struct sockaddr * servaddr; | 484 | const struct sockaddr * servaddr; |
476 | socklen_t addrlen; | 485 | socklen_t addrlen; |
486 | va_list ap; | ||
487 | enum MHD_OPTION opt; | ||
477 | 488 | ||
478 | if ((options & MHD_USE_SSL) != 0) | 489 | if ((options & MHD_USE_SSL) != 0) |
479 | return NULL; | 490 | return NULL; |
@@ -549,6 +560,24 @@ MHD_start_daemon(unsigned int options, | |||
549 | retVal->default_handler.dh_cls = dh_cls; | 560 | retVal->default_handler.dh_cls = dh_cls; |
550 | retVal->default_handler.uri_prefix = ""; | 561 | retVal->default_handler.uri_prefix = ""; |
551 | retVal->default_handler.next = NULL; | 562 | retVal->default_handler.next = NULL; |
563 | retVal->max_connections = MHD_MAX_CONNECTIONS_DEFAULT; | ||
564 | retVal->pool_size = MHD_POOL_SIZE_DEFAULT; | ||
565 | va_start(ap, dh_cls); | ||
566 | while (MHD_OPTION_END != (opt = va_arg(ap, enum MHD_OPTION))) { | ||
567 | switch (opt) { | ||
568 | case MHD_OPTION_CONNECTION_MEMORY_LIMIT: | ||
569 | retVal->pool_size = va_arg(ap, unsigned int); | ||
570 | break; | ||
571 | case MHD_OPTION_CONNECTION_LIMIT: | ||
572 | retVal->max_connections = va_arg(ap, unsigned int); | ||
573 | break; | ||
574 | default: | ||
575 | fprintf(stderr, | ||
576 | "Invalid MHD_OPTION argument! (Did you terminate the list with MHD_OPTION_END?)\n"); | ||
577 | abort(); | ||
578 | } | ||
579 | } | ||
580 | va_end(ap); | ||
552 | if ( ( (0 != (options & MHD_USE_THREAD_PER_CONNECTION)) || | 581 | if ( ( (0 != (options & MHD_USE_THREAD_PER_CONNECTION)) || |
553 | (0 != (options & MHD_USE_SELECT_INTERNALLY)) ) && | 582 | (0 != (options & MHD_USE_SELECT_INTERNALLY)) ) && |
554 | (0 != pthread_create(&retVal->pid, | 583 | (0 != pthread_create(&retVal->pid, |
diff --git a/src/daemon/daemontest_post.c b/src/daemon/daemontest_post.c index 9f5f6c6f..3c69dadc 100644 --- a/src/daemon/daemontest_post.c +++ b/src/daemon/daemontest_post.c | |||
@@ -39,6 +39,7 @@ | |||
39 | 39 | ||
40 | #define POST_DATA "name=daniel&project=curl" | 40 | #define POST_DATA "name=daniel&project=curl" |
41 | 41 | ||
42 | static int oneone; | ||
42 | 43 | ||
43 | static int apc_all(void * cls, | 44 | static int apc_all(void * cls, |
44 | const struct sockaddr * addr, | 45 | const struct sockaddr * addr, |
@@ -76,40 +77,36 @@ static int ahc_echo(void * cls, | |||
76 | unsigned int * upload_data_size) { | 77 | unsigned int * upload_data_size) { |
77 | struct MHD_Response * response; | 78 | struct MHD_Response * response; |
78 | int ret; | 79 | int ret; |
80 | const char * r1; | ||
81 | const char * r2; | ||
79 | 82 | ||
80 | if (0 != strcmp("POST", method)) { | 83 | if (0 != strcmp("POST", method)) { |
81 | printf("METHOD: %s\n", method); | 84 | printf("METHOD: %s\n", method); |
82 | return MHD_NO; /* unexpected method */ | 85 | return MHD_NO; /* unexpected method */ |
83 | } | 86 | } |
84 | if ( (*upload_data_size < 24) && | 87 | r1 = MHD_lookup_connection_value(connection, |
85 | (*upload_data_size > 0) ) | 88 | MHD_POSTDATA_KIND, |
86 | return MHD_YES; /* continue */ | 89 | "name"); |
87 | if (*upload_data_size == 24) { | 90 | r2 = MHD_lookup_connection_value(connection, |
88 | *upload_data_size = 0; | 91 | MHD_POSTDATA_KIND, |
89 | if ( (0 != strcmp("daniel", | 92 | "project"); |
90 | MHD_lookup_connection_value(connection, | 93 | if ( (r1 != NULL) && |
91 | MHD_POSTDATA_KIND, | 94 | (r2 != NULL) && |
92 | "name"))) || | 95 | (0 == strcmp("daniel", |
93 | (0 != strcmp("curl", | 96 | r1)) && |
94 | MHD_lookup_connection_value(connection, | 97 | (0 == strcmp("curl", |
95 | MHD_POSTDATA_KIND, | 98 | r2)) ) { |
96 | "project"))) ) { | 99 | response = MHD_create_response_from_data(strlen(url), |
97 | printf("POST DATA not processed correctly!\n"); | 100 | (void*) url, |
98 | return MHD_NO; | 101 | MHD_NO, |
99 | } | 102 | MHD_YES); |
100 | 103 | ret = MHD_queue_response(connection, | |
101 | return MHD_YES; /* continue */ | 104 | MHD_HTTP_OK, |
105 | response); | ||
106 | MHD_destroy_response(response); | ||
107 | return MHD_YES; /* done */ | ||
102 | } | 108 | } |
103 | /* FIXME: check connection headers... */ | 109 | return MHD_YES; |
104 | response = MHD_create_response_from_data(strlen(url), | ||
105 | (void*) url, | ||
106 | MHD_NO, | ||
107 | MHD_YES); | ||
108 | ret = MHD_queue_response(connection, | ||
109 | MHD_HTTP_OK, | ||
110 | response); | ||
111 | MHD_destroy_response(response); | ||
112 | return ret; | ||
113 | } | 110 | } |
114 | 111 | ||
115 | 112 | ||
@@ -118,7 +115,7 @@ static int testInternalPost() { | |||
118 | CURL * c; | 115 | CURL * c; |
119 | char buf[2048]; | 116 | char buf[2048]; |
120 | struct CBC cbc; | 117 | struct CBC cbc; |
121 | 118 | ||
122 | cbc.buf = buf; | 119 | cbc.buf = buf; |
123 | cbc.size = 2048; | 120 | cbc.size = 2048; |
124 | cbc.pos = 0; | 121 | cbc.pos = 0; |
@@ -156,9 +153,14 @@ static int testInternalPost() { | |||
156 | curl_easy_setopt(c, | 153 | curl_easy_setopt(c, |
157 | CURLOPT_TIMEOUT, | 154 | CURLOPT_TIMEOUT, |
158 | 2L); | 155 | 2L); |
159 | curl_easy_setopt(c, | 156 | if (oneone) |
160 | CURLOPT_HTTP_VERSION, | 157 | curl_easy_setopt(c, |
161 | CURL_HTTP_VERSION_1_0); | 158 | CURLOPT_HTTP_VERSION, |
159 | CURL_HTTP_VERSION_1_1); | ||
160 | else | ||
161 | curl_easy_setopt(c, | ||
162 | CURLOPT_HTTP_VERSION, | ||
163 | CURL_HTTP_VERSION_1_0); | ||
162 | curl_easy_setopt(c, | 164 | curl_easy_setopt(c, |
163 | CURLOPT_CONNECTTIMEOUT, | 165 | CURLOPT_CONNECTTIMEOUT, |
164 | 2L); | 166 | 2L); |
@@ -233,9 +235,14 @@ static int testMultithreadedPost() { | |||
233 | curl_easy_setopt(c, | 235 | curl_easy_setopt(c, |
234 | CURLOPT_TIMEOUT, | 236 | CURLOPT_TIMEOUT, |
235 | 2L); | 237 | 2L); |
236 | curl_easy_setopt(c, | 238 | if (oneone) |
237 | CURLOPT_HTTP_VERSION, | 239 | curl_easy_setopt(c, |
238 | CURL_HTTP_VERSION_1_0); | 240 | CURLOPT_HTTP_VERSION, |
241 | CURL_HTTP_VERSION_1_1); | ||
242 | else | ||
243 | curl_easy_setopt(c, | ||
244 | CURLOPT_HTTP_VERSION, | ||
245 | CURL_HTTP_VERSION_1_0); | ||
239 | curl_easy_setopt(c, | 246 | curl_easy_setopt(c, |
240 | CURLOPT_CONNECTTIMEOUT, | 247 | CURLOPT_CONNECTTIMEOUT, |
241 | 2L); | 248 | 2L); |
@@ -321,9 +328,14 @@ static int testExternalPost() { | |||
321 | curl_easy_setopt(c, | 328 | curl_easy_setopt(c, |
322 | CURLOPT_TIMEOUT, | 329 | CURLOPT_TIMEOUT, |
323 | 5L); | 330 | 5L); |
324 | curl_easy_setopt(c, | 331 | if (oneone) |
325 | CURLOPT_HTTP_VERSION, | 332 | curl_easy_setopt(c, |
326 | CURL_HTTP_VERSION_1_0); | 333 | CURLOPT_HTTP_VERSION, |
334 | CURL_HTTP_VERSION_1_1); | ||
335 | else | ||
336 | curl_easy_setopt(c, | ||
337 | CURLOPT_HTTP_VERSION, | ||
338 | CURL_HTTP_VERSION_1_0); | ||
327 | curl_easy_setopt(c, | 339 | curl_easy_setopt(c, |
328 | CURLOPT_CONNECTTIMEOUT, | 340 | CURLOPT_CONNECTTIMEOUT, |
329 | 5L); | 341 | 5L); |
@@ -429,11 +441,12 @@ int main(int argc, | |||
429 | char * const * argv) { | 441 | char * const * argv) { |
430 | unsigned int errorCount = 0; | 442 | unsigned int errorCount = 0; |
431 | 443 | ||
444 | oneone = NULL != strstr(argv[0], "11"); | ||
432 | if (0 != curl_global_init(CURL_GLOBAL_WIN32)) | 445 | if (0 != curl_global_init(CURL_GLOBAL_WIN32)) |
433 | return 2; | 446 | return 2; |
434 | errorCount += testInternalPost(); | 447 | errorCount += testInternalPost(); |
435 | errorCount += testMultithreadedPost(); | 448 | errorCount += testMultithreadedPost(); |
436 | errorCount += testExternalPost(); | 449 | errorCount += testExternalPost(); |
437 | if (errorCount != 0) | 450 | if (errorCount != 0) |
438 | fprintf(stderr, | 451 | fprintf(stderr, |
439 | "Error (code: %u)\n", | 452 | "Error (code: %u)\n", |
diff --git a/src/daemon/daemontest_put.c b/src/daemon/daemontest_put.c index ca64688b..dfcccb0a 100644 --- a/src/daemon/daemontest_put.c +++ b/src/daemon/daemontest_put.c | |||
@@ -32,6 +32,8 @@ | |||
32 | #include <string.h> | 32 | #include <string.h> |
33 | #include <time.h> | 33 | #include <time.h> |
34 | 34 | ||
35 | static int oneone; | ||
36 | |||
35 | static int apc_all(void * cls, | 37 | static int apc_all(void * cls, |
36 | const struct sockaddr * addr, | 38 | const struct sockaddr * addr, |
37 | socklen_t addrlen) { | 39 | socklen_t addrlen) { |
@@ -164,9 +166,14 @@ static int testInternalPut() { | |||
164 | curl_easy_setopt(c, | 166 | curl_easy_setopt(c, |
165 | CURLOPT_TIMEOUT, | 167 | CURLOPT_TIMEOUT, |
166 | 15L); | 168 | 15L); |
167 | curl_easy_setopt(c, | 169 | if (oneone) |
168 | CURLOPT_HTTP_VERSION, | 170 | curl_easy_setopt(c, |
169 | CURL_HTTP_VERSION_1_0); | 171 | CURLOPT_HTTP_VERSION, |
172 | CURL_HTTP_VERSION_1_1); | ||
173 | else | ||
174 | curl_easy_setopt(c, | ||
175 | CURLOPT_HTTP_VERSION, | ||
176 | CURL_HTTP_VERSION_1_0); | ||
170 | curl_easy_setopt(c, | 177 | curl_easy_setopt(c, |
171 | CURLOPT_CONNECTTIMEOUT, | 178 | CURLOPT_CONNECTTIMEOUT, |
172 | 15L); | 179 | 15L); |
@@ -246,9 +253,14 @@ static int testMultithreadedPut() { | |||
246 | curl_easy_setopt(c, | 253 | curl_easy_setopt(c, |
247 | CURLOPT_TIMEOUT, | 254 | CURLOPT_TIMEOUT, |
248 | 15L); | 255 | 15L); |
249 | curl_easy_setopt(c, | 256 | if (oneone) |
250 | CURLOPT_HTTP_VERSION, | 257 | curl_easy_setopt(c, |
251 | CURL_HTTP_VERSION_1_0); | 258 | CURLOPT_HTTP_VERSION, |
259 | CURL_HTTP_VERSION_1_1); | ||
260 | else | ||
261 | curl_easy_setopt(c, | ||
262 | CURLOPT_HTTP_VERSION, | ||
263 | CURL_HTTP_VERSION_1_0); | ||
252 | curl_easy_setopt(c, | 264 | curl_easy_setopt(c, |
253 | CURLOPT_CONNECTTIMEOUT, | 265 | CURLOPT_CONNECTTIMEOUT, |
254 | 15L); | 266 | 15L); |
@@ -339,9 +351,14 @@ static int testExternalPut() { | |||
339 | curl_easy_setopt(c, | 351 | curl_easy_setopt(c, |
340 | CURLOPT_TIMEOUT, | 352 | CURLOPT_TIMEOUT, |
341 | 15L); | 353 | 15L); |
342 | curl_easy_setopt(c, | 354 | if (oneone) |
343 | CURLOPT_HTTP_VERSION, | 355 | curl_easy_setopt(c, |
344 | CURL_HTTP_VERSION_1_0); | 356 | CURLOPT_HTTP_VERSION, |
357 | CURL_HTTP_VERSION_1_1); | ||
358 | else | ||
359 | curl_easy_setopt(c, | ||
360 | CURLOPT_HTTP_VERSION, | ||
361 | CURL_HTTP_VERSION_1_0); | ||
345 | curl_easy_setopt(c, | 362 | curl_easy_setopt(c, |
346 | CURLOPT_CONNECTTIMEOUT, | 363 | CURLOPT_CONNECTTIMEOUT, |
347 | 15L); | 364 | 15L); |
@@ -447,6 +464,7 @@ int main(int argc, | |||
447 | char * const * argv) { | 464 | char * const * argv) { |
448 | unsigned int errorCount = 0; | 465 | unsigned int errorCount = 0; |
449 | 466 | ||
467 | oneone = NULL != strstr(argv[0], "11"); | ||
450 | if (0 != curl_global_init(CURL_GLOBAL_WIN32)) | 468 | if (0 != curl_global_init(CURL_GLOBAL_WIN32)) |
451 | return 2; | 469 | return 2; |
452 | errorCount += testInternalPut(); | 470 | errorCount += testInternalPut(); |
diff --git a/src/daemon/internal.h b/src/daemon/internal.h index 101acfdd..efadb33a 100644 --- a/src/daemon/internal.h +++ b/src/daemon/internal.h | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <errno.h> | 37 | #include <errno.h> |
38 | #include <fcntl.h> | 38 | #include <fcntl.h> |
39 | #include <signal.h> | 39 | #include <signal.h> |
40 | #include <sys/mman.h> | ||
40 | 41 | ||
41 | #include "config.h" | 42 | #include "config.h" |
42 | #include "plibc.h" | 43 | #include "plibc.h" |
@@ -49,8 +50,6 @@ | |||
49 | 50 | ||
50 | #include <pthread.h> | 51 | #include <pthread.h> |
51 | 52 | ||
52 | #define MHD_MAX_BUF_SIZE 2048 | ||
53 | |||
54 | #define MAX(a,b) ((a)<(b)) ? (b) : (a) | 53 | #define MAX(a,b) ((a)<(b)) ? (b) : (a) |
55 | 54 | ||
56 | 55 | ||
@@ -157,41 +156,72 @@ struct MHD_Response { | |||
157 | 156 | ||
158 | 157 | ||
159 | struct MHD_Connection { | 158 | struct MHD_Connection { |
159 | |||
160 | /** | ||
161 | * This is a linked list. | ||
162 | */ | ||
160 | struct MHD_Connection * next; | 163 | struct MHD_Connection * next; |
161 | 164 | ||
165 | /** | ||
166 | * Reference to the MHD_Daemon struct. | ||
167 | */ | ||
162 | struct MHD_Daemon * daemon; | 168 | struct MHD_Daemon * daemon; |
163 | 169 | ||
170 | /** | ||
171 | * Linked list of parsed headers. | ||
172 | */ | ||
164 | struct MHD_HTTP_Header * headers_received; | 173 | struct MHD_HTTP_Header * headers_received; |
165 | 174 | ||
175 | /** | ||
176 | * Response to transmit (initially NULL). | ||
177 | */ | ||
166 | struct MHD_Response * response; | 178 | struct MHD_Response * response; |
167 | 179 | ||
168 | /** | 180 | /** |
169 | * Request method. Should be GET/POST/etc. | 181 | * The memory pool is created whenever we first read |
182 | * from the TCP stream and destroyed at the end of | ||
183 | * each request (and re-created for the next request). | ||
184 | * In the meantime, this pointer is NULL. The | ||
185 | * pool is used for all connection-related data | ||
186 | * except for the response (which maybe shared between | ||
187 | * connections) and the IP address (which persists | ||
188 | * across individual requests). | ||
189 | */ | ||
190 | struct MemoryPool * pool; | ||
191 | |||
192 | /** | ||
193 | * Request method. Should be GET/POST/etc. Allocated | ||
194 | * in pool. | ||
170 | */ | 195 | */ |
171 | char * method; | 196 | char * method; |
172 | 197 | ||
173 | /** | 198 | /** |
174 | * Requested URL (everything after "GET" only). | 199 | * Requested URL (everything after "GET" only). Allocated |
200 | * in pool. | ||
175 | */ | 201 | */ |
176 | char * url; | 202 | char * url; |
177 | 203 | ||
178 | /** | 204 | /** |
179 | * HTTP version string (i.e. http/1.1) | 205 | * HTTP version string (i.e. http/1.1). Allocated |
206 | * in pool. | ||
180 | */ | 207 | */ |
181 | char * version; | 208 | char * version; |
182 | 209 | ||
183 | /** | 210 | /** |
184 | * Buffer for reading requests. | 211 | * Buffer for reading requests. Allocated |
212 | * in pool. | ||
185 | */ | 213 | */ |
186 | char * read_buffer; | 214 | char * read_buffer; |
187 | 215 | ||
188 | /** | 216 | /** |
189 | * Buffer for writing response. | 217 | * Buffer for writing response (headers only). Allocated |
218 | * in pool. | ||
190 | */ | 219 | */ |
191 | char * write_buffer; | 220 | char * write_buffer; |
192 | 221 | ||
193 | /** | 222 | /** |
194 | * Foreign address (of length addr_len). | 223 | * Foreign address (of length addr_len). MALLOCED (not |
224 | * in pool!). | ||
195 | */ | 225 | */ |
196 | struct sockaddr_in * addr; | 226 | struct sockaddr_in * addr; |
197 | 227 | ||
@@ -201,12 +231,30 @@ struct MHD_Connection { | |||
201 | */ | 231 | */ |
202 | pthread_t pid; | 232 | pthread_t pid; |
203 | 233 | ||
234 | /** | ||
235 | * Size of read_buffer (in bytes). | ||
236 | */ | ||
204 | size_t read_buffer_size; | 237 | size_t read_buffer_size; |
205 | 238 | ||
239 | /** | ||
240 | * Position where we currently append data in | ||
241 | * read_buffer (last valid position). | ||
242 | */ | ||
206 | size_t readLoc; | 243 | size_t readLoc; |
207 | 244 | ||
245 | /** | ||
246 | * Size of write_buffer (in bytes). | ||
247 | */ | ||
208 | size_t write_buffer_size; | 248 | size_t write_buffer_size; |
209 | 249 | ||
250 | /** | ||
251 | * Offset where we are with sending from write_buffer. | ||
252 | */ | ||
253 | size_t writePos; | ||
254 | |||
255 | /** | ||
256 | * Last valid location in write_buffer. | ||
257 | */ | ||
210 | size_t writeLoc; | 258 | size_t writeLoc; |
211 | 259 | ||
212 | /** | 260 | /** |
@@ -264,6 +312,11 @@ struct MHD_Connection { | |||
264 | int headersSent; | 312 | int headersSent; |
265 | 313 | ||
266 | /** | 314 | /** |
315 | * Are we processing the POST data? | ||
316 | */ | ||
317 | int post_processed; | ||
318 | |||
319 | /** | ||
267 | * HTTP response code. Only valid if response object | 320 | * HTTP response code. Only valid if response object |
268 | * is already set. | 321 | * is already set. |
269 | */ | 322 | */ |
@@ -279,6 +332,9 @@ struct MHD_Daemon { | |||
279 | 332 | ||
280 | struct MHD_Access_Handler default_handler; | 333 | struct MHD_Access_Handler default_handler; |
281 | 334 | ||
335 | /** | ||
336 | * Linked list of our current connections. | ||
337 | */ | ||
282 | struct MHD_Connection * connections; | 338 | struct MHD_Connection * connections; |
283 | 339 | ||
284 | MHD_AcceptPolicyCallback apc; | 340 | MHD_AcceptPolicyCallback apc; |
@@ -301,11 +357,24 @@ struct MHD_Daemon { | |||
301 | int shutdown; | 357 | int shutdown; |
302 | 358 | ||
303 | /** | 359 | /** |
360 | * Size of the per-connection memory pools. | ||
361 | */ | ||
362 | unsigned int pool_size; | ||
363 | |||
364 | /** | ||
365 | * Limit on the number of parallel connections. | ||
366 | */ | ||
367 | unsigned int max_connections; | ||
368 | |||
369 | /** | ||
304 | * Daemon's options. | 370 | * Daemon's options. |
305 | */ | 371 | */ |
306 | enum MHD_OPTION options; | 372 | enum MHD_OPTION options; |
307 | 373 | ||
308 | unsigned short port; | 374 | /** |
375 | * Listen port. | ||
376 | */ | ||
377 | unsigned short port; | ||
309 | 378 | ||
310 | }; | 379 | }; |
311 | 380 | ||
diff --git a/src/daemon/memorypool.c b/src/daemon/memorypool.c new file mode 100644 index 00000000..5b54cd2e --- /dev/null +++ b/src/daemon/memorypool.c | |||
@@ -0,0 +1,180 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | (C) 2007 Daniel Pittman | ||
4 | |||
5 | libmicrohttpd is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | libmicrohttpd is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with libmicrohttpd; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file memorypool.c | ||
23 | * @brief memory pool | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | |||
27 | #include "memorypool.h" | ||
28 | |||
29 | struct MemoryPool { | ||
30 | |||
31 | /** | ||
32 | * Pointer to the pool's memory | ||
33 | */ | ||
34 | char * memory; | ||
35 | |||
36 | /** | ||
37 | * Size of the pool. | ||
38 | */ | ||
39 | unsigned int size; | ||
40 | |||
41 | /** | ||
42 | * Offset of the first unallocated byte. | ||
43 | */ | ||
44 | unsigned int pos; | ||
45 | |||
46 | /** | ||
47 | * Offset of the last unallocated byte. | ||
48 | */ | ||
49 | unsigned int end; | ||
50 | |||
51 | /** | ||
52 | * 0 if pool was malloc'ed, 1 if mmapped. | ||
53 | */ | ||
54 | int is_mmap; | ||
55 | }; | ||
56 | |||
57 | /** | ||
58 | * Create a memory pool. | ||
59 | * | ||
60 | * @param max maximum size of the pool | ||
61 | */ | ||
62 | struct MemoryPool * MHD_pool_create(unsigned int max) { | ||
63 | struct MemoryPool * pool; | ||
64 | |||
65 | pool = malloc(sizeof(struct MemoryPool)); | ||
66 | if (pool == NULL) | ||
67 | return NULL; | ||
68 | pool->memory = MMAP(NULL, max, PROT_READ | PROT_WRITE, | ||
69 | MAP_ANONYMOUS, -1, 0); | ||
70 | if ( (pool->memory == MAP_FAILED) || | ||
71 | (pool->memory == NULL) ) { | ||
72 | pool->memory = malloc(max); | ||
73 | if (pool->memory == NULL) { | ||
74 | free(pool); | ||
75 | return NULL; | ||
76 | } | ||
77 | pool->is_mmap = 0; | ||
78 | } else { | ||
79 | pool->is_mmap = 1; | ||
80 | } | ||
81 | pool->pos = 0; | ||
82 | pool->end = max; | ||
83 | pool->size = max; | ||
84 | return pool; | ||
85 | } | ||
86 | |||
87 | /** | ||
88 | * Destroy a memory pool. | ||
89 | */ | ||
90 | void MHD_pool_destroy(struct MemoryPool * pool) { | ||
91 | if (pool == NULL) | ||
92 | return; | ||
93 | if (pool->is_mmap == 0) | ||
94 | free(pool->memory); | ||
95 | else | ||
96 | MUNMAP(pool->memory, pool->size); | ||
97 | free(pool); | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * Allocate size bytes from the pool. | ||
102 | * @return NULL if the pool cannot support size more | ||
103 | * bytes | ||
104 | */ | ||
105 | void * MHD_pool_allocate(struct MemoryPool * pool, | ||
106 | unsigned int size, | ||
107 | int from_end) { | ||
108 | void * ret; | ||
109 | |||
110 | if ( (pool->pos + size > pool->end) || | ||
111 | (pool->pos + size < pool->pos) ) | ||
112 | return NULL; | ||
113 | if (from_end == MHD_YES) { | ||
114 | ret = &pool->memory[pool->end - size]; | ||
115 | pool->end -= size; | ||
116 | } else { | ||
117 | ret = &pool->memory[pool->pos]; | ||
118 | pool->pos += size; | ||
119 | } | ||
120 | return ret; | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * Reallocate a block of memory obtained from the pool. | ||
125 | * This is particularly efficient when growing or | ||
126 | * shrinking the block that was last (re)allocated. | ||
127 | * If the given block is not the most recenlty | ||
128 | * (re)allocated block, the memory of the previous | ||
129 | * allocation may be leaked until the pool is | ||
130 | * destroyed (and copying the data maybe required). | ||
131 | * | ||
132 | * @param old the existing block | ||
133 | * @param old_size the size of the existing block | ||
134 | * @param new_size the new size of the block | ||
135 | * @return new address of the block, or | ||
136 | * NULL if the pool cannot support new_size | ||
137 | * bytes (old continues to be valid for old_size) | ||
138 | */ | ||
139 | void * MHD_pool_reallocate(struct MemoryPool * pool, | ||
140 | void * old, | ||
141 | unsigned int old_size, | ||
142 | unsigned int new_size) { | ||
143 | void * ret; | ||
144 | |||
145 | if ( (pool->end < old_size) || | ||
146 | (pool->end < new_size) ) | ||
147 | return NULL; /* unsatisfiable or bogus request */ | ||
148 | |||
149 | if ( (pool->pos >= old_size) && | ||
150 | (&pool->memory[pool->pos - old_size] == old) ) { | ||
151 | /* was the previous allocation - optimize! */ | ||
152 | if (pool->pos + new_size - old_size <= pool->end) { | ||
153 | /* fits */ | ||
154 | pool->pos += new_size - old_size; | ||
155 | if (new_size < old_size) /* shrinking - zero again! */ | ||
156 | memset(&pool->memory[pool->pos], | ||
157 | 0, | ||
158 | old_size - new_size); | ||
159 | return old; | ||
160 | } | ||
161 | /* does not fit */ | ||
162 | return NULL; | ||
163 | } | ||
164 | if (new_size <= old_size) | ||
165 | return old; /* cannot shrink, no need to move */ | ||
166 | if ( (pool->pos + new_size >= pool->pos) && | ||
167 | (pool->pos + new_size <= pool->end) ) { | ||
168 | /* fits */ | ||
169 | ret = &pool->memory[pool->pos]; | ||
170 | memcpy(ret, | ||
171 | old, | ||
172 | old_size); | ||
173 | pool->pos += new_size; | ||
174 | return ret; | ||
175 | } | ||
176 | /* does not fit */ | ||
177 | return NULL; | ||
178 | } | ||
179 | |||
180 | /* end of memorypool.c */ | ||
diff --git a/src/daemon/memorypool.h b/src/daemon/memorypool.h new file mode 100644 index 00000000..c49e0d8e --- /dev/null +++ b/src/daemon/memorypool.h | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | (C) 2007 Daniel Pittman | ||
4 | |||
5 | libmicrohttpd is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published | ||
7 | by the Free Software Foundation; either version 2, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | libmicrohttpd is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with libmicrohttpd; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
18 | Boston, MA 02111-1307, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file memorypool.h | ||
23 | * @brief memory pool; mostly used for efficient (de)allocation | ||
24 | * for each connection and bounding memory use for each | ||
25 | * request | ||
26 | * @author Christian Grothoff | ||
27 | */ | ||
28 | |||
29 | #ifndef MEMORYPOOL_H | ||
30 | #define MEMORYPOOL_H | ||
31 | |||
32 | #include "internal.h" | ||
33 | |||
34 | /** | ||
35 | * Opaque handle for a memory pool. | ||
36 | * Pools are not reentrant and must not be used | ||
37 | * by multiple threads. | ||
38 | */ | ||
39 | struct MemoryPool; | ||
40 | |||
41 | /** | ||
42 | * Create a memory pool. | ||
43 | * | ||
44 | * @param max maximum size of the pool | ||
45 | */ | ||
46 | struct MemoryPool * MHD_pool_create(unsigned int max); | ||
47 | |||
48 | /** | ||
49 | * Destroy a memory pool. | ||
50 | */ | ||
51 | void MHD_pool_destroy(struct MemoryPool * pool); | ||
52 | |||
53 | /** | ||
54 | * Allocate size bytes from the pool. | ||
55 | * | ||
56 | * @param from_end allocate from end of pool (set to MHD_YES); | ||
57 | * use this for small, persistent allocations that | ||
58 | * will never be reallocated | ||
59 | * @return NULL if the pool cannot support size more | ||
60 | * bytes | ||
61 | */ | ||
62 | void * MHD_pool_allocate(struct MemoryPool * pool, | ||
63 | unsigned int size, | ||
64 | int from_end); | ||
65 | |||
66 | /** | ||
67 | * Reallocate a block of memory obtained from the pool. | ||
68 | * This is particularly efficient when growing or | ||
69 | * shrinking the block that was last (re)allocated. | ||
70 | * If the given block is not the most recenlty | ||
71 | * (re)allocated block, the memory of the previous | ||
72 | * allocation may be leaked until the pool is | ||
73 | * destroyed (and copying the data maybe required). | ||
74 | * | ||
75 | * @param old the existing block | ||
76 | * @param old_size the size of the existing block | ||
77 | * @param new_size the new size of the block | ||
78 | * @return new address of the block, or | ||
79 | * NULL if the pool cannot support new_size | ||
80 | * bytes (old continues to be valid for old_size) | ||
81 | */ | ||
82 | void * MHD_pool_reallocate(struct MemoryPool * pool, | ||
83 | void * old, | ||
84 | unsigned int old_size, | ||
85 | unsigned int new_size); | ||
86 | |||
87 | #endif | ||
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h index 01f31ca2..5efaca09 100644 --- a/src/include/microhttpd.h +++ b/src/include/microhttpd.h | |||
@@ -187,7 +187,24 @@ extern "C" { | |||
187 | #define MHD_HTTP_VERSION_1_0 "HTTP/1.0" | 187 | #define MHD_HTTP_VERSION_1_0 "HTTP/1.0" |
188 | #define MHD_HTTP_VERSION_1_1 "HTTP/1.1" | 188 | #define MHD_HTTP_VERSION_1_1 "HTTP/1.1" |
189 | 189 | ||
190 | /** | ||
191 | * HTTP methods | ||
192 | */ | ||
193 | #define MHD_HTTP_METHOD_CONNECT "CONNECT" | ||
194 | #define MHD_HTTP_METHOD_DELETE "DELETE" | ||
195 | #define MHD_HTTP_METHOD_GET "GET" | ||
196 | #define MHD_HTTP_METHOD_HEAD "HEAD" | ||
197 | #define MHD_HTTP_METHOD_OPTIONS "OPTIONS" | ||
198 | #define MHD_HTTP_METHOD_POST "POST" | ||
199 | #define MHD_HTTP_METHOD_PUT "PUT" | ||
200 | #define MHD_HTTP_METHOD_TRACE "TRACE" | ||
190 | 201 | ||
202 | /** | ||
203 | * HTTP POST encodings, see also | ||
204 | * http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 | ||
205 | */ | ||
206 | #define MHD_HTTP_POST_ENCODING_FORM_URLENCODED "application/x-www-form-urlencoded" | ||
207 | #define MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA "multipart/form-data" | ||
191 | 208 | ||
192 | /** | 209 | /** |
193 | * Options for the MHD daemon. Note that if neither | 210 | * Options for the MHD daemon. Note that if neither |
@@ -248,8 +265,16 @@ enum MHD_OPTION { | |||
248 | MHD_OPTION_END = 0, | 265 | MHD_OPTION_END = 0, |
249 | 266 | ||
250 | /** | 267 | /** |
251 | * FIXME: add options for buffer sizes here... | 268 | * Maximum memory size per connection (followed by an |
269 | * unsigned int). | ||
270 | */ | ||
271 | MHD_OPTION_CONNECTION_MEMORY_LIMIT = 1, | ||
272 | |||
273 | /** | ||
274 | * Maximum number of concurrenct connections to | ||
275 | * accept (followed by an unsigned int). | ||
252 | */ | 276 | */ |
277 | MHD_OPTION_CONNECTION_LIMIT = 2, | ||
253 | 278 | ||
254 | }; | 279 | }; |
255 | 280 | ||
@@ -265,22 +290,28 @@ enum MHD_ValueKind { | |||
265 | MHD_RESPONSE_HEADER_KIND = 0, | 290 | MHD_RESPONSE_HEADER_KIND = 0, |
266 | 291 | ||
267 | /** | 292 | /** |
268 | * HTTP header | 293 | * HTTP header. |
269 | */ | 294 | */ |
270 | MHD_HEADER_KIND = 1, | 295 | MHD_HEADER_KIND = 1, |
271 | 296 | ||
272 | /** | 297 | /** |
273 | * Cookies | 298 | * Cookies. Note that the original HTTP header containing |
299 | * the cookie(s) will still be available and intact. | ||
274 | */ | 300 | */ |
275 | MHD_COOKIE_KIND = 2, | 301 | MHD_COOKIE_KIND = 2, |
276 | 302 | ||
277 | /** | 303 | /** |
278 | * POST data | 304 | * POST data. This is available only if a content encoding |
305 | * supported by MHD is used (currently only URL encoding), | ||
306 | * and only if the posted content fits within the available | ||
307 | * memory pool. Note that in that case, the upload data | ||
308 | * given to the MHD_AccessHandlerCallback will be | ||
309 | * empty (since it has already been processed). | ||
279 | */ | 310 | */ |
280 | MHD_POSTDATA_KIND = 4, | 311 | MHD_POSTDATA_KIND = 4, |
281 | 312 | ||
282 | /** | 313 | /** |
283 | * GET (URI) arguments | 314 | * GET (URI) arguments. |
284 | */ | 315 | */ |
285 | MHD_GET_ARGUMENT_KIND = 8, | 316 | MHD_GET_ARGUMENT_KIND = 8, |
286 | 317 | ||
@@ -326,9 +357,16 @@ typedef int | |||
326 | * @param url the requested url | 357 | * @param url the requested url |
327 | * @param method the HTTP method used ("GET", "PUT", etc.) | 358 | * @param method the HTTP method used ("GET", "PUT", etc.) |
328 | * @param version the HTTP version string (i.e. "HTTP/1.1") | 359 | * @param version the HTTP version string (i.e. "HTTP/1.1") |
360 | * @param upload_data the data being uploaded (excluding HEADERS, | ||
361 | * for a POST that fits into memory and that is encoded | ||
362 | * with a supported encoding, the POST data will NOT be | ||
363 | * given in upload_data and is instead available as | ||
364 | * part of MHD_get_connection_values; very large POST | ||
365 | * data *will* be made available incrementally in | ||
366 | * upload_data) | ||
329 | * @param upload_data_size set initially to the size of the | 367 | * @param upload_data_size set initially to the size of the |
330 | * upload_data provided; the method must update this | 368 | * upload_data provided; the method must update this |
331 | * value to the number of bytes NOT processed | 369 | * value to the number of bytes NOT processed; |
332 | * @return MHS_YES if the connection was handled successfully, | 370 | * @return MHS_YES if the connection was handled successfully, |
333 | * MHS_NO if the socket must be closed due to a serios | 371 | * MHS_NO if the socket must be closed due to a serios |
334 | * error while handling the request | 372 | * error while handling the request |