diff options
Diffstat (limited to 'src/daemon/connection.c')
-rw-r--r-- | src/daemon/connection.c | 483 |
1 files changed, 341 insertions, 142 deletions
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 | } |