stream_process_reply.c (48906B)
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 if (MHD_SIZE_UNKNOWN == r->cntn_size) 1133 return true; 1134 mhd_STREAM_ABORT (c, \ 1135 mhd_CONN_CLOSE_FILE_TOO_SHORT, \ 1136 "The file for file-backed response is smaller " \ 1137 "than specified by application."); 1138 return false; 1139 default: 1140 break; 1141 } 1142 mhd_UNREACHABLE (); 1143 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; 1144 return false; 1145 } 1146 1147 1148 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 1149 mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c) 1150 { 1151 struct MHD_Response *const restrict r = c->rp.response; 1152 uint64_t left_to_send; 1153 1154 mhd_assert (c->rp.props.send_reply_body); 1155 mhd_assert (c->rp.rsp_cntn_read_pos != r->cntn_size); 1156 1157 mhd_stream_call_dcc_cleanup_if_needed (c); 1158 1159 if (0 == r->cntn_size) 1160 { /* 0-byte response is always ready */ 1161 c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT; 1162 return true; 1163 } 1164 1165 mhd_assert (mhd_REPLY_CNTN_LOC_NOWHERE != c->rp.cntn_loc); 1166 mhd_assert (mhd_RESPONSE_CONTENT_DATA_INVALID != r->cntn_dtype); 1167 1168 if (MHD_SIZE_UNKNOWN == r->cntn_size) 1169 left_to_send = MHD_SIZE_UNKNOWN; 1170 else 1171 left_to_send = r->cntn_size - c->rp.rsp_cntn_read_pos; 1172 1173 mhd_assert (0 != left_to_send); 1174 1175 if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc) 1176 { 1177 (void)0; /* Nothing to do, buffers are ready */ 1178 } 1179 else if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc) 1180 { 1181 if (mhd_RESPONSE_CONTENT_DATA_CALLBACK == r->cntn_dtype) 1182 { 1183 const struct MHD_DynamicContentCreatorAction *act; 1184 size_t size_to_fill = 1185 c->write_buffer_size - c->write_buffer_append_offset; 1186 size_t filled; 1187 1188 if (size_to_fill > left_to_send) 1189 size_to_fill = (size_t)left_to_send; 1190 1191 mhd_assert (c->write_buffer_append_offset < c->write_buffer_size); 1192 mhd_assert (NULL == c->rp.app_act_ctx.connection); 1193 1194 c->rp.app_act_ctx.connection = c; 1195 c->rp.app_act.act = mhd_DCC_ACTION_NO_ACTION; 1196 1197 act = 1198 r->cntn.dyn.cb (r->cntn.dyn.cls, 1199 &(c->rp.app_act_ctx), 1200 c->rp.rsp_cntn_read_pos, 1201 (void *) 1202 (c->write_buffer + c->write_buffer_append_offset), 1203 size_to_fill); 1204 c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */ 1205 if (!preprocess_dcc_action (c, act)) 1206 return true; 1207 if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act) 1208 { 1209 mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size); 1210 mhd_assert (c->rp.props.end_by_closing); 1211 1212 c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT; 1213 1214 return true; 1215 } 1216 mhd_assert (mhd_DCC_ACTION_CONTINUE == c->rp.app_act.act); 1217 // TODO: implement iov sending 1218 1219 filled = c->rp.app_act.data.cntnue.buf_data_size; 1220 if (size_to_fill < filled) 1221 { 1222 mhd_STREAM_ABORT (c, 1223 mhd_CONN_CLOSE_APP_ERROR, 1224 "Closing connection (application returned more data " 1225 "than requested)."); 1226 return true; 1227 } 1228 c->rp.rsp_cntn_read_pos += filled; 1229 c->write_buffer_append_offset += filled; 1230 } 1231 else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype) 1232 { 1233 size_t size_to_fill = 1234 c->write_buffer_size - c->write_buffer_append_offset; 1235 size_t filled; 1236 1237 if (size_to_fill > left_to_send) 1238 size_to_fill = (size_t)left_to_send; 1239 1240 mhd_assert (c->write_buffer_append_offset < c->write_buffer_size); 1241 1242 if (!read_response_file (c, 1243 r, 1244 size_to_fill, 1245 c->write_buffer + c->write_buffer_append_offset, 1246 &filled)) 1247 return true; /* Error, the stream is closed */ 1248 if (0u == filled) 1249 { 1250 mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size); 1251 c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT; 1252 1253 return true; 1254 } 1255 1256 c->rp.rsp_cntn_read_pos += filled; 1257 c->write_buffer_append_offset += filled; 1258 } 1259 else 1260 { 1261 mhd_assert (0 && "Impossible value"); 1262 mhd_UNREACHABLE (); 1263 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; 1264 c->rp.rsp_cntn_read_pos = r->cntn_size; 1265 } 1266 } 1267 else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc) 1268 { 1269 size_t copy_size; 1270 1271 mhd_assert (NULL == c->rp.resp_iov.iov); 1272 mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == r->cntn_dtype); 1273 1274 copy_size = r->cntn.iovec.cnt * sizeof(mhd_iovec); 1275 c->rp.resp_iov.iov = (mhd_iovec *) 1276 mhd_stream_alloc_memory (c, 1277 copy_size); 1278 if (NULL == c->rp.resp_iov.iov) 1279 { 1280 /* not enough memory */ 1281 mhd_STREAM_ABORT (c, 1282 mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY, 1283 "No memory in the pool for the response data."); 1284 return false; 1285 } 1286 memcpy (c->rp.resp_iov.iov, 1287 &(r->cntn.iovec.iov), 1288 copy_size); 1289 c->rp.resp_iov.cnt = r->cntn.iovec.cnt; 1290 c->rp.resp_iov.sent = 0; 1291 } 1292 #if defined(mhd_USE_SENDFILE) 1293 else if (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc) 1294 { 1295 (void)0; /* Nothing to do, file should be read directly */ 1296 } 1297 #endif /* mhd_USE_SENDFILE */ 1298 else 1299 { 1300 mhd_assert (0 && "Impossible value"); 1301 mhd_UNREACHABLE (); 1302 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; 1303 c->rp.rsp_cntn_read_pos = r->cntn_size; 1304 } 1305 1306 c->stage = mhd_HTTP_STAGE_UNCHUNKED_BODY_READY; 1307 return false; 1308 } 1309 1310 1311 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 1312 mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c) 1313 { 1314 size_t filled; 1315 struct MHD_Response *const restrict r = c->rp.response; 1316 static const size_t max_chunk = 0xFFFFFF; 1317 char chunk_hdr[6]; /* 6: max strlen of "FFFFFF" */ 1318 /* "FFFFFF" + "\r\n" */ 1319 static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2; 1320 /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */ 1321 static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2; 1322 size_t chunk_hdr_len; 1323 uint64_t left_to_send; 1324 size_t size_to_fill; 1325 1326 mhd_assert (0 == c->write_buffer_append_offset); 1327 mhd_assert (0 == c->write_buffer_send_offset); 1328 1329 mhd_stream_call_dcc_cleanup_if_needed (c); 1330 1331 /* The buffer must be reasonably large enough */ 1332 if (32 > c->write_buffer_size) 1333 { 1334 mhd_STREAM_ABORT (c, 1335 mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY, 1336 "No memory in the pool for the reply chunked content."); 1337 return true; 1338 } 1339 mhd_assert (max_chunk_overhead < \ 1340 (c->write_buffer_size)); 1341 1342 if (MHD_SIZE_UNKNOWN == r->cntn_size) 1343 left_to_send = MHD_SIZE_UNKNOWN; 1344 else 1345 left_to_send = r->cntn_size - c->rp.rsp_cntn_read_pos; 1346 1347 mhd_assert (0 != left_to_send); 1348 if (0 != left_to_send) 1349 { 1350 size_to_fill = 1351 c->write_buffer_size - max_chunk_overhead; 1352 /* Limit size for the callback to the max usable size */ 1353 if (max_chunk < size_to_fill) 1354 size_to_fill = max_chunk; 1355 if (left_to_send < size_to_fill) 1356 size_to_fill = (size_t)left_to_send; 1357 } 1358 else 1359 size_to_fill = 0; 1360 1361 if ((0 == left_to_send) 1362 && (mhd_RESPONSE_CONTENT_DATA_CALLBACK != r->cntn_dtype)) 1363 { 1364 c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT; 1365 return true; 1366 } 1367 else if (mhd_RESPONSE_CONTENT_DATA_BUFFER == r->cntn_dtype) 1368 { 1369 mhd_assert (size_to_fill <= \ 1370 r->cntn_size - (size_t)c->rp.rsp_cntn_read_pos); 1371 memcpy (c->write_buffer + max_chunk_hdr_len, 1372 r->cntn.buf + (size_t)c->rp.rsp_cntn_read_pos, 1373 size_to_fill); 1374 filled = size_to_fill; 1375 } 1376 else if (mhd_RESPONSE_CONTENT_DATA_CALLBACK == r->cntn_dtype) 1377 { 1378 const struct MHD_DynamicContentCreatorAction *act; 1379 1380 mhd_assert (NULL == c->rp.app_act_ctx.connection); 1381 1382 c->rp.app_act_ctx.connection = c; 1383 c->rp.app_act.act = mhd_DCC_ACTION_NO_ACTION; 1384 1385 act = 1386 r->cntn.dyn.cb (r->cntn.dyn.cls, 1387 &(c->rp.app_act_ctx), 1388 c->rp.rsp_cntn_read_pos, 1389 (void *) 1390 (c->write_buffer + max_chunk_hdr_len), 1391 size_to_fill); 1392 c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */ 1393 if (!preprocess_dcc_action (c, act)) 1394 return true; 1395 if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act) 1396 { 1397 mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size); 1398 c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT; 1399 1400 return true; 1401 } 1402 mhd_assert (mhd_DCC_ACTION_CONTINUE == c->rp.app_act.act); 1403 // TODO: implement iov sending 1404 1405 filled = c->rp.app_act.data.cntnue.buf_data_size; 1406 if (size_to_fill < filled) 1407 { 1408 mhd_STREAM_ABORT (c, 1409 mhd_CONN_CLOSE_APP_ERROR, 1410 "Closing connection (application returned more data " 1411 "than requested)."); 1412 return true; 1413 } 1414 } 1415 else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype) 1416 { 1417 if (!read_response_file (c, 1418 r, 1419 size_to_fill, 1420 c->write_buffer + max_chunk_hdr_len, 1421 &filled)) 1422 return true; /* Error, the stream is closed */ 1423 if (0u == filled) 1424 { 1425 mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size); 1426 c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT; 1427 1428 return true; 1429 } 1430 } 1431 else 1432 { 1433 mhd_assert (0 && "Not implemented yet"); 1434 filled = 0; 1435 } 1436 1437 chunk_hdr_len = mhd_uint32_to_strx ((uint_fast32_t)filled, 1438 chunk_hdr, 1439 sizeof(chunk_hdr)); 1440 mhd_assert (chunk_hdr_len != 0); 1441 mhd_assert (chunk_hdr_len < sizeof(chunk_hdr)); 1442 c->write_buffer_send_offset = max_chunk_hdr_len - (chunk_hdr_len + 2); 1443 memcpy (c->write_buffer + c->write_buffer_send_offset, 1444 chunk_hdr, 1445 chunk_hdr_len); 1446 c->write_buffer[max_chunk_hdr_len - 2] = '\r'; 1447 c->write_buffer[max_chunk_hdr_len - 1] = '\n'; 1448 c->write_buffer[max_chunk_hdr_len + filled] = '\r'; 1449 c->write_buffer[max_chunk_hdr_len + filled + 1] = '\n'; 1450 c->write_buffer_append_offset = max_chunk_hdr_len + filled + 2; 1451 if (0 != filled) 1452 c->rp.rsp_cntn_read_pos += filled; 1453 else 1454 c->rp.rsp_cntn_read_pos = r->cntn_size; 1455 1456 c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_READY; 1457 1458 return false; 1459 } 1460 1461 1462 /** 1463 * Allocate the connection's write buffer (if necessary) and fill it 1464 * with response footers. 1465 * Inner version. 1466 * 1467 * @param c the connection 1468 * @return 'true' if footers formed successfully, 1469 * 'false' if not enough buffer 1470 */ 1471 static MHD_FN_PAR_NONNULL_ALL_ bool 1472 prep_chunked_footer_inn (struct MHD_Connection *restrict c) 1473 { 1474 char *buf; /**< the buffer to write footers to */ 1475 size_t buf_size; /**< the size of the @a buf */ 1476 size_t used_size; /**< the used size of the @a buf */ 1477 // struct MHD_HTTP_Res_Header *pos; 1478 1479 mhd_assert (c->rp.props.chunked); 1480 mhd_assert (mhd_HTTP_STAGE_CHUNKED_BODY_SENT == c->stage); 1481 mhd_assert (NULL != c->rp.response); 1482 1483 buf_size = mhd_stream_maximize_write_buffer (c); 1484 /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */ 1485 if (buf_size < 5) 1486 return false; 1487 mhd_assert (NULL != c->write_buffer); 1488 buf = c->write_buffer + c->write_buffer_append_offset; 1489 mhd_assert (NULL != buf); 1490 used_size = 0; 1491 buf[used_size++] = '0'; 1492 buf[used_size++] = '\r'; 1493 buf[used_size++] = '\n'; 1494 1495 #if 0 // TODO: use dynamic/connection's footers 1496 for (pos = c->rp.response->first_header; NULL != pos; pos = pos->next) 1497 { 1498 if (MHD_FOOTER_KIND == pos->kind) 1499 { 1500 size_t new_used_size; /* resulting size with this header */ 1501 /* '4' is colon, space, linefeeds */ 1502 new_used_size = used_size + pos->header_size + pos->value_size + 4; 1503 if (new_used_size > buf_size) 1504 return MHD_NO; 1505 memcpy (buf + used_size, pos->header, pos->header_size); 1506 used_size += pos->header_size; 1507 buf[used_size++] = ':'; 1508 buf[used_size++] = ' '; 1509 memcpy (buf + used_size, pos->value, pos->value_size); 1510 used_size += pos->value_size; 1511 buf[used_size++] = '\r'; 1512 buf[used_size++] = '\n'; 1513 mhd_assert (used_size == new_used_size); 1514 } 1515 } 1516 #endif 1517 1518 if (used_size + 2 > buf_size) 1519 return false; 1520 buf[used_size++] = '\r'; 1521 buf[used_size++] = '\n'; 1522 1523 c->write_buffer_append_offset += used_size; 1524 mhd_assert (c->write_buffer_append_offset <= c->write_buffer_size); 1525 1526 return true; 1527 } 1528 1529 1530 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 1531 mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c) 1532 { 1533 if (!prep_chunked_footer_inn (c)) 1534 { 1535 mhd_STREAM_ABORT (c, 1536 mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY, 1537 "No memory in the pool for the reply chunked footer."); 1538 return true; 1539 } 1540 c->stage = mhd_HTTP_STAGE_FOOTERS_SENDING; 1541 return false; 1542 }