GNU libmicrohttpd 1.0.2
Loading...
Searching...
No Matches
connection.c
Go to the documentation of this file.
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
4 Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20*/
28#include "internal.h"
29#include "mhd_limits.h"
30#include "connection.h"
31#include "memorypool.h"
32#include "response.h"
33#include "mhd_mono_clock.h"
34#include "mhd_str.h"
35#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
36#include "mhd_locks.h"
37#endif
38#include "mhd_sockets.h"
39#include "mhd_compat.h"
40#include "mhd_itc.h"
41#ifdef MHD_LINUX_SOLARIS_SENDFILE
42#include <sys/sendfile.h>
43#endif /* MHD_LINUX_SOLARIS_SENDFILE */
44#if defined(HAVE_FREEBSD_SENDFILE) || defined(HAVE_DARWIN_SENDFILE)
45#include <sys/types.h>
46#include <sys/socket.h>
47#include <sys/uio.h>
48#endif /* HAVE_FREEBSD_SENDFILE || HAVE_DARWIN_SENDFILE */
49#ifdef HTTPS_SUPPORT
50#include "connection_https.h"
51#endif /* HTTPS_SUPPORT */
52#ifdef HAVE_SYS_PARAM_H
53/* For FreeBSD version identification */
54#include <sys/param.h>
55#endif /* HAVE_SYS_PARAM_H */
56#include "mhd_send.h"
57#include "mhd_assert.h"
58
65#define MHD_ALLOW_BARE_LF_AS_CRLF_(discp_lvl) (0 >= discp_lvl)
66
75#define MHD_CHUNK_HEADER_REASONABLE_LEN 24
76
80#define HTTP_100_CONTINUE "HTTP/1.1 100 Continue\r\n\r\n"
81
86#ifdef HAVE_MESSAGES
87#define ERR_MSG_REQUEST_TOO_BIG \
88 "<html>" \
89 "<head><title>Request too big</title></head>" \
90 "<body>Request HTTP header is too big for the memory constraints " \
91 "of this webserver.</body>" \
92 "</html>"
93#else
94#define ERR_MSG_REQUEST_TOO_BIG ""
95#endif
96
100#ifdef HAVE_MESSAGES
101#define ERR_MSG_REQUEST_HEADER_TOO_BIG \
102 "<html>" \
103 "<head><title>Request too big</title></head>" \
104 "<body><p>The total size of the request headers, which includes the " \
105 "request target and the request field lines, exceeds the memory " \
106 "constraints of this web server.</p>" \
107 "<p>The request could be re-tried with shorter field lines, a shorter " \
108 "request target or a shorter request method token.</p></body>" \
109 "</html>"
110#else
111#define ERR_MSG_REQUEST_HEADER_TOO_BIG ""
112#endif
113
117#ifdef HAVE_MESSAGES
118#define ERR_MSG_REQUEST_HEADER_WITH_COOKIES_TOO_BIG \
119 "<html>" \
120 "<head><title>Request too big</title></head>" \
121 "<body><p>The total size of the request headers, which includes the " \
122 "request target and the request field lines, exceeds the memory " \
123 "constraints of this web server.</p> " \
124 "<p>The request could be re-tried with smaller " \
125 "<b>&quot;Cookie:&quot;</b> field value, shorter other field lines, " \
126 "a shorter request target or a shorter request method token.</p></body> " \
127 "</html>"
128#else
129#define ERR_MSG_REQUEST_HEADER_WITH_COOKIES_TOO_BIG ""
130#endif
131
136#ifdef HAVE_MESSAGES
137#define ERR_MSG_REQUEST_CHUNK_LINE_EXT_TOO_BIG \
138 "<html>" \
139 "<head><title>Request too big</title></head>" \
140 "<body><p>The total size of the request target, the request field lines " \
141 "and the chunk size line exceeds the memory constraints of this web " \
142 "server.</p>" \
143 "<p>The request could be re-tried without chunk extensions, with a smaller " \
144 "chunk size, shorter field lines, a shorter request target or a shorter " \
145 "request method token.</p></body>" \
146 "</html>"
147#else
148#define ERR_MSG_REQUEST_CHUNK_LINE_EXT_TOO_BIG ""
149#endif
150
155#ifdef HAVE_MESSAGES
156#define ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG \
157 "<html>" \
158 "<head><title>Request too big</title></head>" \
159 "<body><p>The total size of the request target, the request field lines " \
160 "and the chunk size line exceeds the memory constraints of this web " \
161 "server.</p>" \
162 "<p>The request could be re-tried with a smaller " \
163 "chunk size, shorter field lines, a shorter request target or a shorter " \
164 "request method token.</p></body>" \
165 "</html>"
166#else
167#define ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG ""
168#endif
169
173#ifdef HAVE_MESSAGES
174#define ERR_MSG_REQUEST_FOOTER_TOO_BIG \
175 "<html>" \
176 "<head><title>Request too big</title></head>" \
177 "<body><p>The total size of the request headers, which includes the " \
178 "request target, the request field lines and the chunked trailer " \
179 "section exceeds the memory constraints of this web server.</p>" \
180 "<p>The request could be re-tried with a shorter chunked trailer " \
181 "section, shorter field lines, a shorter request target or " \
182 "a shorter request method token.</p></body>" \
183 "</html>"
184#else
185#define ERR_MSG_REQUEST_FOOTER_TOO_BIG ""
186#endif
187
191#ifdef HAVE_MESSAGES
192#define RQ_LINE_TOO_MANY_WSP \
193 "<html>" \
194 "<head><title>Request broken</title></head>" \
195 "<body>The request line has more then two whitespaces.</body>" \
196 "</html>"
197#else
198#define RQ_LINE_TOO_MANY_WSP ""
199#endif
200
205#ifdef HAVE_MESSAGES
206#define BARE_CR_IN_HEADER \
207 "<html>" \
208 "<head><title>Request broken</title></head>" \
209 "<body>Request HTTP header has bare CR character without " \
210 "following LF character.</body>" \
211 "</html>"
212#else
213#define BARE_CR_IN_HEADER ""
214#endif
215
220#ifdef HAVE_MESSAGES
221#define BARE_CR_IN_FOOTER \
222 "<html>" \
223 "<head><title>Request broken</title></head>" \
224 "<body>Request HTTP footer has bare CR character without " \
225 "following LF character.</body>" \
226 "</html>"
227#else
228#define BARE_CR_IN_FOOTER ""
229#endif
230
235#ifdef HAVE_MESSAGES
236#define BARE_LF_IN_HEADER \
237 "<html>" \
238 "<head><title>Request broken</title></head>" \
239 "<body>Request HTTP header has bare LF character without " \
240 "preceding CR character.</body>" \
241 "</html>"
242#else
243#define BARE_LF_IN_HEADER ""
244#endif
245
250#ifdef HAVE_MESSAGES
251#define BARE_LF_IN_FOOTER \
252 "<html>" \
253 "<head><title>Request broken</title></head>" \
254 "<body>Request HTTP footer has bare LF character without " \
255 "preceding CR character.</body>" \
256 "</html>"
257#else
258#define BARE_LF_IN_FOOTER ""
259#endif
260
264#ifdef HAVE_MESSAGES
265#define RQ_TARGET_INVALID_CHAR \
266 "<html>" \
267 "<head><title>Request broken</title></head>" \
268 "<body>HTTP request has invalid characters in " \
269 "the request-target.</body>" \
270 "</html>"
271#else
272#define RQ_TARGET_INVALID_CHAR ""
273#endif
274
278#ifdef HAVE_MESSAGES
279#define ERR_RSP_OBS_FOLD \
280 "<html>" \
281 "<head><title>Request broken</title></head>" \
282 "<body>Obsolete line folding is used in HTTP request header.</body>" \
283 "</html>"
284#else
285#define ERR_RSP_OBS_FOLD ""
286#endif
287
291#ifdef HAVE_MESSAGES
292#define ERR_RSP_OBS_FOLD_FOOTER \
293 "<html>" \
294 "<head><title>Request broken</title></head>" \
295 "<body>Obsolete line folding is used in HTTP request footer.</body>" \
296 "</html>"
297#else
298#define ERR_RSP_OBS_FOLD_FOOTER ""
299#endif
300
305#ifdef HAVE_MESSAGES
306#define ERR_RSP_WSP_BEFORE_HEADER \
307 "<html>" \
308 "<head><title>Request broken</title></head>" \
309 "<body>HTTP request has whitespace between the request line and " \
310 "the first header.</body>" \
311 "</html>"
312#else
313#define ERR_RSP_WSP_BEFORE_HEADER ""
314#endif
315
320#ifdef HAVE_MESSAGES
321#define ERR_RSP_WSP_BEFORE_FOOTER \
322 "<html>" \
323 "<head><title>Request broken</title></head>" \
324 "<body>First HTTP footer line has whitespace at the first " \
325 "position.</body>" \
326 "</html>"
327#else
328#define ERR_RSP_WSP_BEFORE_FOOTER ""
329#endif
330
335#ifdef HAVE_MESSAGES
336#define ERR_RSP_WSP_IN_HEADER_NAME \
337 "<html>" \
338 "<head><title>Request broken</title></head>" \
339 "<body>HTTP request has whitespace before the first colon " \
340 "in header line.</body>" \
341 "</html>"
342#else
343#define ERR_RSP_WSP_IN_HEADER_NAME ""
344#endif
345
350#ifdef HAVE_MESSAGES
351#define ERR_RSP_WSP_IN_FOOTER_NAME \
352 "<html>" \
353 "<head><title>Request broken</title></head>" \
354 "<body>HTTP request has whitespace before the first colon " \
355 "in footer line.</body>" \
356 "</html>"
357#else
358#define ERR_RSP_WSP_IN_FOOTER_NAME ""
359#endif
360
364#ifdef HAVE_MESSAGES
365#define ERR_RSP_INVALID_CHR_IN_HEADER \
366 "<html>" \
367 "<head><title>Request broken</title></head>" \
368 "<body>HTTP request has invalid character in header.</body>" \
369 "</html>"
370#else
371#define ERR_RSP_INVALID_CHR_IN_HEADER ""
372#endif
373
377#ifdef HAVE_MESSAGES
378#define ERR_RSP_INVALID_CHR_IN_FOOTER \
379 "<html>" \
380 "<head><title>Request broken</title></head>" \
381 "<body>HTTP request has invalid character in footer.</body>" \
382 "</html>"
383#else
384#define ERR_RSP_INVALID_CHR_IN_FOOTER ""
385#endif
386
390#ifdef HAVE_MESSAGES
391#define ERR_RSP_HEADER_WITHOUT_COLON \
392 "<html>" \
393 "<head><title>Request broken</title></head>" \
394 "<body>HTTP request header line has no colon character.</body>" \
395 "</html>"
396#else
397#define ERR_RSP_HEADER_WITHOUT_COLON ""
398#endif
399
403#ifdef HAVE_MESSAGES
404#define ERR_RSP_FOOTER_WITHOUT_COLON \
405 "<html>" \
406 "<head><title>Request broken</title></head>" \
407 "<body>HTTP request footer line has no colon character.</body>" \
408 "</html>"
409#else
410#define ERR_RSP_FOOTER_WITHOUT_COLON ""
411#endif
412
416#ifdef HAVE_MESSAGES
417#define ERR_RSP_EMPTY_HEADER_NAME \
418 "<html>" \
419 "<head><title>Request broken</title></head>" \
420 "<body>HTTP request header has empty header name.</body>" \
421 "</html>"
422#else
423#define ERR_RSP_EMPTY_HEADER_NAME ""
424#endif
425
429#ifdef HAVE_MESSAGES
430#define ERR_RSP_EMPTY_FOOTER_NAME \
431 "<html>" \
432 "<head><title>Request broken</title></head>" \
433 "<body>HTTP request footer has empty footer name.</body>" \
434 "</html>"
435#else
436#define ERR_RSP_EMPTY_FOOTER_NAME ""
437#endif
438
446#ifdef HAVE_MESSAGES
447#define REQUEST_LACKS_HOST \
448 "<html>" \
449 "<head><title>&quot;Host:&quot; header required</title></head>" \
450 "<body>HTTP/1.1 request without <b>&quot;Host:&quot;</b>.</body>" \
451 "</html>"
452
453#else
454#define REQUEST_LACKS_HOST ""
455#endif
456
460#ifdef HAVE_MESSAGES
461#define REQUEST_UNSUPPORTED_TR_ENCODING \
462 "<html>" \
463 "<head><title>Unsupported Transfer-Encoding</title></head>" \
464 "<body>The Transfer-Encoding used in request is not supported.</body>" \
465 "</html>"
466#else
467#define REQUEST_UNSUPPORTED_TR_ENCODING ""
468#endif
469
474#ifdef HAVE_MESSAGES
475#define REQUEST_LENGTH_WITH_TR_ENCODING \
476 "<html>" \
477 "<head><title>Malformed request</title></head>" \
478 "<body>Wrong combination of the request headers: both Transfer-Encoding " \
479 "and Content-Length headers are used at the same time.</body>" \
480 "</html>"
481#else
482#define REQUEST_LENGTH_WITH_TR_ENCODING ""
483#endif
484
492#ifdef HAVE_MESSAGES
493#define REQUEST_MALFORMED \
494 "<html><head><title>Request malformed</title></head>" \
495 "<body>HTTP request is syntactically incorrect.</body></html>"
496#else
497#define REQUEST_MALFORMED ""
498#endif
499
504#ifdef HAVE_MESSAGES
505#define REQUEST_CHUNKED_MALFORMED \
506 "<html><head><title>Request malformed</title></head>" \
507 "<body>HTTP chunked encoding is syntactically incorrect.</body></html>"
508#else
509#define REQUEST_CHUNKED_MALFORMED ""
510#endif
511
515#ifdef HAVE_MESSAGES
516#define REQUEST_CHUNK_TOO_LARGE \
517 "<html><head><title>Request content too large</title></head>" \
518 "<body>The chunk size used in HTTP chunked encoded " \
519 "request is too large.</body></html>"
520#else
521#define REQUEST_CHUNK_TOO_LARGE ""
522#endif
523
527#ifdef HAVE_MESSAGES
528#define REQUEST_CONTENTLENGTH_TOOLARGE \
529 "<html><head><title>Request content too large</title></head>" \
530 "<body>HTTP request has too large value for " \
531 "<b>Content-Length</b> header.</body></html>"
532#else
533#define REQUEST_CONTENTLENGTH_TOOLARGE ""
534#endif
535
540#ifdef HAVE_MESSAGES
541#define REQUEST_CONTENTLENGTH_MALFORMED \
542 "<html><head><title>Request malformed</title></head>" \
543 "<body>HTTP request has wrong value for " \
544 "<b>Content-Length</b> header.</body></html>"
545#else
546#define REQUEST_CONTENTLENGTH_MALFORMED ""
547#endif
548
555#ifdef HAVE_MESSAGES
556#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP \
557 "<html><head><title>Internal server error</title></head>" \
558 "<body>Please ask the developer of this Web server to carefully " \
559 "read the GNU libmicrohttpd documentation about connection " \
560 "management and blocking.</body></html>"
561#else
562#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP ""
563#endif
564
568#ifdef HAVE_MESSAGES
569#define REQ_HTTP_VER_IS_TOO_OLD \
570 "<html><head><title>Requested HTTP version is not supported</title></head>" \
571 "<body>Requested HTTP version is too old and not " \
572 "supported.</body></html>"
573#else
574#define REQ_HTTP_VER_IS_TOO_OLD ""
575#endif
576
580#ifdef HAVE_MESSAGES
581#define REQ_HTTP_VER_IS_NOT_SUPPORTED \
582 "<html><head><title>Requested HTTP version is not supported</title></head>" \
583 "<body>Requested HTTP version is not supported.</body></html>"
584#else
585#define REQ_HTTP_VER_IS_NOT_SUPPORTED ""
586#endif
587
588
592#define MHD_SENFILE_CHUNK_ (0x20000)
593
597#define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000)
598
599#ifdef HAVE_MESSAGES
605static const char *
606str_conn_error_ (ssize_t mhd_err_code)
607{
608 switch (mhd_err_code)
609 {
610 case MHD_ERR_AGAIN_:
611 return _ ("The operation would block, retry later");
613 return _ ("The connection was forcibly closed by remote peer");
614 case MHD_ERR_NOTCONN_:
615 return _ ("The socket is not connected");
616 case MHD_ERR_NOMEM_:
617 return _ ("Not enough system resources to serve the request");
618 case MHD_ERR_BADF_:
619 return _ ("Bad FD value");
620 case MHD_ERR_INVAL_:
621 return _ ("Argument value is invalid");
623 return _ ("Argument value is not supported");
624 case MHD_ERR_PIPE_:
625 return _ ("The socket is no longer available for sending");
626 case MHD_ERR_TLS_:
627 return _ ("TLS encryption or decryption error");
628 default:
629 break; /* Mute compiler warning */
630 }
631 if (0 <= mhd_err_code)
632 return _ ("Not an error code");
633
634 mhd_assert (0); /* Should never be reachable */
635 return _ ("Wrong error code value");
636}
637
638
639#endif /* HAVE_MESSAGES */
640
650void *
652 size_t size)
653{
654 struct MHD_Connection *const c = connection; /* a short alias */
655 struct MemoryPool *const pool = c->pool; /* a short alias */
656 size_t need_to_be_freed = 0;
657 void *res;
658
659 res = MHD_pool_try_alloc (pool,
660 size,
661 &need_to_be_freed);
662 if (NULL != res)
663 return res;
664
666 c->write_buffer,
668 {
670 need_to_be_freed)
671 {
672 char *buf;
673 const size_t new_buf_size = c->write_buffer_size - need_to_be_freed;
674 buf = MHD_pool_reallocate (pool,
675 c->write_buffer,
677 new_buf_size);
678 mhd_assert (c->write_buffer == buf);
679 mhd_assert (c->write_buffer_append_offset <= new_buf_size);
680 mhd_assert (c->write_buffer_send_offset <= new_buf_size);
681 c->write_buffer_size = new_buf_size;
682 c->write_buffer = buf;
683 }
684 else
685 return NULL;
686 }
687 else if (MHD_pool_is_resizable_inplace (pool,
688 c->read_buffer,
690 {
691 if (c->read_buffer_size - c->read_buffer_offset >= need_to_be_freed)
692 {
693 char *buf;
694 const size_t new_buf_size = c->read_buffer_size - need_to_be_freed;
695 buf = MHD_pool_reallocate (pool,
696 c->read_buffer,
698 new_buf_size);
699 mhd_assert (c->read_buffer == buf);
700 mhd_assert (c->read_buffer_offset <= new_buf_size);
701 c->read_buffer_size = new_buf_size;
702 c->read_buffer = buf;
703 }
704 else
705 return NULL;
706 }
707 else
708 return NULL;
709 res = MHD_pool_allocate (pool, size, true);
710 mhd_assert (NULL != res); /* It has been checked that pool has enough space */
711 return res;
712}
713
714
724static ssize_t
726 void *other,
727 size_t i)
728{
729 ssize_t ret;
730
731 if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
732 (MHD_CONNECTION_CLOSED == connection->state) )
733 {
734 return MHD_ERR_NOTCONN_;
735 }
737 i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */
738
739 ret = MHD_recv_ (connection->socket_fd,
740 other,
741 i);
742 if (0 > ret)
743 {
744 const int err = MHD_socket_get_error_ ();
745 if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
746 {
747#ifdef EPOLL_SUPPORT
748 /* Got EAGAIN --- no longer read-ready */
749 connection->epoll_state &=
751#endif /* EPOLL_SUPPORT */
752 return MHD_ERR_AGAIN_;
753 }
754 if (MHD_SCKT_ERR_IS_EINTR_ (err))
755 return MHD_ERR_AGAIN_;
757 return MHD_ERR_CONNRESET_;
759 return MHD_ERR_OPNOTSUPP_;
761 return MHD_ERR_NOTCONN_;
763 return MHD_ERR_INVAL_;
765 return MHD_ERR_NOMEM_;
767 return MHD_ERR_BADF_;
768 /* Treat any other error as a hard error. */
769 return MHD_ERR_NOTCONN_;
770 }
771#ifdef EPOLL_SUPPORT
772 else if (i > (size_t) ret)
773 connection->epoll_state &=
775#endif /* EPOLL_SUPPORT */
776 return ret;
777}
778
779
792_MHD_EXTERN int
794 enum MHD_ValueKind kind,
795 MHD_KeyValueIterator iterator,
796 void *iterator_cls)
797{
798 int ret;
799 struct MHD_HTTP_Req_Header *pos;
800
801 if (NULL == connection)
802 return -1;
803 ret = 0;
804 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
805 if (0 != (pos->kind & kind))
806 {
807 ret++;
808 if ( (NULL != iterator) &&
809 (MHD_NO == iterator (iterator_cls,
810 pos->kind,
811 pos->header,
812 pos->value)) )
813 return ret;
814 }
815 return ret;
816}
817
818
831_MHD_EXTERN int
833 enum MHD_ValueKind kind,
834 MHD_KeyValueIteratorN iterator,
835 void *iterator_cls)
836{
837 int ret;
838 struct MHD_HTTP_Req_Header *pos;
839
840 if (NULL == connection)
841 return -1;
842 ret = 0;
843
844 if (NULL == iterator)
845 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
846 {
847 if (0 != (kind & pos->kind))
848 ret++;
849 }
850 else
851 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
852 if (0 != (kind & pos->kind))
853 {
854 ret++;
855 if (MHD_NO == iterator (iterator_cls,
856 pos->kind,
857 pos->header,
858 pos->header_size,
859 pos->value,
860 pos->value_size))
861 return ret;
862 }
863 return ret;
864}
865
866
884static enum MHD_Result
886 enum MHD_ValueKind kind,
887 const char *key,
888 size_t key_size,
889 const char *value,
890 size_t value_size)
891{
892 struct MHD_HTTP_Req_Header *pos;
893
894 pos = MHD_connection_alloc_memory_ (connection,
895 sizeof (struct MHD_HTTP_Res_Header));
896 if (NULL == pos)
897 return MHD_NO;
898 pos->header = key;
899 pos->header_size = key_size;
900 pos->value = value;
901 pos->value_size = value_size;
902 pos->kind = kind;
903 pos->next = NULL;
904 /* append 'pos' to the linked list of headers */
905 if (NULL == connection->rq.headers_received_tail)
906 {
907 connection->rq.headers_received = pos;
908 connection->rq.headers_received_tail = pos;
909 }
910 else
911 {
912 connection->rq.headers_received_tail->next = pos;
913 connection->rq.headers_received_tail = pos;
914 }
915 return MHD_YES;
916}
917
918
946 enum MHD_ValueKind kind,
947 const char *key,
948 size_t key_size,
949 const char *value,
950 size_t value_size)
951{
952 if ( (MHD_GET_ARGUMENT_KIND != kind) &&
953 ( ((key ? strlen (key) : 0) != key_size) ||
954 ((value ? strlen (value) : 0) != value_size) ) )
955 return MHD_NO; /* binary zero is allowed only in GET arguments */
956
957 return MHD_set_connection_value_n_nocheck_ (connection,
958 kind,
959 key,
960 key_size,
961 value,
962 value_size);
963}
964
965
993 enum MHD_ValueKind kind,
994 const char *key,
995 const char *value)
996{
997 return MHD_set_connection_value_n_nocheck_ (connection,
998 kind,
999 key,
1000 NULL != key
1001 ? strlen (key)
1002 : 0,
1003 value,
1004 NULL != value
1005 ? strlen (value)
1006 : 0);
1007}
1008
1009
1020_MHD_EXTERN const char *
1022 enum MHD_ValueKind kind,
1023 const char *key)
1024{
1025 const char *value;
1026
1027 value = NULL;
1028 (void) MHD_lookup_connection_value_n (connection,
1029 kind,
1030 key,
1031 (NULL == key) ? 0 : strlen (key),
1032 &value,
1033 NULL);
1034 return value;
1035}
1036
1037
1059 enum MHD_ValueKind kind,
1060 const char *key,
1061 size_t key_size,
1062 const char **value_ptr,
1063 size_t *value_size_ptr)
1064{
1065 struct MHD_HTTP_Req_Header *pos;
1066
1067 if (NULL == connection)
1068 return MHD_NO;
1069
1070 if (NULL == key)
1071 {
1072 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1073 {
1074 if ( (0 != (kind & pos->kind)) &&
1075 (NULL == pos->header) )
1076 break;
1077 }
1078 }
1079 else
1080 {
1081 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1082 {
1083 if ( (0 != (kind & pos->kind)) &&
1084 (key_size == pos->header_size) &&
1085 ( (key == pos->header) ||
1087 pos->header,
1088 key_size) ) ) )
1089 break;
1090 }
1091 }
1092
1093 if (NULL == pos)
1094 return MHD_NO;
1095
1096 if (NULL != value_ptr)
1097 *value_ptr = pos->value;
1098
1099 if (NULL != value_size_ptr)
1100 *value_size_ptr = pos->value_size;
1101
1102 return MHD_YES;
1103}
1104
1105
1121static bool
1123 const char *header,
1124 size_t header_len,
1125 const char *token,
1126 size_t token_len)
1127{
1128 struct MHD_HTTP_Req_Header *pos;
1129
1130 if ((NULL == connection) || (NULL == header) || (0 == header[0]) ||
1131 (NULL == token) || (0 == token[0]))
1132 return false;
1133
1134 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1135 {
1136 if ((0 != (pos->kind & MHD_HEADER_KIND)) &&
1137 (header_len == pos->header_size) &&
1138 ( (header == pos->header) ||
1140 pos->header,
1141 header_len)) ) &&
1142 (MHD_str_has_token_caseless_ (pos->value, token, token_len)))
1143 return true;
1144 }
1145 return false;
1146}
1147
1148
1160#define MHD_lookup_header_s_token_ci(c,h,tkn) \
1161 MHD_lookup_header_token_ci ((c),(h),MHD_STATICSTR_LEN_ (h), \
1162 (tkn),MHD_STATICSTR_LEN_ (tkn))
1163
1164
1172static bool
1174{
1175 const char *expect;
1176
1177 if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
1178 return false;
1179
1180 if (0 == connection->rq.remaining_upload_size)
1181 return false;
1182
1183 if (MHD_NO ==
1189 &expect,
1190 NULL))
1191 return false;
1192
1193 if (MHD_str_equal_caseless_ (expect,
1194 "100-continue"))
1195 return true;
1196
1197 return false;
1198}
1199
1200
1207void
1209{
1210 const struct MHD_Daemon *daemon = connection->daemon;
1211
1212 if (0 == (daemon->options & MHD_USE_TURBO))
1213 {
1214#ifdef HTTPS_SUPPORT
1215 /* For TLS connection use shutdown of TLS layer
1216 * and do not shutdown TCP socket. This give more
1217 * chances to send TLS closure data to remote side.
1218 * Closure of TLS layer will be interpreted by
1219 * remote side as end of transmission. */
1220 if (0 != (daemon->options & MHD_USE_TLS))
1221 {
1222 if (! MHD_tls_connection_shutdown (connection))
1223 shutdown (connection->socket_fd,
1224 SHUT_WR);
1225 }
1226 else /* Combined with next 'shutdown()'. */
1227#endif /* HTTPS_SUPPORT */
1228 shutdown (connection->socket_fd,
1229 SHUT_WR);
1230 }
1231 connection->state = MHD_CONNECTION_CLOSED;
1233}
1234
1235
1245void
1247 enum MHD_RequestTerminationCode termination_code)
1248{
1249 struct MHD_Daemon *daemon = connection->daemon;
1250 struct MHD_Response *resp = connection->rp.response;
1251
1252 mhd_assert (! connection->suspended);
1253#ifdef MHD_USE_THREADS
1254 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
1255 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
1256#endif /* MHD_USE_THREADS */
1257 if ( (NULL != daemon->notify_completed) &&
1258 (connection->rq.client_aware) )
1259 daemon->notify_completed (daemon->notify_completed_cls,
1260 connection,
1261 &connection->rq.client_context,
1262 termination_code);
1263 connection->rq.client_aware = false;
1264 if (NULL != resp)
1265 {
1266 connection->rp.response = NULL;
1267 MHD_destroy_response (resp);
1268 }
1269 if (NULL != connection->pool)
1270 {
1271 MHD_pool_destroy (connection->pool);
1272 connection->pool = NULL;
1273 }
1274
1275 MHD_connection_mark_closed_ (connection);
1276}
1277
1278
1279#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
1290void
1292{
1293 struct MHD_Daemon *daemon = connection->daemon;
1294 struct MHD_UpgradeResponseHandle *urh = connection->urh;
1295
1296#ifdef MHD_USE_THREADS
1297 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
1300#endif /* MHD_USE_THREADS */
1301
1302 if (0 == (daemon->options & MHD_USE_TLS))
1303 return; /* Nothing to do with non-TLS connection. */
1304
1305 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
1306 DLL_remove (daemon->urh_head,
1307 daemon->urh_tail,
1308 urh);
1309#ifdef EPOLL_SUPPORT
1310 if (MHD_D_IS_USING_EPOLL_ (daemon) &&
1311 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
1312 EPOLL_CTL_DEL,
1313 connection->socket_fd,
1314 NULL)) )
1315 {
1316 MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
1317 }
1318 if (urh->in_eready_list)
1319 {
1320 EDLL_remove (daemon->eready_urh_head,
1321 daemon->eready_urh_tail,
1322 urh);
1323 urh->in_eready_list = false;
1324 }
1325#endif /* EPOLL_SUPPORT */
1326 if (MHD_INVALID_SOCKET != urh->mhd.socket)
1327 {
1328#ifdef EPOLL_SUPPORT
1329 if (MHD_D_IS_USING_EPOLL_ (daemon) &&
1330 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
1331 EPOLL_CTL_DEL,
1332 urh->mhd.socket,
1333 NULL)) )
1334 {
1335 MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
1336 }
1337#endif /* EPOLL_SUPPORT */
1338 /* Reflect remote disconnect to application by breaking
1339 * socketpair connection. */
1340 shutdown (urh->mhd.socket, SHUT_RDWR);
1341 }
1342 /* Socketpair sockets will remain open as they will be
1343 * used with MHD_UPGRADE_ACTION_CLOSE. They will be
1344 * closed by cleanup_upgraded_connection() during
1345 * connection's final cleanup.
1346 */
1347}
1348
1349
1350#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT*/
1351
1352
1360static void
1362 const char *emsg)
1363{
1364 connection->stop_with_error = true;
1365 connection->discard_request = true;
1366#ifdef HAVE_MESSAGES
1367 if (NULL != emsg)
1368 MHD_DLOG (connection->daemon,
1369 "%s\n",
1370 emsg);
1371#else /* ! HAVE_MESSAGES */
1372 (void) emsg; /* Mute compiler warning. */
1373#endif /* ! HAVE_MESSAGES */
1374 MHD_connection_close_ (connection,
1376}
1377
1378
1383#ifdef HAVE_MESSAGES
1384#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, emsg)
1385#else
1386#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, NULL)
1387#endif
1388
1389
1402static enum MHD_Result
1404{
1405 ssize_t ret;
1406 struct MHD_Response *response;
1407
1408 response = connection->rp.response;
1409 mhd_assert (connection->rp.props.send_reply_body);
1410
1411 if ( (0 == response->total_size) ||
1412 /* TODO: replace the next check with assert */
1413 (connection->rp.rsp_write_position == response->total_size) )
1414 return MHD_YES; /* 0-byte response is always ready */
1415 if (NULL != response->data_iov)
1416 {
1417 size_t copy_size;
1418
1419 if (NULL != connection->rp.resp_iov.iov)
1420 return MHD_YES;
1421 copy_size = response->data_iovcnt * sizeof(MHD_iovec_);
1422 connection->rp.resp_iov.iov = MHD_connection_alloc_memory_ (connection,
1423 copy_size);
1424 if (NULL == connection->rp.resp_iov.iov)
1425 {
1426 MHD_mutex_unlock_chk_ (&response->mutex);
1427 /* not enough memory */
1428 CONNECTION_CLOSE_ERROR (connection,
1429 _ ("Closing connection (out of memory)."));
1430 return MHD_NO;
1431 }
1432 memcpy (connection->rp.resp_iov.iov,
1433 response->data_iov,
1434 copy_size);
1435 connection->rp.resp_iov.cnt = response->data_iovcnt;
1436 connection->rp.resp_iov.sent = 0;
1437 return MHD_YES;
1438 }
1439 if (NULL == response->crc)
1440 return MHD_YES;
1441 if ( (response->data_start <=
1442 connection->rp.rsp_write_position) &&
1443 (response->data_size + response->data_start >
1444 connection->rp.rsp_write_position) )
1445 return MHD_YES; /* response already ready */
1446#if defined(_MHD_HAVE_SENDFILE)
1447 if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
1448 {
1449 /* will use sendfile, no need to bother response crc */
1450 return MHD_YES;
1451 }
1452#endif /* _MHD_HAVE_SENDFILE */
1453
1454 ret = response->crc (response->crc_cls,
1455 connection->rp.rsp_write_position,
1456 (char *) response->data,
1457 (size_t) MHD_MIN ((uint64_t) response->data_buffer_size,
1458 response->total_size
1459 - connection->rp.rsp_write_position));
1460 if (0 > ret)
1461 {
1462 /* either error or http 1.0 transfer, close socket! */
1463 /* TODO: do not update total size, check whether response
1464 * was really with unknown size */
1465 response->total_size = connection->rp.rsp_write_position;
1466#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1467 MHD_mutex_unlock_chk_ (&response->mutex);
1468#endif
1470 MHD_connection_close_ (connection,
1472 else
1473 CONNECTION_CLOSE_ERROR (connection,
1474 _ ("Closing connection (application reported " \
1475 "error generating data)."));
1476 return MHD_NO;
1477 }
1478 response->data_start = connection->rp.rsp_write_position;
1479 response->data_size = (size_t) ret;
1480 if (0 == ret)
1481 {
1483#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1484 MHD_mutex_unlock_chk_ (&response->mutex);
1485#endif
1486 return MHD_NO;
1487 }
1488 return MHD_YES;
1489}
1490
1491
1504static enum MHD_Result
1506 bool *p_finished)
1507{
1508 ssize_t ret;
1509 struct MHD_Response *response;
1510 static const size_t max_chunk = 0xFFFFFF;
1511 char chunk_hdr[6]; /* 6: max strlen of "FFFFFF" */
1512 /* "FFFFFF" + "\r\n" */
1513 static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2;
1514 /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */
1515 static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2;
1516 size_t chunk_hdr_len;
1517 uint64_t left_to_send;
1518 size_t size_to_fill;
1519
1520 response = connection->rp.response;
1521 mhd_assert (NULL != response->crc || NULL != response->data);
1522
1523 mhd_assert (0 == connection->write_buffer_append_offset);
1524
1525 /* The buffer must be reasonably large enough */
1526 if (128 > connection->write_buffer_size)
1527 {
1528 size_t size;
1529
1530 size = connection->write_buffer_size + MHD_pool_get_free (connection->pool);
1531 if (128 > size)
1532 {
1533#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1534 MHD_mutex_unlock_chk_ (&response->mutex);
1535#endif
1536 /* not enough memory */
1537 CONNECTION_CLOSE_ERROR (connection,
1538 _ ("Closing connection (out of memory)."));
1539 return MHD_NO;
1540 }
1541 /* Limit the buffer size to the largest usable size for chunks */
1542 if ( (max_chunk + max_chunk_overhead) < size)
1543 size = max_chunk + max_chunk_overhead;
1544 mhd_assert ((NULL == connection->write_buffer) || \
1545 MHD_pool_is_resizable_inplace (connection->pool, \
1546 connection->write_buffer, \
1547 connection->write_buffer_size));
1548 connection->write_buffer =
1549 MHD_pool_reallocate (connection->pool,
1550 connection->write_buffer,
1551 connection->write_buffer_size,
1552 size);
1553 mhd_assert (NULL != connection->write_buffer);
1554 connection->write_buffer_size = size;
1555 }
1556 mhd_assert (max_chunk_overhead < connection->write_buffer_size);
1557
1558 if (MHD_SIZE_UNKNOWN == response->total_size)
1559 left_to_send = MHD_SIZE_UNKNOWN;
1560 else
1561 left_to_send = response->total_size
1562 - connection->rp.rsp_write_position;
1563
1564 size_to_fill = connection->write_buffer_size - max_chunk_overhead;
1565 /* Limit size for the callback to the max usable size */
1566 if (max_chunk < size_to_fill)
1567 size_to_fill = max_chunk;
1568 if (left_to_send < size_to_fill)
1569 size_to_fill = (size_t) left_to_send;
1570
1571 if (0 == left_to_send)
1572 /* nothing to send, don't bother calling crc */
1574 else if ( (response->data_start <=
1575 connection->rp.rsp_write_position) &&
1576 (response->data_start + response->data_size >
1577 connection->rp.rsp_write_position) )
1578 {
1579 /* difference between rsp_write_position and data_start is less
1580 than data_size which is size_t type, no need to check for overflow */
1581 const size_t data_write_offset
1582 = (size_t) (connection->rp.rsp_write_position
1583 - response->data_start);
1584 /* buffer already ready, use what is there for the chunk */
1585 mhd_assert (SSIZE_MAX >= (response->data_size - data_write_offset));
1586 mhd_assert (response->data_size >= data_write_offset);
1587 ret = (ssize_t) (response->data_size - data_write_offset);
1588 if ( ((size_t) ret) > size_to_fill)
1589 ret = (ssize_t) size_to_fill;
1590 memcpy (&connection->write_buffer[max_chunk_hdr_len],
1591 &response->data[data_write_offset],
1592 (size_t) ret);
1593 }
1594 else
1595 {
1596 if (NULL == response->crc)
1597 { /* There is no way to reach this code */
1598#if defined(MHD_USE_THREADS)
1599 MHD_mutex_unlock_chk_ (&response->mutex);
1600#endif
1601 CONNECTION_CLOSE_ERROR (connection,
1602 _ ("No callback for the chunked data."));
1603 return MHD_NO;
1604 }
1605 ret = response->crc (response->crc_cls,
1606 connection->rp.rsp_write_position,
1607 &connection->write_buffer[max_chunk_hdr_len],
1608 size_to_fill);
1609 }
1611 {
1612 /* error, close socket! */
1613 /* TODO: remove update of the response size */
1614 response->total_size = connection->rp.rsp_write_position;
1615#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1616 MHD_mutex_unlock_chk_ (&response->mutex);
1617#endif
1618 CONNECTION_CLOSE_ERROR (connection,
1619 _ ("Closing connection (application error " \
1620 "generating response)."));
1621 return MHD_NO;
1622 }
1624 {
1625 *p_finished = true;
1626 /* TODO: remove update of the response size */
1627 response->total_size = connection->rp.rsp_write_position;
1628 return MHD_YES;
1629 }
1630 if (0 == ret)
1631 {
1633#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1634 MHD_mutex_unlock_chk_ (&response->mutex);
1635#endif
1636 return MHD_NO;
1637 }
1638 if (size_to_fill < (size_t) ret)
1639 {
1640#if defined(MHD_USE_THREADS)
1641 MHD_mutex_unlock_chk_ (&response->mutex);
1642#endif
1643 CONNECTION_CLOSE_ERROR (connection,
1644 _ ("Closing connection (application returned " \
1645 "more data than requested)."));
1646 return MHD_NO;
1647 }
1648 chunk_hdr_len = MHD_uint32_to_strx ((uint32_t) ret, chunk_hdr,
1649 sizeof(chunk_hdr));
1650 mhd_assert (chunk_hdr_len != 0);
1651 mhd_assert (chunk_hdr_len < sizeof(chunk_hdr));
1652 *p_finished = false;
1653 connection->write_buffer_send_offset =
1654 (max_chunk_hdr_len - (chunk_hdr_len + 2));
1655 memcpy (connection->write_buffer + connection->write_buffer_send_offset,
1656 chunk_hdr,
1657 chunk_hdr_len);
1658 connection->write_buffer[max_chunk_hdr_len - 2] = '\r';
1659 connection->write_buffer[max_chunk_hdr_len - 1] = '\n';
1660 connection->write_buffer[max_chunk_hdr_len + (size_t) ret] = '\r';
1661 connection->write_buffer[max_chunk_hdr_len + (size_t) ret + 1] = '\n';
1662 connection->rp.rsp_write_position += (size_t) ret;
1663 connection->write_buffer_append_offset = max_chunk_hdr_len + (size_t) ret + 2;
1664 return MHD_YES;
1665}
1666
1667
1690static enum MHD_ConnKeepAlive
1692{
1693 struct MHD_Connection *const c = connection;
1694 struct MHD_Response *const r = c->rp.response;
1696 mhd_assert (NULL != r);
1698 return MHD_CONN_MUST_CLOSE;
1699
1700#ifdef UPGRADE_SUPPORT
1701 /* TODO: Move below the next check when MHD stops closing connections
1702 * when response is queued in first callback */
1703 if (NULL != r->upgrade_handler)
1704 {
1705 /* No "close" token is enforced by 'add_response_header_connection()' */
1707 /* Valid HTTP version is enforced by 'MHD_queue_response()' */
1710 return MHD_CONN_MUST_UPGRADE;
1711 }
1712#endif /* UPGRADE_SUPPORT */
1713
1714 mhd_assert ( (! c->stop_with_error) || (c->discard_request));
1715 if ((c->read_closed) || (c->discard_request))
1716 return MHD_CONN_MUST_CLOSE;
1717
1719 return MHD_CONN_MUST_CLOSE;
1721 return MHD_CONN_MUST_CLOSE;
1722
1724 return MHD_CONN_MUST_CLOSE;
1725
1728 "close"))
1729 return MHD_CONN_MUST_CLOSE;
1730
1731 if ((MHD_HTTP_VER_1_0 == connection->rq.http_ver) ||
1732 (0 != (connection->rp.response->flags & MHD_RF_HTTP_1_0_SERVER)))
1733 {
1734 if (MHD_lookup_header_s_token_ci (connection,
1736 "Keep-Alive"))
1738
1739 return MHD_CONN_MUST_CLOSE;
1740 }
1741
1744
1745 return MHD_CONN_MUST_CLOSE;
1746}
1747
1748
1758static bool
1759get_date_str (char *date)
1760{
1761 static const char *const days[] = {
1762 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1763 };
1764 static const char *const mons[] = {
1765 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1766 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1767 };
1768 static const size_t buf_len = 29;
1769 struct tm now;
1770 time_t t;
1771 const char *src;
1772#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
1773 ! defined(HAVE_GMTIME_R)
1774 struct tm *pNow;
1775#endif
1776
1777 if ((time_t) -1 == time (&t))
1778 return false;
1779#if defined(HAVE_C11_GMTIME_S)
1780 if (NULL == gmtime_s (&t,
1781 &now))
1782 return false;
1783#elif defined(HAVE_W32_GMTIME_S)
1784 if (0 != gmtime_s (&now,
1785 &t))
1786 return false;
1787#elif defined(HAVE_GMTIME_R)
1788 if (NULL == gmtime_r (&t,
1789 &now))
1790 return false;
1791#else
1792 pNow = gmtime (&t);
1793 if (NULL == pNow)
1794 return false;
1795 now = *pNow;
1796#endif
1797
1798 /* Day of the week */
1799 src = days[now.tm_wday % 7];
1800 date[0] = src[0];
1801 date[1] = src[1];
1802 date[2] = src[2];
1803 date[3] = ',';
1804 date[4] = ' ';
1805 /* Day of the month */
1806 if (2 != MHD_uint8_to_str_pad ((uint8_t) now.tm_mday, 2,
1807 date + 5, buf_len - 5))
1808 return false;
1809 date[7] = ' ';
1810 /* Month */
1811 src = mons[now.tm_mon % 12];
1812 date[8] = src[0];
1813 date[9] = src[1];
1814 date[10] = src[2];
1815 date[11] = ' ';
1816 /* Year */
1817 if (4 != MHD_uint16_to_str ((uint16_t) (1900 + now.tm_year), date + 12,
1818 buf_len - 12))
1819 return false;
1820 date[16] = ' ';
1821 /* Time */
1822 MHD_uint8_to_str_pad ((uint8_t) now.tm_hour, 2, date + 17, buf_len - 17);
1823 date[19] = ':';
1824 MHD_uint8_to_str_pad ((uint8_t) now.tm_min, 2, date + 20, buf_len - 20);
1825 date[22] = ':';
1826 MHD_uint8_to_str_pad ((uint8_t) now.tm_sec, 2, date + 23, buf_len - 23);
1827 date[25] = ' ';
1828 date[26] = 'G';
1829 date[27] = 'M';
1830 date[28] = 'T';
1831
1832 return true;
1833}
1834
1835
1843static bool
1844get_date_header (char *header)
1845{
1846 if (! get_date_str (header + 6))
1847 {
1848 header[0] = 0;
1849 return false;
1850 }
1851 header[0] = 'D';
1852 header[1] = 'a';
1853 header[2] = 't';
1854 header[3] = 'e';
1855 header[4] = ':';
1856 header[5] = ' ';
1857 header[35] = '\r';
1858 header[36] = '\n';
1859 header[37] = 0;
1860 return true;
1861}
1862
1863
1876static bool
1878 bool required)
1879{
1880 size_t new_size;
1881 size_t avail_size;
1882 const size_t def_grow_size = connection->daemon->pool_increment;
1883 void *rb;
1884
1885 avail_size = MHD_pool_get_free (connection->pool);
1886 if (0 == avail_size)
1887 return false; /* No more space available */
1888 if (0 == connection->read_buffer_size)
1889 new_size = avail_size / 2; /* Use half of available buffer for reading */
1890 else
1891 {
1892 size_t grow_size;
1893
1894 grow_size = avail_size / 8;
1895 if (def_grow_size > grow_size)
1896 { /* Shortage of space */
1897 const size_t left_free =
1898 connection->read_buffer_size - connection->read_buffer_offset;
1899 mhd_assert (connection->read_buffer_size >= \
1900 connection->read_buffer_offset);
1901 if ((def_grow_size <= grow_size + left_free)
1902 && (left_free < def_grow_size))
1903 grow_size = def_grow_size - left_free; /* Use precise 'def_grow_size' for new free space */
1904 else if (! required)
1905 return false; /* Grow is not mandatory, leave some space in pool */
1906 else
1907 {
1908 /* Shortage of space, but grow is mandatory */
1909 const size_t small_inc =
1910 ((MHD_BUF_INC_SIZE > def_grow_size) ?
1911 def_grow_size : MHD_BUF_INC_SIZE) / 8;
1912 if (small_inc < avail_size)
1913 grow_size = small_inc;
1914 else
1915 grow_size = avail_size;
1916 }
1917 }
1918 new_size = connection->read_buffer_size + grow_size;
1919 }
1920 /* Make sure that read buffer will not be moved */
1921 if ((NULL != connection->read_buffer) &&
1922 ! MHD_pool_is_resizable_inplace (connection->pool,
1923 connection->read_buffer,
1924 connection->read_buffer_size))
1925 {
1926 mhd_assert (0);
1927 return false;
1928 }
1929 /* we can actually grow the buffer, do it! */
1930 rb = MHD_pool_reallocate (connection->pool,
1931 connection->read_buffer,
1932 connection->read_buffer_size,
1933 new_size);
1934 if (NULL == rb)
1935 {
1936 /* This should NOT be possible: we just computed 'new_size' so that
1937 it should fit. If it happens, somehow our read buffer is not in
1938 the right position in the pool, say because someone called
1939 MHD_pool_allocate() without 'from_end' set to 'true'? Anyway,
1940 should be investigated! (Ideally provide all data from
1941 *pool and connection->read_buffer and new_size for debugging). */
1942 mhd_assert (0);
1943 return false;
1944 }
1945 mhd_assert (connection->read_buffer == rb);
1946 connection->read_buffer = rb;
1947 mhd_assert (NULL != connection->read_buffer);
1948 connection->read_buffer_size = new_size;
1949 return true;
1950}
1951
1952
1957static void
1959{
1960 struct MHD_Connection *const c = connection;
1961 void *new_buf;
1962
1963 if ((NULL == c->read_buffer) || (0 == c->read_buffer_size))
1964 {
1965 mhd_assert (0 == c->read_buffer_size);
1967 return;
1968 }
1969
1971 if (0 == c->read_buffer_offset)
1972 {
1974 c->read_buffer = NULL;
1975 c->read_buffer_size = 0;
1976 }
1977 else
1978 {
1980 c->read_buffer_size));
1983 mhd_assert (c->read_buffer == new_buf);
1984 c->read_buffer = new_buf;
1986 }
1987}
1988
1989
1996static size_t
1998{
1999 struct MHD_Connection *const c = connection;
2000 struct MemoryPool *const pool = connection->pool;
2001 void *new_buf;
2002 size_t new_size;
2003 size_t free_size;
2004
2005 mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
2008
2009 free_size = MHD_pool_get_free (pool);
2010 if (0 != free_size)
2011 {
2012 new_size = c->write_buffer_size + free_size;
2013 /* This function must not move the buffer position.
2014 * MHD_pool_reallocate () may return the new position only if buffer was
2015 * allocated 'from_end' or is not the last allocation,
2016 * which should not happen. */
2017 mhd_assert ((NULL == c->write_buffer) || \
2019 c->write_buffer_size));
2020 new_buf = MHD_pool_reallocate (pool,
2021 c->write_buffer,
2023 new_size);
2024 mhd_assert ((c->write_buffer == new_buf) || (NULL == c->write_buffer));
2025 c->write_buffer = new_buf;
2026 c->write_buffer_size = new_size;
2028 {
2029 /* All data have been sent, reset offsets to zero. */
2032 }
2033 }
2034
2036}
2037
2038
2039#if 0 /* disable unused function */
2048static void
2049connection_shrink_write_buffer (struct MHD_Connection *connection)
2050{
2051 struct MHD_Connection *const c = connection;
2052 struct MemoryPool *const pool = connection->pool;
2053 void *new_buf;
2054
2055 mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
2058
2059 if ( (NULL == c->write_buffer) || (0 == c->write_buffer_size))
2060 {
2063 c->write_buffer = NULL;
2064 return;
2065 }
2067 return;
2068
2069 new_buf = MHD_pool_reallocate (pool, c->write_buffer, c->write_buffer_size,
2071 mhd_assert ((c->write_buffer == new_buf) || \
2072 (0 == c->write_buffer_append_offset));
2074 if (0 == c->write_buffer_size)
2075 c->write_buffer = NULL;
2076 else
2077 c->write_buffer = new_buf;
2078}
2079
2080
2081#endif /* unused function */
2082
2083
2091static void
2093{
2094 /* Read buffer is not needed for this request, shrink it.*/
2095 connection_shrink_read_buffer (connection);
2096}
2097
2098
2126
2127
2139static enum replyBodyUse
2141 unsigned int rcode)
2142{
2143 struct MHD_Connection *const c = connection;
2145 mhd_assert (100 <= rcode);
2146 mhd_assert (999 >= rcode);
2147
2148 if (199 >= rcode)
2149 return RP_BODY_NONE;
2150
2151 if (MHD_HTTP_NO_CONTENT == rcode)
2152 return RP_BODY_NONE;
2153
2154#if 0
2155 /* This check is not needed as upgrade handler is used only with code 101 */
2156#ifdef UPGRADE_SUPPORT
2157 if (NULL != rp.response->upgrade_handler)
2158 return RP_BODY_NONE;
2159#endif /* UPGRADE_SUPPORT */
2160#endif
2161
2162#if 0
2163 /* CONNECT is not supported by MHD */
2164 /* Successful responses for connect requests are filtered by
2165 * MHD_queue_response() */
2166 if ( (MHD_HTTP_MTHD_CONNECT == c->rq.http_mthd) &&
2167 (2 == rcode / 100) )
2168 return false; /* Actually pass-through CONNECT is not supported by MHD */
2169#endif
2170
2171 /* Reply body headers could be used.
2172 * Check whether reply body itself must be used. */
2173
2175 return RP_BODY_HEADERS_ONLY;
2176
2177 if (MHD_HTTP_NOT_MODIFIED == rcode)
2178 return RP_BODY_HEADERS_ONLY;
2179
2180 /* Reply body must be sent. The body may have zero length, but body size
2181 * must be indicated by headers ('Content-Length:' or
2182 * 'Transfer-Encoding: chunked'). */
2183 return RP_BODY_SEND;
2184}
2185
2186
2195static void
2197{
2198 struct MHD_Connection *const c = connection;
2199 struct MHD_Response *const r = c->rp.response;
2200 enum replyBodyUse use_rp_body;
2201 bool use_chunked;
2202
2203 mhd_assert (NULL != r);
2204
2205 /* ** Adjust reply properties ** */
2206
2208 use_rp_body = is_reply_body_needed (c, c->rp.responseCode);
2209 c->rp.props.send_reply_body = (use_rp_body > RP_BODY_HEADERS_ONLY);
2211
2212#ifdef UPGRADE_SUPPORT
2213 mhd_assert ( (NULL == r->upgrade_handler) ||
2214 (RP_BODY_NONE == use_rp_body) );
2215#endif /* UPGRADE_SUPPORT */
2216
2218 {
2219 if ((MHD_SIZE_UNKNOWN == r->total_size) ||
2221 { /* Use chunked reply encoding if possible */
2222
2223 /* Check whether chunked encoding is supported by the client */
2225 use_chunked = false;
2226 /* Check whether chunked encoding is allowed for the reply */
2227 else if (0 != (r->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
2229 use_chunked = false;
2230 else
2231 /* If chunked encoding is supported and allowed, and response size
2232 * is unknown, use chunked even for non-Keep-Alive connections.
2233 * See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3
2234 * Also use chunked if it is enforced by application and supported by
2235 * the client. */
2236 use_chunked = true;
2237 }
2238 else
2239 use_chunked = false;
2240
2241 if ( (MHD_SIZE_UNKNOWN == r->total_size) &&
2242 (! use_chunked) )
2243 {
2244 /* End of the stream is indicated by closure */
2246 }
2247 }
2248 else
2249 use_chunked = false; /* chunked encoding cannot be used without body */
2250
2251 c->rp.props.chunked = use_chunked;
2252#ifdef _DEBUG
2253 c->rp.props.set = true;
2254#endif /* _DEBUG */
2255}
2256
2257
2262static void
2264{
2265 struct MHD_Connection *const c = connection;
2266 struct MHD_Response *const r = c->rp.response;
2268 mhd_assert (c->rp.props.set);
2269#ifdef HAVE_MESSAGES
2270 if ( (! c->rp.props.use_reply_body_headers) &&
2271 (0 != r->total_size) )
2272 {
2273 MHD_DLOG (c->daemon,
2274 _ ("This reply with response code %u cannot use reply body. "
2275 "Non-empty response body is ignored and not used.\n"),
2276 (unsigned) (c->rp.responseCode));
2277 }
2278 if ( (! c->rp.props.use_reply_body_headers) &&
2280 {
2281 MHD_DLOG (c->daemon,
2282 _ ("This reply with response code %u cannot use reply body. "
2283 "Application defined \"Content-Length\" header violates"
2284 "HTTP specification.\n"),
2285 (unsigned) (c->rp.responseCode));
2286 }
2287#else
2288 (void) c; /* Mute compiler warning */
2289 (void) r; /* Mute compiler warning */
2290#endif
2291}
2292
2293
2305static bool
2306buffer_append (char *buf,
2307 size_t *ppos,
2308 size_t buf_size,
2309 const char *append,
2310 size_t append_size)
2311{
2312 mhd_assert (NULL != buf); /* Mute static analyzer */
2313 if (buf_size < *ppos + append_size)
2314 return false;
2315 memcpy (buf + *ppos, append, append_size);
2316 *ppos += append_size;
2317 return true;
2318}
2319
2320
2331#define buffer_append_s(buf,ppos,buf_size,str) \
2332 buffer_append (buf,ppos,buf_size,str, MHD_STATICSTR_LEN_ (str))
2333
2334
2354static bool
2356 size_t *ppos,
2357 size_t buf_size,
2358 struct MHD_Response *response,
2359 bool filter_transf_enc,
2360 bool filter_content_len,
2361 bool add_close,
2362 bool add_keep_alive)
2363{
2364 struct MHD_Response *const r = response;
2365 struct MHD_HTTP_Res_Header *hdr;
2366 size_t el_size;
2368 mhd_assert (! add_close || ! add_keep_alive);
2369
2371 filter_transf_enc = false; /* No such header */
2372 if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2373 filter_content_len = false; /* No such header */
2374 if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2375 {
2376 add_close = false; /* No such header */
2377 add_keep_alive = false; /* No such header */
2378 }
2379 else if (0 != (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE))
2380 add_close = false; /* "close" token was already set */
2381
2382 for (hdr = r->first_header; NULL != hdr; hdr = hdr->next)
2383 {
2384 size_t initial_pos = *ppos;
2385 if (MHD_HEADER_KIND != hdr->kind)
2386 continue;
2387 if (filter_transf_enc)
2388 { /* Need to filter-out "Transfer-Encoding" */
2390 hdr->header_size) &&
2392 hdr->header, hdr->header_size)) )
2393 {
2394 filter_transf_enc = false; /* There is the only one such header */
2395 continue; /* Skip "Transfer-Encoding" header */
2396 }
2397 }
2398 if (filter_content_len)
2399 { /* Need to filter-out "Content-Length" */
2401 hdr->header_size) &&
2403 hdr->header, hdr->header_size)) )
2404 {
2405 /* Reset filter flag if only one header is allowed */
2406 filter_transf_enc =
2408 continue; /* Skip "Content-Length" header */
2409 }
2410 }
2411
2412 /* Add user header */
2413 el_size = hdr->header_size + 2 + hdr->value_size + 2;
2414 if (buf_size < *ppos + el_size)
2415 return false;
2416 memcpy (buf + *ppos, hdr->header, hdr->header_size);
2417 (*ppos) += hdr->header_size;
2418 buf[(*ppos)++] = ':';
2419 buf[(*ppos)++] = ' ';
2420 if (add_close || add_keep_alive)
2421 {
2422 /* "Connection:" header must be always the first one */
2425 hdr->header_size));
2426
2427 if (add_close)
2428 {
2429 el_size += MHD_STATICSTR_LEN_ ("close, ");
2430 if (buf_size < initial_pos + el_size)
2431 return false;
2432 memcpy (buf + *ppos, "close, ",
2433 MHD_STATICSTR_LEN_ ("close, "));
2434 *ppos += MHD_STATICSTR_LEN_ ("close, ");
2435 }
2436 else
2437 {
2438 el_size += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2439 if (buf_size < initial_pos + el_size)
2440 return false;
2441 memcpy (buf + *ppos, "Keep-Alive, ",
2442 MHD_STATICSTR_LEN_ ("Keep-Alive, "));
2443 *ppos += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2444 }
2445 add_close = false;
2446 add_keep_alive = false;
2447 }
2448 if (0 != hdr->value_size)
2449 memcpy (buf + *ppos, hdr->value, hdr->value_size);
2450 *ppos += hdr->value_size;
2451 buf[(*ppos)++] = '\r';
2452 buf[(*ppos)++] = '\n';
2453 mhd_assert (initial_pos + el_size == (*ppos));
2454 }
2455 return true;
2456}
2457
2458
2467static enum MHD_Result
2469{
2470 struct MHD_Connection *const c = connection;
2471 struct MHD_Response *const r = c->rp.response;
2472 char *buf;
2473 size_t pos;
2474 size_t buf_size;
2475 size_t el_size;
2476 unsigned rcode;
2477 bool use_conn_close;
2478 bool use_conn_k_alive;
2480 mhd_assert (NULL != r);
2481
2482 /* ** Adjust response properties ** */
2484
2485 mhd_assert (c->rp.props.set);
2489#ifdef UPGRADE_SUPPORT
2490 mhd_assert ((NULL == r->upgrade_handler) || \
2492#else /* ! UPGRADE_SUPPORT */
2494#endif /* ! UPGRADE_SUPPORT */
2496 mhd_assert ((! c->rp.props.send_reply_body) || \
2498#ifdef UPGRADE_SUPPORT
2499 mhd_assert (NULL == r->upgrade_handler || \
2501#endif /* UPGRADE_SUPPORT */
2502
2504
2505 rcode = (unsigned) c->rp.responseCode;
2507 {
2508 /* The closure of connection must be always indicated by header
2509 * to avoid hung connections */
2510 use_conn_close = true;
2511 use_conn_k_alive = false;
2512 }
2513 else if (MHD_CONN_USE_KEEPALIVE == c->keepalive)
2514 {
2515 use_conn_close = false;
2516 /* Add "Connection: keep-alive" if request is HTTP/1.0 or
2517 * if reply is HTTP/1.0
2518 * For HTTP/1.1 add header only if explicitly requested by app
2519 * (by response flag), as "Keep-Alive" is default for HTTP/1.1. */
2520 if ((0 != (r->flags & MHD_RF_SEND_KEEP_ALIVE_HEADER)) ||
2521 (MHD_HTTP_VER_1_0 == c->rq.http_ver) ||
2522 (0 != (r->flags & MHD_RF_HTTP_1_0_SERVER)))
2523 use_conn_k_alive = true;
2524 else
2525 use_conn_k_alive = false;
2526 }
2527 else
2528 {
2529 use_conn_close = false;
2530 use_conn_k_alive = false;
2531 }
2532
2533 /* ** Actually build the response header ** */
2534
2535 /* Get all space available */
2537 buf = c->write_buffer;
2539 buf_size = c->write_buffer_size;
2540 if (0 == buf_size)
2541 return MHD_NO;
2542 mhd_assert (NULL != buf);
2543
2544 /* * The status line * */
2545
2546 /* The HTTP version */
2547 if (! c->rp.responseIcy)
2548 { /* HTTP reply */
2549 if (0 == (r->flags & MHD_RF_HTTP_1_0_SERVER))
2550 { /* HTTP/1.1 reply */
2551 /* Use HTTP/1.1 responses for HTTP/1.0 clients.
2552 * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
2553 if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_1))
2554 return MHD_NO;
2555 }
2556 else
2557 { /* HTTP/1.0 reply */
2558 if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_0))
2559 return MHD_NO;
2560 }
2561 }
2562 else
2563 { /* ICY reply */
2564 if (! buffer_append_s (buf, &pos, buf_size, "ICY"))
2565 return MHD_NO;
2566 }
2567
2568 /* The response code */
2569 if (buf_size < pos + 5) /* space + code + space */
2570 return MHD_NO;
2571 buf[pos++] = ' ';
2572 pos += MHD_uint16_to_str ((uint16_t) rcode, buf + pos,
2573 buf_size - pos);
2574 buf[pos++] = ' ';
2575
2576 /* The reason phrase */
2577 el_size = MHD_get_reason_phrase_len_for (rcode);
2578 if (0 == el_size)
2579 {
2580 if (! buffer_append_s (buf, &pos, buf_size, "Non-Standard Status"))
2581 return MHD_NO;
2582 }
2583 else if (! buffer_append (buf, &pos, buf_size,
2585 el_size))
2586 return MHD_NO;
2587
2588 /* The linefeed */
2589 if (buf_size < pos + 2)
2590 return MHD_NO;
2591 buf[pos++] = '\r';
2592 buf[pos++] = '\n';
2593
2594 /* * The headers * */
2595
2596 /* Main automatic headers */
2597
2598 /* The "Date:" header */
2599 if ( (0 == (r->flags_auto & MHD_RAF_HAS_DATE_HDR)) &&
2601 {
2602 /* Additional byte for unused zero-termination */
2603 if (buf_size < pos + 38)
2604 return MHD_NO;
2605 if (get_date_header (buf + pos))
2606 pos += 37;
2607 }
2608 /* The "Connection:" header */
2609 mhd_assert (! use_conn_close || ! use_conn_k_alive);
2610 mhd_assert (! use_conn_k_alive || ! use_conn_close);
2611 if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2612 {
2613 if (use_conn_close)
2614 {
2615 if (! buffer_append_s (buf, &pos, buf_size,
2616 MHD_HTTP_HEADER_CONNECTION ": close\r\n"))
2617 return MHD_NO;
2618 }
2619 else if (use_conn_k_alive)
2620 {
2621 if (! buffer_append_s (buf, &pos, buf_size,
2622 MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n"))
2623 return MHD_NO;
2624 }
2625 }
2626
2627 /* User-defined headers */
2628
2629 if (! add_user_headers (buf, &pos, buf_size, r,
2630 ! c->rp.props.chunked,
2632 (0 ==
2634 use_conn_close,
2635 use_conn_k_alive))
2636 return MHD_NO;
2637
2638 /* Other automatic headers */
2639
2640 if ( (c->rp.props.use_reply_body_headers) &&
2641 (0 == (r->flags & MHD_RF_HEAD_ONLY_RESPONSE)) )
2642 {
2643 /* Body-specific headers */
2644
2645 if (c->rp.props.chunked)
2646 { /* Chunked encoding is used */
2648 { /* No chunked encoding header set by user */
2649 if (! buffer_append_s (buf, &pos, buf_size,
2651 "chunked\r\n"))
2652 return MHD_NO;
2653 }
2654 }
2655 else /* Chunked encoding is not used */
2656 {
2657 if (MHD_SIZE_UNKNOWN != r->total_size)
2658 { /* The size is known */
2659 if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2660 { /* The response does not have "Content-Length" header */
2661 if (! buffer_append_s (buf, &pos, buf_size,
2663 return MHD_NO;
2664 el_size = MHD_uint64_to_str (r->total_size, buf + pos,
2665 buf_size - pos);
2666 if (0 == el_size)
2667 return MHD_NO;
2668 pos += el_size;
2669
2670 if (buf_size < pos + 2)
2671 return MHD_NO;
2672 buf[pos++] = '\r';
2673 buf[pos++] = '\n';
2674 }
2675 }
2676 }
2677 }
2678
2679 /* * Header termination * */
2680 if (buf_size < pos + 2)
2681 return MHD_NO;
2682 buf[pos++] = '\r';
2683 buf[pos++] = '\n';
2684
2686 return MHD_YES;
2687}
2688
2689
2699static enum MHD_Result
2701{
2702 char *buf;
2703 size_t buf_size;
2704 size_t used_size;
2705 struct MHD_Connection *const c = connection;
2706 struct MHD_HTTP_Res_Header *pos;
2707
2708 mhd_assert (connection->rp.props.chunked);
2709 /* TODO: allow combining of the final footer with the last chunk,
2710 * modify the next assert. */
2712 mhd_assert (NULL != c->rp.response);
2713
2714 buf_size = connection_maximize_write_buffer (c);
2715 /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */
2716 if (buf_size < 5)
2717 return MHD_NO;
2720 mhd_assert (NULL != buf);
2721 used_size = 0;
2722 buf[used_size++] = '0';
2723 buf[used_size++] = '\r';
2724 buf[used_size++] = '\n';
2725
2726 for (pos = c->rp.response->first_header; NULL != pos; pos = pos->next)
2727 {
2728 if (MHD_FOOTER_KIND == pos->kind)
2729 {
2730 size_t new_used_size; /* resulting size with this header */
2731 /* '4' is colon, space, linefeeds */
2732 new_used_size = used_size + pos->header_size + pos->value_size + 4;
2733 if (new_used_size > buf_size)
2734 return MHD_NO;
2735 memcpy (buf + used_size, pos->header, pos->header_size);
2736 used_size += pos->header_size;
2737 buf[used_size++] = ':';
2738 buf[used_size++] = ' ';
2739 memcpy (buf + used_size, pos->value, pos->value_size);
2740 used_size += pos->value_size;
2741 buf[used_size++] = '\r';
2742 buf[used_size++] = '\n';
2743 mhd_assert (used_size == new_used_size);
2744 }
2745 }
2746 if (used_size + 2 > buf_size)
2747 return MHD_NO;
2748 buf[used_size++] = '\r';
2749 buf[used_size++] = '\n';
2750
2751 c->write_buffer_append_offset += used_size;
2753
2754 return MHD_YES;
2755}
2756
2757
2774static void
2776 unsigned int status_code,
2777 const char *message,
2778 size_t message_len,
2779 char *header_name,
2780 size_t header_name_len,
2781 char *header_value,
2782 size_t header_value_len)
2783{
2784 struct MHD_Response *response;
2785 enum MHD_Result iret;
2786
2787 mhd_assert (! connection->stop_with_error); /* Do not send error twice */
2788 if (connection->stop_with_error)
2789 { /* Should not happen */
2790 if (MHD_CONNECTION_CLOSED > connection->state)
2791 connection->state = MHD_CONNECTION_CLOSED;
2792 free (header_name);
2793 free (header_value);
2794 return;
2795 }
2796 connection->stop_with_error = true;
2797 connection->discard_request = true;
2798#ifdef HAVE_MESSAGES
2799 MHD_DLOG (connection->daemon,
2800 _ ("Error processing request (HTTP response code is %u ('%s')). " \
2801 "Closing connection.\n"),
2802 status_code,
2803 message);
2804#endif
2806 {
2807#ifdef HAVE_MESSAGES
2808 MHD_DLOG (connection->daemon,
2809 _ ("Too late to send an error response, " \
2810 "response is being sent already.\n"),
2811 status_code,
2812 message);
2813#endif
2814 CONNECTION_CLOSE_ERROR (connection,
2815 _ ("Too late for error response."));
2816 free (header_name);
2817 free (header_value);
2818 return;
2819 }
2820 /* TODO: remove when special error queue function is implemented */
2822 if (0 != connection->read_buffer_size)
2823 {
2824 /* Read buffer is not needed anymore, discard it
2825 * to free some space for error response. */
2826 MHD_pool_deallocate (connection->pool,
2827 connection->read_buffer,
2828 connection->read_buffer_size);
2829 connection->read_buffer = NULL;
2830 connection->read_buffer_size = 0;
2831 connection->read_buffer_offset = 0;
2832 }
2833 if (NULL != connection->rp.response)
2834 {
2835 MHD_destroy_response (connection->rp.response);
2836 connection->rp.response = NULL;
2837 }
2838 response = MHD_create_response_from_buffer_static (message_len,
2839 message);
2840 if (NULL == response)
2841 {
2842#ifdef HAVE_MESSAGES
2843 MHD_DLOG (connection->daemon,
2844 _ ("Failed to create error response.\n"),
2845 status_code,
2846 message);
2847#endif
2848 /* can't even send a reply, at least close the connection */
2849 connection->state = MHD_CONNECTION_CLOSED;
2850 free (header_name);
2851 free (header_value);
2852 return;
2853 }
2854 mhd_assert ((0 == header_name_len) || (NULL != header_name));
2855 mhd_assert ((NULL == header_name) || (0 != header_name_len));
2856 mhd_assert ((0 == header_value_len) || (NULL != header_value));
2857 mhd_assert ((NULL == header_value) || (0 != header_value_len));
2858 mhd_assert ((NULL == header_name) || (NULL != header_value));
2859 mhd_assert ((NULL != header_value) || (NULL == header_name));
2860 if (NULL != header_name)
2861 {
2862 iret = MHD_add_response_entry_no_alloc_ (response,
2864 header_name, header_name_len,
2865 header_value, header_value_len);
2866 if (MHD_NO == iret)
2867 {
2868 free (header_name);
2869 free (header_value);
2870 }
2871 }
2872 else
2873 iret = MHD_YES;
2874
2875 if (MHD_NO != iret)
2876 {
2877 bool before = connection->in_access_handler;
2878
2879 /* Fake the flag for the internal call */
2880 connection->in_access_handler = true;
2881 iret = MHD_queue_response (connection,
2882 status_code,
2883 response);
2884 connection->in_access_handler = before;
2885 }
2886 MHD_destroy_response (response);
2887 if (MHD_NO == iret)
2888 {
2889 /* can't even send a reply, at least close the connection */
2890 CONNECTION_CLOSE_ERROR (connection,
2891 _ ("Closing connection " \
2892 "(failed to queue error response)."));
2893 return;
2894 }
2895 mhd_assert (NULL != connection->rp.response);
2896 /* Do not reuse this connection. */
2897 connection->keepalive = MHD_CONN_MUST_CLOSE;
2898 if (MHD_NO == build_header_response (connection))
2899 {
2900 /* No memory. Release everything. */
2901 connection->rq.version = NULL;
2902 connection->rq.method = NULL;
2903 connection->rq.url = NULL;
2904 connection->rq.url_len = 0;
2905 connection->rq.headers_received = NULL;
2906 connection->rq.headers_received_tail = NULL;
2907 connection->write_buffer = NULL;
2908 connection->write_buffer_size = 0;
2909 connection->write_buffer_send_offset = 0;
2910 connection->write_buffer_append_offset = 0;
2911 connection->read_buffer
2912 = MHD_pool_reset (connection->pool,
2913 NULL,
2914 0,
2915 0);
2916 connection->read_buffer_size = 0;
2917
2918 /* Retry with empty buffer */
2919 if (MHD_NO == build_header_response (connection))
2920 {
2921 CONNECTION_CLOSE_ERROR (connection,
2922 _ ("Closing connection " \
2923 "(failed to create error response header)."));
2924 return;
2925 }
2926 }
2928}
2929
2930
2934#define transmit_error_response_static(c, code, msg) \
2935 transmit_error_response_len (c, code, \
2936 msg, MHD_STATICSTR_LEN_ (msg), \
2937 NULL, 0, NULL, 0)
2938
2942#define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l) \
2943 transmit_error_response_len (c, code, \
2944 m, MHD_STATICSTR_LEN_ (m), \
2945 hd_n, hd_n_l, \
2946 hd_v, hd_v_l)
2947
2948
2959static bool
2961{
2963 if (! c->rq.have_chunked_upload)
2964 return 0 != c->read_buffer_offset;
2965
2966 /* Chunked upload */
2967 mhd_assert (0 != c->rq.remaining_upload_size); /* Must not be possible in MHD_CONNECTION_BODY_RECEIVING state */
2969 {
2970 /* 0 == c->rq.current_chunk_size: Waiting the chunk size (chunk header).
2971 0 != c->rq.current_chunk_size: Waiting for chunk-closing CRLF. */
2972 return false;
2973 }
2974 return 0 != c->read_buffer_offset; /* Chunk payload data in the read buffer */
2975}
2976
2977
2994
2995
2996#ifndef MHD_MAX_REASONABLE_HEADERS_SIZE_
3006# define MHD_MAX_REASONABLE_HEADERS_SIZE_ (6 * 1024)
3007#endif /* ! MHD_MAX_REASONABLE_HEADERS_SIZE_ */
3008
3009#ifndef MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
3020# define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 8000
3021#endif /* ! MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ */
3022
3023#ifndef MHD_MIN_REASONABLE_HEADERS_SIZE_
3031# define MHD_MIN_REASONABLE_HEADERS_SIZE_ 26
3032#endif /* ! MHD_MIN_REASONABLE_HEADERS_SIZE_ */
3033
3034#ifndef MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
3042# define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 40
3043#endif /* ! MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ */
3044
3045#ifndef MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
3053# define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 16
3054#endif /* ! MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ */
3055
3056#ifndef MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
3062# define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 4
3063#endif /* ! MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ */
3064
3065
3077static unsigned int
3079 enum MHD_ProcRecvDataStage stage,
3080 const char *add_element,
3081 size_t add_element_size)
3082{
3083 size_t method_size;
3084 size_t uri_size;
3085 size_t opt_headers_size;
3086 size_t host_field_line_size;
3087
3090 mhd_assert ((0 == add_element_size) || (NULL != add_element));
3091
3093 {
3095 opt_headers_size =
3096 (size_t) ((c->read_buffer + c->read_buffer_offset)
3097 - c->rq.field_lines.start);
3098 }
3099 else
3100 opt_headers_size = c->rq.field_lines.size;
3101
3102 /* The read buffer is fully used by the request line, the field lines
3103 (headers) and internal information.
3104 The return status code works as a suggestion for the client to reduce
3105 one of the request elements. */
3106
3107 if ((MHD_PROC_RECV_BODY_CHUNKED == stage) &&
3108 (MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ < add_element_size))
3109 {
3110 /* Request could be re-tried easily with smaller chunk sizes */
3112 }
3113
3114 host_field_line_size = 0;
3115 /* The "Host:" field line is mandatory.
3116 The total size of the field lines (headers) cannot be smaller than
3117 the size of the "Host:" field line. */
3118 if ((MHD_PROC_RECV_HEADERS == stage)
3119 && (0 != add_element_size))
3120 {
3121 static const size_t header_host_key_len =
3123 const bool is_host_header =
3124 (header_host_key_len + 1 <= add_element_size)
3125 && ( (0 == add_element[header_host_key_len])
3126 || (':' == add_element[header_host_key_len]) )
3128 add_element,
3129 header_host_key_len);
3130 if (is_host_header)
3131 {
3132 const bool is_parsed = ! (
3134 (add_element_size == c->read_buffer_offset) &&
3135 (c->read_buffer == add_element) );
3136 size_t actual_element_size;
3137
3138 mhd_assert (! is_parsed || (0 == add_element[header_host_key_len]));
3139 /* The actual size should be larger due to CRLF or LF chars,
3140 however the exact termination sequence is not known here and
3141 as perfect precision is not required, to simplify the code
3142 assume the minimal length. */
3143 if (is_parsed)
3144 actual_element_size = add_element_size + 1; /* "1" for LF */
3145 else
3146 actual_element_size = add_element_size;
3147
3148 host_field_line_size = actual_element_size;
3149 mhd_assert (opt_headers_size >= actual_element_size);
3150 opt_headers_size -= actual_element_size;
3151 }
3152 }
3153 if (0 == host_field_line_size)
3154 {
3155 static const size_t host_field_name_len =
3157 size_t host_field_name_value_len;
3161 host_field_name_len,
3162 NULL,
3163 &host_field_name_value_len))
3164 {
3165 /* Calculate the minimal size of the field line: no space between
3166 colon and the field value, line terminated by LR */
3167 host_field_line_size =
3168 host_field_name_len + host_field_name_value_len + 2; /* "2" for ':' and LF */
3169
3170 /* The "Host:" field could be added by application */
3171 if (opt_headers_size >= host_field_line_size)
3172 {
3173 opt_headers_size -= host_field_line_size;
3174 /* Take into account typical space after colon and CR at the end of the line */
3175 if (opt_headers_size >= 2)
3176 opt_headers_size -= 2;
3177 }
3178 else
3179 host_field_line_size = 0; /* No "Host:" field line set by the client */
3180 }
3181 }
3182
3183 uri_size = c->rq.req_target_len;
3185 method_size = 0; /* Do not recommend shorter request method */
3186 else
3187 {
3188 mhd_assert (NULL != c->rq.method);
3189 method_size = strlen (c->rq.method);
3190 }
3191
3192 if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3193 {
3194 /* Typically the easiest way to reduce request header size is
3195 a removal of some optional headers. */
3196 if (opt_headers_size > (uri_size / 8))
3197 {
3198 if ((opt_headers_size / 2) > method_size)
3200 else
3201 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3202 }
3203 else
3204 { /* Request target is MUCH larger than headers */
3205 if ((uri_size / 16) > method_size)
3206 return MHD_HTTP_URI_TOO_LONG;
3207 else
3208 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3209 }
3210 }
3211 if ((size_t) MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3212 {
3213 /* If request target size if larger than maximum reasonable size
3214 recommend client to reduce the request target size (length). */
3215 if ((uri_size / 16) > method_size)
3216 return MHD_HTTP_URI_TOO_LONG; /* Request target is MUCH larger than headers */
3217 else
3218 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3219 }
3220
3221 /* The read buffer is too small to handle reasonably large requests */
3222
3223 if ((size_t) MHD_MIN_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3224 {
3225 /* Recommend application to retry with minimal headers */
3226 if ((opt_headers_size * 4) > uri_size)
3227 {
3228 if (opt_headers_size > method_size)
3230 else
3231 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3232 }
3233 else
3234 { /* Request target is significantly larger than headers */
3235 if (uri_size > method_size * 4)
3236 return MHD_HTTP_URI_TOO_LONG;
3237 else
3238 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3239 }
3240 }
3241 if ((size_t) MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3242 {
3243 /* Recommend application to retry with a shorter request target */
3244 if (uri_size > method_size * 4)
3245 return MHD_HTTP_URI_TOO_LONG;
3246 else
3247 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3248 }
3249
3250 if ((size_t) MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ < method_size)
3251 {
3252 /* The request target (URI) and headers are (reasonably) very small.
3253 Some non-standard long request method is used. */
3254 /* The last resort response as it means "the method is not supported
3255 by the server for any URI". */
3257 }
3258
3259 /* The almost impossible situation: all elements are small, but cannot
3260 fit the buffer. The application set the buffer size to
3261 critically low value? */
3262
3263 if ((1 < opt_headers_size) || (1 < uri_size))
3264 {
3265 if (opt_headers_size >= uri_size)
3267 else
3268 return MHD_HTTP_URI_TOO_LONG;
3269 }
3270
3271 /* Nothing to reduce in the request.
3272 Reply with some status. */
3273 if (0 != host_field_line_size)
3275
3276 return MHD_HTTP_URI_TOO_LONG;
3277}
3278
3279
3290static void
3292 const char *add_header,
3293 size_t add_header_size)
3294{
3295 unsigned int err_code;
3296
3297 err_code = get_no_space_err_status_code (c,
3299 add_header,
3300 add_header_size);
3302 err_code,
3304}
3305
3306
3307#ifdef COOKIE_SUPPORT
3313static void
3314handle_req_cookie_no_space (struct MHD_Connection *c)
3315{
3316 unsigned int err_code;
3317
3318 err_code = get_no_space_err_status_code (c,
3320 NULL,
3321 0);
3323 err_code,
3325}
3326
3327
3328#endif /* COOKIE_SUPPORT */
3329
3330
3341static void
3343 const char *chunk_size_line,
3344 size_t chunk_size_line_size)
3345{
3346 unsigned int err_code;
3347
3348 if (NULL != chunk_size_line)
3349 {
3350 const char *semicol;
3351 /* Check for chunk extension */
3352 semicol = memchr (chunk_size_line, ';', chunk_size_line_size);
3353 if (NULL != semicol)
3354 { /* Chunk extension present. It could be removed without any loss of the
3355 details of the request. */
3359 }
3360 }
3361 err_code = get_no_space_err_status_code (c,
3363 chunk_size_line,
3364 chunk_size_line_size);
3366 err_code,
3368}
3369
3370
3381static void
3383 const char *add_footer,
3384 size_t add_footer_size)
3385{
3386 (void) add_footer; (void) add_footer_size; /* Unused */
3388
3389 /* Footers should be optional */
3393}
3394
3395
3406static void
3408 enum MHD_ProcRecvDataStage stage)
3409{
3410 mhd_assert (MHD_PROC_RECV_INIT <= stage);
3413 mhd_assert ((MHD_PROC_RECV_INIT != stage) || \
3414 (MHD_CONNECTION_INIT == c->state));
3415 mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \
3417 mhd_assert ((MHD_PROC_RECV_URI != stage) || \
3419 mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \
3421 mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \
3423 mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */
3424 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3426 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3428 mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \
3430 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3431 (! c->rq.have_chunked_upload));
3432 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3433 (c->rq.have_chunked_upload));
3434 switch (stage)
3435 {
3436 case MHD_PROC_RECV_INIT:
3438 /* Some data has been received, but it is not clear yet whether
3439 * the received data is an valid HTTP request */
3441 _ ("No space left in the read buffer when " \
3442 "receiving the initial part of " \
3443 "the request line."));
3444 return;
3445 case MHD_PROC_RECV_URI:
3447 /* Some data has been received, but the request line is incomplete */
3450 /* A quick simple check whether the incomplete line looks
3451 * like an HTTP request */
3452 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
3454 {
3458 return;
3459 }
3461 _ ("No space left in the read buffer when " \
3462 "receiving the URI in " \
3463 "the request line. " \
3464 "The request uses non-standard HTTP request " \
3465 "method token."));
3466 return;
3469 return;
3472 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3475 {
3476 /* The connection must not be in MHD_EVENT_LOOP_INFO_READ state
3477 when external polling is used and some data left unprocessed. */
3479 /* failed to grow the read buffer, and the
3480 client which is supposed to handle the
3481 received data in a *blocking* fashion
3482 (in this mode) did not handle the data as
3483 it was supposed to!
3484 => we would either have to do busy-waiting
3485 (on the client, which would likely fail),
3486 or if we do nothing, we would just timeout
3487 on the connection (if a timeout is even
3488 set!).
3489 Solution: we kill the connection with an error */
3493 }
3494 else
3495 {
3496 if (MHD_PROC_RECV_BODY_NORMAL == stage)
3497 {
3498 /* A header probably has been added to a suspended connection and
3499 it took precisely all the space in the buffer.
3500 Very low probability. */
3503 }
3504 else
3505 {
3508 { /* Receiving content of the chunk */
3509 /* A header probably has been added to a suspended connection and
3510 it took precisely all the space in the buffer.
3511 Very low probability. */
3513 }
3514 else
3515 {
3516 if (0 != c->rq.current_chunk_size)
3517 { /* Waiting for chunk-closing CRLF */
3518 /* Not really possible as some payload should be
3519 processed and the space used by payload should be available. */
3521 }
3522 else
3523 { /* Reading the line with the chunk size */
3525 c->read_buffer,
3527 }
3528 }
3529 }
3530 }
3531 return;
3534 return;
3535 /* The next cases should not be possible */
3537 default:
3538 break;
3539 }
3540 mhd_assert (0);
3541}
3542
3543
3557static bool
3559{
3563 bool rbuff_grow_desired;
3567 bool rbuff_grow_required;
3568
3571
3572 rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size);
3573 if (rbuff_grow_required)
3574 rbuff_grow_desired = true;
3575 else
3576 {
3577 rbuff_grow_desired = (c->read_buffer_offset + c->daemon->pool_increment >
3578 c->read_buffer_size);
3579
3580 if ((rbuff_grow_desired) &&
3582 {
3583 if (! c->rq.have_chunked_upload)
3584 {
3586 /* Do not grow read buffer more than necessary to process the current
3587 request. */
3588 rbuff_grow_desired =
3590 }
3591 else
3592 {
3594 if (0 == c->rq.current_chunk_size)
3595 rbuff_grow_desired = /* Reading value of the next chunk size */
3597 c->read_buffer_size);
3598 else
3599 {
3600 const uint64_t cur_chunk_left =
3602 /* Do not grow read buffer more than necessary to process the current
3603 chunk with terminating CRLF. */
3605 rbuff_grow_desired =
3606 ((cur_chunk_left + 2) > (uint64_t) (c->read_buffer_size));
3607 }
3608 }
3609 }
3610 }
3611
3612 if (! rbuff_grow_desired)
3613 return true; /* No need to increase the buffer */
3614
3615 if (try_grow_read_buffer (c, rbuff_grow_required))
3616 return true; /* Buffer increase succeed */
3617
3618 if (! rbuff_grow_required)
3619 return true; /* Can continue without buffer increase */
3620
3621 /* Failed to increase the read buffer size, but need to read the data
3622 from the network.
3623 No more space left in the buffer, no more space to increase the buffer. */
3624
3625 /* 'PROCESS_READ' event state flag must be set only if the last application
3626 callback has processed some data. If any data is processed then some
3627 space in the read buffer must be available. */
3629
3630 if ((! MHD_D_IS_USING_THREADS_ (c->daemon))
3633 {
3634 /* The application is handling processing cycles.
3635 The data could be processed later. */
3637 return true;
3638 }
3639 else
3640 {
3641 enum MHD_ProcRecvDataStage stage;
3642
3643 switch (c->state)
3644 {
3646 stage = MHD_PROC_RECV_INIT;
3647 break;
3650 stage = MHD_PROC_RECV_METHOD;
3651 else if (0 == c->rq.req_target_len)
3652 stage = MHD_PROC_RECV_URI;
3653 else
3654 stage = MHD_PROC_RECV_HTTPVER;
3655 break;
3657 stage = MHD_PROC_RECV_HEADERS;
3658 break;
3660 stage = c->rq.have_chunked_upload ?
3662 break;
3664 stage = MHD_PROC_RECV_FOOTERS;
3665 break;
3684#ifdef UPGRADE_SUPPORT
3685 case MHD_CONNECTION_UPGRADE:
3686#endif
3687 default:
3689 mhd_assert (0);
3690 }
3691
3692 handle_recv_no_space (c, stage);
3693 }
3694 return false;
3695}
3696
3697
3706static void
3708{
3709 /* Do not update states of suspended connection */
3710 if (connection->suspended)
3711 return; /* States will be updated after resume. */
3712#ifdef HTTPS_SUPPORT
3713 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
3714 { /* HTTPS connection. */
3715 switch (connection->tls_state)
3716 {
3717 case MHD_TLS_CONN_INIT:
3719 return;
3722 if (0 == gnutls_record_get_direction (connection->tls_session))
3724 else
3726 return;
3728 break; /* Do normal processing */
3732 return;
3733 case MHD_TLS_CONN_TLS_CLOSING: /* Not implemented yet */
3734 case MHD_TLS_CONN_TLS_CLOSED: /* Not implemented yet */
3736 case MHD_TLS_CONN_NO_TLS: /* Not possible */
3737 default:
3738 MHD_PANIC (_ ("Invalid TLS state value.\n"));
3739 }
3740 }
3741#endif /* HTTPS_SUPPORT */
3742 while (1)
3743 {
3744#if DEBUG_STATES
3745 MHD_DLOG (connection->daemon,
3746 _ ("In function %s handling connection at state: %s\n"),
3747 MHD_FUNC_,
3748 MHD_state_to_string (connection->state));
3749#endif
3750 switch (connection->state)
3751 {
3755 break;
3757 mhd_assert (0);
3758 break;
3761 break;
3764 mhd_assert (0);
3765 break;
3768 break;
3770 if ((connection->rq.some_payload_processed) &&
3772 {
3773 /* Some data was processed, the buffer must have some free space */
3774 mhd_assert (connection->read_buffer_offset < \
3775 connection->read_buffer_size);
3776 if (! connection->rq.have_chunked_upload)
3777 {
3778 /* Not a chunked upload. Do not read more than necessary to
3779 process the current request. */
3780 if (connection->rq.remaining_upload_size >=
3781 connection->read_buffer_offset)
3783 else
3785 }
3786 else
3787 {
3788 /* Chunked upload. The size of the current request is unknown.
3789 Continue reading as the space in the read buffer is available. */
3791 }
3792 }
3793 else
3795 break;
3797 mhd_assert (0);
3798 break;
3801 break;
3803 mhd_assert (0);
3804 break;
3807 break;
3809 mhd_assert (0);
3810 break;
3812 /* headers in buffer, keep writing */
3814 break;
3816 mhd_assert (0);
3817 break;
3820 break;
3823 break;
3826 break;
3829 break;
3831 mhd_assert (0);
3832 break;
3835 break;
3837 mhd_assert (0);
3838 break;
3841 return; /* do nothing, not even reading */
3842#ifdef UPGRADE_SUPPORT
3843 case MHD_CONNECTION_UPGRADE:
3844 mhd_assert (0);
3845 break;
3846#endif /* UPGRADE_SUPPORT */
3847 default:
3848 mhd_assert (0);
3849 }
3850
3851 if (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info))
3852 {
3853 /* Check whether the space is available to receive data */
3854 if (! check_and_grow_read_buffer_space (connection))
3855 {
3856 mhd_assert (connection->discard_request);
3857 continue;
3858 }
3859 }
3860 break; /* Everything was processed. */
3861 }
3862}
3863
3864
3877static enum MHD_Result
3879 const char *key,
3880 size_t key_size,
3881 const char *value,
3882 size_t value_size,
3883 enum MHD_ValueKind kind)
3884{
3885 struct MHD_Connection *connection = (struct MHD_Connection *) cls;
3886 if (MHD_NO ==
3887 MHD_set_connection_value_n (connection,
3888 kind,
3889 key,
3890 key_size,
3891 value,
3892 value_size))
3893 {
3894#ifdef HAVE_MESSAGES
3895 MHD_DLOG (connection->daemon,
3896 _ ("Not enough memory in pool to allocate header record!\n"));
3897#endif
3901 return MHD_NO;
3902 }
3903 return MHD_YES;
3904}
3905
3906
3907#ifdef COOKIE_SUPPORT
3908
3912enum _MHD_ParseCookie
3913{
3914 MHD_PARSE_COOKIE_OK = MHD_YES,
3915 MHD_PARSE_COOKIE_OK_LAX = 2,
3916 MHD_PARSE_COOKIE_MALFORMED = -1,
3917 MHD_PARSE_COOKIE_NO_MEMORY = MHD_NO
3918};
3919
3920
3933static enum _MHD_ParseCookie
3934parse_cookies_string (char *str,
3935 const size_t str_len,
3936 struct MHD_Connection *connection)
3937{
3938 size_t i;
3939 bool non_strict;
3940 /* Skip extra whitespaces and empty cookies */
3941 const bool allow_wsp_empty = (0 >= connection->daemon->client_discipline);
3942 /* Allow whitespaces around '=' character */
3943 const bool wsp_around_eq = (-3 >= connection->daemon->client_discipline);
3944 /* Allow whitespaces in quoted cookie value */
3945 const bool wsp_in_quoted = (-2 >= connection->daemon->client_discipline);
3946 /* Allow tab as space after semicolon between cookies */
3947 const bool tab_as_sp = (0 >= connection->daemon->client_discipline);
3948 /* Allow no space after semicolon between cookies */
3949 const bool allow_no_space = (0 >= connection->daemon->client_discipline);
3950
3951 non_strict = false;
3952 i = 0;
3953 while (i < str_len)
3954 {
3955 size_t name_start;
3956 size_t name_len;
3957 size_t value_start;
3958 size_t value_len;
3959 bool val_quoted;
3960 /* Skip any whitespaces and empty cookies */
3961 while (' ' == str[i] || '\t' == str[i] || ';' == str[i])
3962 {
3963 if (! allow_wsp_empty)
3964 return MHD_PARSE_COOKIE_MALFORMED;
3965 non_strict = true;
3966 i++;
3967 if (i == str_len)
3968 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
3969 }
3970 /* 'i' must point to the first char of cookie-name */
3971 name_start = i;
3972 /* Find the end of the cookie-name */
3973 do
3974 {
3975 const char l = str[i];
3976 if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) ||
3977 (';' == l) || (0 == l))
3978 break;
3979 } while (str_len > ++i);
3980 name_len = i - name_start;
3981 /* Skip any whitespaces */
3982 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
3983 {
3984 if (! wsp_around_eq)
3985 return MHD_PARSE_COOKIE_MALFORMED;
3986 non_strict = true;
3987 i++;
3988 }
3989 if ((str_len == i) || ('=' != str[i]) || (0 == name_len))
3990 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */
3991 /* 'i' must point to the '=' char */
3992 mhd_assert ('=' == str[i]);
3993 i++;
3994 /* Skip any whitespaces */
3995 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
3996 {
3997 if (! wsp_around_eq)
3998 return MHD_PARSE_COOKIE_MALFORMED;
3999 non_strict = true;
4000 i++;
4001 }
4002 /* 'i' must point to the first char of cookie-value */
4003 if (str_len == i)
4004 {
4005 value_start = 0;
4006 value_len = 0;
4007#ifdef _DEBUG
4008 val_quoted = false; /* This assignment used in assert */
4009#endif
4010 }
4011 else
4012 {
4013 bool valid_cookie;
4014 val_quoted = ('"' == str[i]);
4015 if (val_quoted)
4016 i++;
4017 value_start = i;
4018 /* Find the end of the cookie-value */
4019 while (str_len > i)
4020 {
4021 const char l = str[i];
4022 if ((';' == l) || ('"' == l) || (',' == l) || (';' == l) ||
4023 ('\\' == l) || (0 == l))
4024 break;
4025 if ((' ' == l) || ('\t' == l))
4026 {
4027 if (! val_quoted)
4028 break;
4029 if (! wsp_in_quoted)
4030 return MHD_PARSE_COOKIE_MALFORMED;
4031 non_strict = true;
4032 }
4033 i++;
4034 }
4035 value_len = i - value_start;
4036 if (val_quoted)
4037 {
4038 if ((str_len == i) || ('"' != str[i]))
4039 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */
4040 i++;
4041 }
4042 /* Skip any whitespaces */
4043 if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i])))
4044 {
4045 do
4046 {
4047 i++;
4048 } while (str_len > i && (' ' == str[i] || '\t' == str[i]));
4049 /* Whitespace at the end? */
4050 if (str_len > i)
4051 {
4052 if (! allow_wsp_empty)
4053 return MHD_PARSE_COOKIE_MALFORMED;
4054 non_strict = true;
4055 }
4056 }
4057 if (str_len == i)
4058 valid_cookie = true;
4059 else if (';' == str[i])
4060 valid_cookie = true;
4061 else
4062 valid_cookie = false;
4063
4064 if (! valid_cookie)
4065 return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */
4066 }
4067 mhd_assert (0 != name_len);
4068 str[name_start + name_len] = 0; /* Zero-terminate the name */
4069 if (0 != value_len)
4070 {
4071 mhd_assert (value_start + value_len <= str_len);
4072 str[value_start + value_len] = 0; /* Zero-terminate the value */
4073 if (MHD_NO ==
4076 str + name_start,
4077 name_len,
4078 str + value_start,
4079 value_len))
4080 return MHD_PARSE_COOKIE_NO_MEMORY;
4081 }
4082 else
4083 {
4084 if (MHD_NO ==
4087 str + name_start,
4088 name_len,
4089 "",
4090 0))
4091 return MHD_PARSE_COOKIE_NO_MEMORY;
4092 }
4093 if (str_len > i)
4094 {
4095 mhd_assert (0 == str[i] || ';' == str[i]);
4096 mhd_assert (! val_quoted || ';' == str[i]);
4097 mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len);
4098 i++;
4099 if (str_len == i)
4100 { /* No next cookie after semicolon */
4101 if (! allow_wsp_empty)
4102 return MHD_PARSE_COOKIE_MALFORMED;
4103 non_strict = true;
4104 }
4105 else if (' ' != str[i])
4106 {/* No space after semicolon */
4107 if (('\t' == str[i]) && tab_as_sp)
4108 i++;
4109 else if (! allow_no_space)
4110 return MHD_PARSE_COOKIE_MALFORMED;
4111 non_strict = true;
4112 }
4113 else
4114 {
4115 i++;
4116 if (str_len == i)
4117 {
4118 if (! allow_wsp_empty)
4119 return MHD_PARSE_COOKIE_MALFORMED;
4120 non_strict = true;
4121 }
4122 }
4123 }
4124 }
4125 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
4126}
4127
4128
4135static enum _MHD_ParseCookie
4136parse_cookie_header (struct MHD_Connection *connection)
4137{
4138 const char *hdr;
4139 size_t hdr_len;
4140 char *cpy;
4141 size_t i;
4142 enum _MHD_ParseCookie parse_res;
4143 struct MHD_HTTP_Req_Header *const saved_tail =
4144 connection->rq.headers_received_tail;
4145 const bool allow_partially_correct_cookie =
4146 (1 >= connection->daemon->client_discipline);
4147
4148 if (MHD_NO ==
4154 &hdr,
4155 &hdr_len))
4156 return MHD_PARSE_COOKIE_OK;
4157 if (0 == hdr_len)
4158 return MHD_PARSE_COOKIE_OK;
4159
4160 cpy = MHD_connection_alloc_memory_ (connection,
4161 hdr_len + 1);
4162 if (NULL == cpy)
4163 parse_res = MHD_PARSE_COOKIE_NO_MEMORY;
4164 else
4165 {
4166 memcpy (cpy,
4167 hdr,
4168 hdr_len);
4169 cpy[hdr_len] = '\0';
4170
4171 i = 0;
4172 /* Skip all initial whitespaces */
4173 while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i]))
4174 i++;
4175
4176 parse_res = parse_cookies_string (cpy + i, hdr_len - i, connection);
4177 }
4178
4179 switch (parse_res)
4180 {
4181 case MHD_PARSE_COOKIE_OK:
4182 break;
4183 case MHD_PARSE_COOKIE_OK_LAX:
4184#ifdef HAVE_MESSAGES
4185 if (saved_tail != connection->rq.headers_received_tail)
4186 MHD_DLOG (connection->daemon,
4187 _ ("The Cookie header has been parsed, but it is not fully "
4188 "compliant with the standard.\n"));
4189#endif /* HAVE_MESSAGES */
4190 break;
4191 case MHD_PARSE_COOKIE_MALFORMED:
4192 if (saved_tail != connection->rq.headers_received_tail)
4193 {
4194 if (! allow_partially_correct_cookie)
4195 {
4196 /* Remove extracted values from partially broken cookie */
4197 /* Memory remains allocated until the end of the request processing */
4198 connection->rq.headers_received_tail = saved_tail;
4199 saved_tail->next = NULL;
4200#ifdef HAVE_MESSAGES
4201 MHD_DLOG (connection->daemon,
4202 _ ("The Cookie header has been ignored as it contains "
4203 "malformed data.\n"));
4204#endif /* HAVE_MESSAGES */
4205 }
4206#ifdef HAVE_MESSAGES
4207 else
4208 MHD_DLOG (connection->daemon,
4209 _ ("The Cookie header has been only partially parsed as it "
4210 "contains malformed data.\n"));
4211#endif /* HAVE_MESSAGES */
4212 }
4213#ifdef HAVE_MESSAGES
4214 else
4215 MHD_DLOG (connection->daemon,
4216 _ ("The Cookie header has malformed data.\n"));
4217#endif /* HAVE_MESSAGES */
4218 break;
4219 case MHD_PARSE_COOKIE_NO_MEMORY:
4220#ifdef HAVE_MESSAGES
4221 MHD_DLOG (connection->daemon,
4222 _ ("Not enough memory in the connection pool to "
4223 "parse client cookies!\n"));
4224#endif /* HAVE_MESSAGES */
4225 break;
4226 default:
4227 mhd_assert (0);
4228 break;
4229 }
4230#ifndef HAVE_MESSAGES
4231 (void) saved_tail; /* Mute compiler warning */
4232#endif /* ! HAVE_MESSAGES */
4233
4234 return parse_res;
4235}
4236
4237
4238#endif /* COOKIE_SUPPORT */
4239
4240
4244#define HTTP_VER_LEN (MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1))
4245
4255static bool
4257 const char *http_string,
4258 size_t len)
4259{
4260 const char *const h = http_string;
4261 mhd_assert (NULL != http_string);
4262
4263 /* String must start with 'HTTP/d.d', case-sensetive match.
4264 * See https://www.rfc-editor.org/rfc/rfc9112#name-http-version */
4265 if ((HTTP_VER_LEN != len) ||
4266 ('H' != h[0]) || ('T' != h[1]) || ('T' != h[2]) || ('P' != h[3]) ||
4267 ('/' != h[4])
4268 || ('.' != h[6]) ||
4269 (('0' > h[5]) || ('9' < h[5])) ||
4270 (('0' > h[7]) || ('9' < h[7])))
4271 {
4272 connection->rq.http_ver = MHD_HTTP_VER_INVALID;
4276 return false;
4277 }
4278 if (1 == h[5] - '0')
4279 {
4280 /* HTTP/1.x */
4281 if (1 == h[7] - '0')
4282 connection->rq.http_ver = MHD_HTTP_VER_1_1;
4283 else if (0 == h[7] - '0')
4284 connection->rq.http_ver = MHD_HTTP_VER_1_0;
4285 else
4286 connection->rq.http_ver = MHD_HTTP_VER_1_2__1_9;
4287
4288 return true;
4289 }
4290
4291 if (0 == h[5] - '0')
4292 {
4293 /* Too old major version */
4294 connection->rq.http_ver = MHD_HTTP_VER_TOO_OLD;
4298 return false;
4299 }
4300
4301 connection->rq.http_ver = MHD_HTTP_VER_FUTURE;
4305 return false;
4306}
4307
4308
4316static void
4318 const char *method,
4319 size_t len)
4320{
4321 const char *const m = method;
4322 mhd_assert (NULL != m);
4323 mhd_assert (0 != len);
4324
4325 if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET) == len) &&
4326 (0 == memcmp (m, MHD_HTTP_METHOD_GET, len)))
4327 connection->rq.http_mthd = MHD_HTTP_MTHD_GET;
4328 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_HEAD) == len) &&
4329 (0 == memcmp (m, MHD_HTTP_METHOD_HEAD, len)))
4330 connection->rq.http_mthd = MHD_HTTP_MTHD_HEAD;
4331 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_POST) == len) &&
4332 (0 == memcmp (m, MHD_HTTP_METHOD_POST, len)))
4333 connection->rq.http_mthd = MHD_HTTP_MTHD_POST;
4334 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_PUT) == len) &&
4335 (0 == memcmp (m, MHD_HTTP_METHOD_PUT, len)))
4336 connection->rq.http_mthd = MHD_HTTP_MTHD_PUT;
4337 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_DELETE) == len) &&
4338 (0 == memcmp (m, MHD_HTTP_METHOD_DELETE, len)))
4339 connection->rq.http_mthd = MHD_HTTP_MTHD_DELETE;
4340 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_CONNECT) == len) &&
4341 (0 == memcmp (m, MHD_HTTP_METHOD_CONNECT, len)))
4342 connection->rq.http_mthd = MHD_HTTP_MTHD_CONNECT;
4343 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_OPTIONS) == len) &&
4344 (0 == memcmp (m, MHD_HTTP_METHOD_OPTIONS, len)))
4345 connection->rq.http_mthd = MHD_HTTP_MTHD_OPTIONS;
4346 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_TRACE) == len) &&
4347 (0 == memcmp (m, MHD_HTTP_METHOD_TRACE, len)))
4348 connection->rq.http_mthd = MHD_HTTP_MTHD_TRACE;
4349 else
4350 connection->rq.http_mthd = MHD_HTTP_MTHD_OTHER;
4351}
4352
4353
4361static void
4363{
4364 struct MHD_Daemon *daemon = connection->daemon;
4365 size_t processed;
4366
4367 if (NULL != connection->rp.response)
4368 return; /* already queued a response */
4369 processed = 0;
4370 connection->rq.client_aware = true;
4371 connection->in_access_handler = true;
4372 if (MHD_NO ==
4373 daemon->default_handler (daemon->default_handler_cls,
4374 connection,
4375 connection->rq.url,
4376 connection->rq.method,
4377 connection->rq.version,
4378 NULL,
4379 &processed,
4380 &connection->rq.client_context))
4381 {
4382 connection->in_access_handler = false;
4383 /* serious internal error, close connection */
4384 CONNECTION_CLOSE_ERROR (connection,
4385 _ ("Application reported internal error, " \
4386 "closing connection."));
4387 return;
4388 }
4389 connection->in_access_handler = false;
4390}
4391
4392
4400static void
4402{
4403 struct MHD_Daemon *daemon = connection->daemon;
4404 size_t available;
4405 bool instant_retry;
4406 char *buffer_head;
4407 const int discp_lvl = daemon->client_discipline;
4408 /* Treat bare LF as the end of the line.
4409 RFC 9112, section 2.2-3
4410 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
4411 Bare LF is processed as end of the line or rejected as broken request. */
4412 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
4413 /* Allow "Bad WhiteSpace" in chunk extension.
4414 RFC 9112, Section 7.1.1, Paragraph 2 */
4415 const bool allow_bws = (2 < discp_lvl);
4416
4417 mhd_assert (NULL == connection->rp.response);
4418
4419 buffer_head = connection->read_buffer;
4420 available = connection->read_buffer_offset;
4421 do
4422 {
4423 size_t to_be_processed;
4424 size_t left_unprocessed;
4425 size_t processed_size;
4426
4427 instant_retry = false;
4428 if (connection->rq.have_chunked_upload)
4429 {
4431 if ( (connection->rq.current_chunk_offset ==
4432 connection->rq.current_chunk_size) &&
4433 (0 != connection->rq.current_chunk_size) )
4434 {
4435 size_t i;
4436 mhd_assert (0 != available);
4437 /* skip new line at the *end* of a chunk */
4438 i = 0;
4439 if ( (2 <= available) &&
4440 ('\r' == buffer_head[0]) &&
4441 ('\n' == buffer_head[1]) )
4442 i += 2; /* skip CRLF */
4443 else if (bare_lf_as_crlf && ('\n' == buffer_head[0]))
4444 i++; /* skip bare LF */
4445 else if (2 > available)
4446 break; /* need more upload data */
4447 if (0 == i)
4448 {
4449 /* malformed encoding */
4453 return;
4454 }
4455 available -= i;
4456 buffer_head += i;
4457 connection->rq.current_chunk_offset = 0;
4458 connection->rq.current_chunk_size = 0;
4459 if (0 == available)
4460 break;
4461 }
4462 if (0 != connection->rq.current_chunk_size)
4463 {
4464 uint64_t cur_chunk_left;
4465 mhd_assert (connection->rq.current_chunk_offset < \
4466 connection->rq.current_chunk_size);
4467 /* we are in the middle of a chunk, give
4468 as much as possible to the client (without
4469 crossing chunk boundaries) */
4470 cur_chunk_left
4471 = connection->rq.current_chunk_size
4472 - connection->rq.current_chunk_offset;
4473 if (cur_chunk_left > available)
4474 to_be_processed = available;
4475 else
4476 { /* cur_chunk_left <= (size_t)available */
4477 to_be_processed = (size_t) cur_chunk_left;
4478 if (available > to_be_processed)
4479 instant_retry = true;
4480 }
4481 }
4482 else
4483 { /* Need the parse the chunk size line */
4485 size_t num_dig;
4486 uint64_t chunk_size;
4487 bool broken;
4488 bool overflow;
4489
4490 mhd_assert (0 != available);
4491
4492 overflow = false;
4493 chunk_size = 0; /* Mute possible compiler warning.
4494 The real value will be set later. */
4495
4496 num_dig = MHD_strx_to_uint64_n_ (buffer_head,
4497 available,
4498 &chunk_size);
4499 mhd_assert (num_dig <= available);
4500 if (num_dig == available)
4501 continue; /* Need line delimiter */
4502
4503 broken = (0 == num_dig);
4504 if (broken)
4505 /* Check whether result is invalid due to uint64_t overflow */
4506 overflow = ((('0' <= buffer_head[0]) && ('9' >= buffer_head[0])) ||
4507 (('A' <= buffer_head[0]) && ('F' >= buffer_head[0])) ||
4508 (('a' <= buffer_head[0]) && ('f' >= buffer_head[0])));
4509 else
4510 {
4515 size_t chunk_size_line_len;
4516
4517 chunk_size_line_len = 0;
4518 if ((';' == buffer_head[num_dig]) ||
4519 (allow_bws &&
4520 ((' ' == buffer_head[num_dig]) ||
4521 ('\t' == buffer_head[num_dig]))))
4522 { /* Chunk extension */
4523 size_t i;
4524
4525 /* Skip bad whitespaces (if any) */
4526 for (i = num_dig; i < available; ++i)
4527 {
4528 if ((' ' != buffer_head[i]) && ('\t' != buffer_head[i]))
4529 break;
4530 }
4531 if (i == available)
4532 break; /* need more data */
4533 if (';' == buffer_head[i])
4534 {
4535 for (++i; i < available; ++i)
4536 {
4537 if ('\n' == buffer_head[i])
4538 break;
4539 }
4540 if (i == available)
4541 break; /* need more data */
4542 mhd_assert (i > num_dig);
4543 mhd_assert (1 <= i);
4544 /* Found LF position */
4545 if (bare_lf_as_crlf)
4546 chunk_size_line_len = i; /* Don't care about CR before LF */
4547 else if ('\r' == buffer_head[i - 1])
4548 chunk_size_line_len = i;
4549 }
4550 else
4551 { /* No ';' after "bad whitespace" */
4552 mhd_assert (allow_bws);
4553 mhd_assert (0 == chunk_size_line_len);
4554 }
4555 }
4556 else
4557 {
4558 mhd_assert (available >= num_dig);
4559 if ((2 <= (available - num_dig)) &&
4560 ('\r' == buffer_head[num_dig]) &&
4561 ('\n' == buffer_head[num_dig + 1]))
4562 chunk_size_line_len = num_dig + 2;
4563 else if (bare_lf_as_crlf &&
4564 ('\n' == buffer_head[num_dig]))
4565 chunk_size_line_len = num_dig + 1;
4566 else if (2 > (available - num_dig))
4567 break; /* need more data */
4568 }
4569
4570 if (0 != chunk_size_line_len)
4571 { /* Valid termination of the chunk size line */
4572 mhd_assert (chunk_size_line_len <= available);
4573 /* Start reading payload data of the chunk */
4574 connection->rq.current_chunk_offset = 0;
4575 connection->rq.current_chunk_size = chunk_size;
4576
4577 available -= chunk_size_line_len;
4578 buffer_head += chunk_size_line_len;
4579
4580 if (0 == chunk_size)
4581 { /* The final (termination) chunk */
4582 connection->rq.remaining_upload_size = 0;
4583 break;
4584 }
4585 if (available > 0)
4586 instant_retry = true;
4587 continue;
4588 }
4589 /* Invalid chunk size line */
4590 }
4591
4592 if (! overflow)
4596 else
4600 return;
4601 }
4602 }
4603 else
4604 {
4605 /* no chunked encoding, give all to the client */
4607 mhd_assert (0 != connection->rq.remaining_upload_size);
4608 if (connection->rq.remaining_upload_size < available)
4609 to_be_processed = (size_t) connection->rq.remaining_upload_size;
4610 else
4611 to_be_processed = available;
4612 }
4613 left_unprocessed = to_be_processed;
4614 connection->rq.client_aware = true;
4615 connection->in_access_handler = true;
4616 if (MHD_NO ==
4617 daemon->default_handler (daemon->default_handler_cls,
4618 connection,
4619 connection->rq.url,
4620 connection->rq.method,
4621 connection->rq.version,
4622 buffer_head,
4623 &left_unprocessed,
4624 &connection->rq.client_context))
4625 {
4626 connection->in_access_handler = false;
4627 /* serious internal error, close connection */
4628 CONNECTION_CLOSE_ERROR (connection,
4629 _ ("Application reported internal error, " \
4630 "closing connection."));
4631 return;
4632 }
4633 connection->in_access_handler = false;
4634
4635 if (left_unprocessed > to_be_processed)
4636 MHD_PANIC (_ ("libmicrohttpd API violation.\n"));
4637
4638 connection->rq.some_payload_processed =
4639 (left_unprocessed != to_be_processed);
4640
4641 if (0 != left_unprocessed)
4642 {
4643 instant_retry = false; /* client did not process everything */
4644#ifdef HAVE_MESSAGES
4645 if ((! connection->rq.some_payload_processed) &&
4646 (! connection->suspended))
4647 {
4648 /* client did not process any upload data, complain if
4649 the setup was incorrect, which may prevent us from
4650 handling the rest of the request */
4651 if (MHD_D_IS_USING_THREADS_ (daemon))
4652 MHD_DLOG (daemon,
4653 _ ("WARNING: Access Handler Callback has not processed " \
4654 "any upload data and connection is not suspended. " \
4655 "This may result in hung connection.\n"));
4656 }
4657#endif /* HAVE_MESSAGES */
4658 }
4659 processed_size = to_be_processed - left_unprocessed;
4660 /* dh left "processed" bytes in buffer for next time... */
4661 buffer_head += processed_size;
4662 available -= processed_size;
4663 if (! connection->rq.have_chunked_upload)
4664 {
4666 connection->rq.remaining_upload_size -= processed_size;
4667 }
4668 else
4669 {
4671 connection->rq.current_chunk_offset += processed_size;
4672 }
4673 } while (instant_retry);
4674 /* TODO: zero out reused memory region */
4675 if ( (available > 0) &&
4676 (buffer_head != connection->read_buffer) )
4677 memmove (connection->read_buffer,
4678 buffer_head,
4679 available);
4680 else
4681 mhd_assert ((0 == available) || \
4682 (connection->read_buffer_offset == available));
4683 connection->read_buffer_offset = available;
4684}
4685
4686
4695static enum MHD_Result
4697 enum MHD_CONNECTION_STATE next_state)
4698{
4699 if ( (connection->write_buffer_append_offset !=
4700 connection->write_buffer_send_offset)
4701 /* || data_in_tls_buffers == true */
4702 )
4703 return MHD_NO;
4704 connection->write_buffer_append_offset = 0;
4705 connection->write_buffer_send_offset = 0;
4706 connection->state = next_state;
4707 return MHD_YES;
4708}
4709
4710
4718static void
4720{
4721 const char *clen;
4722 const char *enc;
4723 size_t val_len;
4724
4725#ifdef COOKIE_SUPPORT
4726 if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (connection))
4727 {
4728 handle_req_cookie_no_space (connection);
4729 return;
4730 }
4731#endif /* COOKIE_SUPPORT */
4732 if ( (-3 < connection->daemon->client_discipline) &&
4733 (MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver)) &&
4734 (MHD_NO ==
4740 NULL,
4741 NULL)) )
4742 {
4743#ifdef HAVE_MESSAGES
4744 MHD_DLOG (connection->daemon,
4745 _ ("Received HTTP/1.1 request without `Host' header.\n"));
4746#endif
4750 return;
4751 }
4752
4753 /* The presence of the request body is indicated by "Content-Length:" or
4754 "Transfer-Encoding:" request headers.
4755 Unless one of these two headers is used, the request has no request body.
4756 See RFC9112, Section 6, paragraph 4. */
4757 connection->rq.remaining_upload_size = 0;
4758 if (MHD_NO !=
4764 &enc,
4765 NULL))
4766 {
4767 if (! MHD_str_equal_caseless_ (enc,
4768 "chunked"))
4769 {
4773 return;
4774 }
4775 else if (MHD_NO !=
4781 NULL,
4782 NULL))
4783 {
4784 /* TODO: add individual settings */
4785 if (1 <= connection->daemon->client_discipline)
4786 {
4790 return;
4791 }
4792 else
4793 {
4794 /* Must close connection after reply to prevent potential attack */
4795 connection->keepalive = MHD_CONN_MUST_CLOSE;
4796#ifdef HAVE_MESSAGES
4797 MHD_DLOG (connection->daemon,
4798 _ ("The 'Content-Length' request header is ignored "
4799 "as chunked Transfer-Encoding is used "
4800 "for this request.\n"));
4801#endif /* HAVE_MESSAGES */
4802 }
4803 }
4804 connection->rq.have_chunked_upload = true;
4806 }
4807 else if (MHD_NO !=
4813 &clen,
4814 &val_len))
4815 {
4816 size_t num_digits;
4817
4818 num_digits = MHD_str_to_uint64_n_ (clen,
4819 val_len,
4820 &connection->rq.remaining_upload_size);
4821
4822 if (((0 == num_digits) &&
4823 (0 != val_len) &&
4824 ('0' <= clen[0]) && ('9' >= clen[0]))
4825 || (MHD_SIZE_UNKNOWN == connection->rq.remaining_upload_size))
4826 {
4827 connection->rq.remaining_upload_size = 0;
4828#ifdef HAVE_MESSAGES
4829 MHD_DLOG (connection->daemon,
4830 _ ("Too large value of 'Content-Length' header. " \
4831 "Closing connection.\n"));
4832#endif
4836 }
4837 else if ((val_len != num_digits) ||
4838 (0 == num_digits))
4839 {
4840 connection->rq.remaining_upload_size = 0;
4841#ifdef HAVE_MESSAGES
4842 MHD_DLOG (connection->daemon,
4843 _ ("Failed to parse 'Content-Length' header. " \
4844 "Closing connection.\n"));
4845#endif
4849 }
4850 }
4851}
4852
4853
4861_MHD_static_inline void
4863{
4864 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
4865}
4866
4867
4872_MHD_static_inline void
4874{
4876 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
4878}
4879
4880
4881#ifndef MHD_MAX_EMPTY_LINES_SKIP
4886#define MHD_MAX_EMPTY_LINES_SKIP 1024
4887#endif /* ! MHD_MAX_EMPTY_LINES_SKIP */
4888
4896static bool
4898{
4899 size_t p;
4900 const int discp_lvl = c->daemon->client_discipline;
4901 /* Allow to skip one or more empty lines before the request line.
4902 RFC 9112, section 2.2 */
4903 const bool skip_empty_lines = (1 >= discp_lvl);
4904 /* Allow to skip more then one empty line before the request line.
4905 RFC 9112, section 2.2 */
4906 const bool skip_several_empty_lines = (skip_empty_lines && (0 >= discp_lvl));
4907 /* Allow to skip number of unlimited empty lines before the request line.
4908 RFC 9112, section 2.2 */
4909 const bool skip_unlimited_empty_lines =
4910 (skip_empty_lines && (-3 >= discp_lvl));
4911 /* Treat bare LF as the end of the line.
4912 RFC 9112, section 2.2 */
4913 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
4914 /* Treat tab as whitespace delimiter.
4915 RFC 9112, section 3 */
4916 const bool tab_as_wsp = (0 >= discp_lvl);
4917 /* Treat VT (vertical tab) and FF (form feed) as whitespace delimiters.
4918 RFC 9112, section 3 */
4919 const bool other_wsp_as_wsp = (-1 >= discp_lvl);
4920 /* Treat continuous whitespace block as a single space.
4921 RFC 9112, section 3 */
4922 const bool wsp_blocks = (-1 >= discp_lvl);
4923 /* Parse whitespace in URI, special parsing of the request line.
4924 RFC 9112, section 3.2 */
4925 const bool wsp_in_uri = (0 >= discp_lvl);
4926 /* Keep whitespace in URI, give app URI with whitespace instead of
4927 automatic redirect to fixed URI.
4928 Violates RFC 9112, section 3.2 */
4929 const bool wsp_in_uri_keep = (-2 >= discp_lvl);
4930 /* Keep bare CR character as is.
4931 Violates RFC 9112, section 2.2 */
4932 const bool bare_cr_keep = (wsp_in_uri_keep && (-3 >= discp_lvl));
4933 /* Treat bare CR as space; replace it with space before processing.
4934 RFC 9112, section 2.2 */
4935 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
4936
4939 mhd_assert (NULL == c->rq.method || \
4944 0 != c->rq.hdrs.rq_line.proc_pos);
4945
4946 if (0 == c->read_buffer_offset)
4947 {
4949 return false; /* No data to process */
4950 }
4951 p = c->rq.hdrs.rq_line.proc_pos;
4952 mhd_assert (p <= c->read_buffer_offset);
4953
4954 /* Skip empty lines, if any (and if allowed) */
4955 /* See RFC 9112, section 2.2 */
4956 if ((0 == p)
4957 && (skip_empty_lines))
4958 {
4959 /* Skip empty lines before the request line.
4960 See RFC 9112, section 2.2 */
4961 bool is_empty_line;
4963 mhd_assert (NULL == c->rq.method);
4964 mhd_assert (NULL == c->rq.url);
4965 mhd_assert (0 == c->rq.url_len);
4967 mhd_assert (0 == c->rq.req_target_len);
4968 mhd_assert (NULL == c->rq.version);
4969 do
4970 {
4971 is_empty_line = false;
4972 if ('\r' == c->read_buffer[0])
4973 {
4974 if (1 == c->read_buffer_offset)
4975 return false; /* Not enough data yet */
4976 if ('\n' == c->read_buffer[1])
4977 {
4978 is_empty_line = true;
4979 c->read_buffer += 2;
4980 c->read_buffer_size -= 2;
4981 c->read_buffer_offset -= 2;
4983 }
4984 }
4985 else if (('\n' == c->read_buffer[0]) &&
4986 (bare_lf_as_crlf))
4987 {
4988 is_empty_line = true;
4989 c->read_buffer += 1;
4990 c->read_buffer_size -= 1;
4991 c->read_buffer_offset -= 1;
4993 }
4994 if (is_empty_line)
4995 {
4996 if ((! skip_unlimited_empty_lines) &&
4997 (((unsigned int) ((skip_several_empty_lines) ?
5000 {
5002 _ ("Too many meaningless extra empty lines " \
5003 "received before the request"));
5004 return true; /* Process connection closure */
5005 }
5006 if (0 == c->read_buffer_offset)
5007 return false; /* No more data to process */
5008 }
5009 } while (is_empty_line);
5010 }
5011 /* All empty lines are skipped */
5012
5014 /* Read and parse the request line */
5016
5017 while (p < c->read_buffer_offset)
5018 {
5019 const char chr = c->read_buffer[p];
5020 bool end_of_line;
5021 /*
5022 The processing logic is different depending on the configured strictness:
5023
5024 When whitespace BLOCKS are NOT ALLOWED, the end of the whitespace is
5025 processed BEFORE processing of the current character.
5026 When whitespace BLOCKS are ALLOWED, the end of the whitespace is
5027 processed AFTER processing of the current character.
5028
5029 When space char in the URI is ALLOWED, the delimiter between the URI and
5030 the HTTP version string is processed only at the END of the line.
5031 When space in the URI is NOT ALLOWED, the delimiter between the URI and
5032 the HTTP version string is processed as soon as the FIRST whitespace is
5033 found after URI start.
5034 */
5035
5036 end_of_line = false;
5037
5038 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5039 (c->rq.hdrs.rq_line.last_ws_end > \
5041 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_start) || \
5042 (0 != c->rq.hdrs.rq_line.last_ws_end));
5043
5044 /* Check for the end of the line */
5045 if ('\r' == chr)
5046 {
5047 if (p + 1 == c->read_buffer_offset)
5048 {
5049 c->rq.hdrs.rq_line.proc_pos = p;
5050 return false; /* Not enough data yet */
5051 }
5052 else if ('\n' == c->read_buffer[p + 1])
5053 end_of_line = true;
5054 else
5055 {
5056 /* Bare CR alone */
5057 /* Must be rejected or replaced with space char.
5058 See RFC 9112, section 2.2 */
5059 if (bare_cr_as_sp)
5060 {
5061 c->read_buffer[p] = ' ';
5063 continue; /* Re-start processing of the current character */
5064 }
5065 else if (! bare_cr_keep)
5066 {
5067 /* A quick simple check whether this line looks like an HTTP request */
5068 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5070 {
5074 }
5075 else
5077 _ ("Bare CR characters are not allowed " \
5078 "in the request line.\n"));
5079 return true; /* Error in the request */
5080 }
5081 }
5082 }
5083 else if ('\n' == chr)
5084 {
5085 /* Bare LF may be recognised as a line delimiter.
5086 See RFC 9112, section 2.2 */
5087 if (bare_lf_as_crlf)
5088 end_of_line = true;
5089 else
5090 {
5091 /* While RFC does not enforce error for bare LF character,
5092 if this char is not treated as a line delimiter, it should be
5093 rejected to avoid any security weakness due to request smuggling. */
5094 /* A quick simple check whether this line looks like an HTTP request */
5095 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5097 {
5101 }
5102 else
5104 _ ("Bare LF characters are not allowed " \
5105 "in the request line.\n"));
5106 return true; /* Error in the request */
5107 }
5108 }
5109
5110 if (end_of_line)
5111 {
5112 /* Handle the end of the request line */
5113
5114 if (NULL != c->rq.method)
5115 {
5116 if (wsp_in_uri)
5117 {
5118 /* The end of the URI and the start of the HTTP version string
5119 should be determined now. */
5120 mhd_assert (NULL == c->rq.version);
5121 mhd_assert (0 == c->rq.req_target_len);
5122 if (0 != c->rq.hdrs.rq_line.last_ws_end)
5123 {
5124 /* Determine the end and the length of the URI */
5125 if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5126 {
5127 c->read_buffer [c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5128 c->rq.req_target_len =
5130 - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5131 }
5132 else if ((c->rq.hdrs.rq_line.last_ws_start + 1 <
5133 c->rq.hdrs.rq_line.last_ws_end) &&
5134 (HTTP_VER_LEN == (p - c->rq.hdrs.rq_line.last_ws_end)))
5135 {
5136 /* Found only HTTP method and HTTP version and more than one
5137 whitespace between them. Assume zero-length URI. */
5138 mhd_assert (wsp_blocks);
5140 c->read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5141 c->rq.hdrs.rq_line.rq_tgt =
5143 c->rq.req_target_len = 0;
5146 }
5147 /* Determine the start of the HTTP version string */
5148 if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5149 {
5151 }
5152 }
5153 }
5154 else
5155 {
5156 /* The end of the URI and the start of the HTTP version string
5157 should be already known. */
5158 if ((NULL == c->rq.version)
5159 && (NULL != c->rq.hdrs.rq_line.rq_tgt)
5160 && (HTTP_VER_LEN == p - (size_t) (c->rq.hdrs.rq_line.rq_tgt
5161 - c->read_buffer))
5162 && (0 != c->read_buffer[(size_t)
5163 (c->rq.hdrs.rq_line.rq_tgt
5164 - c->read_buffer) - 1]))
5165 {
5166 /* Found only HTTP method and HTTP version and more than one
5167 whitespace between them. Assume zero-length URI. */
5168 size_t uri_pos;
5169 mhd_assert (wsp_blocks);
5170 mhd_assert (0 == c->rq.req_target_len);
5171 uri_pos = (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer) - 1;
5172 mhd_assert (uri_pos < p);
5173 c->rq.version = c->rq.hdrs.rq_line.rq_tgt;
5174 c->read_buffer[uri_pos] = 0; /* Zero terminate the URI */
5175 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + uri_pos;
5176 c->rq.req_target_len = 0;
5179 }
5180 }
5181
5182 if (NULL != c->rq.version)
5183 {
5185 if (! parse_http_version (c, c->rq.version,
5186 p
5187 - (size_t) (c->rq.version
5188 - c->read_buffer)))
5189 {
5191 return true; /* Unsupported / broken HTTP version */
5192 }
5193 c->read_buffer[p] = 0; /* Zero terminate the HTTP version strings */
5194 if ('\r' == chr)
5195 {
5196 p++; /* Consume CR */
5197 mhd_assert (p < c->read_buffer_offset); /* The next character has been already checked */
5198 }
5199 p++; /* Consume LF */
5200 c->read_buffer += p;
5201 c->read_buffer_size -= p;
5202 c->read_buffer_offset -= p;
5204 c->rq.req_target_len);
5206 (0 != c->rq.req_target_len));
5208 ((size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5209 - c->rq.hdrs.rq_line.rq_tgt) < \
5210 c->rq.req_target_len));
5212 (c->rq.hdrs.rq_line.rq_tgt_qmark >= \
5213 c->rq.hdrs.rq_line.rq_tgt));
5214 return true; /* The request line is successfully parsed */
5215 }
5216 }
5217 /* Error in the request line */
5218
5219 /* A quick simple check whether this line looks like an HTTP request */
5220 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5222 {
5226 }
5227 else
5229 _ ("The request line is malformed.\n"));
5230
5231 return true;
5232 }
5233
5234 /* Process possible end of the previously found whitespace delimiter */
5235 if ((! wsp_blocks) &&
5236 (p == c->rq.hdrs.rq_line.last_ws_end) &&
5237 (0 != c->rq.hdrs.rq_line.last_ws_end))
5238 {
5239 /* Previous character was a whitespace char and whitespace blocks
5240 are not allowed. */
5241 /* The current position is the next character after
5242 a whitespace delimiter */
5243 if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5244 {
5245 /* The current position is the start of the URI */
5246 mhd_assert (0 == c->rq.req_target_len);
5247 mhd_assert (NULL == c->rq.version);
5248 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5249 /* Reset the whitespace marker */
5251 c->rq.hdrs.rq_line.last_ws_end = 0;
5252 }
5253 else
5254 {
5255 /* It was a whitespace after the start of the URI */
5256 if (! wsp_in_uri)
5257 {
5258 mhd_assert ((0 != c->rq.req_target_len) || \
5259 (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5260 mhd_assert (NULL == c->rq.version); /* Too many whitespaces? This error is handled at whitespace start */
5261 c->rq.version = c->read_buffer + p;
5262 /* Reset the whitespace marker */
5264 c->rq.hdrs.rq_line.last_ws_end = 0;
5265 }
5266 }
5267 }
5268
5269 /* Process the current character.
5270 Is it not the end of the line. */
5271 if ((' ' == chr)
5272 || (('\t' == chr) && (tab_as_wsp))
5273 || ((other_wsp_as_wsp) && ((0xb == chr) || (0xc == chr))))
5274 {
5275 /* A whitespace character */
5276 if ((0 == c->rq.hdrs.rq_line.last_ws_end) ||
5277 (p != c->rq.hdrs.rq_line.last_ws_end) ||
5278 (! wsp_blocks))
5279 {
5280 /* Found first whitespace char of the new whitespace block */
5281 if (NULL == c->rq.method)
5282 {
5283 /* Found the end of the HTTP method string */
5287 mhd_assert (0 == c->rq.req_target_len);
5288 mhd_assert (NULL == c->rq.version);
5289 if (0 == p)
5290 {
5292 _ ("The request line starts with "
5293 "a whitespace.\n"));
5294 return true; /* Error in the request */
5295 }
5296 c->read_buffer[p] = 0; /* Zero-terminate the request method string */
5297 c->rq.method = c->read_buffer;
5298 parse_http_std_method (c, c->rq.method, p);
5299 }
5300 else
5301 {
5302 /* A whitespace after the start of the URI */
5303 if (! wsp_in_uri)
5304 {
5305 /* Whitespace in URI is not allowed to be parsed */
5306 if (NULL == c->rq.version)
5307 {
5309 /* This is a delimiter between URI and HTTP version string */
5310 c->read_buffer[p] = 0; /* Zero-terminate request URI string */
5311 mhd_assert (((size_t) (c->rq.hdrs.rq_line.rq_tgt \
5312 - c->read_buffer)) <= p);
5313 c->rq.req_target_len =
5314 p - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5315 }
5316 else
5317 {
5318 /* This is a delimiter AFTER version string */
5319
5320 /* A quick simple check whether this line looks like an HTTP request */
5321 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5323 {
5327 }
5328 else
5330 _ ("The request line has more than "
5331 "two whitespaces.\n"));
5332 return true; /* Error in the request */
5333 }
5334 }
5335 else
5336 {
5337 /* Whitespace in URI is allowed to be parsed */
5338 if (0 != c->rq.hdrs.rq_line.last_ws_end)
5339 {
5340 /* The whitespace after the start of the URI has been found already */
5344 }
5345 }
5346 }
5348 c->rq.hdrs.rq_line.last_ws_end = p + 1; /* Will be updated on the next char parsing */
5349 }
5350 else
5351 {
5352 /* Continuation of the whitespace block */
5354 mhd_assert (0 != p);
5355 c->rq.hdrs.rq_line.last_ws_end = p + 1;
5356 }
5357 }
5358 else
5359 {
5360 /* Non-whitespace char, not the end of the line */
5361 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5362 (c->rq.hdrs.rq_line.last_ws_end == p) || \
5363 wsp_in_uri);
5364
5365 if ((p == c->rq.hdrs.rq_line.last_ws_end) &&
5366 (0 != c->rq.hdrs.rq_line.last_ws_end) &&
5367 (wsp_blocks))
5368 {
5369 /* The end of the whitespace block */
5370 if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5371 {
5372 /* This is the first character of the URI */
5373 mhd_assert (0 == c->rq.req_target_len);
5374 mhd_assert (NULL == c->rq.version);
5375 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5376 /* Reset the whitespace marker */
5378 c->rq.hdrs.rq_line.last_ws_end = 0;
5379 }
5380 else
5381 {
5382 if (! wsp_in_uri)
5383 {
5384 /* This is the first character of the HTTP version */
5386 mhd_assert ((0 != c->rq.req_target_len) || \
5387 (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5388 mhd_assert (NULL == c->rq.version); /* Handled at whitespace start */
5389 c->rq.version = c->read_buffer + p;
5390 /* Reset the whitespace marker */
5392 c->rq.hdrs.rq_line.last_ws_end = 0;
5393 }
5394 }
5395 }
5396
5397 /* Handle other special characters */
5398 if ('?' == chr)
5399 {
5400 if ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) &&
5401 (NULL != c->rq.hdrs.rq_line.rq_tgt))
5402 {
5404 }
5405 }
5406 else if ((0xb == chr) || (0xc == chr))
5407 {
5408 /* VT or LF characters */
5409 mhd_assert (! other_wsp_as_wsp);
5410 if ((NULL != c->rq.hdrs.rq_line.rq_tgt) &&
5411 (NULL == c->rq.version) &&
5412 (wsp_in_uri))
5413 {
5415 }
5416 else
5417 {
5419 _ ("Invalid character is in the "
5420 "request line.\n"));
5421 return true; /* Error in the request */
5422 }
5423 }
5424 else if (0 == chr)
5425 {
5426 /* NUL character */
5428 _ ("The NUL character is in the "
5429 "request line.\n"));
5430 return true; /* Error in the request */
5431 }
5432 }
5433
5434 p++;
5435 }
5436
5437 c->rq.hdrs.rq_line.proc_pos = p;
5438 return false; /* Not enough data yet */
5439}
5440
5441
5442#ifndef MHD_MAX_FIXED_URI_LEN
5446#define MHD_MAX_FIXED_URI_LEN (64 * 1024)
5447#endif /* ! MHD_MAX_FIXED_URI_LEN */
5448
5456static void
5458{
5459 char *b;
5460 size_t fixed_uri_len;
5461 size_t i;
5462 size_t o;
5463 char *hdr_name;
5464 size_t hdr_name_len;
5465
5469 c->rq.req_target_len);
5470 fixed_uri_len = c->rq.req_target_len
5471 + 2 * c->rq.hdrs.rq_line.num_ws_in_uri;
5472 if ( (fixed_uri_len + 200 > c->daemon->pool_size) ||
5473 (fixed_uri_len > MHD_MAX_FIXED_URI_LEN) ||
5474 (NULL == (b = malloc (fixed_uri_len + 1))) )
5475 {
5477 _ ("The request has whitespace character is " \
5478 "in the URI and the URI is too large to " \
5479 "send automatic redirect to fixed URI.\n"));
5480 return;
5481 }
5482 i = 0;
5483 o = 0;
5484
5485 do
5486 {
5487 const char chr = c->rq.hdrs.rq_line.rq_tgt[i++];
5488
5489 mhd_assert ('\r' != chr); /* Replaced during request line parsing */
5490 mhd_assert ('\n' != chr); /* Rejected during request line parsing */
5491 mhd_assert (0 != chr); /* Rejected during request line parsing */
5492 switch (chr)
5493 {
5494 case ' ':
5495 b[o++] = '%';
5496 b[o++] = '2';
5497 b[o++] = '0';
5498 break;
5499 case '\t':
5500 b[o++] = '%';
5501 b[o++] = '0';
5502 b[o++] = '9';
5503 break;
5504 case 0x0B: /* VT (vertical tab) */
5505 b[o++] = '%';
5506 b[o++] = '0';
5507 b[o++] = 'B';
5508 break;
5509 case 0x0C: /* FF (form feed) */
5510 b[o++] = '%';
5511 b[o++] = '0';
5512 b[o++] = 'C';
5513 break;
5514 default:
5515 b[o++] = chr;
5516 break;
5517 }
5518 } while (i < c->rq.req_target_len);
5519 mhd_assert (fixed_uri_len == o);
5520 b[o] = 0; /* Zero-terminate the result */
5521
5523 hdr_name = malloc (hdr_name_len + 1);
5524 if (NULL != hdr_name)
5525 {
5526 memcpy (hdr_name,
5528 hdr_name_len + 1);
5529 /* hdr_name and b are free()d within this call */
5533 hdr_name,
5534 hdr_name_len,
5535 b,
5536 o);
5537 return;
5538 }
5539 free (b);
5541 _ ("The request has whitespace character is in the " \
5542 "URI.\n"));
5543 return;
5544}
5545
5546
5553static bool
5555{
5556#ifdef _DEBUG
5557 size_t params_len;
5558#endif /* _DEBUG */
5560 mhd_assert (NULL == c->rq.url);
5561 mhd_assert (0 == c->rq.url_len);
5566 (c->rq.req_target_len > \
5567 (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5568 - c->rq.hdrs.rq_line.rq_tgt)));
5569
5570 /* Log callback before the request-target is modified/decoded */
5571 if (NULL != c->daemon->uri_log_callback)
5572 {
5573 c->rq.client_aware = true;
5574 c->rq.client_context =
5576 c->rq.hdrs.rq_line.rq_tgt,
5577 c);
5578 }
5579
5580 if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark)
5581 {
5582#ifdef _DEBUG
5583 params_len =
5585 - (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark - c->rq.hdrs.rq_line.rq_tgt);
5586#endif /* _DEBUG */
5587 c->rq.hdrs.rq_line.rq_tgt_qmark[0] = 0; /* Replace '?' with zero termination */
5588 if (MHD_NO == MHD_parse_arguments_ (c,
5590 c->rq.hdrs.rq_line.rq_tgt_qmark + 1,
5592 c))
5593 {
5595 return false;
5596 }
5597 }
5598#ifdef _DEBUG
5599 else
5600 params_len = 0;
5601#endif /* _DEBUG */
5602
5603 mhd_assert (strlen (c->rq.hdrs.rq_line.rq_tgt) == \
5604 c->rq.req_target_len - params_len);
5605
5606 /* Finally unescape URI itself */
5607 c->rq.url_len =
5609 c,
5610 c->rq.hdrs.rq_line.rq_tgt);
5611 c->rq.url = c->rq.hdrs.rq_line.rq_tgt;
5612
5613 return true;
5614}
5615
5616
5624static bool
5626{
5627 const int discp_lvl = c->daemon->client_discipline;
5628 /* Parse whitespace in URI, special parsing of the request line */
5629 const bool wsp_in_uri = (0 >= discp_lvl);
5630 /* Keep whitespace in URI, give app URI with whitespace instead of
5631 automatic redirect to fixed URI */
5632 const bool wsp_in_uri_keep = (-2 >= discp_lvl);
5633
5634 if (! get_request_line_inner (c))
5635 {
5636 /* End of the request line has not been found yet */
5637 mhd_assert ((! wsp_in_uri) || NULL == c->rq.version);
5638 if ((NULL != c->rq.version) &&
5639 (HTTP_VER_LEN <
5641 - (size_t) (c->rq.version - c->read_buffer))))
5642 {
5647 return true; /* Error in the request */
5648 }
5649 return false;
5650 }
5652 return true; /* Error in the request */
5653
5655 mhd_assert (NULL == c->rq.url);
5656 mhd_assert (0 == c->rq.url_len);
5658 if (0 != c->rq.hdrs.rq_line.num_ws_in_uri)
5659 {
5660 if (! wsp_in_uri)
5661 {
5665 return true; /* Error in the request */
5666 }
5667 if (! wsp_in_uri_keep)
5668 {
5670 return true; /* Error in the request */
5671 }
5672 }
5673 if (! process_request_target (c))
5674 return true; /* Error in processing */
5675
5677 return true;
5678}
5679
5680
5703
5704
5716static enum MHD_HdrLineReadRes_
5718 bool process_footers,
5719 struct _MHD_str_w_len *hdr_name,
5720 struct _MHD_str_w_len *hdr_value)
5721{
5722 const int discp_lvl = c->daemon->client_discipline;
5723 /* Treat bare LF as the end of the line.
5724 RFC 9112, section 2.2-3
5725 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
5726 Bare LF is processed as end of the line or rejected as broken request. */
5727 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
5728 /* Keep bare CR character as is.
5729 Violates RFC 9112, section 2.2-4 */
5730 const bool bare_cr_keep = (-3 >= discp_lvl);
5731 /* Treat bare CR as space; replace it with space before processing.
5732 RFC 9112, section 2.2-4 */
5733 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
5734 /* Treat NUL as space; replace it with space before processing.
5735 RFC 9110, section 5.5-5 */
5736 const bool nul_as_sp = (-1 >= discp_lvl);
5737 /* Allow folded header lines.
5738 RFC 9112, section 5.2-4 */
5739 const bool allow_folded = (0 >= discp_lvl);
5740 /* Do not reject headers with the whitespace at the start of the first line.
5741 When allowed, the first line with whitespace character at the first
5742 position is ignored (as well as all possible line foldings of the first
5743 line).
5744 RFC 9112, section 2.2-8 */
5745 const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl);
5746 /* Allow whitespace in header (field) name.
5747 Violates RFC 9110, section 5.1-2 */
5748 const bool allow_wsp_in_name = (-2 >= discp_lvl);
5749 /* Allow zero-length header (field) name.
5750 Violates RFC 9110, section 5.1-2 */
5751 const bool allow_empty_name = (-2 >= discp_lvl);
5752 /* Allow whitespace before colon.
5753 Violates RFC 9112, section 5.1-2 */
5754 const bool allow_wsp_before_colon = (-3 >= discp_lvl);
5755 /* Do not abort the request when header line has no colon, just skip such
5756 bad lines.
5757 RFC 9112, section 5-1 */
5758 const bool allow_line_without_colon = (-2 >= discp_lvl);
5759
5760 size_t p;
5762#if ! defined (HAVE_MESSAGES) && ! defined(_DEBUG)
5763 (void) process_footers; /* Unused parameter */
5764#endif /* !HAVE_MESSAGES && !_DEBUG */
5765
5766 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
5768 c->state);
5769
5770 p = c->rq.hdrs.hdr.proc_pos;
5771
5772 mhd_assert (p <= c->read_buffer_offset);
5773 while (p < c->read_buffer_offset)
5774 {
5775 const char chr = c->read_buffer[p];
5776 bool end_of_line;
5777
5778 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
5779 (c->rq.hdrs.hdr.name_len < p));
5780 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p));
5781 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
5782 (c->rq.hdrs.hdr.name_end_found));
5783 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
5784 (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start));
5785 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
5786 (0 != c->rq.hdrs.hdr.name_len));
5787 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
5788 (0 == c->rq.hdrs.hdr.name_len) || \
5789 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len));
5790 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
5791 (0 == c->rq.hdrs.hdr.value_start) || \
5792 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start));
5793
5794 /* Check for the end of the line */
5795 if ('\r' == chr)
5796 {
5797 if (0 != p)
5798 {
5799 /* Line is not empty, need to check for possible line folding */
5800 if (p + 2 >= c->read_buffer_offset)
5801 break; /* Not enough data yet to check for folded line */
5802 }
5803 else
5804 {
5805 /* Line is empty, no need to check for possible line folding */
5806 if (p + 2 > c->read_buffer_offset)
5807 break; /* Not enough data yet to check for the end of the line */
5808 }
5809 if ('\n' == c->read_buffer[p + 1])
5810 end_of_line = true;
5811 else
5812 {
5813 /* Bare CR alone */
5814 /* Must be rejected or replaced with space char.
5815 See RFC 9112, section 2.2-4 */
5816 if (bare_cr_as_sp)
5817 {
5818 c->read_buffer[p] = ' ';
5820 continue; /* Re-start processing of the current character */
5821 }
5822 else if (! bare_cr_keep)
5823 {
5824 if (! process_footers)
5828 else
5832 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
5833 }
5834 end_of_line = false;
5835 }
5836 }
5837 else if ('\n' == chr)
5838 {
5839 /* Bare LF may be recognised as a line delimiter.
5840 See RFC 9112, section 2.2-3 */
5841 if (bare_lf_as_crlf)
5842 {
5843 if (0 != p)
5844 {
5845 /* Line is not empty, need to check for possible line folding */
5846 if (p + 1 >= c->read_buffer_offset)
5847 break; /* Not enough data yet to check for folded line */
5848 }
5849 end_of_line = true;
5850 }
5851 else
5852 {
5853 if (! process_footers)
5857 else
5861 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
5862 }
5863 }
5864 else
5865 end_of_line = false;
5866
5867 if (end_of_line)
5868 {
5869 /* Handle the end of the line */
5873 const size_t line_len = p + (('\r' == chr) ? 2 : 1);
5874 char next_line_char;
5875 mhd_assert (line_len <= c->read_buffer_offset);
5876
5877 if (0 == p)
5878 {
5879 /* Zero-length header line. This is the end of the request header
5880 section.
5881 RFC 9112, Section 2.1-1 */
5884 mhd_assert (0 == c->rq.hdrs.hdr.name_len);
5885 mhd_assert (0 == c->rq.hdrs.hdr.ws_start);
5886 mhd_assert (0 == c->rq.hdrs.hdr.value_start);
5887 /* Consume the line with CRLF (or bare LF) */
5888 c->read_buffer += line_len;
5889 c->read_buffer_offset -= line_len;
5890 c->read_buffer_size -= line_len;
5892 }
5893
5894 mhd_assert (line_len < c->read_buffer_offset);
5895 mhd_assert (0 != line_len);
5896 mhd_assert ('\n' == c->read_buffer[line_len - 1]);
5897 next_line_char = c->read_buffer[line_len];
5898 if ((' ' == next_line_char) ||
5899 ('\t' == next_line_char))
5900 {
5901 /* Folded line */
5902 if (! allow_folded)
5903 {
5904 if (! process_footers)
5908 else
5912
5913 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
5914 }
5915 /* Replace CRLF (or bare LF) character(s) with space characters.
5916 See RFC 9112, Section 5.2-4 */
5917 c->read_buffer[p] = ' ';
5918 if ('\r' == chr)
5919 c->read_buffer[p + 1] = ' ';
5920 continue; /* Re-start processing of the current character */
5921 }
5922 else
5923 {
5924 /* It is not a folded line, it's the real end of the non-empty line */
5925 bool skip_line = false;
5926 mhd_assert (0 != p);
5927 if (c->rq.hdrs.hdr.starts_with_ws)
5928 {
5929 /* This is the first line and it starts with whitespace. This line
5930 must be discarded completely.
5931 See RFC 9112, Section 2.2-8 */
5932 mhd_assert (allow_wsp_at_start);
5933#ifdef HAVE_MESSAGES
5934 MHD_DLOG (c->daemon,
5935 _ ("Whitespace-prefixed first header line " \
5936 "has been skipped.\n"));
5937#endif /* HAVE_MESSAGES */
5938 skip_line = true;
5939 }
5940 else if (! c->rq.hdrs.hdr.name_end_found)
5941 {
5942 if (! allow_line_without_colon)
5943 {
5944 if (! process_footers)
5948 else
5952
5953 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
5954 }
5955 /* Skip broken line completely */
5957 skip_line = true;
5958 }
5959 if (skip_line)
5960 {
5961 /* Skip the entire line */
5962 c->read_buffer += line_len;
5963 c->read_buffer_offset -= line_len;
5964 c->read_buffer_size -= line_len;
5965 p = 0;
5966 /* Reset processing state */
5967 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
5968 /* Start processing of the next line */
5969 continue;
5970 }
5971 else
5972 {
5973 /* This line should be valid header line */
5974 size_t value_len;
5975 mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name);
5976
5977 hdr_name->str = c->read_buffer + 0; /* The name always starts at the first character */
5978 hdr_name->len = c->rq.hdrs.hdr.name_len;
5979 mhd_assert (0 == hdr_name->str[hdr_name->len]);
5980
5981 if (0 == c->rq.hdrs.hdr.value_start)
5982 {
5983 c->rq.hdrs.hdr.value_start = p;
5984 c->read_buffer[p] = 0;
5985 value_len = 0;
5986 }
5987 else if (0 != c->rq.hdrs.hdr.ws_start)
5988 {
5989 mhd_assert (p > c->rq.hdrs.hdr.ws_start);
5991 c->read_buffer[c->rq.hdrs.hdr.ws_start] = 0;
5992 value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start;
5993 }
5994 else
5995 {
5996 mhd_assert (p > c->rq.hdrs.hdr.ws_start);
5997 c->read_buffer[p] = 0;
5998 value_len = p - c->rq.hdrs.hdr.value_start;
5999 }
6000 hdr_value->str = c->read_buffer + c->rq.hdrs.hdr.value_start;
6001 hdr_value->len = value_len;
6002 mhd_assert (0 == hdr_value->str[hdr_value->len]);
6003 /* Consume the entire line */
6004 c->read_buffer += line_len;
6005 c->read_buffer_offset -= line_len;
6006 c->read_buffer_size -= line_len;
6008 }
6009 }
6010 }
6011 else if ((' ' == chr) || ('\t' == chr))
6012 {
6013 if (0 == p)
6014 {
6015 if (! allow_wsp_at_start)
6016 {
6017 if (! process_footers)
6021 else
6025 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6026 }
6027 c->rq.hdrs.hdr.starts_with_ws = true;
6028 }
6029 else if ((! c->rq.hdrs.hdr.name_end_found) &&
6030 (! c->rq.hdrs.hdr.starts_with_ws))
6031 {
6032 /* Whitespace in header name / between header name and colon */
6033 if (allow_wsp_in_name || allow_wsp_before_colon)
6034 {
6035 if (0 == c->rq.hdrs.hdr.ws_start)
6036 c->rq.hdrs.hdr.ws_start = p;
6037 }
6038 else
6039 {
6040 if (! process_footers)
6044 else
6048
6049 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6050 }
6051 }
6052 else
6053 {
6054 /* Whitespace before/inside/after header (field) value */
6055 if (0 == c->rq.hdrs.hdr.ws_start)
6056 c->rq.hdrs.hdr.ws_start = p;
6057 }
6058 }
6059 else if (0 == chr)
6060 {
6061 if (! nul_as_sp)
6062 {
6063 if (! process_footers)
6067 else
6071
6072 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6073 }
6074 c->read_buffer[p] = ' ';
6075 continue; /* Re-start processing of the current character */
6076 }
6077 else
6078 {
6079 /* Not a whitespace, not the end of the header line */
6080 mhd_assert ('\r' != chr);
6081 mhd_assert ('\n' != chr);
6082 mhd_assert ('\0' != chr);
6083 if ((! c->rq.hdrs.hdr.name_end_found) &&
6084 (! c->rq.hdrs.hdr.starts_with_ws))
6085 {
6086 /* Processing the header (field) name */
6087 if (':' == chr)
6088 {
6089 if (0 == c->rq.hdrs.hdr.ws_start)
6090 c->rq.hdrs.hdr.name_len = p;
6091 else
6092 {
6093 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6094 if (! allow_wsp_before_colon)
6095 {
6096 if (! process_footers)
6100 else
6104 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6105 }
6107#ifndef MHD_FAVOR_SMALL_CODE
6108 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6109#endif /* ! MHD_FAVOR_SMALL_CODE */
6110 }
6111 if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name)
6112 {
6113 if (! process_footers)
6117 else
6121 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6122 }
6123 c->rq.hdrs.hdr.name_end_found = true;
6124 c->read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */
6125 }
6126 else
6127 {
6128 if (0 != c->rq.hdrs.hdr.ws_start)
6129 {
6130 /* End of the whitespace in header (field) name */
6131 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6132 if (! allow_wsp_in_name)
6133 {
6134 if (! process_footers)
6138 else
6142
6143 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6144 }
6145#ifndef MHD_FAVOR_SMALL_CODE
6146 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6147#endif /* ! MHD_FAVOR_SMALL_CODE */
6148 }
6149 }
6150 }
6151 else
6152 {
6153 /* Processing the header (field) value */
6154 if (0 == c->rq.hdrs.hdr.value_start)
6155 c->rq.hdrs.hdr.value_start = p;
6156#ifndef MHD_FAVOR_SMALL_CODE
6157 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6158#endif /* ! MHD_FAVOR_SMALL_CODE */
6159 }
6160#ifdef MHD_FAVOR_SMALL_CODE
6161 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6162#endif /* MHD_FAVOR_SMALL_CODE */
6163 }
6164 p++;
6165 }
6166 c->rq.hdrs.hdr.proc_pos = p;
6167 return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */
6168}
6169
6170
6181static bool
6182get_req_headers (struct MHD_Connection *c, bool process_footers)
6183{
6184 do
6185 {
6186 struct _MHD_str_w_len hdr_name;
6187 struct _MHD_str_w_len hdr_value;
6188 enum MHD_HdrLineReadRes_ res;
6189
6190 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6192 c->state);
6193
6194 #ifdef _DEBUG
6195 hdr_name.str = NULL;
6196 hdr_value.str = NULL;
6197#endif /* _DEBUG */
6198 res = get_req_header (c, process_footers, &hdr_name, &hdr_value);
6200 {
6201 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6203 c->state);
6204 mhd_assert (NULL != hdr_name.str);
6205 mhd_assert (NULL != hdr_value.str);
6206 /* Values must be zero-terminated and must not have binary zeros */
6207 mhd_assert (strlen (hdr_name.str) == hdr_name.len);
6208 mhd_assert (strlen (hdr_value.str) == hdr_value.len);
6209 /* Values must not have whitespaces at the start or at the end */
6210 mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != ' '));
6211 mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != '\t'));
6212 mhd_assert ((hdr_name.len == 0) || \
6213 (hdr_name.str[hdr_name.len - 1] != ' '));
6214 mhd_assert ((hdr_name.len == 0) || \
6215 (hdr_name.str[hdr_name.len - 1] != '\t'));
6216 mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != ' '));
6217 mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != '\t'));
6218 mhd_assert ((hdr_value.len == 0) || \
6219 (hdr_value.str[hdr_value.len - 1] != ' '));
6220 mhd_assert ((hdr_value.len == 0) || \
6221 (hdr_value.str[hdr_value.len - 1] != '\t'));
6222
6223 if (MHD_NO ==
6225 (! process_footers) ?
6228 hdr_name.str, hdr_name.len,
6229 hdr_value.str, hdr_value.len))
6230 {
6231 size_t add_element_size;
6232
6233 mhd_assert (hdr_name.str < hdr_value.str);
6234
6235#ifdef HAVE_MESSAGES
6236 MHD_DLOG (c->daemon,
6237 _ ("Failed to allocate memory in the connection memory " \
6238 "pool to store %s.\n"),
6239 (! process_footers) ? _ ("header") : _ ("footer"));
6240#endif /* HAVE_MESSAGES */
6241
6242 add_element_size = hdr_value.len
6243 + (size_t) (hdr_value.str - hdr_name.str);
6244
6245 if (! process_footers)
6246 handle_req_headers_no_space (c, hdr_name.str, add_element_size);
6247 else
6248 handle_req_footers_no_space (c, hdr_name.str, add_element_size);
6249
6251 return true;
6252 }
6253 /* Reset processing state */
6255 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6257 c->state);
6258 /* Read the next header (field) line */
6259 continue;
6260 }
6262 {
6263 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6265 c->state);
6266 return false;
6267 }
6268 else if (MHD_HDR_LINE_READING_DATA_ERROR == res)
6269 {
6270 mhd_assert ((process_footers ? \
6275 return true;
6276 }
6278 break;
6279 } while (1);
6280
6281#ifdef HAVE_MESSAGES
6282 if (1 == c->rq.num_cr_sp_replaced)
6283 {
6284 MHD_DLOG (c->daemon,
6285 _ ("One bare CR character has been replaced with space " \
6286 "in %s.\n"),
6287 (! process_footers) ?
6288 _ ("the request line or in the request headers") :
6289 _ ("the request footers"));
6290 }
6291 else if (0 != c->rq.num_cr_sp_replaced)
6292 {
6293 MHD_DLOG (c->daemon,
6294 _ ("%" PRIu64 " bare CR characters have been replaced with " \
6295 "spaces in the request line and/or in the request %s.\n"),
6296 (uint64_t) c->rq.num_cr_sp_replaced,
6297 (! process_footers) ? _ ("headers") : _ ("footers"));
6298 }
6299 if (1 == c->rq.skipped_broken_lines)
6300 {
6301 MHD_DLOG (c->daemon,
6302 _ ("One %s line without colon has been skipped.\n"),
6303 (! process_footers) ? _ ("header") : _ ("footer"));
6304 }
6305 else if (0 != c->rq.skipped_broken_lines)
6306 {
6307 MHD_DLOG (c->daemon,
6308 _ ("%" PRIu64 " %s lines without colons has been skipped.\n"),
6309 (uint64_t) c->rq.skipped_broken_lines,
6310 (! process_footers) ? _ ("header") : _ ("footer"));
6311 }
6312#endif /* HAVE_MESSAGES */
6313
6314 mhd_assert (c->rq.method < c->read_buffer);
6315 if (! process_footers)
6316 {
6317 c->rq.header_size = (size_t) (c->read_buffer - c->rq.method);
6319 c->rq.field_lines.size =
6320 (size_t) ((c->read_buffer - c->rq.field_lines.start) - 1);
6321 if ('\r' == *(c->read_buffer - 2))
6322 c->rq.field_lines.size--;
6324
6326 {
6327 /* Try to re-use some of the last bytes of the request header */
6328 /* Do this only if space in the read buffer is limited AND
6329 amount of read ahead data is small. */
6334 const char *last_elmnt_end;
6335 size_t shift_back_size;
6336 if (NULL != c->rq.headers_received_tail)
6337 last_elmnt_end =
6340 else
6341 last_elmnt_end = c->rq.version + HTTP_VER_LEN;
6342 mhd_assert ((last_elmnt_end + 1) < c->read_buffer);
6343 shift_back_size = (size_t) (c->read_buffer - (last_elmnt_end + 1));
6344 if (0 != c->read_buffer_offset)
6345 memmove (c->read_buffer - shift_back_size,
6346 c->read_buffer,
6348 c->read_buffer -= shift_back_size;
6349 c->read_buffer_size += shift_back_size;
6350 }
6351 }
6352 else
6354
6355 return true;
6356}
6357
6358
6366void
6368{
6369 struct MHD_Daemon *daemon = connection->daemon;
6370#if defined(MHD_USE_THREADS)
6371 mhd_assert (NULL == daemon->worker_pool);
6372#endif /* MHD_USE_THREADS */
6373
6374 if (0 == connection->connection_timeout_ms)
6375 return; /* Skip update of activity for connections
6376 without timeout timer. */
6377 if (connection->suspended)
6378 return; /* no activity on suspended connections */
6379
6382 return; /* each connection has personal timeout */
6383
6384 if (connection->connection_timeout_ms != daemon->connection_timeout_ms)
6385 return; /* custom timeout, no need to move it in "normal" DLL */
6386#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6387 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
6388#endif
6389 /* move connection to head of timeout list (by remove + add operation) */
6391 daemon->normal_timeout_tail,
6392 connection);
6394 daemon->normal_timeout_tail,
6395 connection);
6396#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6397 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
6398#endif
6399}
6400
6401
6411void
6413 bool socket_error)
6414{
6415 ssize_t bytes_read;
6416
6417 if ( (MHD_CONNECTION_CLOSED == connection->state) ||
6418 (connection->suspended) )
6419 return;
6420#ifdef HTTPS_SUPPORT
6421 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6422 { /* HTTPS connection. */
6423 if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6424 {
6425 if (! MHD_run_tls_handshake_ (connection))
6426 return;
6427 }
6428 }
6429#endif /* HTTPS_SUPPORT */
6430
6431 mhd_assert (NULL != connection->read_buffer);
6432 if (connection->read_buffer_size == connection->read_buffer_offset)
6433 return; /* No space for receiving data. */
6434
6435 bytes_read = connection->recv_cls (connection,
6436 &connection->read_buffer
6437 [connection->read_buffer_offset],
6438 connection->read_buffer_size
6439 - connection->read_buffer_offset);
6440 if ((bytes_read < 0) || socket_error)
6441 {
6442 if ((MHD_ERR_AGAIN_ == bytes_read) && ! socket_error)
6443 return; /* No new data to process. */
6444 if ((bytes_read > 0) && connection->sk_nonblck)
6445 { /* Try to detect the socket error */
6446 int dummy;
6447 bytes_read = connection->recv_cls (connection, &dummy, sizeof (dummy));
6448 }
6449 if (MHD_ERR_CONNRESET_ == bytes_read)
6450 {
6452 (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6453 {
6454#ifdef HAVE_MESSAGES
6455 MHD_DLOG (connection->daemon,
6456 _ ("Socket has been disconnected when reading request.\n"));
6457#endif
6458 connection->discard_request = true;
6459 }
6460 MHD_connection_close_ (connection,
6462 return;
6463 }
6464
6465#ifdef HAVE_MESSAGES
6466 if (MHD_CONNECTION_INIT != connection->state)
6467 MHD_DLOG (connection->daemon,
6468 _ ("Connection socket is closed when reading " \
6469 "request due to the error: %s\n"),
6470 (bytes_read < 0) ? str_conn_error_ (bytes_read) :
6471 "detected connection closure");
6472#endif
6473 CONNECTION_CLOSE_ERROR (connection,
6474 NULL);
6475 return;
6476 }
6477
6478 if (0 == bytes_read)
6479 { /* Remote side closed connection. */
6480 connection->read_closed = true;
6482 (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6483 {
6484#ifdef HAVE_MESSAGES
6485 MHD_DLOG (connection->daemon,
6486 _ ("Connection was closed by remote side with incomplete "
6487 "request.\n"));
6488#endif
6489 connection->discard_request = true;
6490 MHD_connection_close_ (connection,
6492 }
6493 else if (MHD_CONNECTION_INIT == connection->state)
6494 /* This termination code cannot be reported to the application
6495 * because application has not been informed yet about this request */
6496 MHD_connection_close_ (connection,
6498 else
6499 MHD_connection_close_ (connection,
6501 return;
6502 }
6503 connection->read_buffer_offset += (size_t) bytes_read;
6504 MHD_update_last_activity_ (connection);
6505#if DEBUG_STATES
6506 MHD_DLOG (connection->daemon,
6507 _ ("In function %s handling connection at state: %s\n"),
6508 MHD_FUNC_,
6509 MHD_state_to_string (connection->state));
6510#endif
6511 /* TODO: check whether the next 'switch()' really needed */
6512 switch (connection->state)
6513 {
6520 /* nothing to do but default action */
6521 if (connection->read_closed)
6522 {
6523 /* TODO: check whether this really needed */
6524 MHD_connection_close_ (connection,
6526 }
6527 return;
6529 return;
6530#ifdef UPGRADE_SUPPORT
6531 case MHD_CONNECTION_UPGRADE:
6532 mhd_assert (0);
6533 return;
6534#endif /* UPGRADE_SUPPORT */
6536 /* shrink read buffer to how much is actually used */
6537 /* TODO: remove shrink as it handled in special function */
6538 if ((0 != connection->read_buffer_size) &&
6539 (connection->read_buffer_size != connection->read_buffer_offset))
6540 {
6541 mhd_assert (NULL != connection->read_buffer);
6542 connection->read_buffer =
6543 MHD_pool_reallocate (connection->pool,
6544 connection->read_buffer,
6545 connection->read_buffer_size,
6546 connection->read_buffer_offset);
6547 connection->read_buffer_size = connection->read_buffer_offset;
6548 }
6549 break;
6555 /* Milestone state, no data should be read */
6556 mhd_assert (0); /* Should not be possible */
6557 break;
6568 default:
6569 mhd_assert (0); /* Should not be possible */
6570 break;
6571 }
6572 return;
6573}
6574
6575
6584void
6586{
6587 struct MHD_Response *response;
6588 ssize_t ret;
6589 if (connection->suspended)
6590 return;
6591
6592#ifdef HTTPS_SUPPORT
6593 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6594 { /* HTTPS connection. */
6595 if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6596 {
6597 if (! MHD_run_tls_handshake_ (connection))
6598 return;
6599 }
6600 }
6601#endif /* HTTPS_SUPPORT */
6602
6603#if DEBUG_STATES
6604 MHD_DLOG (connection->daemon,
6605 _ ("In function %s handling connection at state: %s\n"),
6606 MHD_FUNC_,
6607 MHD_state_to_string (connection->state));
6608#endif
6609 switch (connection->state)
6610 {
6617 mhd_assert (0);
6618 return;
6620 ret = MHD_send_data_ (connection,
6622 [connection->continue_message_write_offset],
6624 - connection->continue_message_write_offset,
6625 true);
6626 if (ret < 0)
6627 {
6628 if (MHD_ERR_AGAIN_ == ret)
6629 return;
6630#ifdef HAVE_MESSAGES
6631 MHD_DLOG (connection->daemon,
6632 _ ("Failed to send data in request for %s.\n"),
6633 connection->rq.url);
6634#endif
6635 CONNECTION_CLOSE_ERROR (connection,
6636 NULL);
6637 return;
6638 }
6639#if _MHD_DEBUG_SEND_DATA
6640 fprintf (stderr,
6641 _ ("Sent 100 continue response: `%.*s'\n"),
6642 (int) ret,
6644#endif
6645 connection->continue_message_write_offset += (size_t) ret;
6646 MHD_update_last_activity_ (connection);
6647 return;
6653 mhd_assert (0);
6654 return;
6656 mhd_assert (0);
6657 return;
6659 {
6660 struct MHD_Response *const resp = connection->rp.response;
6661 const size_t wb_ready = connection->write_buffer_append_offset
6662 - connection->write_buffer_send_offset;
6663 mhd_assert (connection->write_buffer_append_offset >= \
6664 connection->write_buffer_send_offset);
6665 mhd_assert (NULL != resp);
6666 mhd_assert ( (0 == resp->data_size) || \
6667 (0 == resp->data_start) || \
6668 (NULL != resp->crc) );
6669 mhd_assert ( (0 == connection->rp.rsp_write_position) || \
6670 (resp->total_size ==
6671 connection->rp.rsp_write_position) );
6672 mhd_assert ((MHD_CONN_MUST_UPGRADE != connection->keepalive) || \
6673 (! connection->rp.props.send_reply_body));
6674
6675 if ( (connection->rp.props.send_reply_body) &&
6676 (NULL == resp->crc) &&
6677 (NULL == resp->data_iov) &&
6678 /* TODO: remove the next check as 'send_reply_body' is used */
6679 (0 == connection->rp.rsp_write_position) &&
6680 (! connection->rp.props.chunked) )
6681 {
6682 mhd_assert (resp->total_size >= resp->data_size);
6683 mhd_assert (0 == resp->data_start);
6684 /* Send response headers alongside the response body, if the body
6685 * data is available. */
6686 ret = MHD_send_hdr_and_body_ (connection,
6687 &connection->write_buffer
6688 [connection->write_buffer_send_offset],
6689 wb_ready,
6690 false,
6691 resp->data,
6692 resp->data_size,
6693 (resp->total_size == resp->data_size));
6694 }
6695 else
6696 {
6697 /* This is response for HEAD request or reply body is not allowed
6698 * for any other reason or reply body is dynamically generated. */
6699 /* Do not send the body data even if it's available. */
6700 ret = MHD_send_hdr_and_body_ (connection,
6701 &connection->write_buffer
6702 [connection->write_buffer_send_offset],
6703 wb_ready,
6704 false,
6705 NULL,
6706 0,
6707 ((0 == resp->total_size) ||
6708 (! connection->rp.props.send_reply_body)
6709 ));
6710 }
6711
6712 if (ret < 0)
6713 {
6714 if (MHD_ERR_AGAIN_ == ret)
6715 return;
6716#ifdef HAVE_MESSAGES
6717 MHD_DLOG (connection->daemon,
6718 _ ("Failed to send the response headers for the " \
6719 "request for `%s'. Error: %s\n"),
6720 connection->rq.url,
6721 str_conn_error_ (ret));
6722#endif
6723 CONNECTION_CLOSE_ERROR (connection,
6724 NULL);
6725 return;
6726 }
6727 /* 'ret' is not negative, it's safe to cast it to 'size_t'. */
6728 if (((size_t) ret) > wb_ready)
6729 {
6730 /* The complete header and some response data have been sent,
6731 * update both offsets. */
6732 mhd_assert (0 == connection->rp.rsp_write_position);
6733 mhd_assert (! connection->rp.props.chunked);
6734 mhd_assert (connection->rp.props.send_reply_body);
6735 connection->write_buffer_send_offset += wb_ready;
6736 connection->rp.rsp_write_position = ((size_t) ret) - wb_ready;
6737 }
6738 else
6739 connection->write_buffer_send_offset += (size_t) ret;
6740 MHD_update_last_activity_ (connection);
6741 if (MHD_CONNECTION_HEADERS_SENDING != connection->state)
6742 return;
6743 check_write_done (connection,
6745 return;
6746 }
6748 return;
6750 response = connection->rp.response;
6751 if (connection->rp.rsp_write_position <
6752 connection->rp.response->total_size)
6753 {
6754 uint64_t data_write_offset;
6755
6756#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6757 if (NULL != response->crc)
6758 MHD_mutex_lock_chk_ (&response->mutex);
6759#endif
6760 if (MHD_NO == try_ready_normal_body (connection))
6761 {
6762 /* mutex was already unlocked by try_ready_normal_body */
6763 return;
6764 }
6765#if defined(_MHD_HAVE_SENDFILE)
6766 if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
6767 {
6768 mhd_assert (NULL == response->data_iov);
6769 ret = MHD_send_sendfile_ (connection);
6770 }
6771 else /* combined with the next 'if' */
6772#endif /* _MHD_HAVE_SENDFILE */
6773 if (NULL != response->data_iov)
6774 {
6775 ret = MHD_send_iovec_ (connection,
6776 &connection->rp.resp_iov,
6777 true);
6778 }
6779 else
6780 {
6781 data_write_offset = connection->rp.rsp_write_position
6782 - response->data_start;
6783 if (data_write_offset > (uint64_t) SIZE_MAX)
6784 MHD_PANIC (_ ("Data offset exceeds limit.\n"));
6785 ret = MHD_send_data_ (connection,
6786 &response->data
6787 [(size_t) data_write_offset],
6788 response->data_size
6789 - (size_t) data_write_offset,
6790 true);
6791#if _MHD_DEBUG_SEND_DATA
6792 if (ret > 0)
6793 fprintf (stderr,
6794 _ ("Sent %d-byte DATA response: `%.*s'\n"),
6795 (int) ret,
6796 (int) ret,
6797 &rp.response->data[connection->rp.rsp_write_position
6798 - rp.response->data_start]);
6799#endif
6800 }
6801#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6802 if (NULL != response->crc)
6803 MHD_mutex_unlock_chk_ (&response->mutex);
6804#endif
6805 if (ret < 0)
6806 {
6807 if (MHD_ERR_AGAIN_ == ret)
6808 return;
6809#ifdef HAVE_MESSAGES
6810 MHD_DLOG (connection->daemon,
6811 _ ("Failed to send the response body for the " \
6812 "request for `%s'. Error: %s\n"),
6813 connection->rq.url,
6814 str_conn_error_ (ret));
6815#endif
6816 CONNECTION_CLOSE_ERROR (connection,
6817 NULL);
6818 return;
6819 }
6820 connection->rp.rsp_write_position += (size_t) ret;
6821 MHD_update_last_activity_ (connection);
6822 }
6823 if (connection->rp.rsp_write_position ==
6824 connection->rp.response->total_size)
6826 return;
6828 mhd_assert (0);
6829 return;
6831 ret = MHD_send_data_ (connection,
6832 &connection->write_buffer
6833 [connection->write_buffer_send_offset],
6834 connection->write_buffer_append_offset
6835 - connection->write_buffer_send_offset,
6836 true);
6837 if (ret < 0)
6838 {
6839 if (MHD_ERR_AGAIN_ == ret)
6840 return;
6841#ifdef HAVE_MESSAGES
6842 MHD_DLOG (connection->daemon,
6843 _ ("Failed to send the chunked response body for the " \
6844 "request for `%s'. Error: %s\n"),
6845 connection->rq.url,
6846 str_conn_error_ (ret));
6847#endif
6848 CONNECTION_CLOSE_ERROR (connection,
6849 NULL);
6850 return;
6851 }
6852 connection->write_buffer_send_offset += (size_t) ret;
6853 MHD_update_last_activity_ (connection);
6854 if (MHD_CONNECTION_CHUNKED_BODY_READY != connection->state)
6855 return;
6856 check_write_done (connection,
6857 (connection->rp.response->total_size ==
6858 connection->rp.rsp_write_position) ?
6861 return;
6864 mhd_assert (0);
6865 return;
6867 ret = MHD_send_data_ (connection,
6868 &connection->write_buffer
6869 [connection->write_buffer_send_offset],
6870 connection->write_buffer_append_offset
6871 - connection->write_buffer_send_offset,
6872 true);
6873 if (ret < 0)
6874 {
6875 if (MHD_ERR_AGAIN_ == ret)
6876 return;
6877#ifdef HAVE_MESSAGES
6878 MHD_DLOG (connection->daemon,
6879 _ ("Failed to send the footers for the " \
6880 "request for `%s'. Error: %s\n"),
6881 connection->rq.url,
6882 str_conn_error_ (ret));
6883#endif
6884 CONNECTION_CLOSE_ERROR (connection,
6885 NULL);
6886 return;
6887 }
6888 connection->write_buffer_send_offset += (size_t) ret;
6889 MHD_update_last_activity_ (connection);
6890 if (MHD_CONNECTION_FOOTERS_SENDING != connection->state)
6891 return;
6892 check_write_done (connection,
6894 return;
6896 mhd_assert (0);
6897 return;
6899 return;
6900#ifdef UPGRADE_SUPPORT
6901 case MHD_CONNECTION_UPGRADE:
6902 mhd_assert (0);
6903 return;
6904#endif /* UPGRADE_SUPPORT */
6905 default:
6906 mhd_assert (0);
6907 CONNECTION_CLOSE_ERROR (connection,
6908 _ ("Internal error.\n"));
6909 break;
6910 }
6911 return;
6912}
6913
6914
6921static bool
6923{
6924 const uint64_t timeout = c->connection_timeout_ms;
6925 uint64_t now;
6926 uint64_t since_actv;
6927
6928 if (c->suspended)
6929 return false;
6930 if (0 == timeout)
6931 return false;
6933 since_actv = now - c->last_activity;
6934 /* Keep the next lines in sync with #connection_get_wait() to avoid
6935 * undesired side-effects like busy-waiting. */
6936 if (timeout < since_actv)
6937 {
6938 if (UINT64_MAX / 2 < since_actv)
6939 {
6940 const uint64_t jump_back = c->last_activity - now;
6941 /* Very unlikely that it is more than quarter-million years pause.
6942 * More likely that system clock jumps back. */
6943 if (5000 >= jump_back)
6944 {
6945#ifdef HAVE_MESSAGES
6946 MHD_DLOG (c->daemon,
6947 _ ("Detected system clock %u milliseconds jump back.\n"),
6948 (unsigned int) jump_back);
6949#endif
6950 return false;
6951 }
6952#ifdef HAVE_MESSAGES
6953 MHD_DLOG (c->daemon,
6954 _ ("Detected too large system clock %" PRIu64 " milliseconds "
6955 "jump back.\n"),
6956 jump_back);
6957#endif
6958 }
6959 return true;
6960 }
6961 return false;
6962}
6963
6964
6973static void
6975{
6976 struct MHD_Daemon *daemon = connection->daemon;
6977#ifdef MHD_USE_THREADS
6978 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
6979 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
6980 mhd_assert (NULL == daemon->worker_pool);
6981#endif /* MHD_USE_THREADS */
6982
6983 if (connection->in_cleanup)
6984 return; /* Prevent double cleanup. */
6985 connection->in_cleanup = true;
6986 if (NULL != connection->rp.response)
6987 {
6988 MHD_destroy_response (connection->rp.response);
6989 connection->rp.response = NULL;
6990 }
6991#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6992 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
6993#endif
6994 if (connection->suspended)
6995 {
6998 connection);
6999 connection->suspended = false;
7000 }
7001 else
7002 {
7003 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
7004 {
7005 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7007 daemon->normal_timeout_tail,
7008 connection);
7009 else
7011 daemon->manual_timeout_tail,
7012 connection);
7013 }
7015 daemon->connections_tail,
7016 connection);
7017 }
7018 DLL_insert (daemon->cleanup_head,
7019 daemon->cleanup_tail,
7020 connection);
7021 connection->resuming = false;
7022 connection->in_idle = false;
7023#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7024 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
7025#endif
7027 {
7028 /* if we were at the connection limit before and are in
7029 thread-per-connection mode, signal the main thread
7030 to resume accepting connections */
7031 if ( (MHD_ITC_IS_VALID_ (daemon->itc)) &&
7032 (! MHD_itc_activate_ (daemon->itc, "c")) )
7033 {
7034#ifdef HAVE_MESSAGES
7035 MHD_DLOG (daemon,
7036 _ ("Failed to signal end of connection via inter-thread " \
7037 "communication channel.\n"));
7038#endif
7039 }
7040 }
7041}
7042
7043
7049void
7051{
7052 size_t read_buf_size;
7053
7054#ifdef HTTPS_SUPPORT
7055 mhd_assert ( (0 == (c->daemon->options & MHD_USE_TLS)) || \
7056 (MHD_TLS_CONN_INIT == c->tls_state) );
7057 mhd_assert ( (0 != (c->daemon->options & MHD_USE_TLS)) || \
7058 (MHD_TLS_CONN_NO_TLS == c->tls_state) );
7059#endif /* HTTPS_SUPPORT */
7061
7064
7065 memset (&c->rq, 0, sizeof(c->rq));
7066 memset (&c->rp, 0, sizeof(c->rp));
7067
7068 c->write_buffer = NULL;
7069 c->write_buffer_size = 0;
7072
7074
7075 c->read_buffer_offset = 0;
7076 read_buf_size = c->daemon->pool_size / 2;
7077 c->read_buffer
7078 = MHD_pool_allocate (c->pool,
7079 read_buf_size,
7080 false);
7081 c->read_buffer_size = read_buf_size;
7082}
7083
7084
7091static void
7093 bool reuse)
7094{
7095 struct MHD_Connection *const c = connection;
7096 struct MHD_Daemon *const d = connection->daemon;
7097
7098 if (! reuse)
7099 {
7100 /* Next function will destroy response, notify client,
7101 * destroy memory pool, and set connection state to "CLOSED" */
7103 c->stop_with_error ?
7106 c->read_buffer = NULL;
7107 c->read_buffer_size = 0;
7108 c->read_buffer_offset = 0;
7109 c->write_buffer = NULL;
7110 c->write_buffer_size = 0;
7113 }
7114 else
7115 {
7116 /* Reset connection to process the next request */
7117 size_t new_read_buf_size;
7120
7121 if ( (NULL != d->notify_completed) &&
7122 (c->rq.client_aware) )
7124 c,
7125 &c->rq.client_context,
7127 c->rq.client_aware = false;
7128
7129 if (NULL != c->rp.response)
7131 c->rp.response = NULL;
7132
7135 c->event_loop_info =
7136 (0 == c->read_buffer_offset) ?
7138
7139 memset (&c->rq, 0, sizeof(c->rq));
7140
7141 /* iov (if any) will be deallocated by MHD_pool_reset */
7142 memset (&c->rp, 0, sizeof(c->rp));
7143
7144 c->write_buffer = NULL;
7145 c->write_buffer_size = 0;
7149
7150 /* Reset the read buffer to the starting size,
7151 preserving the bytes we have already read. */
7152 new_read_buf_size = c->daemon->pool_size / 2;
7153 if (c->read_buffer_offset > new_read_buf_size)
7154 new_read_buf_size = c->read_buffer_offset;
7155
7156 c->read_buffer
7157 = MHD_pool_reset (c->pool,
7158 c->read_buffer,
7160 new_read_buf_size);
7161 c->read_buffer_size = new_read_buf_size;
7162 }
7163 c->rq.client_context = NULL;
7164}
7165
7166
7179enum MHD_Result
7181{
7182 struct MHD_Daemon *daemon = connection->daemon;
7183 enum MHD_Result ret;
7184#ifdef MHD_USE_THREADS
7185 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
7186 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
7187#endif /* MHD_USE_THREADS */
7188 /* 'daemon' is not used if epoll is not available and asserts are disabled */
7189 (void) daemon; /* Mute compiler warning */
7190
7191 connection->in_idle = true;
7192 while (! connection->suspended)
7193 {
7194#ifdef HTTPS_SUPPORT
7195 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
7196 { /* HTTPS connection. */
7197 if ((MHD_TLS_CONN_INIT <= connection->tls_state) &&
7198 (MHD_TLS_CONN_CONNECTED > connection->tls_state))
7199 break;
7200 }
7201#endif /* HTTPS_SUPPORT */
7202#if DEBUG_STATES
7203 MHD_DLOG (daemon,
7204 _ ("In function %s handling connection at state: %s\n"),
7205 MHD_FUNC_,
7206 MHD_state_to_string (connection->state));
7207#endif
7208 switch (connection->state)
7209 {
7212 if (get_request_line (connection))
7213 {
7216 || (connection->discard_request));
7217 continue;
7218 }
7220 break;
7224 continue;
7226 if (get_req_headers (connection, false))
7227 {
7229 mhd_assert ((MHD_CONNECTION_HEADERS_RECEIVED == connection->state) || \
7230 (connection->discard_request));
7231 continue;
7232 }
7234 break;
7236 parse_connection_headers (connection);
7237 if (MHD_CONNECTION_HEADERS_RECEIVED != connection->state)
7238 continue;
7240 if (connection->suspended)
7241 break;
7242 continue;
7244 call_connection_handler (connection); /* first call */
7245 if (MHD_CONNECTION_HEADERS_PROCESSED != connection->state)
7246 continue;
7247 if (connection->suspended)
7248 continue;
7249
7250 if ( (NULL == connection->rp.response) &&
7251 (need_100_continue (connection)) &&
7252 /* If the client is already sending the payload (body)
7253 there is no need to send "100 Continue" */
7254 (0 == connection->read_buffer_offset) )
7255 {
7257 break;
7258 }
7259 if ( (NULL != connection->rp.response) &&
7260 (0 != connection->rq.remaining_upload_size) )
7261 {
7262 /* we refused (no upload allowed!) */
7263 connection->rq.remaining_upload_size = 0;
7264 /* force close, in case client still tries to upload... */
7265 connection->discard_request = true;
7266 }
7267 connection->state = (0 == connection->rq.remaining_upload_size)
7270 if (connection->suspended)
7271 break;
7272 continue;
7274 if (connection->continue_message_write_offset ==
7276 {
7278 continue;
7279 }
7280 break;
7282 mhd_assert (0 != connection->rq.remaining_upload_size);
7283 mhd_assert (! connection->discard_request);
7284 mhd_assert (NULL == connection->rp.response);
7285 if (0 != connection->read_buffer_offset)
7286 {
7287 process_request_body (connection); /* loop call */
7288 if (MHD_CONNECTION_BODY_RECEIVING != connection->state)
7289 continue;
7290 }
7291 /* Modify here when queueing of the response during data processing
7292 will be supported */
7293 mhd_assert (! connection->discard_request);
7294 mhd_assert (NULL == connection->rp.response);
7295 if (0 == connection->rq.remaining_upload_size)
7296 {
7297 connection->state = MHD_CONNECTION_BODY_RECEIVED;
7298 continue;
7299 }
7300 break;
7302 mhd_assert (! connection->discard_request);
7303 mhd_assert (NULL == connection->rp.response);
7304 if (0 == connection->rq.remaining_upload_size)
7305 {
7306 if (connection->rq.have_chunked_upload)
7307 {
7308 /* Reset counter variables reused for footers */
7309 connection->rq.num_cr_sp_replaced = 0;
7310 connection->rq.skipped_broken_lines = 0;
7313 }
7314 else
7316 continue;
7317 }
7318 break;
7320 if (get_req_headers (connection, true))
7321 {
7323 mhd_assert ((MHD_CONNECTION_FOOTERS_RECEIVED == connection->state) || \
7324 (connection->discard_request));
7325 continue;
7326 }
7328 break;
7330 /* The header, the body, and the footers of the request has been received,
7331 * switch to the final processing of the request. */
7333 continue;
7335 call_connection_handler (connection); /* "final" call */
7336 if (connection->state != MHD_CONNECTION_FULL_REQ_RECEIVED)
7337 continue;
7338 if (NULL == connection->rp.response)
7339 break; /* try again next time */
7340 /* Response is ready, start reply */
7341 connection->state = MHD_CONNECTION_START_REPLY;
7342 continue;
7344 mhd_assert (NULL != connection->rp.response);
7346 if (MHD_NO == build_header_response (connection))
7347 {
7348 /* oops - close! */
7349 CONNECTION_CLOSE_ERROR (connection,
7350 _ ("Closing connection (failed to create "
7351 "response header).\n"));
7352 continue;
7353 }
7355 break;
7356
7358 /* no default action */
7359 break;
7361#ifdef UPGRADE_SUPPORT
7362 if (NULL != connection->rp.response->upgrade_handler)
7363 {
7364 connection->state = MHD_CONNECTION_UPGRADE;
7365 /* This connection is "upgraded". Pass socket to application. */
7366 if (MHD_NO ==
7368 connection))
7369 {
7370 /* upgrade failed, fail hard */
7371 CONNECTION_CLOSE_ERROR (connection,
7372 NULL);
7373 continue;
7374 }
7375 /* Response is not required anymore for this connection. */
7376 if (1)
7377 {
7378 struct MHD_Response *const resp = connection->rp.response;
7379
7380 connection->rp.response = NULL;
7381 MHD_destroy_response (resp);
7382 }
7383 continue;
7384 }
7385#endif /* UPGRADE_SUPPORT */
7386
7387 if (connection->rp.props.send_reply_body)
7388 {
7389 if (connection->rp.props.chunked)
7391 else
7393 }
7394 else
7396 continue;
7398 mhd_assert (connection->rp.props.send_reply_body);
7399 mhd_assert (! connection->rp.props.chunked);
7400 /* nothing to do here */
7401 break;
7403 mhd_assert (connection->rp.props.send_reply_body);
7404 mhd_assert (! connection->rp.props.chunked);
7405#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7406 if (NULL != connection->rp.response->crc)
7407 MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7408#endif
7409 if (0 == connection->rp.response->total_size)
7410 {
7411#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7412 if (NULL != connection->rp.response->crc)
7413 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7414#endif
7415 if (connection->rp.props.chunked)
7417 else
7419 continue;
7420 }
7421 if (MHD_NO != try_ready_normal_body (connection))
7422 {
7423#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7424 if (NULL != connection->rp.response->crc)
7425 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7426#endif
7428 /* Buffering for flushable socket was already enabled*/
7429
7430 break;
7431 }
7432 /* mutex was already unlocked by "try_ready_normal_body */
7433 /* not ready, no socket action */
7434 break;
7436 mhd_assert (connection->rp.props.send_reply_body);
7437 mhd_assert (connection->rp.props.chunked);
7438 /* nothing to do here */
7439 break;
7441 mhd_assert (connection->rp.props.send_reply_body);
7442 mhd_assert (connection->rp.props.chunked);
7443#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7444 if (NULL != connection->rp.response->crc)
7445 MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7446#endif
7447 if ( (0 == connection->rp.response->total_size) ||
7448 (connection->rp.rsp_write_position ==
7449 connection->rp.response->total_size) )
7450 {
7451#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7452 if (NULL != connection->rp.response->crc)
7453 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7454#endif
7456 continue;
7457 }
7458 if (1)
7459 { /* pseudo-branch for local variables scope */
7460 bool finished;
7461 if (MHD_NO != try_ready_chunked_body (connection, &finished))
7462 {
7463#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7464 if (NULL != connection->rp.response->crc)
7465 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7466#endif
7467 connection->state = finished ? MHD_CONNECTION_CHUNKED_BODY_SENT :
7469 continue;
7470 }
7471 /* mutex was already unlocked by try_ready_chunked_body */
7472 }
7473 break;
7475 mhd_assert (connection->rp.props.send_reply_body);
7476 mhd_assert (connection->rp.props.chunked);
7477 mhd_assert (connection->write_buffer_send_offset <= \
7478 connection->write_buffer_append_offset);
7479
7481 {
7482 /* oops - close! */
7483 CONNECTION_CLOSE_ERROR (connection,
7484 _ ("Closing connection (failed to create " \
7485 "response footer)."));
7486 continue;
7487 }
7488 mhd_assert (connection->write_buffer_send_offset < \
7489 connection->write_buffer_append_offset);
7491 continue;
7493 mhd_assert (connection->rp.props.send_reply_body);
7494 mhd_assert (connection->rp.props.chunked);
7495 /* no default action */
7496 break;
7498 if (MHD_HTTP_PROCESSING == connection->rp.responseCode)
7499 {
7500 /* After this type of response, we allow sending another! */
7502 MHD_destroy_response (connection->rp.response);
7503 connection->rp.response = NULL;
7504 /* FIXME: maybe partially reset memory pool? */
7505 continue;
7506 }
7507 /* Reset connection after complete reply */
7508 connection_reset (connection,
7509 MHD_CONN_USE_KEEPALIVE == connection->keepalive &&
7510 ! connection->read_closed &&
7511 ! connection->discard_request);
7512 continue;
7514 cleanup_connection (connection);
7515 connection->in_idle = false;
7516 return MHD_NO;
7517#ifdef UPGRADE_SUPPORT
7518 case MHD_CONNECTION_UPGRADE:
7519 connection->in_idle = false;
7520 return MHD_YES; /* keep open */
7521#endif /* UPGRADE_SUPPORT */
7522 default:
7523 mhd_assert (0);
7524 break;
7525 }
7526 break;
7527 }
7528 if (connection_check_timedout (connection))
7529 {
7530 MHD_connection_close_ (connection,
7532 connection->in_idle = false;
7533 return MHD_YES;
7534 }
7536 ret = MHD_YES;
7537#ifdef EPOLL_SUPPORT
7538 if ( (! connection->suspended) &&
7539 MHD_D_IS_USING_EPOLL_ (daemon) )
7540 {
7541 ret = MHD_connection_epoll_update_ (connection);
7542 }
7543#endif /* EPOLL_SUPPORT */
7544 connection->in_idle = false;
7545 return ret;
7546}
7547
7548
7549#ifdef EPOLL_SUPPORT
7558enum MHD_Result
7559MHD_connection_epoll_update_ (struct MHD_Connection *connection)
7560{
7561 struct MHD_Daemon *const daemon = connection->daemon;
7562
7564
7565 if ((0 != (MHD_EVENT_LOOP_INFO_PROCESS & connection->event_loop_info)) &&
7566 (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)))
7567 {
7568 /* Make sure that connection waiting for processing will be processed */
7569 EDLL_insert (daemon->eready_head,
7570 daemon->eready_tail,
7571 connection);
7572 connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
7573 }
7574
7575 if ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) &&
7576 (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
7577 ( ( (MHD_EVENT_LOOP_INFO_WRITE == connection->event_loop_info) &&
7578 (0 == (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY))) ||
7579 ( (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info)) &&
7580 (0 == (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) ) ) )
7581 {
7582 /* add to epoll set */
7583 struct epoll_event event;
7584
7585 event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
7586 event.data.ptr = connection;
7587 if (0 != epoll_ctl (daemon->epoll_fd,
7588 EPOLL_CTL_ADD,
7589 connection->socket_fd,
7590 &event))
7591 {
7592#ifdef HAVE_MESSAGES
7593 if (0 != (daemon->options & MHD_USE_ERROR_LOG))
7594 MHD_DLOG (daemon,
7595 _ ("Call to epoll_ctl failed: %s\n"),
7597#endif
7598 connection->state = MHD_CONNECTION_CLOSED;
7599 cleanup_connection (connection);
7600 return MHD_NO;
7601 }
7602 connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
7603 }
7604 return MHD_YES;
7605}
7606
7607
7608#endif
7609
7610
7616void
7618{
7619 connection->recv_cls = &recv_param_adapter;
7620}
7621
7622
7635_MHD_EXTERN const union MHD_ConnectionInfo *
7637 enum MHD_ConnectionInfoType info_type,
7638 ...)
7639{
7640 switch (info_type)
7641 {
7642#ifdef HTTPS_SUPPORT
7644 if (NULL == connection->tls_session)
7645 return NULL;
7646 if (1)
7647 { /* Workaround to mute compiler warning */
7648 gnutls_cipher_algorithm_t res;
7649 res = gnutls_cipher_get (connection->tls_session);
7650 connection->connection_info_dummy.cipher_algorithm = (int) res;
7651 }
7652 return &connection->connection_info_dummy;
7654 if (NULL == connection->tls_session)
7655 return NULL;
7656 if (1)
7657 { /* Workaround to mute compiler warning */
7658 gnutls_protocol_t res;
7659 res = gnutls_protocol_get_version (connection->tls_session);
7660 connection->connection_info_dummy.protocol = (int) res;
7661 }
7662 return &connection->connection_info_dummy;
7664 if (NULL == connection->tls_session)
7665 return NULL;
7666 connection->connection_info_dummy.tls_session = connection->tls_session;
7667 return &connection->connection_info_dummy;
7668#else /* ! HTTPS_SUPPORT */
7672#endif /* ! HTTPS_SUPPORT */
7674 return NULL; /* Not implemented */
7676 if (0 < connection->addr_len)
7677 {
7678 mhd_assert (sizeof (connection->addr) == \
7679 sizeof (connection->connection_info_dummy.client_addr));
7680 memcpy (&connection->connection_info_dummy.client_addr,
7681 &connection->addr,
7682 sizeof(connection->addr));
7683 return &connection->connection_info_dummy;
7684 }
7685 return NULL;
7687 connection->connection_info_dummy.daemon =
7688 MHD_get_master (connection->daemon);
7689 return &connection->connection_info_dummy;
7691 connection->connection_info_dummy.connect_fd = connection->socket_fd;
7692 return &connection->connection_info_dummy;
7695 connection->socket_context;
7696 return &connection->connection_info_dummy;
7698 connection->connection_info_dummy.suspended =
7699 connection->suspended ? MHD_YES : MHD_NO;
7700 return &connection->connection_info_dummy;
7702#if SIZEOF_UNSIGNED_INT <= (SIZEOF_UINT64_T - 2)
7703 if (UINT_MAX < connection->connection_timeout_ms / 1000)
7705 else
7706#endif /* SIZEOF_UNSIGNED_INT <=(SIZEOF_UINT64_T - 2) */
7708 (unsigned int) (connection->connection_timeout_ms / 1000);
7709 return &connection->connection_info_dummy;
7711 if ( (MHD_CONNECTION_HEADERS_RECEIVED > connection->state) ||
7712 (MHD_CONNECTION_CLOSED == connection->state) )
7713 return NULL; /* invalid, too early! */
7714 connection->connection_info_dummy.header_size = connection->rq.header_size;
7715 return &connection->connection_info_dummy;
7717 if (NULL == connection->rp.response)
7718 return NULL;
7719 connection->connection_info_dummy.http_status = connection->rp.responseCode;
7720 return &connection->connection_info_dummy;
7721 default:
7722 return NULL;
7723 }
7724}
7725
7726
7738 enum MHD_CONNECTION_OPTION option,
7739 ...)
7740{
7741 va_list ap;
7742 struct MHD_Daemon *daemon;
7743 unsigned int ui_val;
7744
7745 daemon = connection->daemon;
7746 switch (option)
7747 {
7749 if (0 == connection->connection_timeout_ms)
7751 va_start (ap, option);
7752 ui_val = va_arg (ap, unsigned int);
7753 va_end (ap);
7754#if (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT
7755 if ((UINT64_MAX / 4000 - 1) < ui_val)
7756 {
7757#ifdef HAVE_MESSAGES
7758 MHD_DLOG (connection->daemon,
7759 _ ("The specified connection timeout (%u) is too " \
7760 "large. Maximum allowed value (%" PRIu64 ") will be used " \
7761 "instead.\n"),
7762 ui_val,
7763 (UINT64_MAX / 4000 - 1));
7764#endif
7765 ui_val = UINT64_MAX / 4000 - 1;
7766 }
7767#endif /* (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT */
7768 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
7769 {
7770#if defined(MHD_USE_THREADS)
7771 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
7772#endif
7773 if (! connection->suspended)
7774 {
7775 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7777 daemon->normal_timeout_tail,
7778 connection);
7779 else
7781 daemon->manual_timeout_tail,
7782 connection);
7783 connection->connection_timeout_ms = ((uint64_t) ui_val) * 1000;
7784 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7786 daemon->normal_timeout_tail,
7787 connection);
7788 else
7790 daemon->manual_timeout_tail,
7791 connection);
7792 }
7793#if defined(MHD_USE_THREADS)
7794 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
7795#endif
7796 }
7797 return MHD_YES;
7798 default:
7799 return MHD_NO;
7800 }
7801}
7802
7803
7851 unsigned int status_code,
7852 struct MHD_Response *response)
7853{
7854 struct MHD_Daemon *daemon;
7855 bool reply_icy;
7856
7857 if ((NULL == connection) || (NULL == response))
7858 return MHD_NO;
7859
7860 daemon = connection->daemon;
7861 if ((! connection->in_access_handler) && (! connection->suspended) &&
7862 MHD_D_IS_USING_THREADS_ (daemon))
7863 return MHD_NO;
7864
7865 reply_icy = (0 != (status_code & MHD_ICY_FLAG));
7866 status_code &= ~MHD_ICY_FLAG;
7867
7868#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7869 if ( (! connection->suspended) &&
7870 MHD_D_IS_USING_THREADS_ (daemon) &&
7871 (! MHD_thread_handle_ID_is_current_thread_ (connection->tid)) )
7872 {
7873#ifdef HAVE_MESSAGES
7874 MHD_DLOG (daemon,
7875 _ ("Attempted to queue response on wrong thread!\n"));
7876#endif
7877 return MHD_NO;
7878 }
7879#endif
7880
7881 if (NULL != connection->rp.response)
7882 return MHD_NO; /* The response was already set */
7883
7884 if ( (MHD_CONNECTION_HEADERS_PROCESSED != connection->state) &&
7885 (MHD_CONNECTION_FULL_REQ_RECEIVED != connection->state) )
7886 return MHD_NO; /* Wrong connection state */
7887
7888 if (daemon->shutdown)
7889 return MHD_NO;
7890
7891#ifdef UPGRADE_SUPPORT
7892 if (NULL != response->upgrade_handler)
7893 {
7894 struct MHD_HTTP_Res_Header *conn_header;
7895 if (0 == (daemon->options & MHD_ALLOW_UPGRADE))
7896 {
7897#ifdef HAVE_MESSAGES
7898 MHD_DLOG (daemon,
7899 _ ("Attempted 'upgrade' connection on daemon without" \
7900 " MHD_ALLOW_UPGRADE option!\n"));
7901#endif
7902 return MHD_NO;
7903 }
7904 if (MHD_HTTP_SWITCHING_PROTOCOLS != status_code)
7905 {
7906#ifdef HAVE_MESSAGES
7907 MHD_DLOG (daemon,
7908 _ ("Application used invalid status code for" \
7909 " 'upgrade' response!\n"));
7910#endif
7911 return MHD_NO;
7912 }
7913 if (0 == (response->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
7914 {
7915#ifdef HAVE_MESSAGES
7916 MHD_DLOG (daemon,
7917 _ ("Application used invalid response" \
7918 " without \"Connection\" header!\n"));
7919#endif
7920 return MHD_NO;
7921 }
7922 conn_header = response->first_header;
7923 mhd_assert (NULL != conn_header);
7926 if (! MHD_str_has_s_token_caseless_ (conn_header->value,
7927 "upgrade"))
7928 {
7929#ifdef HAVE_MESSAGES
7930 MHD_DLOG (daemon,
7931 _ ("Application used invalid response" \
7932 " without \"upgrade\" token in" \
7933 " \"Connection\" header!\n"));
7934#endif
7935 return MHD_NO;
7936 }
7937 if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
7938 {
7939#ifdef HAVE_MESSAGES
7940 MHD_DLOG (daemon,
7941 _ ("Connection \"Upgrade\" can be used only " \
7942 "with HTTP/1.1 connections!\n"));
7943#endif
7944 return MHD_NO;
7945 }
7946 }
7947#endif /* UPGRADE_SUPPORT */
7948 if (MHD_HTTP_SWITCHING_PROTOCOLS == status_code)
7949 {
7950#ifdef UPGRADE_SUPPORT
7951 if (NULL == response->upgrade_handler)
7952 {
7953#ifdef HAVE_MESSAGES
7954 MHD_DLOG (daemon,
7955 _ ("Application used status code 101 \"Switching Protocols\" " \
7956 "with non-'upgrade' response!\n"));
7957#endif /* HAVE_MESSAGES */
7958 return MHD_NO;
7959 }
7960#else /* ! UPGRADE_SUPPORT */
7961#ifdef HAVE_MESSAGES
7962 MHD_DLOG (daemon,
7963 _ ("Application used status code 101 \"Switching Protocols\", " \
7964 "but this MHD was built without \"Upgrade\" support!\n"));
7965#endif /* HAVE_MESSAGES */
7966 return MHD_NO;
7967#endif /* ! UPGRADE_SUPPORT */
7968 }
7969 if ( (100 > status_code) ||
7970 (999 < status_code) )
7971 {
7972#ifdef HAVE_MESSAGES
7973 MHD_DLOG (daemon,
7974 _ ("Refused wrong status code (%u). " \
7975 "HTTP requires three digits status code!\n"),
7976 status_code);
7977#endif
7978 return MHD_NO;
7979 }
7980 if (200 > status_code)
7981 {
7982 if (MHD_HTTP_VER_1_0 == connection->rq.http_ver)
7983 {
7984#ifdef HAVE_MESSAGES
7985 MHD_DLOG (daemon,
7986 _ ("Wrong status code (%u) refused. " \
7987 "HTTP/1.0 clients do not support 1xx status codes!\n"),
7988 (status_code));
7989#endif
7990 return MHD_NO;
7991 }
7992 if (0 != (response->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
7994 {
7995#ifdef HAVE_MESSAGES
7996 MHD_DLOG (daemon,
7997 _ ("Wrong status code (%u) refused. " \
7998 "HTTP/1.0 reply mode does not support 1xx status codes!\n"),
7999 (status_code));
8000#endif
8001 return MHD_NO;
8002 }
8003 }
8004 if ( (MHD_HTTP_MTHD_CONNECT == connection->rq.http_mthd) &&
8005 (2 == status_code / 100) )
8006 {
8007#ifdef HAVE_MESSAGES
8008 MHD_DLOG (daemon,
8009 _ ("Successful (%u) response code cannot be used to answer " \
8010 "\"CONNECT\" request!\n"),
8011 (status_code));
8012#endif
8013 return MHD_NO;
8014 }
8015
8016 if ( (0 != (MHD_RF_HEAD_ONLY_RESPONSE & response->flags)) &&
8017 (RP_BODY_HEADERS_ONLY < is_reply_body_needed (connection, status_code)) )
8018 {
8019#ifdef HAVE_MESSAGES
8020 MHD_DLOG (daemon,
8021 _ ("HEAD-only response cannot be used when the request requires "
8022 "reply body to be sent!\n"));
8023#endif
8024 return MHD_NO;
8025 }
8026
8027#ifdef HAVE_MESSAGES
8028 if ( (0 != (MHD_RF_INSANITY_HEADER_CONTENT_LENGTH & response->flags)) &&
8029 (0 != (MHD_RAF_HAS_CONTENT_LENGTH & response->flags_auto)) )
8030 {
8031 MHD_DLOG (daemon,
8032 _ ("The response has application-defined \"Content-Length\" " \
8033 "header. The reply to the request will be not " \
8034 "HTTP-compliant and may result in hung connection or " \
8035 "other problems!\n"));
8036 }
8037#endif
8038
8039 MHD_increment_response_rc (response);
8040 connection->rp.response = response;
8041 connection->rp.responseCode = status_code;
8042 connection->rp.responseIcy = reply_icy;
8043#if defined(_MHD_HAVE_SENDFILE)
8044 if ( (response->fd == -1) ||
8045 (response->is_pipe) ||
8046 (0 != (connection->daemon->options & MHD_USE_TLS))
8047#if defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED) && \
8048 defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE)
8049 || (! daemon->sigpipe_blocked && ! connection->sk_spipe_suppress)
8050#endif /* MHD_SEND_SPIPE_SUPPRESS_NEEDED &&
8051 MHD_SEND_SPIPE_SUPPRESS_POSSIBLE */
8052 )
8053 connection->rp.resp_sender = MHD_resp_sender_std;
8054 else
8055 connection->rp.resp_sender = MHD_resp_sender_sendfile;
8056#endif /* _MHD_HAVE_SENDFILE */
8057 /* FIXME: if 'is_pipe' is set, TLS is off, and we have *splice*, we could use splice()
8058 to avoid two user-space copies... */
8059
8060 if ( (MHD_HTTP_MTHD_HEAD == connection->rq.http_mthd) ||
8061 (MHD_HTTP_OK > status_code) ||
8062 (MHD_HTTP_NO_CONTENT == status_code) ||
8063 (MHD_HTTP_NOT_MODIFIED == status_code) )
8064 {
8065 /* if this is a "HEAD" request, or a status code for
8066 which a body is not allowed, pretend that we
8067 have already sent the full message body. */
8068 /* TODO: remove the next assignment, use 'rp_props.send_reply_body' in
8069 * checks */
8070 connection->rp.rsp_write_position = response->total_size;
8071 }
8072 if (MHD_CONNECTION_HEADERS_PROCESSED == connection->state)
8073 {
8074 /* response was queued "early", refuse to read body / footers or
8075 further requests! */
8076 connection->discard_request = true;
8077 connection->state = MHD_CONNECTION_START_REPLY;
8078 connection->rq.remaining_upload_size = 0;
8079 }
8080 if (! connection->in_idle)
8081 (void) MHD_connection_handle_idle (connection);
8082 MHD_update_last_activity_ (connection);
8083 return MHD_YES;
8084}
8085
8086
8087/* end of connection.c */
#define ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG
Definition connection.c:167
#define REQUEST_CONTENTLENGTH_TOOLARGE
Definition connection.c:533
static enum MHD_Result build_connection_chunked_response_footer(struct MHD_Connection *connection)
static ssize_t recv_param_adapter(struct MHD_Connection *connection, void *other, size_t i)
Definition connection.c:725
#define ERR_RSP_EMPTY_FOOTER_NAME
Definition connection.c:436
static enum MHD_Result build_header_response(struct MHD_Connection *connection)
static void connection_close_error(struct MHD_Connection *connection, const char *emsg)
static bool process_request_target(struct MHD_Connection *c)
void MHD_connection_set_initial_state_(struct MHD_Connection *c)
#define MHD_CHUNK_HEADER_REASONABLE_LEN
Definition connection.c:75
#define MHD_lookup_header_s_token_ci(c, h, tkn)
MHD_ProcRecvDataStage
@ MHD_PROC_RECV_COOKIE
@ MHD_PROC_RECV_BODY_CHUNKED
@ MHD_PROC_RECV_HTTPVER
@ MHD_PROC_RECV_INIT
@ MHD_PROC_RECV_URI
@ MHD_PROC_RECV_METHOD
@ MHD_PROC_RECV_BODY_NORMAL
@ MHD_PROC_RECV_HEADERS
@ MHD_PROC_RECV_FOOTERS
#define REQUEST_CHUNK_TOO_LARGE
Definition connection.c:521
void MHD_connection_handle_write(struct MHD_Connection *connection)
static void MHD_connection_update_event_loop_info(struct MHD_Connection *connection)
#define buffer_append_s(buf, ppos, buf_size, str)
void * MHD_connection_alloc_memory_(struct MHD_Connection *connection, size_t size)
Definition connection.c:651
#define MHD_MAX_FIXED_URI_LEN
static enum MHD_Result try_ready_normal_body(struct MHD_Connection *connection)
#define REQUEST_CHUNKED_MALFORMED
Definition connection.c:509
#define REQ_HTTP_VER_IS_NOT_SUPPORTED
Definition connection.c:585
#define MHD_MAX_REASONABLE_HEADERS_SIZE_
#define RQ_LINE_TOO_MANY_WSP
Definition connection.c:198
static void call_connection_handler(struct MHD_Connection *connection)
static enum MHD_Result connection_add_header(void *cls, const char *key, size_t key_size, const char *value, size_t value_size, enum MHD_ValueKind kind)
static void process_request_body(struct MHD_Connection *connection)
#define ERR_RSP_INVALID_CHR_IN_FOOTER
Definition connection.c:384
static void setup_reply_properties(struct MHD_Connection *connection)
#define HTTP_100_CONTINUE
Definition connection.c:80
static void send_redirect_fixed_rq_target(struct MHD_Connection *c)
static void handle_recv_no_space(struct MHD_Connection *c, enum MHD_ProcRecvDataStage stage)
#define transmit_error_response_static(c, code, msg)
#define REQUEST_MALFORMED
Definition connection.c:497
static bool get_request_line(struct MHD_Connection *c)
MHD_HdrLineReadRes_
@ MHD_HDR_LINE_READING_NEED_MORE_DATA
@ MHD_HDR_LINE_READING_GOT_END_OF_HEADER
@ MHD_HDR_LINE_READING_GOT_HEADER
@ MHD_HDR_LINE_READING_DATA_ERROR
_MHD_static_inline void reset_rq_header_processing_state(struct MHD_Connection *c)
#define ERR_RSP_HEADER_WITHOUT_COLON
Definition connection.c:397
static void connection_shrink_read_buffer(struct MHD_Connection *connection)
#define ERR_RSP_WSP_IN_FOOTER_NAME
Definition connection.c:358
#define MHD_ALLOW_BARE_LF_AS_CRLF_(discp_lvl)
Definition connection.c:65
#define HTTP_VER_LEN
static enum replyBodyUse is_reply_body_needed(struct MHD_Connection *connection, unsigned int rcode)
void MHD_set_http_callbacks_(struct MHD_Connection *connection)
static void transmit_error_response_len(struct MHD_Connection *connection, unsigned int status_code, const char *message, size_t message_len, char *header_name, size_t header_name_len, char *header_value, size_t header_value_len)
#define REQ_HTTP_VER_IS_TOO_OLD
Definition connection.c:574
#define ERR_MSG_REQUEST_TOO_BIG
Definition connection.c:94
#define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
#define ERR_MSG_REQUEST_FOOTER_TOO_BIG
Definition connection.c:185
#define ERR_MSG_REQUEST_CHUNK_LINE_EXT_TOO_BIG
Definition connection.c:148
static bool connection_check_timedout(struct MHD_Connection *c)
static enum MHD_ConnKeepAlive keepalive_possible(struct MHD_Connection *connection)
static void handle_req_chunk_size_line_no_space(struct MHD_Connection *c, const char *chunk_size_line, size_t chunk_size_line_size)
#define REQUEST_LACKS_HOST
Definition connection.c:454
#define BARE_CR_IN_FOOTER
Definition connection.c:228
static bool try_grow_read_buffer(struct MHD_Connection *connection, bool required)
static bool parse_http_version(struct MHD_Connection *connection, const char *http_string, size_t len)
#define BARE_LF_IN_HEADER
Definition connection.c:243
#define ERR_RSP_EMPTY_HEADER_NAME
Definition connection.c:423
static bool need_100_continue(struct MHD_Connection *connection)
#define ERR_RSP_WSP_IN_HEADER_NAME
Definition connection.c:343
static bool get_req_headers(struct MHD_Connection *c, bool process_footers)
#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP
Definition connection.c:562
#define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
static unsigned int get_no_space_err_status_code(struct MHD_Connection *c, enum MHD_ProcRecvDataStage stage, const char *add_element, size_t add_element_size)
#define REQUEST_UNSUPPORTED_TR_ENCODING
Definition connection.c:467
enum MHD_Result MHD_connection_handle_idle(struct MHD_Connection *connection)
static void cleanup_connection(struct MHD_Connection *connection)
#define ERR_RSP_OBS_FOLD_FOOTER
Definition connection.c:298
replyBodyUse
@ RP_BODY_HEADERS_ONLY
@ RP_BODY_NONE
@ RP_BODY_SEND
static void parse_http_std_method(struct MHD_Connection *connection, const char *method, size_t len)
#define ERR_RSP_OBS_FOLD
Definition connection.c:285
static bool check_and_grow_read_buffer_space(struct MHD_Connection *c)
static enum MHD_Result try_ready_chunked_body(struct MHD_Connection *connection, bool *p_finished)
static bool get_date_str(char *date)
#define BARE_LF_IN_FOOTER
Definition connection.c:258
static void connection_reset(struct MHD_Connection *connection, bool reuse)
#define REQUEST_LENGTH_WITH_TR_ENCODING
Definition connection.c:482
#define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
#define RQ_TARGET_INVALID_CHAR
Definition connection.c:272
static bool get_request_line_inner(struct MHD_Connection *c)
static void handle_req_footers_no_space(struct MHD_Connection *c, const char *add_footer, size_t add_footer_size)
void MHD_connection_handle_read(struct MHD_Connection *connection, bool socket_error)
static void connection_switch_from_recv_to_send(struct MHD_Connection *connection)
#define ERR_RSP_WSP_BEFORE_FOOTER
Definition connection.c:328
static void parse_connection_headers(struct MHD_Connection *connection)
#define ERR_RSP_INVALID_CHR_IN_HEADER
Definition connection.c:371
void MHD_update_last_activity_(struct MHD_Connection *connection)
#define MHD_MAX_EMPTY_LINES_SKIP
#define MHD_MIN_REASONABLE_HEADERS_SIZE_
#define BARE_CR_IN_HEADER
Definition connection.c:213
#define ERR_MSG_REQUEST_HEADER_TOO_BIG
Definition connection.c:111
static bool add_user_headers(char *buf, size_t *ppos, size_t buf_size, struct MHD_Response *response, bool filter_transf_enc, bool filter_content_len, bool add_close, bool add_keep_alive)
#define REQUEST_CONTENTLENGTH_MALFORMED
Definition connection.c:546
static enum MHD_HdrLineReadRes_ get_req_header(struct MHD_Connection *c, bool process_footers, struct _MHD_str_w_len *hdr_name, struct _MHD_str_w_len *hdr_value)
static void check_connection_reply(struct MHD_Connection *connection)
static void handle_req_headers_no_space(struct MHD_Connection *c, const char *add_header, size_t add_header_size)
void MHD_connection_close_(struct MHD_Connection *connection, enum MHD_RequestTerminationCode termination_code)
static size_t connection_maximize_write_buffer(struct MHD_Connection *connection)
static bool has_unprocessed_upload_body_data_in_buffer(struct MHD_Connection *c)
void MHD_connection_mark_closed_(struct MHD_Connection *connection)
#define CONNECTION_CLOSE_ERROR(c, emsg)
#define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l)
static bool buffer_append(char *buf, size_t *ppos, size_t buf_size, const char *append, size_t append_size)
static enum MHD_Result check_write_done(struct MHD_Connection *connection, enum MHD_CONNECTION_STATE next_state)
#define ERR_RSP_WSP_BEFORE_HEADER
Definition connection.c:313
#define ERR_MSG_REQUEST_HEADER_WITH_COOKIES_TOO_BIG
Definition connection.c:129
_MHD_static_inline void switch_to_rq_headers_processing(struct MHD_Connection *c)
static bool get_date_header(char *header)
static bool MHD_lookup_header_token_ci(const struct MHD_Connection *connection, const char *header, size_t header_len, const char *token, size_t token_len)
#define ERR_RSP_FOOTER_WITHOUT_COLON
Definition connection.c:410
#define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
Methods for managing connections.
#define MHD_ERR_INVAL_
Definition connection.h:63
#define MHD_ERR_CONNRESET_
Definition connection.h:42
#define MHD_ERR_NOMEM_
Definition connection.h:53
#define MHD_connection_finish_forward_(conn)
Definition connection.h:174
#define MHD_ERR_TLS_
Definition connection.h:78
#define MHD_ERR_AGAIN_
Definition connection.h:37
#define MHD_ERR_OPNOTSUPP_
Definition connection.h:68
#define MHD_ERR_BADF_
Definition connection.h:58
#define MHD_ERR_PIPE_
Definition connection.h:73
#define MHD_ERR_NOTCONN_
Definition connection.h:48
bool MHD_tls_connection_shutdown(struct MHD_Connection *connection)
bool MHD_run_tls_handshake_(struct MHD_Connection *connection)
Methods for managing connections.
#define MHD_HTTP_HEADER_CONTENT_LENGTH
Definition microhttpd.h:596
#define MHD_HTTP_HEADER_CONNECTION
Definition microhttpd.h:590
#define MHD_HTTP_HEADER_TRANSFER_ENCODING
Definition microhttpd.h:654
#define MHD_HTTP_HEADER_COOKIE
Definition microhttpd.h:754
#define MHD_HTTP_HEADER_EXPECT
Definition microhttpd.h:608
#define MHD_HTTP_HEADER_LOCATION
Definition microhttpd.h:628
#define MHD_HTTP_HEADER_HOST
Definition microhttpd.h:614
#define MHD_HTTP_INTERNAL_SERVER_ERROR
Definition microhttpd.h:453
#define MHD_HTTP_OK
Definition microhttpd.h:350
#define MHD_HTTP_MOVED_PERMANENTLY
Definition microhttpd.h:374
#define MHD_HTTP_URI_TOO_LONG
Definition microhttpd.h:419
#define MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE
Definition microhttpd.h:447
#define MHD_HTTP_PROCESSING
Definition microhttpd.h:345
#define MHD_HTTP_NOT_IMPLEMENTED
Definition microhttpd.h:455
#define MHD_HTTP_SWITCHING_PROTOCOLS
Definition microhttpd.h:343
#define MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED
Definition microhttpd.h:463
#define MHD_HTTP_CONTENT_TOO_LARGE
Definition microhttpd.h:417
#define MHD_HTTP_NOT_MODIFIED
Definition microhttpd.h:380
#define MHD_HTTP_NO_CONTENT
Definition microhttpd.h:358
#define MHD_HTTP_BAD_REQUEST
Definition microhttpd.h:391
#define MHD_HTTP_METHOD_TRACE
#define MHD_HTTP_METHOD_OPTIONS
#define MHD_HTTP_METHOD_GET
#define MHD_HTTP_METHOD_HEAD
#define MHD_HTTP_METHOD_POST
#define MHD_HTTP_METHOD_PUT
#define MHD_HTTP_METHOD_CONNECT
#define MHD_HTTP_METHOD_DELETE
_MHD_EXTERN enum MHD_Result MHD_set_connection_value(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key, const char *value)
Definition connection.c:992
enum MHD_Result(* MHD_KeyValueIterator)(void *cls, enum MHD_ValueKind kind, const char *key, const char *value)
_MHD_EXTERN const char * MHD_lookup_connection_value(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key)
enum MHD_Result(* MHD_KeyValueIteratorN)(void *cls, enum MHD_ValueKind kind, const char *key, size_t key_size, const char *value, size_t value_size)
_MHD_EXTERN int MHD_get_connection_values_n(struct MHD_Connection *connection, enum MHD_ValueKind kind, MHD_KeyValueIteratorN iterator, void *iterator_cls)
Definition connection.c:832
_MHD_EXTERN int MHD_get_connection_values(struct MHD_Connection *connection, enum MHD_ValueKind kind, MHD_KeyValueIterator iterator, void *iterator_cls)
Definition connection.c:793
static enum MHD_Result MHD_set_connection_value_n_nocheck_(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key, size_t key_size, const char *value, size_t value_size)
Definition connection.c:885
_MHD_EXTERN enum MHD_Result MHD_set_connection_value_n(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key, size_t key_size, const char *value, size_t value_size)
Definition connection.c:945
MHD_ConnectionInfoType
_MHD_EXTERN enum MHD_Result MHD_lookup_connection_value_n(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key, size_t key_size, const char **value_ptr, size_t *value_size_ptr)
MHD_RequestTerminationCode
@ MHD_CONNECTION_INFO_CONNECTION_TIMEOUT
@ MHD_CONNECTION_INFO_SOCKET_CONTEXT
@ MHD_CONNECTION_INFO_GNUTLS_SESSION
@ MHD_CONNECTION_INFO_REQUEST_HEADER_SIZE
@ MHD_CONNECTION_INFO_CIPHER_ALGO
@ MHD_CONNECTION_INFO_CONNECTION_SUSPENDED
@ MHD_CONNECTION_INFO_CLIENT_ADDRESS
@ MHD_CONNECTION_INFO_DAEMON
@ MHD_CONNECTION_INFO_GNUTLS_CLIENT_CERT
@ MHD_CONNECTION_INFO_HTTP_STATUS
@ MHD_CONNECTION_INFO_CONNECTION_FD
@ MHD_CONNECTION_INFO_PROTOCOL
@ MHD_REQUEST_TERMINATED_TIMEOUT_REACHED
@ MHD_REQUEST_TERMINATED_COMPLETED_OK
@ MHD_REQUEST_TERMINATED_WITH_ERROR
@ MHD_REQUEST_TERMINATED_READ_ERROR
@ MHD_REQUEST_TERMINATED_CLIENT_ABORT
_MHD_EXTERN enum MHD_Result MHD_queue_response(struct MHD_Connection *connection, unsigned int status_code, struct MHD_Response *response)
_MHD_EXTERN void MHD_destroy_response(struct MHD_Response *response)
Definition response.c:2289
enum MHD_HdrLineReadRes_ _MHD_FIXED_ENUM
_MHD_EXTERN struct MHD_Response * MHD_create_response_from_buffer_static(size_t size, const void *buffer)
Definition response.c:1507
_MHD_EXTERN enum MHD_Result MHD_set_connection_option(struct MHD_Connection *connection, enum MHD_CONNECTION_OPTION option,...)
#define MHD_ICY_FLAG
Definition microhttpd.h:556
_MHD_EXTERN const union MHD_ConnectionInfo * MHD_get_connection_info(struct MHD_Connection *connection, enum MHD_ConnectionInfoType info_type,...)
#define MHD_HTTP_VERSION_1_0
#define MHD_HTTP_VERSION_1_1
enum MHD_Result MHD_parse_arguments_(struct MHD_Connection *connection, enum MHD_ValueKind kind, char *args, MHD_ArgumentIterator_ cb, void *cls)
Definition internal.c:169
MHD internal shared structures.
MHD_CONNECTION_STATE
Definition internal.h:613
@ MHD_CONNECTION_BODY_RECEIVED
Definition internal.h:663
@ MHD_CONNECTION_CHUNKED_BODY_SENT
Definition internal.h:725
@ MHD_CONNECTION_REQ_HEADERS_RECEIVING
Definition internal.h:636
@ MHD_CONNECTION_BODY_RECEIVING
Definition internal.h:656
@ MHD_CONNECTION_HEADERS_SENDING
Definition internal.h:694
@ MHD_CONNECTION_FOOTERS_SENDING
Definition internal.h:730
@ MHD_CONNECTION_FOOTERS_RECEIVED
Definition internal.h:675
@ MHD_CONNECTION_FULL_REPLY_SENT
Definition internal.h:736
@ MHD_CONNECTION_HEADERS_SENT
Definition internal.h:699
@ MHD_CONNECTION_HEADERS_PROCESSED
Definition internal.h:646
@ MHD_CONNECTION_INIT
Definition internal.h:618
@ MHD_CONNECTION_CLOSED
Definition internal.h:741
@ MHD_CONNECTION_REQ_LINE_RECEIVED
Definition internal.h:631
@ MHD_CONNECTION_NORMAL_BODY_UNREADY
Definition internal.h:705
@ MHD_CONNECTION_HEADERS_RECEIVED
Definition internal.h:641
@ MHD_CONNECTION_NORMAL_BODY_READY
Definition internal.h:710
@ MHD_CONNECTION_START_REPLY
Definition internal.h:688
@ MHD_CONNECTION_FOOTERS_RECEIVING
Definition internal.h:668
@ MHD_CONNECTION_CHUNKED_BODY_READY
Definition internal.h:720
@ MHD_CONNECTION_FULL_REQ_RECEIVED
Definition internal.h:681
@ MHD_CONNECTION_CHUNKED_BODY_UNREADY
Definition internal.h:715
@ MHD_CONNECTION_CONTINUE_SENDING
Definition internal.h:651
@ MHD_CONNECTION_REQ_LINE_RECEIVING
Definition internal.h:624
#define XDLL_insert(head, tail, element)
Definition internal.h:2705
MHD_EpollState
Definition internal.h:169
@ MHD_EPOLL_STATE_SUSPENDED
Definition internal.h:202
@ MHD_EPOLL_STATE_IN_EREADY_EDLL
Definition internal.h:192
@ MHD_EPOLL_STATE_READ_READY
Definition internal.h:181
@ MHD_EPOLL_STATE_IN_EPOLL_SET
Definition internal.h:197
@ MHD_EPOLL_STATE_WRITE_READY
Definition internal.h:187
@ MHD_TLS_CONN_TLS_CLOSING
Definition internal.h:766
@ MHD_TLS_CONN_WR_CLOSING
Definition internal.h:764
@ MHD_TLS_CONN_INVALID_STATE
Definition internal.h:769
@ MHD_TLS_CONN_WR_CLOSED
Definition internal.h:765
@ MHD_TLS_CONN_NO_TLS
Definition internal.h:760
@ MHD_TLS_CONN_INIT
Definition internal.h:761
@ MHD_TLS_CONN_TLS_CLOSED
Definition internal.h:767
@ MHD_TLS_CONN_TLS_FAILED
Definition internal.h:768
@ MHD_TLS_CONN_CONNECTED
Definition internal.h:763
@ MHD_TLS_CONN_HANDSHAKING
Definition internal.h:762
#define DLL_insert(head, tail, element)
Definition internal.h:2658
@ MHD_EVENT_LOOP_INFO_PROCESS_READ
Definition internal.h:235
@ MHD_EVENT_LOOP_INFO_PROCESS
Definition internal.h:229
@ MHD_EVENT_LOOP_INFO_READ
Definition internal.h:219
@ MHD_EVENT_LOOP_INFO_WRITE
Definition internal.h:224
@ MHD_EVENT_LOOP_INFO_CLEANUP
Definition internal.h:241
#define EDLL_insert(head, tail, element)
Definition internal.h:2752
struct MHD_IoVec MHD_iovec_
Definition internal.h:440
#define MHD_MIN(a, b)
Definition internal.h:130
#define MHD_IS_HTTP_VER_SUPPORTED(ver)
Definition internal.h:881
_MHD_static_inline struct MHD_Daemon * MHD_get_master(struct MHD_Daemon *const daemon)
Definition internal.h:2900
@ MHD_RAF_HAS_DATE_HDR
Definition internal.h:411
@ MHD_RAF_HAS_CONTENT_LENGTH
Definition internal.h:410
@ MHD_RAF_HAS_CONNECTION_CLOSE
Definition internal.h:408
@ MHD_RAF_HAS_TRANS_ENC_CHUNKED
Definition internal.h:409
@ MHD_RAF_HAS_CONNECTION_HDR
Definition internal.h:407
@ MHD_HTTP_VER_1_0
Definition internal.h:860
@ MHD_HTTP_VER_1_1
Definition internal.h:865
@ MHD_HTTP_VER_TOO_OLD
Definition internal.h:855
@ MHD_HTTP_VER_INVALID
Definition internal.h:845
@ MHD_HTTP_VER_UNKNOWN
Definition internal.h:850
@ MHD_HTTP_VER_1_2__1_9
Definition internal.h:870
@ MHD_HTTP_VER_FUTURE
Definition internal.h:875
MHD_ConnKeepAlive
Definition internal.h:818
@ MHD_CONN_USE_KEEPALIVE
Definition internal.h:832
@ MHD_CONN_MUST_UPGRADE
Definition internal.h:837
@ MHD_CONN_MUST_CLOSE
Definition internal.h:822
@ MHD_CONN_KEEPALIVE_UNKOWN
Definition internal.h:827
#define MHD_IS_HTTP_VER_1_1_COMPAT(ver)
Definition internal.h:890
#define MHD_BUF_INC_SIZE
Definition internal.h:142
@ MHD_HTTP_MTHD_GET
Definition internal.h:907
@ MHD_HTTP_MTHD_CONNECT
Definition internal.h:927
@ MHD_HTTP_MTHD_DELETE
Definition internal.h:923
@ MHD_HTTP_MTHD_OPTIONS
Definition internal.h:931
@ MHD_HTTP_MTHD_TRACE
Definition internal.h:935
@ MHD_HTTP_MTHD_HEAD
Definition internal.h:911
@ MHD_HTTP_MTHD_POST
Definition internal.h:915
@ MHD_HTTP_MTHD_OTHER
Definition internal.h:939
@ MHD_HTTP_MTHD_NO_METHOD
Definition internal.h:903
@ MHD_HTTP_MTHD_PUT
Definition internal.h:919
#define EDLL_remove(head, tail, element)
Definition internal.h:2772
#define PRIu64
Definition internal.h:53
#define XDLL_remove(head, tail, element)
Definition internal.h:2727
#define MHD_D_IS_USING_THREAD_PER_CONN_(d)
Definition internal.h:2578
#define DLL_remove(head, tail, element)
Definition internal.h:2680
#define MHD_D_IS_USING_THREADS_(d)
Definition internal.h:2574
#define MHD_D_IS_USING_EPOLL_(d)
Definition internal.h:2550
void * MHD_pool_reallocate(struct MemoryPool *pool, void *old, size_t old_size, size_t new_size)
Definition memorypool.c:533
void MHD_pool_destroy(struct MemoryPool *pool)
Definition memorypool.c:341
bool MHD_pool_is_resizable_inplace(struct MemoryPool *pool, void *block, size_t block_size)
Definition memorypool.c:440
void MHD_pool_deallocate(struct MemoryPool *pool, void *block, size_t block_size)
Definition memorypool.c:625
void * MHD_pool_try_alloc(struct MemoryPool *pool, size_t size, size_t *required_bytes)
Definition memorypool.c:481
size_t MHD_pool_get_free(struct MemoryPool *pool)
Definition memorypool.c:374
void * MHD_pool_reset(struct MemoryPool *pool, void *keep, size_t copy_bytes, size_t new_size)
Definition memorypool.c:729
void * MHD_pool_allocate(struct MemoryPool *pool, size_t size, bool from_end)
Definition memorypool.c:399
memory pool; mostly used for efficient (de)allocation for each connection and bounding memory use for...
macros for mhd_assert()
#define mhd_assert(ignore)
Definition mhd_assert.h:45
Header for platform missing functions.
Header for platform-independent inter-thread communication.
#define MHD_PANIC(msg)
Definition mhd_itc.h:45
limits values definitions
#define SSIZE_MAX
Definition mhd_limits.h:121
#define UINT64_MAX
Definition mhd_limits.h:93
#define SIZE_MAX
Definition mhd_limits.h:111
#define UINT_MAX
Definition mhd_limits.h:53
Header for platform-independent locks abstraction.
#define MHD_mutex_unlock_chk_(ignore)
Definition mhd_locks.h:198
#define MHD_mutex_lock_chk_(ignore)
Definition mhd_locks.h:196
#define NULL
uint64_t MHD_monotonic_msec_counter(void)
internal monotonic clock functions implementations
#define _(String)
Definition mhd_options.h:42
#define _MHD_EXTERN
Definition mhd_options.h:53
#define MHD_FUNC_
ssize_t MHD_send_hdr_and_body_(struct MHD_Connection *connection, const char *header, size_t header_size, bool never_push_hdr, const char *body, size_t body_size, bool complete_response)
Definition mhd_send.c:905
ssize_t MHD_send_iovec_(struct MHD_Connection *connection, struct MHD_iovec_track_ *const r_iov, bool push_data)
Definition mhd_send.c:1622
ssize_t MHD_send_data_(struct MHD_Connection *connection, const char *buffer, size_t buffer_size, bool push_data)
Definition mhd_send.c:753
Declarations of send() wrappers.
#define MHD_SCKT_ERR_IS_(err, code)
#define MHD_SCKT_ERR_IS_EAGAIN_(err)
#define MHD_SCKT_ERR_IS_LOW_RESOURCES_(err)
#define MHD_socket_last_strerr_()
#define MHD_SCKT_EOPNOTSUPP_
#define MHD_SCKT_EBADF_
#define MHD_socket_get_error_()
#define MHD_SCKT_ERR_IS_REMOTE_DISCNN_(err)
#define MHD_SCKT_EINVAL_
#define MHD_SCKT_ERR_IS_EINTR_(err)
#define MHD_SCKT_SEND_MAX_SIZE_
#define MHD_SCKT_ENOTCONN_
#define MHD_recv_(s, b, l)
#define MHD_SEND_SPIPE_SUPPRESS_NEEDED
int MHD_str_equal_caseless_(const char *str1, const char *str2)
Definition mhd_str.c:683
size_t MHD_uint8_to_str_pad(uint8_t val, uint8_t min_digits, char *buf, size_t buf_size)
Definition mhd_str.c:1629
size_t MHD_uint16_to_str(uint16_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1550
size_t MHD_str_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition mhd_str.c:1238
size_t MHD_uint64_to_str(uint64_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1591
size_t MHD_strx_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition mhd_str.c:1415
int MHD_str_equal_caseless_n_(const char *const str1, const char *const str2, size_t maxlen)
Definition mhd_str.c:717
bool MHD_str_has_token_caseless_(const char *str, const char *const token, size_t token_len)
Definition mhd_str.c:782
bool MHD_str_equal_caseless_bin_n_(const char *const str1, const char *const str2, size_t len)
Definition mhd_str.c:749
size_t MHD_uint32_to_strx(uint32_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1516
Header for string manipulating helpers.
#define MHD_str_has_s_token_caseless_(str, tkn)
Definition mhd_str.h:154
#define MHD_STATICSTR_LEN_(macro)
#define MHD_thread_handle_ID_is_current_thread_(hndl_id)
#define MHD_SIZE_UNKNOWN
Definition microhttpd.h:183
MHD_Result
Definition microhttpd.h:163
@ MHD_YES
Definition microhttpd.h:172
@ MHD_NO
Definition microhttpd.h:167
#define MHD_CONTENT_READER_END_OF_STREAM
Definition microhttpd.h:186
_MHD_EXTERN size_t MHD_get_reason_phrase_len_for(unsigned int code)
#define MHD_INVALID_SOCKET
Definition microhttpd.h:207
_MHD_EXTERN const char * MHD_get_reason_phrase_for(unsigned int code)
#define MHD_CONTENT_READER_END_WITH_ERROR
Definition microhttpd.h:187
MHD_ValueKind
@ MHD_FOOTER_KIND
@ MHD_COOKIE_KIND
@ MHD_HEADER_KIND
@ MHD_GET_ARGUMENT_KIND
@ MHD_USE_TURBO
@ MHD_USE_SUPPRESS_DATE_NO_CLOCK
@ MHD_USE_TLS
@ MHD_ALLOW_UPGRADE
@ MHD_USE_ERROR_LOG
@ MHD_RF_SEND_KEEP_ALIVE_HEADER
@ MHD_RF_HEAD_ONLY_RESPONSE
@ MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
@ MHD_RF_HTTP_1_0_SERVER
@ MHD_RF_INSANITY_HEADER_CONTENT_LENGTH
MHD_CONNECTION_OPTION
@ MHD_CONNECTION_OPTION_TIMEOUT
bool MHD_add_response_entry_no_alloc_(struct MHD_Response *response, enum MHD_ValueKind kind, char *header, size_t header_len, char *content, size_t content_len)
Definition response.c:167
void MHD_increment_response_rc(struct MHD_Response *response)
Definition response.c:2335
Methods for managing response objects.
enum MHD_Result MHD_response_execute_upgrade_(struct MHD_Response *response, struct MHD_Connection *connection)
MHD_socket socket_fd
Definition internal.h:1483
size_t write_buffer_size
Definition internal.h:1441
size_t write_buffer_send_offset
Definition internal.h:1446
socklen_t addr_len
Definition internal.h:1463
enum MHD_ConnectionEventLoopInfo event_loop_info
Definition internal.h:1570
size_t write_buffer_append_offset
Definition internal.h:1452
char * write_buffer
Definition internal.h:1409
bool stop_with_error
Definition internal.h:1526
bool discard_request
Definition internal.h:1535
ReceiveCallback recv_cls
Definition internal.h:1575
volatile bool resuming
Definition internal.h:1621
void * socket_context
Definition internal.h:1389
uint64_t last_activity
Definition internal.h:1469
enum MHD_ConnKeepAlive keepalive
Definition internal.h:1396
struct MHD_Request rq
Definition internal.h:1365
union MHD_ConnectionInfo connection_info_dummy
Definition internal.h:1626
size_t continue_message_write_offset
Definition internal.h:1458
size_t read_buffer_offset
Definition internal.h:1436
struct MHD_Reply rp
Definition internal.h:1370
struct MemoryPool * pool
Definition internal.h:1380
enum MHD_CONNECTION_STATE state
Definition internal.h:1565
char * read_buffer
Definition internal.h:1403
struct MHD_Daemon * daemon
Definition internal.h:1360
bool in_access_handler
Definition internal.h:1616
bool sk_spipe_suppress
Definition internal.h:1499
uint64_t connection_timeout_ms
Definition internal.h:1476
struct sockaddr_storage * addr
Definition internal.h:1415
size_t read_buffer_size
Definition internal.h:1430
size_t pool_size
Definition internal.h:2136
MHD_AccessHandlerCallback default_handler
Definition internal.h:1867
LogCallback uri_log_callback
Definition internal.h:2057
struct MHD_Connection * normal_timeout_tail
Definition internal.h:2000
int client_discipline
Definition internal.h:2265
void * unescape_callback_cls
Definition internal.h:2072
struct MHD_Connection * connections_tail
Definition internal.h:1900
struct MHD_Connection * suspended_connections_tail
Definition internal.h:1910
struct MHD_Connection * manual_timeout_head
Definition internal.h:2008
struct MHD_Connection * normal_timeout_head
Definition internal.h:1993
MHD_RequestCompletedCallback notify_completed
Definition internal.h:2032
struct MHD_Connection * cleanup_head
Definition internal.h:1915
struct MHD_itc_ itc
Definition internal.h:2198
uint64_t connection_timeout_ms
Definition internal.h:2253
struct MHD_Connection * manual_timeout_tail
Definition internal.h:2015
volatile bool shutdown
Definition internal.h:2203
enum MHD_FLAG options
Definition internal.h:1880
struct MHD_Connection * cleanup_tail
Definition internal.h:1920
bool sigpipe_blocked
Definition internal.h:2286
UnescapeCallback unescape_callback
Definition internal.h:2067
void * notify_completed_cls
Definition internal.h:2037
struct MHD_Connection * suspended_connections_head
Definition internal.h:1905
void * default_handler_cls
Definition internal.h:1872
struct MHD_Connection * connections_head
Definition internal.h:1895
size_t pool_increment
Definition internal.h:2141
void * uri_log_callback_cls
Definition internal.h:2062
enum MHD_ValueKind kind
Definition internal.h:396
const char * value
Definition internal.h:386
struct MHD_HTTP_Req_Header * next
Definition internal.h:366
const char * header
Definition internal.h:376
struct MHD_HTTP_Res_Header * next
Definition internal.h:323
enum MHD_ValueKind kind
Definition internal.h:353
uint64_t rsp_write_position
Definition internal.h:1295
bool responseIcy
Definition internal.h:1288
struct MHD_iovec_track_ resp_iov
Definition internal.h:1303
struct MHD_Response * response
Definition internal.h:1276
unsigned int responseCode
Definition internal.h:1282
struct MHD_Reply_Properties props
Definition internal.h:1312
unsigned int skipped_empty_lines
Definition internal.h:955
struct MHD_HTTP_Req_Header * headers_received
Definition internal.h:1111
union MHD_HeadersProcessing hdrs
Definition internal.h:1241
uint64_t current_chunk_size
Definition internal.h:1161
uint64_t current_chunk_offset
Definition internal.h:1167
bool some_payload_processed
Definition internal.h:1184
size_t skipped_broken_lines
Definition internal.h:1236
void * client_context
Definition internal.h:1192
const char * url
Definition internal.h:1096
size_t url_len
Definition internal.h:1101
const char * version
Definition internal.h:1075
size_t num_cr_sp_replaced
Definition internal.h:1231
enum MHD_HTTP_Version http_ver
Definition internal.h:1080
size_t req_target_len
Definition internal.h:1106
const char * method
Definition internal.h:1085
size_t header_size
Definition internal.h:1124
union MHD_StartOrSize field_lines
Definition internal.h:1134
struct MHD_HTTP_Req_Header * headers_received_tail
Definition internal.h:1116
enum MHD_HTTP_Method http_mthd
Definition internal.h:1090
bool client_aware
Definition internal.h:1199
bool have_chunked_upload
Definition internal.h:1152
uint64_t remaining_upload_size
Definition internal.h:1140
size_t data_buffer_size
Definition internal.h:557
MHD_iovec_ * data_iov
Definition internal.h:588
const char * data
Definition internal.h:489
uint64_t data_start
Definition internal.h:541
MHD_ContentReaderCallback crc
Definition internal.h:501
enum MHD_ResponseAutoFlags flags_auto
Definition internal.h:578
unsigned int data_iovcnt
Definition internal.h:593
size_t data_size
Definition internal.h:552
enum MHD_ResponseFlags flags
Definition internal.h:573
void * crc_cls
Definition internal.h:495
struct MHD_HTTP_Res_Header * first_header
Definition internal.h:478
uint64_t total_size
Definition internal.h:535
MHD_iovec_ * iov
Definition internal.h:453
const char * str
unsigned int connection_timeout
struct MHD_Daemon * daemon
unsigned int http_status
struct sockaddr * client_addr
MHD_socket connect_fd
struct MHD_RequestLineProcessing rq_line
Definition internal.h:1036
struct MHD_HeaderProcessing hdr
Definition internal.h:1041
const char * start
Definition internal.h:1055