stream_funcs.c (39688B)
1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ 2 /* 3 This file is part of GNU libmicrohttpd. 4 Copyright (C) 2022-2026 Evgeny Grin (Karlson2k) 5 6 GNU libmicrohttpd is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 GNU libmicrohttpd is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 Alternatively, you can redistribute GNU libmicrohttpd and/or 17 modify it under the terms of the GNU General Public License as 18 published by the Free Software Foundation; either version 2 of 19 the License, or (at your option) any later version, together 20 with the eCos exception, as follows: 21 22 As a special exception, if other files instantiate templates or 23 use macros or inline functions from this file, or you compile this 24 file and link it with other works to produce a work based on this 25 file, this file does not by itself cause the resulting work to be 26 covered by the GNU General Public License. However the source code 27 for this file must still be made available in accordance with 28 section (3) of the GNU General Public License v2. 29 30 This exception does not invalidate any other reasons why a work 31 based on this file might be covered by the GNU General Public 32 License. 33 34 You should have received copies of the GNU Lesser General Public 35 License and the GNU General Public License along with this library; 36 if not, see <https://www.gnu.org/licenses/>. 37 */ 38 39 /** 40 * @file src/mhd2/stream_funcs.c 41 * @brief The definition of the stream internal functions 42 * @author Karlson2k (Evgeny Grin) 43 */ 44 45 #include "mhd_sys_options.h" 46 47 #include "stream_funcs.h" 48 49 #include "mhd_assert.h" 50 #include "mhd_unreachable.h" 51 52 #ifdef MHD_USE_TRACE_CONN_ADD_CLOSE 53 # include <stdio.h> 54 #endif /* MHD_USE_TRACE_CONN_ADD_CLOSE */ 55 #include "mhd_dbg_print.h" 56 #include <string.h> 57 #include "extr_events_funcs.h" 58 #ifdef MHD_SUPPORT_EPOLL 59 # include <sys/epoll.h> 60 #endif 61 #include "sys_kqueue.h" 62 #include "sys_malloc.h" 63 64 #include "mhd_daemon.h" 65 #include "mhd_connection.h" 66 #include "mhd_response.h" 67 #include "mempool_funcs.h" 68 #include "mhd_str.h" 69 #include "mhd_str_macros.h" 70 71 #include "mhd_sockets_funcs.h" 72 73 #include "request_get_value.h" 74 #include "response_destroy.h" 75 #include "mhd_mono_clock.h" 76 #include "daemon_logger.h" 77 #include "daemon_funcs.h" 78 #include "conn_timeout.h" 79 #include "conn_mark_ready.h" 80 #include "stream_process_reply.h" 81 #include "extr_events_funcs.h" 82 83 #ifdef MHD_SUPPORT_HTTPS 84 # include "mhd_tls_funcs.h" 85 #endif 86 87 #include "mhd_public_api.h" 88 89 90 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void * 91 mhd_stream_alloc_memory (struct MHD_Connection *restrict c, 92 size_t size) 93 { 94 struct mhd_MemoryPool *const restrict pool = c->pool; /* a short alias */ 95 size_t need_to_be_freed = 0; /**< The required amount of additional free memory */ 96 void *res; 97 98 res = mhd_pool_try_alloc (pool, 99 size, 100 &need_to_be_freed); 101 if (NULL != res) 102 return res; 103 104 if (mhd_pool_is_resizable_inplace (pool, 105 c->write_buffer, 106 c->write_buffer_size)) 107 { 108 if (c->write_buffer_size - c->write_buffer_append_offset >= 109 need_to_be_freed) 110 { 111 char *buf; 112 const size_t new_buf_size = c->write_buffer_size - need_to_be_freed; 113 buf = (char *) mhd_pool_reallocate (pool, 114 c->write_buffer, 115 c->write_buffer_size, 116 new_buf_size); 117 mhd_assert (c->write_buffer == buf); 118 mhd_assert (c->write_buffer_append_offset <= new_buf_size); 119 mhd_assert (c->write_buffer_send_offset <= new_buf_size); 120 c->write_buffer_size = new_buf_size; 121 c->write_buffer = buf; 122 } 123 else 124 return NULL; 125 } 126 else if (mhd_pool_is_resizable_inplace (pool, 127 c->read_buffer, 128 c->read_buffer_size)) 129 { 130 if (c->read_buffer_size - c->read_buffer_offset >= need_to_be_freed) 131 { 132 char *buf; 133 const size_t new_buf_size = c->read_buffer_size - need_to_be_freed; 134 buf = (char *) mhd_pool_reallocate (pool, 135 c->read_buffer, 136 c->read_buffer_size, 137 new_buf_size); 138 mhd_assert (c->read_buffer == buf); 139 mhd_assert (c->read_buffer_offset <= new_buf_size); 140 c->read_buffer_size = new_buf_size; 141 c->read_buffer = buf; 142 } 143 else 144 return NULL; 145 } 146 else 147 return NULL; 148 res = mhd_pool_allocate (pool, size, true); 149 mhd_assert (NULL != res); /* It has been checked that pool has enough space */ 150 return res; 151 } 152 153 154 /** 155 * Shrink stream read buffer to the zero size of free space in the buffer 156 * @param c the connection whose read buffer is being manipulated 157 */ 158 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 159 mhd_stream_shrink_read_buffer (struct MHD_Connection *restrict c) 160 { 161 void *new_buf; 162 163 if ((NULL == c->read_buffer) || (0 == c->read_buffer_size)) 164 { 165 mhd_assert (0 == c->read_buffer_size); 166 mhd_assert (0 == c->read_buffer_offset); 167 return; 168 } 169 170 mhd_assert (c->read_buffer_offset <= c->read_buffer_size); 171 if (0 == c->read_buffer_offset) 172 { 173 mhd_pool_deallocate (c->pool, c->read_buffer, c->read_buffer_size); 174 c->read_buffer = NULL; 175 c->read_buffer_size = 0; 176 } 177 else 178 { 179 mhd_assert (mhd_pool_is_resizable_inplace (c->pool, c->read_buffer, \ 180 c->read_buffer_size)); 181 new_buf = mhd_pool_reallocate (c->pool, c->read_buffer, c->read_buffer_size, 182 c->read_buffer_offset); 183 mhd_assert (c->read_buffer == new_buf); 184 c->read_buffer = (char *) new_buf; 185 c->read_buffer_size = c->read_buffer_offset; 186 } 187 } 188 189 190 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ size_t 191 mhd_stream_maximize_write_buffer (struct MHD_Connection *restrict c) 192 { 193 struct mhd_MemoryPool *const restrict pool = c->pool; 194 void *new_buf; 195 size_t new_size; 196 size_t free_size; 197 198 mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size)); 199 mhd_assert (c->write_buffer_append_offset >= c->write_buffer_send_offset); 200 mhd_assert (c->write_buffer_size >= c->write_buffer_append_offset); 201 202 free_size = mhd_pool_get_free (pool); 203 if (0 != free_size) 204 { 205 new_size = c->write_buffer_size + free_size; 206 /* This function must not move the buffer position. 207 * mhd_pool_reallocate () may return the new position only if buffer was 208 * allocated 'from_end' or is not the last allocation, 209 * which should not happen. */ 210 mhd_assert ((NULL == c->write_buffer) || \ 211 mhd_pool_is_resizable_inplace (pool, c->write_buffer, \ 212 c->write_buffer_size)); 213 new_buf = mhd_pool_reallocate (pool, 214 c->write_buffer, 215 c->write_buffer_size, 216 new_size); 217 mhd_assert ((c->write_buffer == new_buf) || (NULL == c->write_buffer)); 218 c->write_buffer = (char *) new_buf; 219 c->write_buffer_size = new_size; 220 if (c->write_buffer_send_offset == c->write_buffer_append_offset) 221 { 222 /* All data have been sent, reset offsets to zero. */ 223 c->write_buffer_send_offset = 0; 224 c->write_buffer_append_offset = 0; 225 } 226 } 227 228 return c->write_buffer_size - c->write_buffer_append_offset; 229 } 230 231 232 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 233 mhd_stream_release_write_buffer (struct MHD_Connection *restrict c) 234 { 235 struct mhd_MemoryPool *const restrict pool = c->pool; 236 237 mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size)); 238 mhd_assert (c->write_buffer_append_offset == c->write_buffer_send_offset); 239 mhd_assert (c->write_buffer_size >= c->write_buffer_append_offset); 240 241 mhd_pool_deallocate (pool, c->write_buffer, c->write_buffer_size); 242 c->write_buffer_send_offset = 0; 243 c->write_buffer_append_offset = 0; 244 c->write_buffer_size = 0; 245 c->write_buffer = NULL; 246 247 } 248 249 250 #ifndef MHD_MAX_REASONABLE_HEADERS_SIZE_ 251 /** 252 * A reasonable headers size (excluding request line) that should be sufficient 253 * for most requests. 254 * If incoming data buffer free space is not enough to process the complete 255 * header (the request line and all headers) and the headers size is larger than 256 * this size then the status code 431 "Request Header Fields Too Large" is 257 * returned to the client. 258 * The larger headers are processed by MHD if enough space is available. 259 */ 260 # define MHD_MAX_REASONABLE_HEADERS_SIZE_ (6 * 1024) 261 #endif /* ! MHD_MAX_REASONABLE_HEADERS_SIZE_ */ 262 263 #ifndef MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 264 /** 265 * A reasonable request target (the request URI) size that should be sufficient 266 * for most requests. 267 * If incoming data buffer free space is not enough to process the complete 268 * header (the request line and all headers) and the request target size is 269 * larger than this size then the status code 414 "URI Too Long" is 270 * returned to the client. 271 * The larger request targets are processed by MHD if enough space is available. 272 * The value chosen according to RFC 9112 Section 3, paragraph 5 273 */ 274 # define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 8000 275 #endif /* ! MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ */ 276 277 #ifndef MHD_MIN_REASONABLE_HEADERS_SIZE_ 278 /** 279 * A reasonable headers size (excluding request line) that should be sufficient 280 * for basic simple requests. 281 * When no space left in the receiving buffer try to avoid replying with 282 * the status code 431 "Request Header Fields Too Large" if headers size 283 * is smaller then this value. 284 */ 285 # define MHD_MIN_REASONABLE_HEADERS_SIZE_ 26 286 #endif /* ! MHD_MIN_REASONABLE_HEADERS_SIZE_ */ 287 288 #ifndef MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 289 /** 290 * A reasonable request target (the request URI) size that should be sufficient 291 * for basic simple requests. 292 * When no space left in the receiving buffer try to avoid replying with 293 * the status code 414 "URI Too Long" if the request target size is smaller then 294 * this value. 295 */ 296 # define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 40 297 #endif /* ! MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ */ 298 299 #ifndef MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 300 /** 301 * A reasonable request method string size that should be sufficient 302 * for basic simple requests. 303 * When no space left in the receiving buffer try to avoid replying with 304 * the status code 501 "Not Implemented" if the request method size is 305 * smaller then this value. 306 */ 307 # define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 16 308 #endif /* ! MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ */ 309 310 #ifndef MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 311 /** 312 * A reasonable minimal chunk line length. 313 * When no space left in the receiving buffer reply with 413 "Content Too Large" 314 * if the chunk line length is larger than this value. 315 */ 316 # define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 4 317 #endif /* ! MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ */ 318 319 320 MHD_INTERNAL 321 MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_IN_SIZE_ (4,3) unsigned int 322 mhd_stream_get_no_space_err_status_code (struct MHD_Connection *restrict c, 323 enum MHD_ProcRecvDataStage stage, 324 size_t add_element_size, 325 const char *restrict add_element) 326 { 327 size_t method_size; 328 size_t uri_size; 329 size_t opt_headers_size; 330 size_t host_field_line_size; 331 332 mhd_assert ((0 == add_element_size) || (NULL != add_element)); 333 334 c->rq.too_large = true; 335 336 if (mhd_HTTP_STAGE_REQ_LINE_RECEIVED < c->stage) 337 { 338 if (mhd_HTTP_STAGE_HEADERS_RECEIVED > c->stage) 339 { 340 mhd_assert (NULL != c->rq.field_lines.start); 341 opt_headers_size = 342 (size_t) ((c->read_buffer + c->read_buffer_offset) 343 - c->rq.field_lines.start); 344 } 345 else 346 opt_headers_size = c->rq.field_lines.size; 347 } 348 else 349 opt_headers_size = 0u; 350 351 /* The read buffer is fully used by the request line, the field lines 352 (headers) and internal information. 353 The return status code works as a suggestion for the client to reduce 354 one of the request elements. */ 355 356 if ((MHD_PROC_RECV_BODY_CHUNKED == stage) && 357 (MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ < add_element_size)) 358 { 359 /* Request could be re-tried easily with smaller chunk sizes */ 360 return MHD_HTTP_STATUS_CONTENT_TOO_LARGE; 361 } 362 363 host_field_line_size = 0; 364 /* The "Host:" field line is mandatory. 365 The total size of the field lines (headers) cannot be smaller than 366 the size of the "Host:" field line. */ 367 if ((MHD_PROC_RECV_HEADERS == stage) 368 && (0 != add_element_size)) 369 { 370 static const size_t header_host_key_len = 371 mhd_SSTR_LEN (MHD_HTTP_HEADER_HOST); 372 const bool is_host_header = 373 (header_host_key_len + 1 <= add_element_size) 374 && ( (0 == add_element[header_host_key_len]) 375 || (':' == add_element[header_host_key_len]) ) 376 && mhd_str_equal_caseless_bin_n (MHD_HTTP_HEADER_HOST, 377 add_element, 378 header_host_key_len); 379 if (is_host_header) 380 { 381 const bool is_parsed = ! ( 382 (mhd_HTTP_STAGE_HEADERS_RECEIVED > c->stage) && 383 (add_element_size == c->read_buffer_offset) && 384 (c->read_buffer == add_element) ); 385 size_t actual_element_size; 386 387 mhd_assert (! is_parsed || (0 == add_element[header_host_key_len])); 388 /* The actual size should be larger due to CRLF or LF chars, 389 however the exact termination sequence is not known here and 390 as perfect precision is not required, to simplify the code 391 assume the minimal length. */ 392 if (is_parsed) 393 actual_element_size = add_element_size + 1; /* "1" for LF */ 394 else 395 actual_element_size = add_element_size; 396 397 host_field_line_size = actual_element_size; 398 mhd_assert (opt_headers_size >= actual_element_size); 399 opt_headers_size -= actual_element_size; 400 } 401 } 402 if (0 == host_field_line_size) 403 { 404 static const size_t host_field_name_len = 405 mhd_SSTR_LEN (MHD_HTTP_HEADER_HOST); 406 struct MHD_StringNullable host_value; 407 408 if (mhd_request_get_value_n (&(c->rq), 409 MHD_VK_HEADER, 410 host_field_name_len, 411 MHD_HTTP_HEADER_HOST, 412 &host_value)) 413 { 414 /* Calculate the minimal size of the field line: no space between 415 colon and the field value, line terminated by LR */ 416 host_field_line_size = 417 host_field_name_len + host_value.len + 2; /* "2" for ':' and LF */ 418 419 /* The "Host:" field could be added by application */ 420 if (opt_headers_size >= host_field_line_size) 421 { 422 opt_headers_size -= host_field_line_size; 423 /* Take into account typical space after colon and CR at the end of the line */ 424 if (opt_headers_size >= 2) 425 opt_headers_size -= 2; 426 } 427 else 428 host_field_line_size = 0; /* No "Host:" field line set by the client */ 429 } 430 } 431 432 uri_size = c->rq.req_target_len; 433 if (mhd_HTTP_METHOD_OTHER != c->rq.http_mthd) 434 method_size = 0; /* Do not recommend shorter request method */ 435 else 436 { 437 mhd_assert (NULL != c->rq.method.cstr); 438 method_size = c->rq.method.len; 439 mhd_assert (method_size == strlen (c->rq.method.cstr)); 440 } 441 442 if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size) 443 { 444 /* Typically the easiest way to reduce request header size is 445 a removal of some optional headers. */ 446 if (opt_headers_size > (uri_size / 8)) 447 { 448 if ((opt_headers_size / 2) > method_size) 449 return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE; 450 else 451 return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */ 452 } 453 else 454 { /* Request target is MUCH larger than headers */ 455 if ((uri_size / 16) > method_size) 456 return MHD_HTTP_STATUS_URI_TOO_LONG; 457 else 458 return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */ 459 } 460 } 461 if ((size_t) MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ < uri_size) 462 { 463 /* If request target size if larger than maximum reasonable size 464 recommend client to reduce the request target size (length). */ 465 if ((uri_size / 16) > method_size) 466 return MHD_HTTP_STATUS_URI_TOO_LONG; /* Request target is MUCH larger than headers */ 467 else 468 return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */ 469 } 470 471 /* The read buffer is too small to handle reasonably large requests */ 472 473 if ((size_t) MHD_MIN_REASONABLE_HEADERS_SIZE_ < opt_headers_size) 474 { 475 /* Recommend application to retry with minimal headers */ 476 if ((opt_headers_size * 4) > uri_size) 477 { 478 if (opt_headers_size > method_size) 479 return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE; 480 else 481 return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */ 482 } 483 else 484 { /* Request target is significantly larger than headers */ 485 if (uri_size > method_size * 4) 486 return MHD_HTTP_STATUS_URI_TOO_LONG; 487 else 488 return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */ 489 } 490 } 491 if ((size_t) MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ < uri_size) 492 { 493 /* Recommend application to retry with a shorter request target */ 494 if (uri_size > method_size * 4) 495 return MHD_HTTP_STATUS_URI_TOO_LONG; 496 else 497 return MHD_HTTP_STATUS_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */ 498 } 499 500 if ((size_t) MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ < method_size) 501 { 502 /* The request target (URI) and headers are (reasonably) very small. 503 Some non-standard long request method is used. */ 504 /* The last resort response as it means "the method is not supported 505 by the server for any URI". */ 506 return MHD_HTTP_STATUS_NOT_IMPLEMENTED; 507 } 508 509 /* The almost impossible situation: all elements are small, but cannot 510 fit the buffer. The application set the buffer size to 511 critically low value? */ 512 513 if ((1 < opt_headers_size) || (1 < uri_size)) 514 { 515 if (opt_headers_size >= uri_size) 516 return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE; 517 else 518 return MHD_HTTP_STATUS_URI_TOO_LONG; 519 } 520 521 /* Nothing to reduce in the request. 522 Reply with some status. */ 523 if (0 != host_field_line_size) 524 return MHD_HTTP_STATUS_REQUEST_HEADER_FIELDS_TOO_LARGE; 525 526 return MHD_HTTP_STATUS_URI_TOO_LONG; 527 } 528 529 530 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 531 mhd_stream_switch_from_recv_to_send (struct MHD_Connection *c) 532 { 533 /* Read buffer is not needed for this request, shrink it.*/ 534 mhd_stream_shrink_read_buffer (c); 535 } 536 537 538 /** 539 * Finish request serving. 540 * The stream will be re-used or closed. 541 * 542 * @param c the connection to use. 543 */ 544 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 545 mhd_stream_finish_req_serving (struct MHD_Connection *restrict c, 546 bool reuse) 547 { 548 struct MHD_Daemon *const restrict d = c->daemon; 549 550 if (! reuse) 551 { 552 mhd_assert (! c->stop_with_error || (NULL == c->rp.response) || \ 553 (c->rp.response->cfg.int_err_resp)); 554 555 /* Next function will notify client and set connection 556 * state to "PRE-CLOSING" */ 557 /* Later response and memory pool will be destroyed */ 558 mhd_conn_start_closing (c, 559 c->stop_with_error ? 560 mhd_CONN_CLOSE_ERR_REPLY_SENT : 561 mhd_CONN_CLOSE_HTTP_COMPLETED, 562 NULL); 563 } 564 else 565 { 566 /* Reset connection to process the next request */ 567 size_t new_read_buf_size; 568 mhd_assert (! c->stop_with_error); 569 mhd_assert (! c->discard_request); 570 mhd_assert (NULL == c->rq.cntn.lbuf.data); 571 572 #if 0 // TODO: notification callback 573 if ( (NULL != d->notify_completed) && 574 (c->rq.app_aware) ) 575 d->notify_completed (d->notify_completed_cls, 576 c, 577 &c->rq.app_context, 578 MHD_REQUEST_ENDED_COMPLETED_OK); 579 c->rq.app_aware = false; 580 #endif 581 582 mhd_stream_call_dcc_cleanup_if_needed (c); 583 if (NULL != c->rp.resp_iov.iov) 584 { 585 free (c->rp.resp_iov.iov); 586 c->rp.resp_iov.iov = NULL; 587 } 588 589 if (NULL != c->rp.response) 590 mhd_response_dec_use_count (c->rp.response); 591 c->rp.response = NULL; 592 593 c->conn_reuse = mhd_CONN_KEEPALIVE_POSSIBLE; 594 c->stage = mhd_HTTP_STAGE_INIT; 595 c->event_loop_info = MHD_EVENT_LOOP_INFO_RECV; /* Dummy state, real state set later */ 596 597 // TODO: move request reset to special function 598 memset (&c->rq, 0, sizeof(c->rq)); 599 600 // TODO: move reply reset to special function 601 /* iov (if any) will be deallocated by mhd_pool_reset */ 602 memset (&c->rp, 0, sizeof(c->rp)); 603 604 #ifndef HAVE_NULL_PTR_ALL_ZEROS 605 // TODO: move request reset to special function 606 mhd_DLINKEDL_INIT_LIST (&(c->rq), fields); 607 #ifdef MHD_SUPPORT_POST_PARSER 608 mhd_DLINKEDL_INIT_LIST (&(c->rq), post_fields); 609 #endif /* MHD_SUPPORT_POST_PARSER */ 610 c->rq.version = NULL; 611 c->rq.url = NULL; 612 c->rq.field_lines.start = NULL; 613 c->rq.app_context = NULL; 614 c->rq.hdrs.rq_line.rq_tgt = NULL; 615 c->rq.hdrs.rq_line.rq_tgt_qmark = NULL; 616 617 // TODO: move reply reset to special function 618 c->rp.app_act_ctx.connection = NULL; 619 c->rp.response = NULL; 620 c->rp.resp_iov.iov = NULL; 621 #endif /* ! HAVE_NULL_PTR_ALL_ZEROS */ 622 623 c->write_buffer = NULL; 624 c->write_buffer_size = 0; 625 c->write_buffer_send_offset = 0; 626 c->write_buffer_append_offset = 0; 627 c->continue_message_write_offset = 0; 628 629 /* Reset the read buffer to the starting size, 630 preserving the bytes we have already read. */ 631 new_read_buf_size = d->conns.cfg.mem_pool_size / 2; 632 if (c->read_buffer_offset > new_read_buf_size) 633 new_read_buf_size = c->read_buffer_offset; 634 635 c->read_buffer 636 = (char *) mhd_pool_reset (c->pool, 637 c->read_buffer, 638 c->read_buffer_offset, 639 new_read_buf_size); 640 c->read_buffer_size = new_read_buf_size; 641 } 642 c->rq.app_context = NULL; 643 } 644 645 646 /* return 'true' is lingering needed, 'false' is lingering is not needed */ 647 static MHD_FN_PAR_NONNULL_ALL_ bool 648 conn_start_socket_closing (struct MHD_Connection *restrict c, 649 bool close_hard) 650 { 651 bool need_lingering; 652 /* Make changes on the socket early to let the kernel and the remote 653 * to process the changes in parallel. */ 654 if (close_hard) 655 { 656 /* Use abortive closing, send RST to remote to indicate a problem */ 657 (void) mhd_socket_set_hard_close (c->sk.fd); 658 c->stage = mhd_HTTP_STAGE_PRE_CLOSING; 659 c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; 660 661 return false; 662 } 663 664 mhd_assert (c->sk.state.rmt_shut_wr || \ 665 ! mhd_SOCKET_ERR_IS_HARD (c->sk.state.discnt_err)); 666 667 need_lingering = ! c->sk.state.rmt_shut_wr; 668 if (need_lingering) 669 { 670 #ifdef MHD_SUPPORT_HTTPS 671 if (mhd_C_HAS_TLS (c)) 672 { 673 if ((0 != (((unsigned int) c->sk.ready) 674 & mhd_SOCKET_NET_STATE_SEND_READY)) 675 || c->sk.props.is_nonblck) 676 need_lingering = 677 (mhd_TLS_PROCED_FAILED != mhd_tls_conn_shutdown (c->tls)); 678 } 679 else 680 #endif /* MHD_SUPPORT_HTTPS */ 681 if (1) 682 { 683 need_lingering = mhd_socket_shut_wr (c->sk.fd); 684 if (need_lingering) 685 need_lingering = (! c->sk.state.rmt_shut_wr); /* Skip as already closed */ 686 } 687 } 688 689 return need_lingering; 690 } 691 692 693 #ifdef MHD_SUPPORT_HTTP2 694 695 static MHD_FN_PAR_NONNULL_ALL_ void 696 conn_h2_start_closing (struct MHD_Connection *restrict c, 697 bool close_hard) 698 { 699 mhd_assert (mhd_C_IS_HTTP2 (c)); 700 mhd_assert (c->h2.dbg.h2_deinited); 701 mhd_assert (! c->rq.app_aware); 702 703 conn_start_socket_closing (c, 704 close_hard); 705 706 mhd_conn_deinit_activity_timeout (c); 707 708 #ifndef NDEBUG 709 c->dbg.closing_started = true; 710 #endif 711 } 712 713 714 #endif /* MHD_SUPPORT_HTTP2 */ 715 716 717 MHD_INTERNAL 718 MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_CSTR_ (3) void 719 mhd_conn_start_closing (struct MHD_Connection *restrict c, 720 enum mhd_ConnCloseReason reason, 721 const char *log_msg) 722 { 723 bool close_hard; 724 enum MHD_RequestEndedCode end_code; 725 enum MHD_StatusCode sc; 726 bool reply_sending_aborted; 727 728 #ifdef MHD_USE_TRACE_CONN_ADD_CLOSE 729 fprintf (stderr, 730 "&&& mhd_conn_start_closing([FD: %2llu], %u, %s%s%s)...\n", 731 (unsigned long long) c->sk.fd, 732 (unsigned int) reason, 733 log_msg ? "\"" : "", 734 log_msg ? log_msg : "[NULL]", 735 log_msg ? "\"" : ""); 736 #endif /* MHD_USE_TRACE_CONN_ADD_CLOSE */ 737 738 #ifdef MHD_SUPPORT_HTTP2 739 if (mhd_C_IS_HTTP2 (c)) 740 { 741 mhd_assert ((mhd_CONN_CLOSE_TIMEDOUT == reason) || 742 (mhd_CONN_CLOSE_DAEMON_SHUTDOWN == reason) || 743 (mhd_CONN_CLOSE_H2_CLOSE_SOFT == reason) || 744 (mhd_CONN_CLOSE_H2_CLOSE_HARD == reason)); 745 mhd_assert (NULL == log_msg); 746 conn_h2_start_closing (c, 747 reason != mhd_CONN_CLOSE_H2_CLOSE_SOFT); 748 return; 749 } 750 #endif /* MHD_SUPPORT_HTTP2 */ 751 752 reply_sending_aborted = 753 ((mhd_HTTP_STAGE_HEADERS_SENDING <= c->stage) 754 && (mhd_HTTP_STAGE_FULL_REPLY_SENT > c->stage)); 755 sc = MHD_SC_INTERNAL_ERROR; 756 switch (reason) 757 { 758 case mhd_CONN_CLOSE_CLIENT_HTTP_ERR_ABORT_CONN: 759 close_hard = true; 760 end_code = MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR; 761 sc = MHD_SC_REQ_MALFORMED; 762 mhd_assert (! reply_sending_aborted); 763 break; 764 case mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REQUEST: 765 close_hard = true; 766 end_code = MHD_REQUEST_ENDED_NO_RESOURCES; 767 mhd_assert (! reply_sending_aborted); 768 break; 769 case mhd_CONN_CLOSE_CLIENT_SHUTDOWN_EARLY: 770 close_hard = true; 771 end_code = MHD_REQUEST_ENDED_CLIENT_ABORT; 772 sc = MHD_SC_CLIENT_SHUTDOWN_EARLY; 773 mhd_assert (! reply_sending_aborted); 774 break; 775 case mhd_CONN_CLOSE_H2_PREFACE_MISSING: 776 close_hard = true; 777 end_code = MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR; 778 sc = MHD_SC_ALPN_H2_NO_PREFACE; 779 break; 780 case mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY: 781 close_hard = true; 782 end_code = (! c->stop_with_error || c->rq.too_large) ? 783 MHD_REQUEST_ENDED_NO_RESOURCES : 784 MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR; 785 sc = MHD_SC_REPLY_POOL_ALLOCATION_FAILURE; 786 if (reply_sending_aborted && (NULL == log_msg)) 787 log_msg = mhd_MSG4LOG ("Response aborted due to insufficient memory " \ 788 "in the connection pool"); 789 break; 790 case mhd_CONN_CLOSE_NO_MEM_FOR_ERR_RESPONSE: 791 close_hard = true; 792 end_code = c->rq.too_large ? 793 MHD_REQUEST_ENDED_NO_RESOURCES : 794 MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR; 795 sc = MHD_SC_ERR_RESPONSE_ALLOCATION_FAILURE; 796 break; 797 case mhd_CONN_CLOSE_APP_ERROR: 798 close_hard = true; 799 end_code = MHD_REQUEST_ENDED_BY_APP_ERROR; 800 sc = MHD_SC_APPLICATION_DATA_GENERATION_FAILURE_CLOSED; 801 if (reply_sending_aborted && (NULL == log_msg)) 802 log_msg = mhd_MSG4LOG ("Response aborted due to application reply " \ 803 "generation failure"); 804 break; 805 case mhd_CONN_CLOSE_APP_ABORTED: 806 close_hard = true; 807 end_code = MHD_REQUEST_ENDED_BY_APP_ABORT; 808 sc = MHD_SC_APPLICATION_CALLBACK_ABORT_ACTION; 809 if (reply_sending_aborted && (NULL == log_msg)) 810 log_msg = mhd_MSG4LOG ("Application aborted reply sending"); 811 break; 812 case mhd_CONN_CLOSE_FILE_OFFSET_TOO_LARGE: 813 close_hard = true; 814 end_code = MHD_REQUEST_ENDED_FILE_ERROR; 815 sc = MHD_SC_REPLY_FILE_OFFSET_TOO_LARGE; 816 if (reply_sending_aborted && (NULL == log_msg)) 817 log_msg = mhd_MSG4LOG ("Response aborted because OS failed " \ 818 "to read too large response file"); 819 break; 820 case mhd_CONN_CLOSE_FILE_READ_ERROR: 821 close_hard = true; 822 end_code = MHD_REQUEST_ENDED_FILE_ERROR; 823 sc = MHD_SC_REPLY_FILE_READ_ERROR; 824 if (reply_sending_aborted && (NULL == log_msg)) 825 log_msg = mhd_MSG4LOG ("Response aborted because OS failed " \ 826 "to read response file"); 827 break; 828 case mhd_CONN_CLOSE_FILE_TOO_SHORT: 829 close_hard = true; 830 end_code = MHD_REQUEST_ENDED_BY_APP_ERROR; 831 sc = MHD_SC_REPLY_FILE_TOO_SHORT; 832 if (reply_sending_aborted && (NULL == log_msg)) 833 log_msg = mhd_MSG4LOG ("Response aborted because response file is " 834 "shorter that expected"); 835 break; 836 #ifdef MHD_SUPPORT_AUTH_DIGEST 837 case mhd_CONN_CLOSE_NONCE_ERROR: 838 close_hard = true; 839 end_code = MHD_REQUEST_ENDED_NONCE_ERROR; 840 sc = MHD_SC_REPLY_NONCE_ERROR; 841 mhd_assert (! reply_sending_aborted); 842 break; 843 #endif /* MHD_SUPPORT_AUTH_DIGEST */ 844 845 case mhd_CONN_CLOSE_INT_ERROR: 846 close_hard = true; 847 end_code = MHD_REQUEST_ENDED_NO_RESOURCES; 848 if (reply_sending_aborted && (NULL == log_msg)) 849 log_msg = mhd_MSG4LOG ("Response aborted due to MHD internal error"); 850 break; 851 case mhd_CONN_CLOSE_EXTR_EVENT_REG_FAILED: 852 close_hard = true; 853 end_code = MHD_REQUEST_ENDED_BY_EXT_EVENT_ERROR; 854 sc = MHD_SC_EXTR_EVENT_REG_FAILED; 855 if (reply_sending_aborted && (NULL == log_msg)) 856 log_msg = mhd_MSG4LOG ("Response aborted due to external event " \ 857 "registration failure"); 858 break; 859 case mhd_CONN_CLOSE_NO_SYS_RESOURCES: 860 close_hard = true; 861 end_code = MHD_REQUEST_ENDED_NO_RESOURCES; 862 sc = MHD_SC_NO_SYS_RESOURCES; 863 if (reply_sending_aborted && (NULL == log_msg)) 864 log_msg = mhd_MSG4LOG ("Response aborted due to lack of " \ 865 "system resources"); 866 break; 867 case mhd_CONN_CLOSE_SOCKET_ERR: 868 close_hard = true; 869 switch (c->sk.state.discnt_err) 870 { 871 case mhd_SOCKET_ERR_NOMEM: 872 end_code = MHD_REQUEST_ENDED_NO_RESOURCES; 873 sc = MHD_SC_NO_SYS_RESOURCES; 874 if (reply_sending_aborted && (NULL == log_msg)) 875 log_msg = mhd_MSG4LOG ("Response aborted because system closed " \ 876 "socket due to lack of system resources"); 877 break; 878 case mhd_SOCKET_ERR_REMT_DISCONN: 879 close_hard = false; 880 end_code = (mhd_HTTP_STAGE_INIT == c->stage) ? 881 MHD_REQUEST_ENDED_COMPLETED_OK /* Not used */ 882 : MHD_REQUEST_ENDED_CLIENT_ABORT; 883 if (reply_sending_aborted) 884 { 885 sc = MHD_SC_CLIENT_CLOSED_CONN_EARLY; 886 if (NULL == log_msg) 887 log_msg = mhd_MSG4LOG ("Response aborted because remote client " \ 888 "closed connection early"); 889 } 890 break; 891 case mhd_SOCKET_ERR_CONNRESET: 892 end_code = MHD_REQUEST_ENDED_CLIENT_ABORT; 893 sc = MHD_SC_CONNECTION_RESET; 894 if (reply_sending_aborted && (NULL == log_msg)) 895 log_msg = mhd_MSG4LOG ("Response aborted due to aborted connection"); 896 break; 897 case mhd_SOCKET_ERR_CONN_BROKEN: 898 case mhd_SOCKET_ERR_NOTCONN: 899 case mhd_SOCKET_ERR_TLS: 900 case mhd_SOCKET_ERR_PIPE: 901 case mhd_SOCKET_ERR_NOT_CHECKED: 902 case mhd_SOCKET_ERR_BADF: 903 case mhd_SOCKET_ERR_INVAL: 904 case mhd_SOCKET_ERR_OPNOTSUPP: 905 case mhd_SOCKET_ERR_NOTSOCK: 906 case mhd_SOCKET_ERR_OTHER: 907 case mhd_SOCKET_ERR_INTERNAL: 908 case mhd_SOCKET_ERR_NO_ERROR: 909 end_code = MHD_REQUEST_ENDED_CONNECTION_ERROR; 910 sc = MHD_SC_CONNECTION_BROKEN; 911 if (reply_sending_aborted && (NULL == log_msg)) 912 log_msg = mhd_MSG4LOG ("Response aborted due to broken connection"); 913 break; 914 case mhd_SOCKET_ERR_AGAIN: 915 case mhd_SOCKET_ERR_INTR: 916 default: 917 mhd_UNREACHABLE (); 918 break; 919 } 920 break; 921 case mhd_CONN_CLOSE_DAEMON_SHUTDOWN: 922 close_hard = true; 923 end_code = MHD_REQUEST_ENDED_DAEMON_SHUTDOWN; 924 break; 925 926 case mhd_CONN_CLOSE_TIMEDOUT: 927 if (mhd_HTTP_STAGE_INIT == c->stage) 928 { 929 close_hard = false; 930 end_code = MHD_REQUEST_ENDED_COMPLETED_OK; /* Not used */ 931 break; 932 } 933 close_hard = true; 934 end_code = MHD_REQUEST_ENDED_TIMEOUT_REACHED; 935 sc = MHD_SC_CONNECTION_TIMEOUT; 936 if (reply_sending_aborted && (NULL == log_msg)) 937 log_msg = mhd_MSG4LOG ("Response aborted due to sending timeout"); 938 break; 939 940 case mhd_CONN_CLOSE_ERR_REPLY_SENT: 941 close_hard = false; 942 end_code = c->rq.too_large ? 943 MHD_REQUEST_ENDED_NO_RESOURCES : 944 MHD_REQUEST_ENDED_HTTP_PROTOCOL_ERROR; 945 break; 946 #ifdef MHD_SUPPORT_UPGRADE 947 case mhd_CONN_CLOSE_UPGRADE: 948 close_hard = false; 949 end_code = MHD_REQUEST_ENDED_COMPLETED_OK_UPGRADE; 950 break; 951 #endif /* MHD_SUPPORT_UPGRADE */ 952 case mhd_CONN_CLOSE_HTTP_COMPLETED: 953 close_hard = false; 954 end_code = MHD_REQUEST_ENDED_COMPLETED_OK; 955 break; 956 957 #ifdef MHD_SUPPORT_HTTP2 958 case mhd_CONN_CLOSE_H2_CLOSE_SOFT: 959 case mhd_CONN_CLOSE_H2_CLOSE_HARD: 960 #endif /* MHD_SUPPORT_HTTP2 */ 961 default: 962 mhd_assert (0 && "Unreachable code"); 963 mhd_UNREACHABLE (); 964 end_code = MHD_REQUEST_ENDED_COMPLETED_OK; 965 close_hard = false; 966 break; 967 } 968 969 mhd_assert ((NULL == log_msg) || (MHD_SC_INTERNAL_ERROR != sc)); 970 971 #ifdef MHD_SUPPORT_UPGRADE 972 if (mhd_CONN_CLOSE_UPGRADE == reason) 973 { 974 mhd_assert (mhd_HTTP_STAGE_UPGRADING == c->stage); 975 c->event_loop_info = MHD_EVENT_LOOP_INFO_UPGRADED; 976 } 977 else 978 #endif /* MHD_SUPPORT_UPGRADE */ 979 if (1) 980 { 981 if (conn_start_socket_closing (c, 982 close_hard)) 983 { 984 (void) 0; // TODO: start local lingering phase 985 c->stage = mhd_HTTP_STAGE_PRE_CLOSING; // TODO: start local lingering phase 986 c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; // TODO: start local lingering phase 987 } 988 else 989 { /* No need / not possible to linger */ 990 c->stage = mhd_HTTP_STAGE_PRE_CLOSING; 991 c->event_loop_info = MHD_EVENT_LOOP_INFO_CLEANUP; 992 } 993 } 994 995 #ifdef MHD_SUPPORT_LOG_FUNCTIONALITY 996 if (NULL != log_msg) 997 { 998 mhd_LOG_MSG (c->daemon, sc, log_msg); 999 } 1000 #else /* ! MHD_SUPPORT_LOG_FUNCTIONALITY */ 1001 (void) log_msg; /* Mute compiler warning */ 1002 (void) sc; /* Mute compiler warning */ 1003 #endif /* ! MHD_SUPPORT_LOG_FUNCTIONALITY */ 1004 1005 #if 0 // TODO: notification callback 1006 mhd_assert ((mhd_HTTP_STAGE_INIT != c->stage) || (! c->rq.app_aware)); 1007 if ( (NULL != d->notify_completed) && 1008 (c->rq.app_aware) ) 1009 d->notify_completed (d->notify_completed_cls, 1010 c, 1011 &c->rq.app_context, 1012 MHD_REQUEST_ENDED_COMPLETED_OK); 1013 #else 1014 (void) end_code; 1015 #endif 1016 c->rq.app_aware = false; 1017 1018 if (! c->suspended) 1019 { 1020 mhd_assert (! c->resuming); 1021 mhd_conn_deinit_activity_timeout (c); 1022 } 1023 1024 #ifndef NDEBUG 1025 c->dbg.closing_started = true; 1026 #endif 1027 } 1028 1029 1030 MHD_INTERNAL 1031 MHD_FN_PAR_NONNULL_ (1) void 1032 mhd_conn_pre_clean_part1 (struct MHD_Connection *restrict c) 1033 { 1034 // TODO: support suspended connections 1035 mhd_conn_mark_unready (c, c->daemon); 1036 1037 mhd_stream_call_dcc_cleanup_if_needed (c); 1038 if (NULL != c->rq.cntn.lbuf.data) 1039 mhd_daemon_free_lbuf (c->daemon, &(c->rq.cntn.lbuf)); 1040 1041 if (mhd_WM_INT_HAS_EXT_EVENTS (c->daemon->wmode_int)) 1042 { 1043 struct MHD_Daemon *const d = c->daemon; 1044 if (NULL != c->events.extrn.app_cntx) 1045 { 1046 c->events.extrn.app_cntx = 1047 mhd_daemon_extr_event_reg (d, 1048 c->sk.fd, 1049 MHD_FD_STATE_NONE, 1050 c->events.extrn.app_cntx, 1051 (struct MHD_EventUpdateContext *) c); 1052 if (NULL != c->events.extrn.app_cntx) 1053 mhd_log_extr_event_dereg_failed (d); 1054 } 1055 } 1056 #ifdef MHD_SUPPORT_EPOLL 1057 else if (mhd_POLL_TYPE_EPOLL == c->daemon->events.poll_type) 1058 { 1059 struct epoll_event event; 1060 1061 event.events = 0; 1062 event.data.ptr = NULL; 1063 if (0 != epoll_ctl (c->daemon->events.data.epoll.e_fd, 1064 EPOLL_CTL_DEL, 1065 c->sk.fd, 1066 &event)) 1067 { 1068 mhd_LOG_MSG (c->daemon, MHD_SC_EVENTS_CONN_REMOVE_FAILED, 1069 "Failed to remove connection socket from epoll."); 1070 } 1071 } 1072 #endif /* MHD_SUPPORT_EPOLL */ 1073 #ifdef MHD_SUPPORT_KQUEUE 1074 else if (mhd_D_IS_USING_KQUEUE (c->daemon)) 1075 { 1076 # ifdef MHD_SUPPORT_UPGRADE 1077 /* Remove socket from kqueue monitoring only if upgrading. 1078 If connection is being closed, the socket is removed automatically 1079 when the socket is closed. */ 1080 if (mhd_HTTP_STAGE_UPGRADING == c->stage) 1081 { 1082 static const struct timespec zero_timeout = {0, 0}; 1083 struct kevent events[2]; 1084 int res; 1085 1086 mhd_KE_SET (events + 0u, 1087 c->sk.fd, 1088 EVFILT_WRITE, 1089 EV_DELETE, 1090 c); 1091 mhd_KE_SET (events + 1u, 1092 c->sk.fd, 1093 EVFILT_READ, 1094 EV_DELETE, 1095 c); 1096 1097 # ifdef MHD_USE_TRACE_POLLING_FDS 1098 fprintf (stderr, 1099 "### (Starting) kevent(%d, changes, 2, [NULL], " 1100 "0, [0, 0])...\n", 1101 c->daemon->events.data.kq.kq_fd); 1102 # endif /* MHD_USE_TRACE_POLLING_FDS */ 1103 res = mhd_kevent (c->daemon->events.data.kq.kq_fd, 1104 events, 1105 2, 1106 NULL, 1107 0, 1108 &zero_timeout); 1109 # ifdef MHD_USE_TRACE_POLLING_FDS 1110 fprintf (stderr, 1111 "### (Finished) kevent(%d, changes, 2, [NULL], " 1112 "0, [0, 0]) -> %d\n", 1113 c->daemon->events.data.kq.kq_fd, 1114 res); 1115 # endif /* MHD_USE_TRACE_POLLING_FDS */ 1116 if (0 > res) 1117 { 1118 mhd_LOG_MSG (c->daemon, MHD_SC_EVENTS_CONN_REMOVE_FAILED, 1119 "Failed to remove upgraded connection socket " 1120 "from kqueue monitoring."); 1121 /* Continue with monitored socket which may wake-up 1122 daemon's monitoring */ 1123 } 1124 } 1125 # endif /* MHD_SUPPORT_UPGRADE */ 1126 (void) 0; 1127 } 1128 #endif /* MHD_SUPPORT_KQUEUE */ 1129 } 1130 1131 1132 MHD_INTERNAL 1133 MHD_FN_PAR_NONNULL_ (1) void 1134 mhd_conn_pre_clean (struct MHD_Connection *restrict c) 1135 { 1136 #ifdef MHD_USE_TRACE_CONN_ADD_CLOSE 1137 fprintf (stderr, 1138 "&&& Closing connection, FD: %2llu\n", 1139 (unsigned long long) c->sk.fd); 1140 #endif /* MHD_USE_TRACE_CONN_ADD_CLOSE */ 1141 1142 mhd_assert (c->dbg.closing_started); 1143 mhd_assert (! c->dbg.pre_cleaned); 1144 1145 #ifdef MHD_SUPPORT_UPGRADE 1146 if (NULL == c->upgr.c) 1147 #endif 1148 mhd_conn_pre_clean_part1 (c); 1149 1150 if (NULL != c->rp.resp_iov.iov) 1151 { 1152 free (c->rp.resp_iov.iov); 1153 c->rp.resp_iov.iov = NULL; 1154 } 1155 if (NULL != c->rp.response) 1156 mhd_response_dec_use_count (c->rp.response); 1157 c->rp.response = NULL; 1158 1159 mhd_assert (NULL != c->pool); 1160 c->read_buffer_offset = 0; 1161 c->read_buffer_size = 0; 1162 c->read_buffer = NULL; 1163 c->write_buffer_send_offset = 0; 1164 c->write_buffer_append_offset = 0; 1165 c->write_buffer_size = 0; 1166 c->write_buffer = NULL; 1167 // TODO: call in the thread where it was allocated for thread-per-connection 1168 mhd_pool_destroy (c->pool); 1169 c->pool = NULL; 1170 1171 c->stage = mhd_HTTP_STAGE_CLOSED; 1172 #ifndef NDEBUG 1173 c->dbg.pre_cleaned = true; 1174 #endif 1175 }