stream_process_reply.c (46286B)
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 pos += r->special_resp.spec_hdr_len; 811 buf[pos++] = '\r'; 812 buf[pos++] = '\n'; 813 } 814 815 /* Main automatic headers */ 816 817 /* The "Date:" header */ 818 if ( (! r->cfg.has_hdr_date) && 819 (! c->daemon->req_cfg.suppress_date) ) 820 { 821 /* Additional byte for unused zero-termination */ 822 if (buf_size < pos + 38) 823 return false; 824 if (get_date_header (buf + pos)) 825 pos += 37; 826 } 827 /* The "Connection:" header */ 828 mhd_assert (! use_conn_close || ! use_conn_k_alive); 829 mhd_assert (! use_conn_k_alive || ! use_conn_close); 830 if (! r->cfg.has_hdr_conn) 831 { 832 if (use_conn_close) 833 { 834 if (! buffer_append_s (buf, &pos, buf_size, 835 MHD_HTTP_HEADER_CONNECTION ": close\r\n")) 836 return false; 837 } 838 else if (use_conn_k_alive) 839 { 840 if (! buffer_append_s (buf, &pos, buf_size, 841 MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n")) 842 return false; 843 } 844 use_conn_close = false; 845 use_conn_k_alive = false; 846 } 847 848 /* Special headers */ 849 #ifdef MHD_SUPPORT_AUTH_DIGEST 850 if (mhd_RESP_HAS_AUTH_DIGEST (r)) 851 { 852 char noncestr[mhd_AUTH_DIGEST_NONCE_LEN]; 853 const struct mhd_RespAuthDigestHeader *dg_hdr; 854 if (! mhd_auth_digest_get_new_nonce (c, 855 noncestr)) 856 { 857 mhd_STREAM_ABORT (c, 858 mhd_CONN_CLOSE_NONCE_ERROR, 859 "Failed to generate a new nonce for Digest Auth."); 860 return false; 861 } 862 for (dg_hdr = mhd_DLINKEDL_GET_FIRST (r, auth_d_hdrs); 863 NULL != dg_hdr; 864 dg_hdr = mhd_DLINKEDL_GET_NEXT (dg_hdr, auth_d_hdrs)) 865 { 866 size_t nonce_pos; 867 nonce_pos = pos + dg_hdr->nonce_pos; 868 if (! buffer_append_mstr (buf, &pos, buf_size, \ 869 &(dg_hdr->hdr))) 870 return false; 871 memcpy (buf + nonce_pos, 872 noncestr, 873 sizeof(noncestr)); 874 } 875 } 876 #endif /* MHD_SUPPORT_AUTH_DIGEST */ 877 878 /* User-defined headers */ 879 880 if (! add_user_headers (buf, &pos, buf_size, r, 881 ! c->rp.props.use_reply_body_headers, 882 use_conn_close, 883 use_conn_k_alive)) 884 return false; 885 886 /* Other automatic headers */ 887 888 if (c->rp.props.use_reply_body_headers) 889 { 890 /* Body-specific headers */ 891 892 if (c->rp.props.chunked) 893 { /* Chunked encoding is used */ 894 mhd_assert (! c->rp.props.end_by_closing); 895 if (! buffer_append_s (buf, &pos, buf_size, 896 MHD_HTTP_HEADER_TRANSFER_ENCODING ": " \ 897 "chunked\r\n")) 898 return false; 899 } 900 else /* Chunked encoding is not used */ 901 { 902 if ((MHD_SIZE_UNKNOWN != r->cntn_size) && 903 (! c->rp.props.end_by_closing) && 904 (! r->cfg.chunked) && 905 (! r->cfg.head_only)) 906 { /* The size is known and can be indicated by the header */ 907 if (! r->cfg.cnt_len_by_app) 908 { /* The response does not have app-defined "Content-Length" header */ 909 if (! buffer_append_s (buf, &pos, buf_size, 910 MHD_HTTP_HEADER_CONTENT_LENGTH ": ")) 911 return false; 912 el_size = mhd_uint64_to_str (r->cntn_size, 913 buf + pos, 914 buf_size - pos); 915 if (0 == el_size) 916 return false; 917 pos += el_size; 918 919 if (buf_size < pos + 2) 920 return false; 921 buf[pos++] = '\r'; 922 buf[pos++] = '\n'; 923 } 924 } 925 else 926 { 927 mhd_assert ((! c->rp.props.send_reply_body) || \ 928 (mhd_CONN_MUST_CLOSE == c->conn_reuse)); 929 (void) 0; 930 } 931 } 932 } 933 934 /* * Header termination * */ 935 if (buf_size < pos + 2) 936 return false; 937 buf[pos++] = '\r'; 938 buf[pos++] = '\n'; 939 940 c->write_buffer_append_offset = pos; 941 return true; 942 } 943 944 945 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 946 mhd_stream_build_header_response (struct MHD_Connection *restrict c) 947 { 948 if (! build_header_response_inn (c)) 949 { 950 #ifdef MHD_SUPPORT_AUTH_DIGEST 951 if (mhd_HTTP_STAGE_PRE_CLOSING <= c->stage) 952 return false; /* Already started closing */ 953 #else /* ! MHD_SUPPORT_AUTH_DIGEST */ 954 mhd_assert (mhd_HTTP_STAGE_START_REPLY == c->stage); 955 #endif /* ! MHD_SUPPORT_AUTH_DIGEST */ 956 mhd_STREAM_ABORT (c, 957 mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY, 958 "No memory in the pool for the reply headers."); 959 return false; 960 } 961 c->stage = mhd_HTTP_STAGE_HEADERS_SENDING; 962 return true; 963 } 964 965 966 /** 967 * Pre-process DCC action provided by application. 968 * 'abort' and 'suspend' actions are fully processed, 969 * 'continue' and 'finish' actions needs to be processed by the caller. 970 * @param c the stream to use 971 * @param act the action provided by application 972 * @return 'true' if action if 'continue' or 'finish' and need to be 973 * processed, 974 * 'false' if action is 'suspend' or 'abort' and is already processed. 975 */ 976 static MHD_FN_PAR_NONNULL_ (1) bool 977 preprocess_dcc_action (struct MHD_Connection *restrict c, 978 const struct MHD_DynamicContentCreatorAction *act) 979 { 980 /** 981 * The action created for the current request 982 */ 983 struct MHD_DynamicContentCreatorAction *const a = 984 &(c->rp.app_act); 985 986 if (NULL != act) 987 { 988 if ((a != act) || 989 ! mhd_DCC_ACTION_IS_VALID (c->rp.app_act.act) || 990 ((MHD_SIZE_UNKNOWN != c->rp.response->cntn_size) && 991 (mhd_DCC_ACTION_FINISH == c->rp.app_act.act))) 992 { 993 mhd_LOG_MSG (c->daemon, MHD_SC_ACTION_INVALID, \ 994 "Provided Dynamic Content Creator action is not " \ 995 "a correct action generated for the current request."); 996 act = NULL; 997 } 998 } 999 if (NULL == act) 1000 a->act = mhd_DCC_ACTION_ABORT; 1001 1002 switch (a->act) 1003 { 1004 case mhd_DCC_ACTION_CONTINUE: 1005 return true; 1006 case mhd_DCC_ACTION_FINISH: 1007 mhd_assert (MHD_SIZE_UNKNOWN == c->rp.response->cntn_size); 1008 return true; 1009 case mhd_DCC_ACTION_SUSPEND: 1010 mhd_assert (0 && "Not implemented yet"); 1011 // TODO: Implement suspend; 1012 mhd_STREAM_ABORT (c, 1013 mhd_CONN_CLOSE_INT_ERROR, 1014 "Suspending connection is not implemented yet"); 1015 return false; 1016 case mhd_DCC_ACTION_ABORT: 1017 mhd_STREAM_ABORT (c, 1018 mhd_CONN_CLOSE_APP_ABORTED, 1019 "Dynamic Content Creator requested abort " \ 1020 "of the request"); 1021 return false; 1022 case mhd_DCC_ACTION_NO_ACTION: 1023 default: 1024 break; 1025 } 1026 mhd_UNREACHABLE (); 1027 mhd_STREAM_ABORT (c, 1028 mhd_CONN_CLOSE_INT_ERROR, 1029 "Impossible code path"); 1030 return false; 1031 } 1032 1033 1034 /** 1035 * Read next portion of the response file to the buffer, based on information 1036 * about amount of response content position. 1037 * Handle file read errors, does NOT update response position (caller 1038 * should do so on success). 1039 * @param c the stream to use 1040 * @param r the response to use 1041 * @param buf_size the size of the @a buf buffer 1042 * @param[out] buf the buffer to fill with the file data 1043 * @param[out] size_filled the pointer to variable to get the size of the data 1044 * actually put to the @a buffer 1045 * @return 'true' if succeed (size_filled and response position are updated), 1046 * 'false' if failed (the stream is closed) 1047 */ 1048 static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ 1049 MHD_FN_PAR_OUT_SIZE_ (4, 3) MHD_FN_PAR_OUT_ (5) bool 1050 read_response_file (struct MHD_Connection *restrict c, 1051 struct MHD_Response *const restrict r, 1052 size_t buf_size, 1053 char buf[MHD_FN_PAR_DYN_ARR_SIZE_ (buf_size)], 1054 size_t *restrict size_filled) 1055 { 1056 mhd_assert (r == c->rp.response); 1057 mhd_assert (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype); 1058 1059 switch (mhd_read_file (r->cntn.file.fd, 1060 r->cntn.file.offset + c->rp.rsp_cntn_read_pos, 1061 buf_size, 1062 buf, 1063 size_filled)) 1064 { 1065 case mhd_FILE_READ_OK: 1066 break; 1067 case mhd_FILE_READ_ERROR: 1068 mhd_STREAM_ABORT (c, \ 1069 mhd_CONN_CLOSE_FILE_READ_ERROR, \ 1070 "Error reading file for file-backed response."); 1071 return false; 1072 case mhd_FILE_READ_OFFSET_TOO_LARGE: 1073 mhd_STREAM_ABORT (c, \ 1074 mhd_CONN_CLOSE_FILE_OFFSET_TOO_LARGE, \ 1075 "The offset for file-backed response is too large " \ 1076 "for this platform."); 1077 return false; 1078 case mhd_FILE_READ_EOF: 1079 mhd_STREAM_ABORT (c, \ 1080 mhd_CONN_CLOSE_FILE_TOO_SHORT, \ 1081 "The file for file-backed response is smaller " \ 1082 "than specified by application."); 1083 return false; 1084 default: 1085 mhd_UNREACHABLE (); 1086 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; 1087 return false; 1088 } 1089 1090 return true; 1091 } 1092 1093 1094 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 1095 mhd_stream_prep_unchunked_body (struct MHD_Connection *restrict c) 1096 { 1097 struct MHD_Response *const restrict r = c->rp.response; 1098 uint64_t left_to_send; 1099 1100 mhd_assert (c->rp.props.send_reply_body); 1101 mhd_assert (c->rp.rsp_cntn_read_pos != r->cntn_size); 1102 1103 mhd_stream_call_dcc_cleanup_if_needed (c); 1104 1105 if (0 == r->cntn_size) 1106 { /* 0-byte response is always ready */ 1107 c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT; 1108 return true; 1109 } 1110 1111 mhd_assert (mhd_REPLY_CNTN_LOC_NOWHERE != c->rp.cntn_loc); 1112 mhd_assert (mhd_RESPONSE_CONTENT_DATA_INVALID != r->cntn_dtype); 1113 1114 if (MHD_SIZE_UNKNOWN == r->cntn_size) 1115 left_to_send = MHD_SIZE_UNKNOWN; 1116 else 1117 left_to_send = r->cntn_size - c->rp.rsp_cntn_read_pos; 1118 1119 mhd_assert (0 != left_to_send); 1120 1121 if (mhd_REPLY_CNTN_LOC_RESP_BUF == c->rp.cntn_loc) 1122 { 1123 (void) 0; /* Nothing to do, buffers are ready */ 1124 } 1125 else if (mhd_REPLY_CNTN_LOC_CONN_BUF == c->rp.cntn_loc) 1126 { 1127 if (mhd_RESPONSE_CONTENT_DATA_CALLBACK == r->cntn_dtype) 1128 { 1129 const struct MHD_DynamicContentCreatorAction *act; 1130 size_t size_to_fill = 1131 c->write_buffer_size - c->write_buffer_append_offset; 1132 size_t filled; 1133 1134 if (size_to_fill > left_to_send) 1135 size_to_fill = (size_t) left_to_send; 1136 1137 mhd_assert (c->write_buffer_append_offset < c->write_buffer_size); 1138 mhd_assert (NULL == c->rp.app_act_ctx.connection); 1139 1140 c->rp.app_act_ctx.connection = c; 1141 c->rp.app_act.act = mhd_DCC_ACTION_NO_ACTION; 1142 1143 act = 1144 r->cntn.dyn.cb (r->cntn.dyn.cls, 1145 &(c->rp.app_act_ctx), 1146 c->rp.rsp_cntn_read_pos, 1147 (void *) 1148 (c->write_buffer + c->write_buffer_append_offset), 1149 size_to_fill); 1150 c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */ 1151 if (! preprocess_dcc_action (c, act)) 1152 return true; 1153 if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act) 1154 { 1155 mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size); 1156 mhd_assert (c->rp.props.end_by_closing); 1157 1158 c->stage = mhd_HTTP_STAGE_FULL_REPLY_SENT; 1159 1160 return true; 1161 } 1162 mhd_assert (mhd_DCC_ACTION_CONTINUE == c->rp.app_act.act); 1163 // TODO: implement iov sending 1164 1165 filled = c->rp.app_act.data.cntnue.buf_data_size; 1166 if (size_to_fill < filled) 1167 { 1168 mhd_STREAM_ABORT (c, 1169 mhd_CONN_CLOSE_APP_ERROR, 1170 "Closing connection (application returned more data " 1171 "than requested)."); 1172 return true; 1173 } 1174 c->rp.rsp_cntn_read_pos += filled; 1175 c->write_buffer_append_offset += filled; 1176 } 1177 else if (mhd_RESPONSE_CONTENT_DATA_FILE == r->cntn_dtype) 1178 { 1179 size_t size_to_fill = 1180 c->write_buffer_size - c->write_buffer_append_offset; 1181 size_t filled; 1182 1183 if (size_to_fill > left_to_send) 1184 size_to_fill = (size_t) left_to_send; 1185 1186 if (! read_response_file (c, 1187 r, 1188 size_to_fill, 1189 c->write_buffer + c->write_buffer_append_offset, 1190 &filled)) 1191 return true; /* Error, the stream is closed */ 1192 1193 c->rp.rsp_cntn_read_pos += filled; 1194 c->write_buffer_append_offset += filled; 1195 } 1196 else 1197 { 1198 mhd_assert (0 && "Impossible value"); 1199 mhd_UNREACHABLE (); 1200 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; 1201 c->rp.rsp_cntn_read_pos = r->cntn_size; 1202 } 1203 } 1204 else if (mhd_REPLY_CNTN_LOC_IOV == c->rp.cntn_loc) 1205 { 1206 size_t copy_size; 1207 1208 mhd_assert (NULL == c->rp.resp_iov.iov); 1209 mhd_assert (mhd_RESPONSE_CONTENT_DATA_IOVEC == r->cntn_dtype); 1210 1211 copy_size = r->cntn.iovec.cnt * sizeof(mhd_iovec); 1212 c->rp.resp_iov.iov = (mhd_iovec *) 1213 mhd_stream_alloc_memory (c, 1214 copy_size); 1215 if (NULL == c->rp.resp_iov.iov) 1216 { 1217 /* not enough memory */ 1218 mhd_STREAM_ABORT (c, 1219 mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY, 1220 "No memory in the pool for the response data."); 1221 return false; 1222 } 1223 memcpy (c->rp.resp_iov.iov, 1224 &(r->cntn.iovec.iov), 1225 copy_size); 1226 c->rp.resp_iov.cnt = r->cntn.iovec.cnt; 1227 c->rp.resp_iov.sent = 0; 1228 } 1229 #if defined(mhd_USE_SENDFILE) 1230 else if (mhd_REPLY_CNTN_LOC_FILE == c->rp.cntn_loc) 1231 { 1232 (void) 0; /* Nothing to do, file should be read directly */ 1233 } 1234 #endif /* mhd_USE_SENDFILE */ 1235 else 1236 { 1237 mhd_assert (0 && "Impossible value"); 1238 mhd_UNREACHABLE (); 1239 c->rp.cntn_loc = mhd_REPLY_CNTN_LOC_NOWHERE; 1240 c->rp.rsp_cntn_read_pos = r->cntn_size; 1241 } 1242 1243 c->stage = mhd_HTTP_STAGE_UNCHUNKED_BODY_READY; 1244 return false; 1245 } 1246 1247 1248 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 1249 mhd_stream_prep_chunked_body (struct MHD_Connection *restrict c) 1250 { 1251 size_t filled; 1252 struct MHD_Response *const restrict r = c->rp.response; 1253 static const size_t max_chunk = 0xFFFFFF; 1254 char chunk_hdr[6]; /* 6: max strlen of "FFFFFF" */ 1255 /* "FFFFFF" + "\r\n" */ 1256 static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2; 1257 /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */ 1258 static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2; 1259 size_t chunk_hdr_len; 1260 uint64_t left_to_send; 1261 size_t size_to_fill; 1262 1263 mhd_assert (0 == c->write_buffer_append_offset); 1264 mhd_assert (0 == c->write_buffer_send_offset); 1265 1266 mhd_stream_call_dcc_cleanup_if_needed (c); 1267 1268 /* The buffer must be reasonably large enough */ 1269 if (32 > c->write_buffer_size) 1270 { 1271 mhd_STREAM_ABORT (c, 1272 mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY, 1273 "No memory in the pool for the reply chunked content."); 1274 return true; 1275 } 1276 mhd_assert (max_chunk_overhead < \ 1277 (c->write_buffer_size)); 1278 1279 if (MHD_SIZE_UNKNOWN == r->cntn_size) 1280 left_to_send = MHD_SIZE_UNKNOWN; 1281 else 1282 left_to_send = r->cntn_size - c->rp.rsp_cntn_read_pos; 1283 1284 mhd_assert (0 != left_to_send); 1285 if (0 != left_to_send) 1286 { 1287 size_to_fill = 1288 c->write_buffer_size - max_chunk_overhead; 1289 /* Limit size for the callback to the max usable size */ 1290 if (max_chunk < size_to_fill) 1291 size_to_fill = max_chunk; 1292 if (left_to_send < size_to_fill) 1293 size_to_fill = (size_t) left_to_send; 1294 } 1295 else 1296 size_to_fill = 0; 1297 1298 if ((0 == left_to_send) && 1299 (mhd_RESPONSE_CONTENT_DATA_CALLBACK != r->cntn_dtype)) 1300 { 1301 c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT; 1302 return true; 1303 } 1304 else if (mhd_RESPONSE_CONTENT_DATA_BUFFER == r->cntn_dtype) 1305 { 1306 mhd_assert (size_to_fill <= \ 1307 r->cntn_size - (size_t) c->rp.rsp_cntn_read_pos); 1308 memcpy (c->write_buffer + max_chunk_hdr_len, 1309 r->cntn.buf + (size_t) c->rp.rsp_cntn_read_pos, 1310 size_to_fill); 1311 filled = size_to_fill; 1312 } 1313 else if (mhd_RESPONSE_CONTENT_DATA_CALLBACK == r->cntn_dtype) 1314 { 1315 const struct MHD_DynamicContentCreatorAction *act; 1316 1317 mhd_assert (NULL == c->rp.app_act_ctx.connection); 1318 1319 c->rp.app_act_ctx.connection = c; 1320 c->rp.app_act.act = mhd_DCC_ACTION_NO_ACTION; 1321 1322 act = 1323 r->cntn.dyn.cb (r->cntn.dyn.cls, 1324 &(c->rp.app_act_ctx), 1325 c->rp.rsp_cntn_read_pos, 1326 (void *) 1327 (c->write_buffer + max_chunk_hdr_len), 1328 size_to_fill); 1329 c->rp.app_act_ctx.connection = NULL; /* Block any attempt to create a new action */ 1330 if (! preprocess_dcc_action (c, act)) 1331 return true; 1332 if (mhd_DCC_ACTION_FINISH == c->rp.app_act.act) 1333 { 1334 mhd_assert (MHD_SIZE_UNKNOWN == r->cntn_size); 1335 c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_SENT; 1336 1337 return true; 1338 } 1339 mhd_assert (mhd_DCC_ACTION_CONTINUE == c->rp.app_act.act); 1340 // TODO: implement iov sending 1341 1342 filled = c->rp.app_act.data.cntnue.buf_data_size; 1343 if (size_to_fill < filled) 1344 { 1345 mhd_STREAM_ABORT (c, 1346 mhd_CONN_CLOSE_APP_ERROR, 1347 "Closing connection (application returned more data " 1348 "than requested)."); 1349 return true; 1350 } 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 else 1362 { 1363 mhd_assert (0 && "Not implemented yet"); 1364 filled = 0; 1365 } 1366 1367 chunk_hdr_len = mhd_uint32_to_strx ((uint_fast32_t) filled, 1368 chunk_hdr, 1369 sizeof(chunk_hdr)); 1370 mhd_assert (chunk_hdr_len != 0); 1371 mhd_assert (chunk_hdr_len < sizeof(chunk_hdr)); 1372 c->write_buffer_send_offset = max_chunk_hdr_len - (chunk_hdr_len + 2); 1373 memcpy (c->write_buffer + c->write_buffer_send_offset, 1374 chunk_hdr, 1375 chunk_hdr_len); 1376 c->write_buffer[max_chunk_hdr_len - 2] = '\r'; 1377 c->write_buffer[max_chunk_hdr_len - 1] = '\n'; 1378 c->write_buffer[max_chunk_hdr_len + filled] = '\r'; 1379 c->write_buffer[max_chunk_hdr_len + filled + 1] = '\n'; 1380 c->write_buffer_append_offset = max_chunk_hdr_len + filled + 2; 1381 if (0 != filled) 1382 c->rp.rsp_cntn_read_pos += filled; 1383 else 1384 c->rp.rsp_cntn_read_pos = r->cntn_size; 1385 1386 c->stage = mhd_HTTP_STAGE_CHUNKED_BODY_READY; 1387 1388 return false; 1389 } 1390 1391 1392 /** 1393 * Allocate the connection's write buffer (if necessary) and fill it 1394 * with response footers. 1395 * Inner version. 1396 * 1397 * @param c the connection 1398 * @return 'true' if footers formed successfully, 1399 * 'false' if not enough buffer 1400 */ 1401 static MHD_FN_PAR_NONNULL_ALL_ bool 1402 prep_chunked_footer_inn (struct MHD_Connection *restrict c) 1403 { 1404 char *buf; /**< the buffer to write footers to */ 1405 size_t buf_size; /**< the size of the @a buf */ 1406 size_t used_size; /**< the used size of the @a buf */ 1407 // struct MHD_HTTP_Res_Header *pos; 1408 1409 mhd_assert (c->rp.props.chunked); 1410 mhd_assert (mhd_HTTP_STAGE_CHUNKED_BODY_SENT == c->stage); 1411 mhd_assert (NULL != c->rp.response); 1412 1413 buf_size = mhd_stream_maximize_write_buffer (c); 1414 /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */ 1415 if (buf_size < 5) 1416 return false; 1417 mhd_assert (NULL != c->write_buffer); 1418 buf = c->write_buffer + c->write_buffer_append_offset; 1419 mhd_assert (NULL != buf); 1420 used_size = 0; 1421 buf[used_size++] = '0'; 1422 buf[used_size++] = '\r'; 1423 buf[used_size++] = '\n'; 1424 1425 #if 0 // TODO: use dynamic/connection's footers 1426 for (pos = c->rp.response->first_header; NULL != pos; pos = pos->next) 1427 { 1428 if (MHD_FOOTER_KIND == pos->kind) 1429 { 1430 size_t new_used_size; /* resulting size with this header */ 1431 /* '4' is colon, space, linefeeds */ 1432 new_used_size = used_size + pos->header_size + pos->value_size + 4; 1433 if (new_used_size > buf_size) 1434 return MHD_NO; 1435 memcpy (buf + used_size, pos->header, pos->header_size); 1436 used_size += pos->header_size; 1437 buf[used_size++] = ':'; 1438 buf[used_size++] = ' '; 1439 memcpy (buf + used_size, pos->value, pos->value_size); 1440 used_size += pos->value_size; 1441 buf[used_size++] = '\r'; 1442 buf[used_size++] = '\n'; 1443 mhd_assert (used_size == new_used_size); 1444 } 1445 } 1446 #endif 1447 1448 if (used_size + 2 > buf_size) 1449 return false; 1450 buf[used_size++] = '\r'; 1451 buf[used_size++] = '\n'; 1452 1453 c->write_buffer_append_offset += used_size; 1454 mhd_assert (c->write_buffer_append_offset <= c->write_buffer_size); 1455 1456 return true; 1457 } 1458 1459 1460 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ bool 1461 mhd_stream_prep_chunked_footer (struct MHD_Connection *restrict c) 1462 { 1463 if (! prep_chunked_footer_inn (c)) 1464 { 1465 mhd_STREAM_ABORT (c, 1466 mhd_CONN_CLOSE_NO_POOL_MEM_FOR_REPLY, 1467 "No memory in the pool for the reply chunked footer."); 1468 return true; 1469 } 1470 c->stage = mhd_HTTP_STAGE_FOOTERS_SENDING; 1471 return false; 1472 }