aboutsummaryrefslogtreecommitdiff
path: root/src/daemon
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2007-08-08 08:07:49 +0000
committerChristian Grothoff <christian@grothoff.org>2007-08-08 08:07:49 +0000
commit0cacf72380222e704cff8c05b329ba6bac41f631 (patch)
tree5fb26264b53a285aece3bb0912645da2275a4715 /src/daemon
parent5b7eec5d2d858dd33aa7f65a7655de1410154f0a (diff)
downloadlibmicrohttpd-0cacf72380222e704cff8c05b329ba6bac41f631.tar.gz
libmicrohttpd-0cacf72380222e704cff8c05b329ba6bac41f631.zip
updates
Diffstat (limited to 'src/daemon')
-rw-r--r--src/daemon/Makefile.am20
-rw-r--r--src/daemon/connection.c483
-rw-r--r--src/daemon/daemon.c65
-rw-r--r--src/daemon/daemontest_post.c91
-rw-r--r--src/daemon/daemontest_put.c36
-rw-r--r--src/daemon/internal.h87
-rw-r--r--src/daemon/memorypool.c180
-rw-r--r--src/daemon/memorypool.h87
8 files changed, 829 insertions, 220 deletions
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
36TESTS = $(check_PROGRAMS) 39TESTS = $(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
50daemontest_post_SOURCES = \ 52daemontest_post_SOURCES = \
51 daemontest_post.c 53 daemontest_post.c
52daemontest_post_LDADD = \ 54daemontest_post_LDADD = \
53 $(top_builddir)/src/daemon/libmicrohttpd.la \ 55 $(top_builddir)/src/daemon/libmicrohttpd.la \
54 @LIBCURL@ 56 @LIBCURL@
55 57
56
57daemontest_put_SOURCES = \ 58daemontest_put_SOURCES = \
58 daemontest_put.c 59 daemontest_put.c
59daemontest_put_LDADD = \ 60daemontest_put_LDADD = \
60 $(top_builddir)/src/daemon/libmicrohttpd.la \ 61 $(top_builddir)/src/daemon/libmicrohttpd.la \
61 @LIBCURL@ 62 @LIBCURL@
62 63
64daemontest_post11_SOURCES = \
65 daemontest_post.c
66daemontest_post11_LDADD = \
67 $(top_builddir)/src/daemon/libmicrohttpd.la \
68 @LIBCURL@
69
70
71daemontest_put11_SOURCES = \
72 daemontest_put.c
73daemontest_put11_LDADD = \
74 $(top_builddir)/src/daemon/libmicrohttpd.la \
75 @LIBCURL@
76
63endif \ No newline at end of file 77endif \ 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 */
41int 47int
42MHD_get_connection_values(struct MHD_Connection * connection, 48MHD_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 */
185static void
186MHD_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 */
160static char * 204static char *
161MHD_get_next_header_line(struct MHD_Connection * connection) { 205MHD_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
215static void 244/**
245 * @return MHD_NO on failure (out of memory), MHD_YES for success
246 */
247static int
216MHD_connection_add_header(struct MHD_Connection * connection, 248MHD_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
256static void 297/**
298 * @return MHD_NO on failure (out of memory), MHD_YES for success
299 */
300static int
257parse_arguments(enum MHD_ValueKind kind, 301parse_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 */
287static void 335static int
288MHD_parse_cookie_header(struct MHD_Connection * connection) { 336MHD_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 */
610static int
611MHD_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 */
653static int
654MHD_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 */
683static void 859static int
684MHD_build_header_response(struct MHD_Connection * connection) { 860MHD_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
281MHD_cleanup_connections(struct MHD_Daemon * daemon) { 304MHD_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
42static int oneone;
42 43
43static int apc_all(void * cls, 44static 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
35static int oneone;
36
35static int apc_all(void * cls, 37static 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
159struct MHD_Connection { 158struct 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
29struct 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 */
62struct 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 */
90void 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 */
105void * 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 */
139void * 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 */
39struct MemoryPool;
40
41/**
42 * Create a memory pool.
43 *
44 * @param max maximum size of the pool
45 */
46struct MemoryPool * MHD_pool_create(unsigned int max);
47
48/**
49 * Destroy a memory pool.
50 */
51void 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 */
62void * 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 */
82void * MHD_pool_reallocate(struct MemoryPool * pool,
83 void * old,
84 unsigned int old_size,
85 unsigned int new_size);
86
87#endif