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