stream_process_reply.c (48553B)
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) 2014-2024 Evgeny Grin (Karlson2k) 5 Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff 6 7 GNU libmicrohttpd is free software; you can redistribute it and/or 8 modify it under the terms of the GNU Lesser General Public 9 License as published by the Free Software Foundation; either 10 version 2.1 of the License, or (at your option) any later version. 11 12 GNU libmicrohttpd is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 Lesser General Public License for more details. 16 17 Alternatively, you can redistribute GNU libmicrohttpd and/or 18 modify it under the terms of the GNU General Public License as 19 published by the Free Software Foundation; either version 2 of 20 the License, or (at your option) any later version, together 21 with the eCos exception, as follows: 22 23 As a special exception, if other files instantiate templates or 24 use macros or inline functions from this file, or you compile this 25 file and link it with other works to produce a work based on this 26 file, this file does not by itself cause the resulting work to be 27 covered by the GNU General Public License. However the source code 28 for this file must still be made available in accordance with 29 section (3) of the GNU General Public License v2. 30 31 This exception does not invalidate any other reasons why a work 32 based on this file might be covered by the GNU General Public 33 License. 34 35 You should have received copies of the GNU Lesser General Public 36 License and the GNU General Public License along with this library; 37 if not, see <https://www.gnu.org/licenses/>. 38 */ 39 40 /** 41 * @file src/mhd2/stream_process_reply.h 42 * @brief The implementation of internal functions for forming and sending 43 * replies for requests 44 * @author Karlson2k (Evgeny Grin) 45 * 46 * Based on the MHD v0.x code by Daniel Pittman, Christian Grothoff and other 47 * contributors. 48 */ 49 50 #include "mhd_sys_options.h" 51 52 #include "sys_bool_type.h" 53 #include "sys_base_types.h" 54 55 #include "mhd_assert.h" 56 #include "mhd_unreachable.h" 57 #include "mhd_predict.h" 58 59 #include <string.h> 60 #ifdef HAVE_TIME_H 61 # include <time.h> 62 #endif 63 64 #include "mhd_daemon.h" 65 #include "mhd_response.h" 66 #include "mhd_reply.h" 67 #include "mhd_connection.h" 68 69 #include "daemon_logger.h" 70 71 #include "mhd_str.h" 72 #include "http_status_str.h" 73 #include "stream_process_reply.h" 74 #include "stream_funcs.h" 75 #include "request_get_value.h" 76 #ifdef MHD_SUPPORT_AUTH_DIGEST 77 # include "mhd_digest_auth_data.h" 78 # include "auth_digest.h" 79 #endif 80 81 #include "mhd_read_file.h" 82 83 #include "mhd_public_api.h" 84 85 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ void 86 mhd_stream_call_dcc_cleanup_if_needed (struct MHD_Connection *restrict c) 87 { 88 if (mhd_DCC_ACTION_CONTINUE != c->rp.app_act.act) 89 return; 90 if (NULL == c->rp.app_act.data.cntnue.iov_data) 91 return; 92 93 mhd_assert (mhd_RESPONSE_CONTENT_DATA_CALLBACK == \ 94 c->rp.response->cntn_dtype); 95 96 if (NULL != c->rp.app_act.data.cntnue.iov_data->iov_fcb) 97 { 98 c->rp.app_act.data.cntnue.iov_data->iov_fcb ( 99 c->rp.app_act.data.cntnue.iov_data->iov_fcb_cls); 100 } 101 102 c->rp.app_act.data.cntnue.iov_data = NULL; 103 } 104 105 106 /** 107 * This enum type describes requirements for reply body and reply bode-specific 108 * headers (namely Content-Length, Transfer-Encoding). 109 */ 110 enum replyBodyUse 111 { 112 /** 113 * No reply body allowed. 114 * Reply body headers 'Content-Length:' or 'Transfer-Encoding: chunked' are 115 * not allowed as well. 116 */ 117 RP_BODY_NONE = 0, 118 119 /** 120 * Do not send reply body. 121 * Reply body headers 'Content-Length:' or 'Transfer-Encoding: chunked' are 122 * allowed, but optional. 123 */ 124 RP_BODY_HEADERS_ONLY = 1, 125 126 /** 127 * Send reply body and 128 * reply body headers 'Content-Length:' or 'Transfer-Encoding: chunked'. 129 * Reply body headers are required. 130 */ 131 RP_BODY_SEND = 2 132 }; 133 134 135 /** 136 * Is it allowed to reuse the connection? 137 * The TCP stream can be reused for the next requests if the connection 138 * is HTTP 1.1 and the "Connection" header either does not exist or 139 * is not set to "close", or if the connection is HTTP 1.0 and the 140 * "Connection" header is explicitly set to "keep-alive". 141 * If no HTTP version is specified (or if it is not 1.0 or 1.1), the connection 142 * is definitively closed. If the "Connection" header is not exactly "close" 143 * or "keep-alive", connection is reused if is it HTTP/1.1. 144 * If response has HTTP/1.0 flag or has "Connection: close" header 145 * then connection must be closed. 146 * If full request has not been read then connection must be closed 147 * as well as more client data may be sent. 148 * 149 * @param c the connection to check for re-use 150 * @return mhd_CONN_KEEPALIVE_POSSIBLE if (based on the request and 151 * the response) a connection could be reused, 152 * MHD_CONN_MUST_CLOSE if connection must be closed after sending 153 * complete reply, 154 * mhd_CONN_MUST_UPGRADE if connection must be upgraded. 155 */ 156 static MHD_FN_PAR_NONNULL_ALL_ enum mhd_ConnReuse 157 get_conn_reuse (struct MHD_Connection *c) 158 { 159 const struct MHD_Response *const restrict rp = c->rp.response; 160 161 mhd_assert (NULL != rp); 162 if (mhd_CONN_MUST_CLOSE == c->conn_reuse) 163 return mhd_CONN_MUST_CLOSE; 164 165 mhd_assert ( (! c->stop_with_error) || (c->discard_request)); 166 if ((c->sk.state.rmt_shut_wr) || (c->discard_request)) 167 return mhd_CONN_MUST_CLOSE; 168 169 if (rp->cfg.close_forced) 170 return mhd_CONN_MUST_CLOSE; 171 172 mhd_assert ((MHD_SIZE_UNKNOWN != rp->cntn_size) || \ 173 (! rp->cfg.mode_1_0)); 174 175 if (! MHD_HTTP_VERSION_IS_1X (c->rq.http_ver)) 176 return mhd_CONN_MUST_CLOSE; 177 178 if (rp->cfg.mode_1_0 && 179 ! mhd_stream_has_header_token_st (c, 180 MHD_HTTP_HEADER_CONNECTION, 181 "keep-alive")) 182 return mhd_CONN_MUST_CLOSE; 183 184 #if 0 // def MHD_SUPPORT_UPGRADE // TODO: Implement upgrade support 185 /* TODO: Move below the next check when MHD stops closing connections 186 * when response is queued in first callback */ 187 if (NULL != r->upgrade_handler) 188 { 189 /* No "close" token is enforced by 'add_response_header_connection()' */ 190 mhd_assert (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE)); 191 /* Valid HTTP version is enforced by 'MHD_queue_response()' */ 192 mhd_assert (MHD_IS_HTTP_VER_SUPPORTED (c->rq.http_ver)); 193 mhd_assert (! c->stop_with_error); 194 return mhd_CONN_MUST_UPGRADE; 195 } 196 #endif /* MHD_SUPPORT_UPGRADE */ 197 198 return mhd_CONN_KEEPALIVE_POSSIBLE; 199 } 200 201 202 /** 203 * Check whether reply body must be used. 204 * 205 * If reply body is needed, it could be zero-sized. 206 * 207 * @param c the connection to check 208 * @param rcode the response code 209 * @return enum value indicating whether response body can be used and 210 * whether response body length headers are allowed or required. 211 * @sa is_reply_body_header_needed() 212 */ 213 static enum replyBodyUse 214 is_reply_body_needed (struct MHD_Connection *restrict c, 215 uint_fast16_t rcode) 216 { 217 mhd_assert (100 <= rcode); 218 mhd_assert (999 >= rcode); 219 220 if (199 >= rcode) 221 return RP_BODY_NONE; 222 223 if (MHD_HTTP_STATUS_NO_CONTENT == rcode) 224 return RP_BODY_NONE; 225 226 #if 0 227 /* This check is not needed as upgrade handler is used only with code 101 */ 228 #ifdef MHD_SUPPORT_UPGRADE 229 if (NULL != rp.response->upgrade_handler) 230 return RP_BODY_NONE; 231 #endif /* MHD_SUPPORT_UPGRADE */ 232 #endif 233 234 #if 0 235 /* CONNECT is not supported by MHD */ 236 /* Successful responses for connect requests are filtered by 237 * MHD_queue_response() */ 238 if ( (mhd_HTTP_METHOD_CONNECT == c->rq.http_mthd) && 239 (2 == rcode / 100) ) 240 return false; /* Actually pass-through CONNECT is not supported by MHD */ 241 #endif 242 243 /* Reply body headers could be used. 244 * Check whether reply body itself must be used. */ 245 246 if (mhd_HTTP_METHOD_HEAD == c->rq.http_mthd) 247 return RP_BODY_HEADERS_ONLY; 248 249 if (MHD_HTTP_STATUS_NOT_MODIFIED == rcode) 250 return RP_BODY_HEADERS_ONLY; 251 252 /* Reply body must be sent. 253 * The body may have zero length, but body size must be indicated by 254 * headers ('Content-Length:' or 'Transfer-Encoding: chunked'). */ 255 return RP_BODY_SEND; 256 } 257 258 259 /** 260 * Setup connection reply properties. 261 * 262 * Reply properties include presence of reply body, transfer-encoding 263 * type and other. 264 * 265 * @param c the connection to process 266 */ 267 static MHD_FN_PAR_NONNULL_ALL_ void 268 setup_reply_properties (struct MHD_Connection *restrict c) 269 { 270 struct MHD_Response *const restrict r = c->rp.response; /**< a short alias */ 271 enum replyBodyUse use_rp_body; 272 bool use_chunked; 273 bool end_by_closing; 274 275 mhd_assert (NULL != r); 276 277 /* ** Adjust reply properties ** */ 278 279 c->conn_reuse = get_conn_reuse (c); 280 use_rp_body = is_reply_body_needed (c, r->sc); 281 c->rp.props.send_reply_body = (use_rp_body > RP_BODY_HEADERS_ONLY); 282 c->rp.props.use_reply_body_headers = (use_rp_body >= RP_BODY_HEADERS_ONLY); 283 284 #if 0 // def MHD_SUPPORT_UPGRADE // TODO: upgrade support 285 mhd_assert ( (NULL == r->upgrade_handler) || 286 (RP_BODY_NONE == use_rp_body) ); 287 #endif /* MHD_SUPPORT_UPGRADE */ 288 289 use_chunked = false; 290 end_by_closing = false; 291 if (c->rp.props.use_reply_body_headers) 292 { 293 if (r->cfg.chunked) 294 { 295 mhd_assert (! r->cfg.mode_1_0); 296 use_chunked = MHD_HTTP_VERSION_IS_LIKE_11 (c->rq.http_ver); 297 } 298 if ((MHD_SIZE_UNKNOWN == r->cntn_size) && 299 (! use_chunked) && 300 (c->rp.props.send_reply_body)) 301 { 302 /* End of the stream is indicated by closure */ 303 end_by_closing = true; 304 } 305 } 306 307 if (end_by_closing) 308 { 309 mhd_assert (mhd_CONN_MUST_UPGRADE != c->conn_reuse); 310 /* End of the stream is indicated by closure */ 311 c->conn_reuse = mhd_CONN_MUST_CLOSE; 312 } 313 314 c->rp.props.chunked = use_chunked; 315 c->rp.props.end_by_closing = end_by_closing; 316 317 if ((! c->rp.props.send_reply_body) || (0 == r->cntn_size)) 318 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; 319 else if (c->rp.props.chunked) 320 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF; 321 else 322 { 323 switch (r->cntn_dtype) 324 { 325 case mhd_RESPONSE_CONTENT_DATA_BUFFER: 326 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_RESP_BUF; 327 break; 328 case mhd_RESPONSE_CONTENT_DATA_IOVEC: 329 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_IOV; 330 break; 331 case mhd_RESPONSE_CONTENT_DATA_FILE: 332 if (mhd_C_HAS_TLS (c)) 333 { 334 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF; 335 break; 336 } 337 #ifdef mhd_USE_SENDFILE 338 if (r->cntn.file.use_sf) 339 { 340 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_FILE; 341 break; 342 } 343 #endif 344 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF; 345 break; 346 case mhd_RESPONSE_CONTENT_DATA_CALLBACK: 347 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_CONN_BUF; 348 break; 349 case mhd_RESPONSE_CONTENT_DATA_INVALID: 350 default: 351 mhd_UNREACHABLE (); 352 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; 353 break; 354 } 355 } 356 357 #ifndef NDEBUG 358 c->rp.props.set = true; 359 #endif /* _DEBUG */ 360 } 361 362 363 /** 364 * Check whether queued response is suitable for @a connection. 365 * @param c the connection to check 366 */ 367 static void 368 check_connection_reply (struct MHD_Connection *restrict c) 369 { 370 struct MHD_Response *const restrict r = c->rp.response; /**< a short alias */ 371 372 mhd_assert (c->rp.props.set); 373 374 if ( (! c->rp.props.use_reply_body_headers) && 375 (0 != r->cntn_size) ) 376 { 377 mhd_LOG_PRINT (c->daemon, MHD_SC_REPLY_NOT_EMPTY_RESPONSE, 378 mhd_LOG_FMT ("This reply with response code %u " \ 379 "cannot use reply content. Non-empty " \ 380 "response content is ignored and not used."), 381 (unsigned) (c->rp.response->sc)); 382 } 383 if ( (! c->rp.props.use_reply_body_headers) && 384 (r->cfg.cnt_len_by_app) ) 385 { 386 mhd_LOG_PRINT (c->daemon, MHD_SC_REPLY_CONTENT_LENGTH_NOT_ALLOWED, 387 mhd_LOG_FMT ("This reply with response code %u " \ 388 "cannot use reply content. Application " \ 389 "defined \"Content-Length\" header " \ 390 "violates HTTP specification."), 391 (unsigned) (c->rp.response->sc)); 392 } 393 } 394 395 396 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ 397 MHD_FN_PAR_OUT_ (1) bool 398 mhd_build_date_str (char date[MHD_FN_PAR_FIX_ARR_SIZE_ (29)]) 399 { 400 static const char *const days[] = { 401 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 402 }; 403 static const char *const mons[] = { 404 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 405 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 406 }; 407 static const size_t buf_len = 29; 408 struct tm now; 409 time_t t; 410 const char *src; 411 #if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \ 412 ! defined(HAVE_GMTIME_R) 413 struct tm *pNow; 414 #endif 415 416 if ((time_t) -1 == time (&t)) 417 return false; 418 #if defined(HAVE_GMTIME_R) 419 if (NULL == gmtime_r (&t, 420 &now)) 421 return false; 422 #elif defined(HAVE_C11_GMTIME_S) 423 if (NULL == gmtime_s (&t, 424 &now)) 425 return false; 426 #elif defined(HAVE_W32_GMTIME_S) 427 if (0 != gmtime_s (&now, 428 &t)) 429 return false; 430 #else 431 pNow = gmtime (&t); 432 if (NULL == pNow) 433 return false; 434 now = *pNow; 435 #endif 436 437 /* Day of the week */ 438 src = days[now.tm_wday % 7]; 439 date[0] = src[0]; 440 date[1] = src[1]; 441 date[2] = src[2]; 442 date[3] = ','; 443 date[4] = ' '; 444 /* Day of the month */ 445 if (2 != mhd_uint8_to_str_pad ((uint8_t) now.tm_mday, 2, 446 date + 5, buf_len - 5)) 447 return false; 448 date[7] = ' '; 449 /* Month */ 450 src = mons[now.tm_mon % 12]; 451 date[8] = src[0]; 452 date[9] = src[1]; 453 date[10] = src[2]; 454 date[11] = ' '; 455 /* Year */ 456 if (4 != mhd_uint16_to_str ((uint_least16_t) (1900 + now.tm_year), date + 12, 457 buf_len - 12)) 458 return false; 459 date[16] = ' '; 460 /* Time */ 461 mhd_uint8_to_str_pad ((uint8_t) now.tm_hour, 2, date + 17, buf_len - 17); 462 date[19] = ':'; 463 mhd_uint8_to_str_pad ((uint8_t) now.tm_min, 2, date + 20, buf_len - 20); 464 date[22] = ':'; 465 mhd_uint8_to_str_pad ((uint8_t) now.tm_sec, 2, date + 23, buf_len - 23); 466 date[25] = ' '; 467 date[26] = 'G'; 468 date[27] = 'M'; 469 date[28] = 'T'; 470 471 return true; 472 } 473 474 475 /** 476 * Produce HTTP DATE header. 477 * Result is always 37 bytes long (plus one terminating null). 478 * 479 * @param[out] header where to write the header, with 480 * at least 38 bytes available space. 481 */ 482 static MHD_FN_PAR_NONNULL_ALL_ 483 MHD_FN_PAR_OUT_ (1) bool 484 get_date_header (char *header) 485 { 486 header[0] = 'D'; 487 header[1] = 'a'; 488 header[2] = 't'; 489 header[3] = 'e'; 490 header[4] = ':'; 491 header[5] = ' '; 492 if (! mhd_build_date_str (header + 6)) 493 { 494 header[0] = 0; 495 return false; 496 } 497 header[35] = '\r'; 498 header[36] = '\n'; 499 header[37] = 0; 500 return true; 501 } 502 503 504 /** 505 * Append data to the buffer if enough space is available, 506 * update position. 507 * @param[out] buf the buffer to append data to 508 * @param buf_size the size of the @a buffer 509 * @param[in,out] ppos the pointer to position in the @a buffer 510 * @param append the data to append 511 * @param append_size the size of the @a append 512 * @return true if data has been added and position has been updated, 513 * false if not enough space is available 514 */ 515 static MHD_FN_PAR_NONNULL_ALL_ bool 516 buffer_append (char *restrict buf, 517 size_t buf_size, 518 size_t *restrict ppos, 519 const char *restrict append, 520 size_t append_size) 521 { 522 mhd_assert (NULL != buf); /* Mute static analyzer */ 523 if (mhd_COND_HARDLY_EVER (*ppos + append_size < append_size)) /* Check for overflow */ 524 return false; 525 if (buf_size < *ppos + append_size) 526 return false; 527 memcpy (buf + *ppos, append, append_size); 528 *ppos += append_size; 529 return true; 530 } 531 532 533 /** 534 * Append CRLF to the buffer if enough space is available, 535 * update position. 536 * @param[out] buf the buffer to append data to 537 * @param buf_size the size of the @a buffer 538 * @param[in,out] ppos the pointer to position in the @a buffer 539 * @return true if CRLF has been added and position has been updated, 540 * false if not enough space is available 541 */ 542 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ bool 543 buffer_append_crlf (char *restrict buf, 544 size_t buf_size, 545 size_t *restrict ppos) 546 { 547 mhd_assert (NULL != buf); /* Mute static analyzer */ 548 if (mhd_COND_HARDLY_EVER (*ppos + 2 < 2)) /* Check for overflow */ 549 return false; 550 if (mhd_COND_ALMOST_NEVER (buf_size < *ppos + 2)) 551 return false; 552 buf[*ppos] = '\r'; 553 buf[*ppos + 1] = '\n'; 554 *ppos += 2; 555 return true; 556 } 557 558 559 /** 560 * Add user-defined headers from response object to 561 * the text buffer. 562 * 563 * @param buf the buffer to add headers to 564 * @param ppos the pointer to the position in the @a buf 565 * @param buf_size the size of the @a buf 566 * @param r the response 567 * @param filter_content_len skip "Content-Length" header if any 568 * @param add_close add "close" token to the 569 * "Connection:" header (if any), ignored if no "Connection:" 570 * header was added by user or if "close" token is already 571 * present in "Connection:" header 572 * @param add_keep_alive add "Keep-Alive" token to the 573 * "Connection:" header (if any) 574 * @return true if succeed, 575 * false if buffer is too small 576 */ 577 static MHD_FN_PAR_NONNULL_ALL_ bool 578 add_user_headers (char *restrict buf, 579 size_t *restrict ppos, 580 size_t buf_size, 581 struct MHD_Response *restrict r, 582 bool filter_content_len, 583 bool add_close, 584 bool add_keep_alive) 585 { 586 struct mhd_ResponseHeader *hdr; /**< Iterates through User-specified headers */ 587 size_t el_size; /**< the size of current element to be added to the @a buf */ 588 589 mhd_assert (! add_close || ! add_keep_alive); 590 mhd_assert (! add_keep_alive || ! add_close); 591 592 if (r->cfg.has_hdr_conn) 593 { 594 add_close = false; /* No such header */ 595 add_keep_alive = false; /* No such header */ 596 } 597 598 for (hdr = mhd_DLINKEDL_GET_FIRST (r, headers); 599 NULL != hdr; 600 hdr = mhd_DLINKEDL_GET_NEXT (hdr, headers)) 601 { 602 size_t initial_pos = *ppos; 603 604 if (filter_content_len) 605 { /* Need to filter-out "Content-Length" */ 606 if (mhd_str_equal_caseless_n_st (MHD_HTTP_HEADER_CONTENT_LENGTH, \ 607 hdr->name.cstr, 608 hdr->name.len)) 609 { 610 /* Reset filter flag */ 611 filter_content_len = false; 612 continue; /* Skip "Content-Length" header */ 613 } 614 } 615 616 /* Add user header */ 617 el_size = hdr->name.len + 2u; 618 if (mhd_COND_ALMOST_NEVER (2u > el_size)) /* Check for overflow */ 619 return false; 620 el_size += hdr->value.len; 621 if (mhd_COND_ALMOST_NEVER (hdr->value.len > el_size)) /* Check for overflow */ 622 return false; 623 el_size += 2u; 624 if (mhd_COND_ALMOST_NEVER (2u > el_size)) /* Check for overflow */ 625 return false; 626 if (mhd_COND_HARDLY_EVER (*ppos + el_size < el_size)) /* Check for overflow */ 627 return false; 628 if (buf_size < *ppos + el_size) 629 return false; 630 memcpy (buf + *ppos, hdr->name.cstr, hdr->name.len); 631 (*ppos) += hdr->name.len; 632 buf[(*ppos)++] = ':'; 633 buf[(*ppos)++] = ' '; 634 if (add_close || add_keep_alive) 635 { 636 /* "Connection:" header must be always the first one */ 637 mhd_assert (mhd_str_equal_caseless_n (hdr->name.cstr, \ 638 MHD_HTTP_HEADER_CONNECTION, \ 639 hdr->name.len)); 640 641 if (add_close) 642 { 643 static const struct MHD_String cn_add = mhd_MSTR_INIT ("close, "); 644 el_size += cn_add.len; 645 if (mhd_COND_ALMOST_NEVER (cn_add.len > el_size)) /* Check for overflow */ 646 return false; 647 if (mhd_COND_HARDLY_EVER (initial_pos + el_size < el_size)) /* Check for overflow */ 648 return false; 649 if (buf_size < initial_pos + el_size) 650 return false; 651 memcpy (buf + *ppos, cn_add.cstr, 652 cn_add.len); 653 *ppos += cn_add.len; 654 } 655 else 656 { 657 static const struct MHD_String cn_add = mhd_MSTR_INIT ("Keep-Alive, "); 658 el_size += cn_add.len; 659 if (mhd_COND_ALMOST_NEVER (cn_add.len > el_size)) /* Check for overflow */ 660 return false; 661 if (mhd_COND_HARDLY_EVER (initial_pos + el_size < el_size)) /* Check for overflow */ 662 return false; 663 if (buf_size < initial_pos + el_size) 664 return false; 665 memcpy (buf + *ppos, cn_add.cstr, 666 cn_add.len); 667 *ppos += cn_add.len; 668 } 669 add_close = false; 670 add_keep_alive = false; 671 } 672 if (0 != hdr->value.len) 673 memcpy (buf + *ppos, hdr->value.cstr, hdr->value.len); 674 *ppos += hdr->value.len; 675 buf[(*ppos)++] = '\r'; 676 buf[(*ppos)++] = '\n'; 677 mhd_assert (initial_pos + el_size == (*ppos)); 678 } 679 return true; 680 } 681 682 683 /** 684 * Append static string to the buffer if enough space is available, 685 * update position. 686 * @param[out] buf the buffer to append data to 687 * @param buf_size the size of the @a buffer 688 * @param[in,out] ppos the pointer to position in the @a buffer 689 * @param str the static string to append 690 * @return true if data has been added and position has been updated, 691 * false if not enough space is available 692 */ 693 #define buffer_append_s(buf,buf_size,ppos,str) \ 694 buffer_append ((buf),(buf_size),(ppos),(str),mhd_SSTR_LEN (str)) 695 696 697 /** 698 * Append MHD_String to the buffer if enough space is available, 699 * update position. 700 * @param[out] buf the buffer to append data to 701 * @param buf_size the size of the @a buffer 702 * @param[in,out] ppos the pointer to position in the @a buffer 703 * @param pmhdstr the pointer to string to append 704 * @return true if data has been added and position has been updated, 705 * false if not enough space is available 706 */ 707 #define buffer_append_mstr(buf,buf_size,ppos,pmhdstr) \ 708 buffer_append ((buf),(buf_size),(ppos), \ 709 (pmhdstr)->cstr, (pmhdstr)->len) 710 711 /** 712 * Allocate the connection's write buffer and fill it with all of the 713 * headers from the response. 714 * Inner version of the function. 715 * 716 * @param c the connection to process 717 * @return 'true' if state has been update, 718 * 'false' if connection is going to be aborted 719 */ 720 static MHD_FN_PAR_NONNULL_ALL_ bool 721 build_header_response_inn (struct MHD_Connection *restrict c) 722 { 723 struct MHD_Response *const restrict r = c->rp.response; 724 char *restrict buf; /**< the output buffer */ 725 size_t pos; /**< append offset in the @a buf */ 726 size_t buf_size; /**< the size of the @a buf */ 727 size_t el_size; /**< the size of current element to be added to the @a buf */ 728 uint_fast16_t rcode; /**< the response code */ 729 bool use_conn_close; /**< Use "Connection: close" header */ 730 bool use_conn_k_alive; /**< Use "Connection: Keep-Alive" header */ 731 732 mhd_assert (NULL != r); 733 734 /* ** Adjust response properties ** */ 735 setup_reply_properties (c); 736 737 mhd_assert (c->rp.props.set); 738 mhd_assert ((mhd_CONN_MUST_CLOSE == c->conn_reuse) || \ 739 (mhd_CONN_KEEPALIVE_POSSIBLE == c->conn_reuse) || \ 740 (mhd_CONN_MUST_UPGRADE == c->conn_reuse)); 741 #if 0 // def MHD_SUPPORT_UPGRADE // TODO: upgrade support 742 mhd_assert ((NULL == r->upgrade_handler) || \ 743 (mhd_CONN_MUST_UPGRADE == c->keepalive)); 744 #else /* ! MHD_SUPPORT_UPGRADE */ 745 mhd_assert (mhd_CONN_MUST_UPGRADE != c->conn_reuse); 746 #endif /* ! MHD_SUPPORT_UPGRADE */ 747 mhd_assert ((! c->rp.props.chunked) || c->rp.props.use_reply_body_headers); 748 mhd_assert ((! c->rp.props.send_reply_body) || \ 749 c->rp.props.use_reply_body_headers); 750 mhd_assert ((! c->rp.props.end_by_closing) || \ 751 (mhd_CONN_MUST_CLOSE == c->conn_reuse)); 752 #if 0 // def MHD_SUPPORT_UPGRADE // TODO: upgrade support 753 mhd_assert (NULL == r->upgrade_handler || \ 754 ! c->rp.props.use_reply_body_headers); 755 #endif /* MHD_SUPPORT_UPGRADE */ 756 757 check_connection_reply (c); 758 759 rcode = (uint_fast16_t) c->rp.response->sc; 760 if (mhd_CONN_MUST_CLOSE == c->conn_reuse) 761 { 762 /* The closure of connection must be always indicated by header 763 * to avoid hung connections */ 764 use_conn_close = true; 765 use_conn_k_alive = false; 766 } 767 else if (mhd_CONN_KEEPALIVE_POSSIBLE == c->conn_reuse) 768 { 769 mhd_assert (! r->cfg.mode_1_0); 770 use_conn_close = false; 771 /* Add "Connection: keep-alive" if request is HTTP/1.0 or 772 * if reply is HTTP/1.0 773 * For HTTP/1.1 add header only if explicitly requested by app 774 * (by response flag), as "Keep-Alive" is default for HTTP/1.1. */ 775 if (r->cfg.mode_1_0 || 776 (MHD_HTTP_VERSION_1_0 == c->rq.http_ver)) 777 use_conn_k_alive = true; 778 else 779 use_conn_k_alive = false; 780 } 781 else 782 { 783 use_conn_close = false; 784 use_conn_k_alive = false; 785 } 786 787 /* ** Actually build the response header ** */ 788 789 /* Get all space available */ 790 mhd_stream_maximize_write_buffer (c); 791 buf = c->write_buffer; 792 pos = c->write_buffer_append_offset; 793 buf_size = c->write_buffer_size; 794 if (0 == buf_size) 795 return false; 796 mhd_assert (NULL != buf); 797 798 // TODO: use pre-calculated header size 799 /* * The status line * */ 800 801 /* The HTTP version */ 802 if (! c->rp.responseIcy) 803 { /* HTTP reply */ 804 if (! r->cfg.mode_1_0) 805 { /* HTTP/1.1 reply */ 806 /* Use HTTP/1.1 responses for HTTP/1.0 clients. 807 * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */ 808 if (! buffer_append_s (buf, buf_size, &pos, MHD_HTTP_VERSION_1_1_STR)) 809 return false; 810 } 811 else 812 { /* HTTP/1.0 reply */ 813 if (! buffer_append_s (buf, buf_size, &pos, MHD_HTTP_VERSION_1_0_STR)) 814 return false; 815 } 816 } 817 else 818 { /* ICY reply */ 819 if (! buffer_append_s (buf, buf_size, &pos, "ICY")) 820 return false; 821 } 822 823 /* The response code */ 824 if (mhd_COND_HARDLY_EVER (pos + 5 < 5)) /* Check for overflow */ 825 return false; 826 if (buf_size < pos + 5) /* space + code + space */ 827 return false; 828 buf[pos++] = ' '; 829 pos += mhd_uint16_to_str ((uint16_t) rcode, buf + pos, 830 buf_size - pos); 831 buf[pos++] = ' '; 832 833 /* The reason phrase */ 834 if (1) 835 { 836 const struct MHD_String *stat_str; 837 stat_str = mhd_HTTP_status_code_to_string_int (rcode); 838 mhd_assert (0 != stat_str->len); 839 if (! buffer_append (buf, buf_size, &pos, 840 stat_str->cstr, 841 stat_str->len)) 842 return false; 843 } 844 /* The linefeed */ 845 if (! buffer_append_crlf (buf, 846 buf_size, 847 &pos)) 848 return false; 849 850 /* * The headers * */ 851 852 /* A special custom header */ 853 if (0 != r->special_resp.spec_hdr_len) 854 { 855 mhd_assert (r->cfg.int_err_resp); 856 if (! buffer_append (buf, buf_size, &pos, 857 r->special_resp.spec_hdr, 858 r->special_resp.spec_hdr_len)) 859 return false; 860 if (! buffer_append_crlf (buf, 861 buf_size, 862 &pos)) 863 return false; 864 } 865 866 /* Main automatic headers */ 867 868 /* The "Date:" header */ 869 if ( (! r->cfg.has_hdr_date) && 870 (! c->daemon->req_cfg.suppress_date) ) 871 { 872 /* Additional byte for unused zero-termination */ 873 if (mhd_COND_HARDLY_EVER (pos + 38 < 38)) /* Check for overflow */ 874 return false; 875 if (buf_size < pos + 38) 876 return false; 877 if (get_date_header (buf + pos)) 878 pos += 37; 879 } 880 /* The "Connection:" header */ 881 mhd_assert (! use_conn_close || ! use_conn_k_alive); 882 mhd_assert (! use_conn_k_alive || ! use_conn_close); 883 if (! r->cfg.has_hdr_conn) 884 { 885 if (use_conn_close) 886 { 887 if (! buffer_append_s (buf, buf_size, &pos, 888 MHD_HTTP_HEADER_CONNECTION ": close\r\n")) 889 return false; 890 } 891 else if (use_conn_k_alive) 892 { 893 if (! buffer_append_s (buf, buf_size, &pos, 894 MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n")) 895 return false; 896 } 897 use_conn_close = false; 898 use_conn_k_alive = false; 899 } 900 901 /* Special headers */ 902 #ifdef MHD_SUPPORT_AUTH_DIGEST 903 if (mhd_RESP_HAS_AUTH_DIGEST (r)) 904 { 905 char noncestr[mhd_AUTH_DIGEST_NONCE_LEN]; 906 const struct mhd_RespAuthDigestHeader *dg_hdr; 907 if (! mhd_auth_digest_get_new_nonce (c, 908 noncestr)) 909 { 910 mhd_STREAM_ABORT (c, 911 mhd_CONN_CLOSE_NONCE_ERROR, 912 "Failed to generate a new nonce for Digest Auth."); 913 return false; 914 } 915 for (dg_hdr = mhd_DLINKEDL_GET_FIRST (r, auth_d_hdrs); 916 NULL != dg_hdr; 917 dg_hdr = mhd_DLINKEDL_GET_NEXT (dg_hdr, auth_d_hdrs)) 918 { 919 size_t nonce_pos; 920 nonce_pos = pos + dg_hdr->nonce_pos; 921 if (! buffer_append_mstr (buf, buf_size, &pos, \ 922 &(dg_hdr->hdr))) 923 return false; 924 memcpy (buf + nonce_pos, 925 noncestr, 926 sizeof(noncestr)); 927 } 928 } 929 #endif /* MHD_SUPPORT_AUTH_DIGEST */ 930 931 /* User-defined headers */ 932 933 if (! add_user_headers (buf, &pos, buf_size, r, 934 ! c->rp.props.use_reply_body_headers, 935 use_conn_close, 936 use_conn_k_alive)) 937 return false; 938 939 /* Other automatic headers */ 940 941 if (c->rp.props.use_reply_body_headers) 942 { 943 /* Body-specific headers */ 944 945 if (c->rp.props.chunked) 946 { /* Chunked encoding is used */ 947 mhd_assert (! c->rp.props.end_by_closing); 948 if (! buffer_append_s (buf, buf_size, &pos, 949 MHD_HTTP_HEADER_TRANSFER_ENCODING ": " \ 950 "chunked\r\n")) 951 return false; 952 } 953 else /* Chunked encoding is not used */ 954 { 955 if ((MHD_SIZE_UNKNOWN != r->cntn_size) && 956 (! c->rp.props.end_by_closing) && 957 (! r->cfg.chunked) && 958 (! r->cfg.head_only)) 959 { /* The size is known and can be indicated by the header */ 960 if (! r->cfg.cnt_len_by_app) 961 { /* The response does not have app-defined "Content-Length" header */ 962 if (! buffer_append_s (buf, buf_size, &pos, 963 MHD_HTTP_HEADER_CONTENT_LENGTH ": ")) 964 return false; 965 el_size = mhd_uint64_to_str (r->cntn_size, 966 buf + pos, 967 buf_size - pos); 968 if (0 == el_size) 969 return false; 970 pos += el_size; 971 972 if (! buffer_append_crlf (buf, 973 buf_size, 974 &pos)) 975 return false; 976 } 977 } 978 else 979 { 980 mhd_assert ((! c->rp.props.send_reply_body) || \ 981 (mhd_CONN_MUST_CLOSE == c->conn_reuse)); 982 (void) 0; 983 } 984 } 985 } 986 987 /* * Header termination * */ 988 if (! buffer_append_crlf (buf, 989 buf_size, 990 &pos)) 991 return false; 992 993 c->write_buffer_append_offset = pos; 994 return true; 995 } 996 997 998 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 999 mhd_stream_build_header_response (struct MHD_Connection *restrict c) 1000 { 1001 if (! build_header_response_inn (c)) 1002 { 1003 #ifdef MHD_SUPPORT_AUTH_DIGEST 1004 if (mhd_HTTP_STAGE_PRE_CLOSING <= c->stage) 1005 return false; /* Already started closing */ 1006 #else /* ! MHD_SUPPORT_AUTH_DIGEST */ 1007 mhd_assert (mhd_HTTP_STAGE_START_REPLY == c->stage); 1008 #endif /* ! MHD_SUPPORT_AUTH_DIGEST */ 1009 mhd_STREAM_ABORT (c, 1010 mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY, 1011 "No memory in the pool for the reply headers."); 1012 return false; 1013 } 1014 c->stage = mhd_HTTP_STAGE_HEADERS_SENDING; 1015 return true; 1016 } 1017 1018 1019 /** 1020 * Pre-process DCC action provided by application. 1021 * 'abort' and 'suspend' actions are fully processed, 1022 * 'continue' and 'finish' actions needs to be processed by the caller. 1023 * @param c the stream to use 1024 * @param act the action provided by application 1025 * @return 'true' if action if 'continue' or 'finish' and need to be 1026 * processed, 1027 * 'false' if action is 'suspend' or 'abort' and is already processed. 1028 */ 1029 static MHD_FN_PAR_NONNULL_ (1) bool 1030 preprocess_dcc_action (struct MHD_Connection *restrict c, 1031 const struct MHD_DynamicContentCreatorAction *act) 1032 { 1033 /** 1034 * The action created for the current request 1035 */ 1036 struct MHD_DynamicContentCreatorAction *const a = 1037 &(c->rp.app_act); 1038 1039 if (NULL != act) 1040 { 1041 if ((a != act) || 1042 ! mhd_DCC_ACTION_IS_VALID (c->rp.app_act.act) || 1043 ((MHD_SIZE_UNKNOWN != c->rp.response->cntn_size) && 1044 (mhd_DCC_ACTION_FINISH == c->rp.app_act.act))) 1045 { 1046 mhd_LOG_MSG (c->daemon, MHD_SC_ACTION_INVALID, \ 1047 "Provided Dynamic Content Creator action is not " \ 1048 "a correct action generated for the current request."); 1049 act = NULL; 1050 } 1051 } 1052 if (NULL == act) 1053 a->act = mhd_DCC_ACTION_ABORT; 1054 1055 switch (a->act) 1056 { 1057 case mhd_DCC_ACTION_CONTINUE: 1058 return true; 1059 case mhd_DCC_ACTION_FINISH: 1060 mhd_assert (MHD_SIZE_UNKNOWN == c->rp.response->cntn_size); 1061 return true; 1062 case mhd_DCC_ACTION_SUSPEND: 1063 mhd_assert (0 && "Not implemented yet"); 1064 // TODO: Implement suspend; 1065 mhd_STREAM_ABORT (c, 1066 mhd_CONN_CLOSE_INT_ERROR, 1067 "Suspending connection is not implemented yet"); 1068 return false; 1069 case mhd_DCC_ACTION_ABORT: 1070 mhd_STREAM_ABORT (c, 1071 mhd_CONN_CLOSE_APP_ABORTED, 1072 "Dynamic Content Creator requested abort " \ 1073 "of the request"); 1074 return false; 1075 case mhd_DCC_ACTION_NO_ACTION: 1076 default: 1077 break; 1078 } 1079 mhd_UNREACHABLE (); 1080 mhd_STREAM_ABORT (c, 1081 mhd_CONN_CLOSE_INT_ERROR, 1082 "Impossible code path"); 1083 return false; 1084 } 1085 1086 1087 /** 1088 * Read the next portion of the response file into the buffer. 1089 * The read starts at the response file offset plus the current response 1090 * content read position. Handles file read errors, does NOT update the 1091 * response content read position; the caller must update it on success. 1092 * @param c the stream to use 1093 * @param r the response to use 1094 * @param buf_size the size of the @p buf buffer 1095 * @param[out] buf the buffer to fill with the file data 1096 * @param[out] size_filled the pointer to variable to get the size of the data 1097 * actually put to the @p buf buffer 1098 * @return 'true' if succeed (size_filled is updated), 1099 * 'false' if failed (the stream is closed) 1100 */ 1101 static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ 1102 MHD_FN_PAR_OUT_SIZE_ (4, 3) MHD_FN_PAR_OUT_ (5) bool 1103 read_response_file (struct MHD_Connection *restrict c, 1104 struct MHD_Response *const restrict r, 1105 size_t buf_size, 1106 char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], 1107 size_t *restrict size_filled) 1108 { 1109 mhd_assert (r == c->rp.response); 1110 mhd_assert (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype); 1111 1112 switch (mhd_read_file (r->cntn.file.fd, 1113 r->cntn.file.offset + c->rp.rsp_cntn_read_pos, 1114 buf_size, 1115 buf, 1116 size_filled)) 1117 { 1118 case mhd_FILE_READ_OK: 1119 return true; 1120 case mhd_FILE_READ_ERROR: 1121 mhd_STREAM_ABORT (c, \ 1122 mhd_CONN_CLOSE_FILE_READ_ERROR, \ 1123 "Error reading file for file-backed response."); 1124 return false; 1125 case mhd_FILE_READ_OFFSET_TOO_LARGE: 1126 mhd_STREAM_ABORT (c, \ 1127 mhd_CONN_CLOSE_FILE_OFFSET_TOO_LARGE, \ 1128 "The offset for file-backed response is too large " \ 1129 "for this platform."); 1130 return false; 1131 case mhd_FILE_READ_EOF: 1132 mhd_STREAM_ABORT (c, \ 1133 mhd_CONN_CLOSE_FILE_TOO_SHORT, \ 1134 "The file for file-backed response is smaller " \ 1135 "than specified by application."); 1136 return false; 1137 default: 1138 break; 1139 } 1140 mhd_UNREACHABLE (); 1141 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; 1142 return false; 1143 } 1144 1145 1146 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 1147 mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c) 1148 { 1149 struct MHD_Response *const restrict r = c->rp.response; 1150 uint64_t left_to_send; 1151 1152 mhd_assert (c->rp.props.send_reply_body); 1153 mhd_assert (c->rp.rsp_cntn_read_pos != r->cntn_size); 1154 1155 mhd_stream_call_dcc_cleanup_if_needed (c); 1156 1157 if (0 == r->cntn_size) 1158 { /* 0-byte response is always ready */ 1159 c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT; 1160 return true; 1161 } 1162 1163 mhd_assert (mhd_REPLY_CNTN_LOC_NOWHERE != c->rp.cntn_loc); 1164 mhd_assert (mhd_RESPONSE_CONTENT_DATA_INVALID != r->cntn_dtype); 1165 1166 if (MHD_SIZE_UNKNOWN == r->cntn_size) 1167 left_to_send = MHD_SIZE_UNKNOWN; 1168 else 1169 left_to_send = r->cntn_size - c->rp.rsp_cntn_read_pos; 1170 1171 mhd_assert (0 != left_to_send); 1172 1173 if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc) 1174 { 1175 (void) 0; /* Nothing to do, buffers are ready */ 1176 } 1177 else if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc) 1178 { 1179 if (mhd_RESPONSE_CONTENT_DATA_CALLBACK == r->cntn_dtype) 1180 { 1181 const struct MHD_DynamicContentCreatorAction *act; 1182 size_t size_to_fill = 1183 c->write_buffer_size - c->write_buffer_append_offset; 1184 size_t filled; 1185 1186 if (size_to_fill > left_to_send) 1187 size_to_fill = (size_t) left_to_send; 1188 1189 mhd_assert (c->write_buffer_append_offset < c->write_buffer_size); 1190 mhd_assert (NULL == c->rp.app_act_ctx.connection); 1191 1192 c->rp.app_act_ctx.connection = c; 1193 c->rp.app_act.act = mhd_DCC_ACTION_NO_ACTION; 1194 1195 act = 1196 r->cntn.dyn.cb (r->cntn.dyn.cls, 1197 &(c->rp.app_act_ctx), 1198 c->rp.rsp_cntn_read_pos, 1199 (void *) 1200 (c->write_buffer + c->write_buffer_append_offset), 1201 size_to_fill); 1202 c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */ 1203 if (! preprocess_dcc_action (c, act)) 1204 return true; 1205 if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act) 1206 { 1207 mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size); 1208 mhd_assert (c->rp.props.end_by_closing); 1209 1210 c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT; 1211 1212 return true; 1213 } 1214 mhd_assert (mhd_DCC_ACTION_CONTINUE == c->rp.app_act.act); 1215 // TODO: implement iov sending 1216 1217 filled = c->rp.app_act.data.cntnue.buf_data_size; 1218 if (size_to_fill < filled) 1219 { 1220 mhd_STREAM_ABORT (c, 1221 mhd_CONN_CLOSE_APP_ERROR, 1222 "Closing connection (application returned more data " 1223 "than requested)."); 1224 return true; 1225 } 1226 c->rp.rsp_cntn_read_pos += filled; 1227 c->write_buffer_append_offset += filled; 1228 } 1229 else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype) 1230 { 1231 size_t size_to_fill = 1232 c->write_buffer_size - c->write_buffer_append_offset; 1233 size_t filled; 1234 1235 if (size_to_fill > left_to_send) 1236 size_to_fill = (size_t) left_to_send; 1237 1238 if (! read_response_file (c, 1239 r, 1240 size_to_fill, 1241 c->write_buffer + c->write_buffer_append_offset, 1242 &filled)) 1243 return true; /* Error, the stream is closed */ 1244 1245 c->rp.rsp_cntn_read_pos += filled; 1246 c->write_buffer_append_offset += filled; 1247 } 1248 else 1249 { 1250 mhd_assert (0 && "Impossible value"); 1251 mhd_UNREACHABLE (); 1252 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; 1253 c->rp.rsp_cntn_read_pos = r->cntn_size; 1254 } 1255 } 1256 else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc) 1257 { 1258 size_t copy_size; 1259 1260 mhd_assert (NULL == c->rp.resp_iov.iov); 1261 mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == r->cntn_dtype); 1262 1263 copy_size = r->cntn.iovec.cnt * sizeof(mhd_iovec); 1264 c->rp.resp_iov.iov = (mhd_iovec *) 1265 mhd_stream_alloc_memory (c, 1266 copy_size); 1267 if (NULL == c->rp.resp_iov.iov) 1268 { 1269 /* not enough memory */ 1270 mhd_STREAM_ABORT (c, 1271 mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY, 1272 "No memory in the pool for the response data."); 1273 return false; 1274 } 1275 memcpy (c->rp.resp_iov.iov, 1276 &(r->cntn.iovec.iov), 1277 copy_size); 1278 c->rp.resp_iov.cnt = r->cntn.iovec.cnt; 1279 c->rp.resp_iov.sent = 0; 1280 } 1281 #if defined(mhd_USE_SENDFILE) 1282 else if (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc) 1283 { 1284 (void) 0; /* Nothing to do, file should be read directly */ 1285 } 1286 #endif /* mhd_USE_SENDFILE */ 1287 else 1288 { 1289 mhd_assert (0 && "Impossible value"); 1290 mhd_UNREACHABLE (); 1291 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; 1292 c->rp.rsp_cntn_read_pos = r->cntn_size; 1293 } 1294 1295 c->stage = mhd_HTTP_STAGE_UNCHUNKED_BODY_READY; 1296 return false; 1297 } 1298 1299 1300 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 1301 mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c) 1302 { 1303 size_t filled; 1304 struct MHD_Response *const restrict r = c->rp.response; 1305 static const size_t max_chunk = 0xFFFFFF; 1306 char chunk_hdr[6]; /* 6: max strlen of "FFFFFF" */ 1307 /* "FFFFFF" + "\r\n" */ 1308 static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2; 1309 /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */ 1310 static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2; 1311 size_t chunk_hdr_len; 1312 uint64_t left_to_send; 1313 size_t size_to_fill; 1314 1315 mhd_assert (0 == c->write_buffer_append_offset); 1316 mhd_assert (0 == c->write_buffer_send_offset); 1317 1318 mhd_stream_call_dcc_cleanup_if_needed (c); 1319 1320 /* The buffer must be reasonably large enough */ 1321 if (32 > c->write_buffer_size) 1322 { 1323 mhd_STREAM_ABORT (c, 1324 mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY, 1325 "No memory in the pool for the reply chunked content."); 1326 return true; 1327 } 1328 mhd_assert (max_chunk_overhead < \ 1329 (c->write_buffer_size)); 1330 1331 if (MHD_SIZE_UNKNOWN == r->cntn_size) 1332 left_to_send = MHD_SIZE_UNKNOWN; 1333 else 1334 left_to_send = r->cntn_size - c->rp.rsp_cntn_read_pos; 1335 1336 mhd_assert (0 != left_to_send); 1337 if (0 != left_to_send) 1338 { 1339 size_to_fill = 1340 c->write_buffer_size - max_chunk_overhead; 1341 /* Limit size for the callback to the max usable size */ 1342 if (max_chunk < size_to_fill) 1343 size_to_fill = max_chunk; 1344 if (left_to_send < size_to_fill) 1345 size_to_fill = (size_t) left_to_send; 1346 } 1347 else 1348 size_to_fill = 0; 1349 1350 if ((0 == left_to_send) && 1351 (mhd_RESPONSE_CONTENT_DATA_CALLBACK != r->cntn_dtype)) 1352 { 1353 c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT; 1354 return true; 1355 } 1356 else if (mhd_RESPONSE_CONTENT_DATA_BUFFER == r->cntn_dtype) 1357 { 1358 mhd_assert (size_to_fill <= \ 1359 r->cntn_size - (size_t) c->rp.rsp_cntn_read_pos); 1360 memcpy (c->write_buffer + max_chunk_hdr_len, 1361 r->cntn.buf + (size_t) c->rp.rsp_cntn_read_pos, 1362 size_to_fill); 1363 filled = size_to_fill; 1364 } 1365 else if (mhd_RESPONSE_CONTENT_DATA_CALLBACK == r->cntn_dtype) 1366 { 1367 const struct MHD_DynamicContentCreatorAction *act; 1368 1369 mhd_assert (NULL == c->rp.app_act_ctx.connection); 1370 1371 c->rp.app_act_ctx.connection = c; 1372 c->rp.app_act.act = mhd_DCC_ACTION_NO_ACTION; 1373 1374 act = 1375 r->cntn.dyn.cb (r->cntn.dyn.cls, 1376 &(c->rp.app_act_ctx), 1377 c->rp.rsp_cntn_read_pos, 1378 (void *) 1379 (c->write_buffer + max_chunk_hdr_len), 1380 size_to_fill); 1381 c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */ 1382 if (! preprocess_dcc_action (c, act)) 1383 return true; 1384 if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act) 1385 { 1386 mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size); 1387 c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT; 1388 1389 return true; 1390 } 1391 mhd_assert (mhd_DCC_ACTION_CONTINUE == c->rp.app_act.act); 1392 // TODO: implement iov sending 1393 1394 filled = c->rp.app_act.data.cntnue.buf_data_size; 1395 if (size_to_fill < filled) 1396 { 1397 mhd_STREAM_ABORT (c, 1398 mhd_CONN_CLOSE_APP_ERROR, 1399 "Closing connection (application returned more data " 1400 "than requested)."); 1401 return true; 1402 } 1403 } 1404 else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype) 1405 { 1406 if (! read_response_file (c, 1407 r, 1408 size_to_fill, 1409 c->write_buffer + max_chunk_hdr_len, 1410 &filled)) 1411 return true; /* Error, the stream is closed */ 1412 } 1413 else 1414 { 1415 mhd_assert (0 && "Not implemented yet"); 1416 filled = 0; 1417 } 1418 1419 chunk_hdr_len = mhd_uint32_to_strx ((uint_fast32_t) filled, 1420 chunk_hdr, 1421 sizeof(chunk_hdr)); 1422 mhd_assert (chunk_hdr_len != 0); 1423 mhd_assert (chunk_hdr_len < sizeof(chunk_hdr)); 1424 c->write_buffer_send_offset = max_chunk_hdr_len - (chunk_hdr_len + 2); 1425 memcpy (c->write_buffer + c->write_buffer_send_offset, 1426 chunk_hdr, 1427 chunk_hdr_len); 1428 c->write_buffer[max_chunk_hdr_len - 2] = '\r'; 1429 c->write_buffer[max_chunk_hdr_len - 1] = '\n'; 1430 c->write_buffer[max_chunk_hdr_len + filled] = '\r'; 1431 c->write_buffer[max_chunk_hdr_len + filled + 1] = '\n'; 1432 c->write_buffer_append_offset = max_chunk_hdr_len + filled + 2; 1433 if (0 != filled) 1434 c->rp.rsp_cntn_read_pos += filled; 1435 else 1436 c->rp.rsp_cntn_read_pos = r->cntn_size; 1437 1438 c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_READY; 1439 1440 return false; 1441 } 1442 1443 1444 /** 1445 * Allocate the connection's write buffer (if necessary) and fill it 1446 * with response footers. 1447 * Inner version. 1448 * 1449 * @param c the connection 1450 * @return 'true' if footers formed successfully, 1451 * 'false' if not enough buffer 1452 */ 1453 static MHD_FN_PAR_NONNULL_ALL_ bool 1454 prep_chunked_footer_inn (struct MHD_Connection *restrict c) 1455 { 1456 char *buf; /**< the buffer to write footers to */ 1457 size_t buf_size; /**< the size of the @a buf */ 1458 size_t used_size; /**< the used size of the @a buf */ 1459 // struct MHD_HTTP_Res_Header *pos; 1460 1461 mhd_assert (c->rp.props.chunked); 1462 mhd_assert (mhd_HTTP_STAGE_CHUNKED_BODY_SENT == c->stage); 1463 mhd_assert (NULL != c->rp.response); 1464 1465 buf_size = mhd_stream_maximize_write_buffer (c); 1466 /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */ 1467 if (buf_size < 5) 1468 return false; 1469 mhd_assert (NULL != c->write_buffer); 1470 buf = c->write_buffer + c->write_buffer_append_offset; 1471 mhd_assert (NULL != buf); 1472 used_size = 0; 1473 buf[used_size++] = '0'; 1474 buf[used_size++] = '\r'; 1475 buf[used_size++] = '\n'; 1476 1477 #if 0 // TODO: use dynamic/connection's footers 1478 for (pos = c->rp.response->first_header; NULL != pos; pos = pos->next) 1479 { 1480 if (MHD_FOOTER_KIND == pos->kind) 1481 { 1482 size_t new_used_size; /* resulting size with this header */ 1483 /* '4' is colon, space, linefeeds */ 1484 new_used_size = used_size + pos->header_size + pos->value_size + 4; 1485 if (new_used_size > buf_size) 1486 return MHD_NO; 1487 memcpy (buf + used_size, pos->header, pos->header_size); 1488 used_size += pos->header_size; 1489 buf[used_size++] = ':'; 1490 buf[used_size++] = ' '; 1491 memcpy (buf + used_size, pos->value, pos->value_size); 1492 used_size += pos->value_size; 1493 buf[used_size++] = '\r'; 1494 buf[used_size++] = '\n'; 1495 mhd_assert (used_size == new_used_size); 1496 } 1497 } 1498 #endif 1499 1500 if (used_size + 2 > buf_size) 1501 return false; 1502 buf[used_size++] = '\r'; 1503 buf[used_size++] = '\n'; 1504 1505 c->write_buffer_append_offset += used_size; 1506 mhd_assert (c->write_buffer_append_offset <= c->write_buffer_size); 1507 1508 return true; 1509 } 1510 1511 1512 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 1513 mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c) 1514 { 1515 if (! prep_chunked_footer_inn (c)) 1516 { 1517 mhd_STREAM_ABORT (c, 1518 mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY, 1519 "No memory in the pool for the reply chunked footer."); 1520 return true; 1521 } 1522 c->stage = mhd_HTTP_STAGE_FOOTERS_SENDING; 1523 return false; 1524 }