GNU libmicrohttpd 1.0.0
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_HEADER ""
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_INVALID_CHR_IN_HEADER ""
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
2104{
2111
2118
2124 RP_BODY_SEND = 2
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) || (RP_BODY_NONE == use_rp_body));
2214#endif /* UPGRADE_SUPPORT */
2215
2217 {
2218 if ((MHD_SIZE_UNKNOWN == r->total_size) ||
2220 { /* Use chunked reply encoding if possible */
2221
2222 /* Check whether chunked encoding is supported by the client */
2224 use_chunked = false;
2225 /* Check whether chunked encoding is allowed for the reply */
2226 else if (0 != (r->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
2228 use_chunked = false;
2229 else
2230 /* If chunked encoding is supported and allowed, and response size
2231 * is unknown, use chunked even for non-Keep-Alive connections.
2232 * See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3
2233 * Also use chunked if it is enforced by application and supported by
2234 * the client. */
2235 use_chunked = true;
2236 }
2237 else
2238 use_chunked = false;
2239
2240 if ( (MHD_SIZE_UNKNOWN == r->total_size) && ! use_chunked)
2241 {
2242 /* End of the stream is indicated by closure */
2244 }
2245 }
2246 else
2247 use_chunked = false; /* chunked encoding cannot be used without body */
2248
2249 c->rp.props.chunked = use_chunked;
2250#ifdef _DEBUG
2251 c->rp.props.set = true;
2252#endif /* _DEBUG */
2253}
2254
2255
2260static void
2262{
2263 struct MHD_Connection *const c = connection;
2264 struct MHD_Response *const r = c->rp.response;
2265 mhd_assert (c->rp.props.set);
2266
2267#ifdef HAVE_MESSAGES
2268 if ((! c->rp.props.use_reply_body_headers) && (0 != r->total_size))
2269 {
2270 MHD_DLOG (c->daemon,
2271 _ ("This reply with response code %u cannot use reply body. "
2272 "Non-empty response body is ignored and not used.\n"),
2273 (unsigned) (c->rp.responseCode));
2274 }
2275 if ( (! c->rp.props.use_reply_body_headers) &&
2277 {
2278 MHD_DLOG (c->daemon,
2279 _ ("This reply with response code %u cannot use reply body. "
2280 "Application defined \"Content-Length\" header violates"
2281 "HTTP specification.\n"),
2282 (unsigned) (c->rp.responseCode));
2283 }
2284#else
2285 (void) c; /* Mute compiler warning */
2286 (void) r; /* Mute compiler warning */
2287#endif
2288}
2289
2290
2302static bool
2303buffer_append (char *buf,
2304 size_t *ppos,
2305 size_t buf_size,
2306 const char *append,
2307 size_t append_size)
2308{
2309 mhd_assert (NULL != buf); /* Mute static analyzer */
2310 if (buf_size < *ppos + append_size)
2311 return false;
2312 memcpy (buf + *ppos, append, append_size);
2313 *ppos += append_size;
2314 return true;
2315}
2316
2317
2328#define buffer_append_s(buf,ppos,buf_size,str) \
2329 buffer_append (buf,ppos,buf_size,str, MHD_STATICSTR_LEN_ (str))
2330
2331
2351static bool
2353 size_t *ppos,
2354 size_t buf_size,
2355 struct MHD_Response *response,
2356 bool filter_transf_enc,
2357 bool filter_content_len,
2358 bool add_close,
2359 bool add_keep_alive)
2360{
2361 struct MHD_Response *const r = response;
2362 struct MHD_HTTP_Res_Header *hdr;
2363 size_t el_size;
2365 mhd_assert (! add_close || ! add_keep_alive);
2366
2368 filter_transf_enc = false; /* No such header */
2369 if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2370 filter_content_len = false; /* No such header */
2371 if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2372 {
2373 add_close = false; /* No such header */
2374 add_keep_alive = false; /* No such header */
2375 }
2376 else if (0 != (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE))
2377 add_close = false; /* "close" token was already set */
2378
2379 for (hdr = r->first_header; NULL != hdr; hdr = hdr->next)
2380 {
2381 size_t initial_pos = *ppos;
2382 if (MHD_HEADER_KIND != hdr->kind)
2383 continue;
2384 if (filter_transf_enc)
2385 { /* Need to filter-out "Transfer-Encoding" */
2387 hdr->header_size) &&
2389 hdr->header, hdr->header_size)) )
2390 {
2391 filter_transf_enc = false; /* There is the only one such header */
2392 continue; /* Skip "Transfer-Encoding" header */
2393 }
2394 }
2395 if (filter_content_len)
2396 { /* Need to filter-out "Content-Length" */
2398 hdr->header_size) &&
2400 hdr->header, hdr->header_size)) )
2401 {
2402 /* Reset filter flag if only one header is allowed */
2403 filter_transf_enc =
2405 continue; /* Skip "Content-Length" header */
2406 }
2407 }
2408
2409 /* Add user header */
2410 el_size = hdr->header_size + 2 + hdr->value_size + 2;
2411 if (buf_size < *ppos + el_size)
2412 return false;
2413 memcpy (buf + *ppos, hdr->header, hdr->header_size);
2414 (*ppos) += hdr->header_size;
2415 buf[(*ppos)++] = ':';
2416 buf[(*ppos)++] = ' ';
2417 if (add_close || add_keep_alive)
2418 {
2419 /* "Connection:" header must be always the first one */
2422 hdr->header_size));
2423
2424 if (add_close)
2425 {
2426 el_size += MHD_STATICSTR_LEN_ ("close, ");
2427 if (buf_size < initial_pos + el_size)
2428 return false;
2429 memcpy (buf + *ppos, "close, ",
2430 MHD_STATICSTR_LEN_ ("close, "));
2431 *ppos += MHD_STATICSTR_LEN_ ("close, ");
2432 }
2433 else
2434 {
2435 el_size += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2436 if (buf_size < initial_pos + el_size)
2437 return false;
2438 memcpy (buf + *ppos, "Keep-Alive, ",
2439 MHD_STATICSTR_LEN_ ("Keep-Alive, "));
2440 *ppos += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2441 }
2442 add_close = false;
2443 add_keep_alive = false;
2444 }
2445 if (0 != hdr->value_size)
2446 memcpy (buf + *ppos, hdr->value, hdr->value_size);
2447 *ppos += hdr->value_size;
2448 buf[(*ppos)++] = '\r';
2449 buf[(*ppos)++] = '\n';
2450 mhd_assert (initial_pos + el_size == (*ppos));
2451 }
2452 return true;
2453}
2454
2455
2464static enum MHD_Result
2466{
2467 struct MHD_Connection *const c = connection;
2468 struct MHD_Response *const r = c->rp.response;
2469 char *buf;
2470 size_t pos;
2471 size_t buf_size;
2472 size_t el_size;
2473 unsigned rcode;
2474 bool use_conn_close;
2475 bool use_conn_k_alive;
2477 mhd_assert (NULL != r);
2478
2479 /* ** Adjust response properties ** */
2480
2482
2483 mhd_assert (c->rp.props.set);
2487#ifdef UPGRADE_SUPPORT
2488 mhd_assert ((NULL == r->upgrade_handler) || \
2490#else /* ! UPGRADE_SUPPORT */
2492#endif /* ! UPGRADE_SUPPORT */
2494 mhd_assert ((! c->rp.props.send_reply_body) || \
2496#ifdef UPGRADE_SUPPORT
2497 mhd_assert (NULL == r->upgrade_handler || \
2499#endif /* UPGRADE_SUPPORT */
2500
2502
2503 rcode = (unsigned) c->rp.responseCode;
2505 {
2506 /* The closure of connection must be always indicated by header
2507 * to avoid hung connections */
2508 use_conn_close = true;
2509 use_conn_k_alive = false;
2510 }
2511 else if (MHD_CONN_USE_KEEPALIVE == c->keepalive)
2512 {
2513 use_conn_close = false;
2514 /* Add "Connection: keep-alive" if request is HTTP/1.0 or
2515 * if reply is HTTP/1.0
2516 * For HTTP/1.1 add header only if explicitly requested by app
2517 * (by response flag), as "Keep-Alive" is default for HTTP/1.1. */
2518 if ((0 != (r->flags & MHD_RF_SEND_KEEP_ALIVE_HEADER)) ||
2519 (MHD_HTTP_VER_1_0 == c->rq.http_ver) ||
2520 (0 != (r->flags & MHD_RF_HTTP_1_0_SERVER)))
2521 use_conn_k_alive = true;
2522 else
2523 use_conn_k_alive = false;
2524 }
2525 else
2526 {
2527 use_conn_close = false;
2528 use_conn_k_alive = false;
2529 }
2530
2531 /* ** Actually build the response header ** */
2532
2533 /* Get all space available */
2535 buf = c->write_buffer;
2537 buf_size = c->write_buffer_size;
2538 if (0 == buf_size)
2539 return MHD_NO;
2540 mhd_assert (NULL != buf);
2541
2542 /* * The status line * */
2543
2544 /* The HTTP version */
2545 if (! c->rp.responseIcy)
2546 { /* HTTP reply */
2547 if (0 == (r->flags & MHD_RF_HTTP_1_0_SERVER))
2548 { /* HTTP/1.1 reply */
2549 /* Use HTTP/1.1 responses for HTTP/1.0 clients.
2550 * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
2551 if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_1))
2552 return MHD_NO;
2553 }
2554 else
2555 { /* HTTP/1.0 reply */
2556 if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_0))
2557 return MHD_NO;
2558 }
2559 }
2560 else
2561 { /* ICY reply */
2562 if (! buffer_append_s (buf, &pos, buf_size, "ICY"))
2563 return MHD_NO;
2564 }
2565
2566 /* The response code */
2567 if (buf_size < pos + 5) /* space + code + space */
2568 return MHD_NO;
2569 buf[pos++] = ' ';
2570 pos += MHD_uint16_to_str ((uint16_t) rcode, buf + pos,
2571 buf_size - pos);
2572 buf[pos++] = ' ';
2573
2574 /* The reason phrase */
2575 el_size = MHD_get_reason_phrase_len_for (rcode);
2576 if (0 == el_size)
2577 {
2578 if (! buffer_append_s (buf, &pos, buf_size, "Non-Standard Status"))
2579 return MHD_NO;
2580 }
2581 else if (! buffer_append (buf, &pos, buf_size,
2583 el_size))
2584 return MHD_NO;
2585
2586 /* The linefeed */
2587 if (buf_size < pos + 2)
2588 return MHD_NO;
2589 buf[pos++] = '\r';
2590 buf[pos++] = '\n';
2591
2592 /* * The headers * */
2593
2594 /* Main automatic headers */
2595
2596 /* The "Date:" header */
2597 if ( (0 == (r->flags_auto & MHD_RAF_HAS_DATE_HDR)) &&
2599 {
2600 /* Additional byte for unused zero-termination */
2601 if (buf_size < pos + 38)
2602 return MHD_NO;
2603 if (get_date_header (buf + pos))
2604 pos += 37;
2605 }
2606 /* The "Connection:" header */
2607 mhd_assert (! use_conn_close || ! use_conn_k_alive);
2608 mhd_assert (! use_conn_k_alive || ! use_conn_close);
2609 if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2610 {
2611 if (use_conn_close)
2612 {
2613 if (! buffer_append_s (buf, &pos, buf_size,
2614 MHD_HTTP_HEADER_CONNECTION ": close\r\n"))
2615 return MHD_NO;
2616 }
2617 else if (use_conn_k_alive)
2618 {
2619 if (! buffer_append_s (buf, &pos, buf_size,
2620 MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n"))
2621 return MHD_NO;
2622 }
2623 }
2624
2625 /* User-defined headers */
2626
2627 if (! add_user_headers (buf, &pos, buf_size, r,
2628 ! c->rp.props.chunked,
2630 (0 ==
2632 use_conn_close,
2633 use_conn_k_alive))
2634 return MHD_NO;
2635
2636 /* Other automatic headers */
2637
2638 if ( (c->rp.props.use_reply_body_headers) &&
2639 (0 == (r->flags & MHD_RF_HEAD_ONLY_RESPONSE)) )
2640 {
2641 /* Body-specific headers */
2642
2643 if (c->rp.props.chunked)
2644 { /* Chunked encoding is used */
2646 { /* No chunked encoding header set by user */
2647 if (! buffer_append_s (buf, &pos, buf_size,
2649 "chunked\r\n"))
2650 return MHD_NO;
2651 }
2652 }
2653 else /* Chunked encoding is not used */
2654 {
2655 if (MHD_SIZE_UNKNOWN != r->total_size)
2656 { /* The size is known */
2657 if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2658 { /* The response does not have "Content-Length" header */
2659 if (! buffer_append_s (buf, &pos, buf_size,
2661 return MHD_NO;
2662 el_size = MHD_uint64_to_str (r->total_size, buf + pos,
2663 buf_size - pos);
2664 if (0 == el_size)
2665 return MHD_NO;
2666 pos += el_size;
2667
2668 if (buf_size < pos + 2)
2669 return MHD_NO;
2670 buf[pos++] = '\r';
2671 buf[pos++] = '\n';
2672 }
2673 }
2674 }
2675 }
2676
2677 /* * Header termination * */
2678 if (buf_size < pos + 2)
2679 return MHD_NO;
2680 buf[pos++] = '\r';
2681 buf[pos++] = '\n';
2682
2684 return MHD_YES;
2685}
2686
2687
2697static enum MHD_Result
2699{
2700 char *buf;
2701 size_t buf_size;
2702 size_t used_size;
2703 struct MHD_Connection *const c = connection;
2704 struct MHD_HTTP_Res_Header *pos;
2705
2706 mhd_assert (connection->rp.props.chunked);
2707 /* TODO: allow combining of the final footer with the last chunk,
2708 * modify the next assert. */
2710 mhd_assert (NULL != c->rp.response);
2711
2712 buf_size = connection_maximize_write_buffer (c);
2713 /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */
2714 if (buf_size < 5)
2715 return MHD_NO;
2718 mhd_assert (NULL != buf);
2719 used_size = 0;
2720 buf[used_size++] = '0';
2721 buf[used_size++] = '\r';
2722 buf[used_size++] = '\n';
2723
2724 for (pos = c->rp.response->first_header; NULL != pos; pos = pos->next)
2725 {
2726 if (MHD_FOOTER_KIND == pos->kind)
2727 {
2728 size_t new_used_size; /* resulting size with this header */
2729 /* '4' is colon, space, linefeeds */
2730 new_used_size = used_size + pos->header_size + pos->value_size + 4;
2731 if (new_used_size > buf_size)
2732 return MHD_NO;
2733 memcpy (buf + used_size, pos->header, pos->header_size);
2734 used_size += pos->header_size;
2735 buf[used_size++] = ':';
2736 buf[used_size++] = ' ';
2737 memcpy (buf + used_size, pos->value, pos->value_size);
2738 used_size += pos->value_size;
2739 buf[used_size++] = '\r';
2740 buf[used_size++] = '\n';
2741 mhd_assert (used_size == new_used_size);
2742 }
2743 }
2744 if (used_size + 2 > buf_size)
2745 return MHD_NO;
2746 buf[used_size++] = '\r';
2747 buf[used_size++] = '\n';
2748
2749 c->write_buffer_append_offset += used_size;
2751
2752 return MHD_YES;
2753}
2754
2755
2772static void
2774 unsigned int status_code,
2775 const char *message,
2776 size_t message_len,
2777 char *header_name,
2778 size_t header_name_len,
2779 char *header_value,
2780 size_t header_value_len)
2781{
2782 struct MHD_Response *response;
2783 enum MHD_Result iret;
2784
2785 mhd_assert (! connection->stop_with_error); /* Do not send error twice */
2786 if (connection->stop_with_error)
2787 { /* Should not happen */
2788 if (MHD_CONNECTION_CLOSED > connection->state)
2789 connection->state = MHD_CONNECTION_CLOSED;
2790 free (header_name);
2791 free (header_value);
2792 return;
2793 }
2794 connection->stop_with_error = true;
2795 connection->discard_request = true;
2796#ifdef HAVE_MESSAGES
2797 MHD_DLOG (connection->daemon,
2798 _ ("Error processing request (HTTP response code is %u ('%s')). " \
2799 "Closing connection.\n"),
2801 message);
2802#endif
2803 if (MHD_CONNECTION_START_REPLY < connection->state)
2804 {
2805#ifdef HAVE_MESSAGES
2806 MHD_DLOG (connection->daemon,
2807 _ ("Too late to send an error response, " \
2808 "response is being sent already.\n"),
2810 message);
2811#endif
2812 CONNECTION_CLOSE_ERROR (connection,
2813 _ ("Too late for error response."));
2814 free (header_name);
2815 free (header_value);
2816 return;
2817 }
2818 /* TODO: remove when special error queue function is implemented */
2820 if (0 != connection->read_buffer_size)
2821 {
2822 /* Read buffer is not needed anymore, discard it
2823 * to free some space for error response. */
2824 MHD_pool_deallocate (connection->pool,
2825 connection->read_buffer,
2826 connection->read_buffer_size);
2827 connection->read_buffer = NULL;
2828 connection->read_buffer_size = 0;
2829 connection->read_buffer_offset = 0;
2830 }
2831 if (NULL != connection->rp.response)
2832 {
2833 MHD_destroy_response (connection->rp.response);
2834 connection->rp.response = NULL;
2835 }
2836 response = MHD_create_response_from_buffer_static (message_len,
2837 message);
2838 if (NULL == response)
2839 {
2840#ifdef HAVE_MESSAGES
2841 MHD_DLOG (connection->daemon,
2842 _ ("Failed to create error response.\n"),
2844 message);
2845#endif
2846 /* can't even send a reply, at least close the connection */
2847 connection->state = MHD_CONNECTION_CLOSED;
2848 free (header_name);
2849 free (header_value);
2850 return;
2851 }
2852 mhd_assert ((0 == header_name_len) || (NULL != header_name));
2853 mhd_assert ((NULL == header_name) || (0 != header_name_len));
2854 mhd_assert ((0 == header_value_len) || (NULL != header_value));
2855 mhd_assert ((NULL == header_value) || (0 != header_value_len));
2856 mhd_assert ((NULL == header_name) || (NULL != header_value));
2857 mhd_assert ((NULL != header_value) || (NULL == header_name));
2858 if (NULL != header_name)
2859 {
2860 iret = MHD_add_response_entry_no_alloc_ (response,
2862 header_name, header_name_len,
2863 header_value, header_value_len);
2864 if (MHD_NO == iret)
2865 {
2866 free (header_name);
2867 free (header_value);
2868 }
2869 }
2870 else
2871 iret = MHD_YES;
2872
2873 if (MHD_NO != iret)
2874 {
2875 bool before = connection->in_access_handler;
2876
2877 /* Fake the flag for the internal call */
2878 connection->in_access_handler = true;
2879 iret = MHD_queue_response (connection,
2881 response);
2882 connection->in_access_handler = before;
2883 }
2884 MHD_destroy_response (response);
2885 if (MHD_NO == iret)
2886 {
2887 /* can't even send a reply, at least close the connection */
2888 CONNECTION_CLOSE_ERROR (connection,
2889 _ ("Closing connection " \
2890 "(failed to queue error response)."));
2891 return;
2892 }
2893 mhd_assert (NULL != connection->rp.response);
2894 /* Do not reuse this connection. */
2895 connection->keepalive = MHD_CONN_MUST_CLOSE;
2896 if (MHD_NO == build_header_response (connection))
2897 {
2898 /* No memory. Release everything. */
2899 connection->rq.version = NULL;
2900 connection->rq.method = NULL;
2901 connection->rq.url = NULL;
2902 connection->rq.url_len = 0;
2903 connection->rq.headers_received = NULL;
2904 connection->rq.headers_received_tail = NULL;
2905 connection->write_buffer = NULL;
2906 connection->write_buffer_size = 0;
2907 connection->write_buffer_send_offset = 0;
2908 connection->write_buffer_append_offset = 0;
2909 connection->read_buffer
2910 = MHD_pool_reset (connection->pool,
2911 NULL,
2912 0,
2913 0);
2914 connection->read_buffer_size = 0;
2915
2916 /* Retry with empty buffer */
2917 if (MHD_NO == build_header_response (connection))
2918 {
2919 CONNECTION_CLOSE_ERROR (connection,
2920 _ ("Closing connection " \
2921 "(failed to create error response header)."));
2922 return;
2923 }
2924 }
2926}
2927
2928
2932#define transmit_error_response_static(c, code, msg) \
2933 transmit_error_response_len (c, code, \
2934 msg, MHD_STATICSTR_LEN_ (msg), \
2935 NULL, 0, NULL, 0)
2936
2940#define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l) \
2941 transmit_error_response_len (c, code, \
2942 m, MHD_STATICSTR_LEN_ (m), \
2943 hd_n, hd_n_l, \
2944 hd_v, hd_v_l)
2945
2946
2957static bool
2959{
2961 if (! c->rq.have_chunked_upload)
2962 return 0 != c->read_buffer_offset;
2963
2964 /* Chunked upload */
2965 mhd_assert (0 != c->rq.remaining_upload_size); /* Must not be possible in MHD_CONNECTION_BODY_RECEIVING state */
2967 {
2968 /* 0 == c->rq.current_chunk_size: Waiting the chunk size (chunk header).
2969 0 != c->rq.current_chunk_size: Waiting for chunk-closing CRLF. */
2970 return false;
2971 }
2972 return 0 != c->read_buffer_offset; /* Chunk payload data in the read buffer */
2973}
2974
2975
2981{
2992
2993
2994#ifndef MHD_MAX_REASONABLE_HEADERS_SIZE_
3004# define MHD_MAX_REASONABLE_HEADERS_SIZE_ (6 * 1024)
3005#endif /* ! MHD_MAX_REASONABLE_HEADERS_SIZE_ */
3006
3007#ifndef MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
3018# define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 8000
3019#endif /* ! MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ */
3020
3021#ifndef MHD_MIN_REASONABLE_HEADERS_SIZE_
3029# define MHD_MIN_REASONABLE_HEADERS_SIZE_ 26
3030#endif /* ! MHD_MIN_REASONABLE_HEADERS_SIZE_ */
3031
3032#ifndef MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
3040# define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 40
3041#endif /* ! MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ */
3042
3043#ifndef MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
3051# define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 16
3052#endif /* ! MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ */
3053
3054#ifndef MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
3060# define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 4
3061#endif /* ! MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ */
3062
3063
3075static unsigned int
3077 enum MHD_ProcRecvDataStage stage,
3078 const char *add_element,
3079 size_t add_element_size)
3080{
3081 size_t method_size;
3082 size_t uri_size;
3083 size_t opt_headers_size;
3084 size_t host_field_line_size;
3085
3086 mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVED < c->state);
3088 mhd_assert ((0 == add_element_size) || (NULL != add_element));
3089
3091 {
3093 opt_headers_size =
3094 (size_t) ((c->read_buffer + c->read_buffer_offset)
3095 - c->rq.field_lines.start);
3096 }
3097 else
3098 opt_headers_size = c->rq.field_lines.size;
3099
3100 /* The read buffer is fully used by the request line, the field lines
3101 (headers) and internal information.
3102 The return status code works as a suggestion for the client to reduce
3103 one of the request elements. */
3104
3105 if ((MHD_PROC_RECV_BODY_CHUNKED == stage) &&
3106 (MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ < add_element_size))
3107 {
3108 /* Request could be re-tried easily with smaller chunk sizes */
3110 }
3111
3112 host_field_line_size = 0;
3113 /* The "Host:" field line is mandatory.
3114 The total size of the field lines (headers) cannot be smaller than
3115 the size of the "Host:" field line. */
3116 if ((MHD_PROC_RECV_HEADERS == stage)
3117 && (0 != add_element_size))
3118 {
3119 static const size_t header_host_key_len =
3121 const bool is_host_header =
3122 (header_host_key_len + 1 <= add_element_size)
3123 && ( (0 == add_element[header_host_key_len])
3124 || (':' == add_element[header_host_key_len]) )
3126 add_element,
3127 header_host_key_len);
3128 if (is_host_header)
3129 {
3130 const bool is_parsed = ! (
3132 (add_element_size == c->read_buffer_offset) &&
3133 (c->read_buffer == add_element) );
3134 size_t actual_element_size;
3135
3136 mhd_assert (! is_parsed || (0 == add_element[header_host_key_len]));
3137 /* The actual size should be larger due to CRLF or LF chars,
3138 however the exact termination sequence is not known here and
3139 as perfect precision is not required, to simplify the code
3140 assume the minimal length. */
3141 if (is_parsed)
3142 actual_element_size = add_element_size + 1; /* "1" for LF */
3143 else
3144 actual_element_size = add_element_size;
3145
3146 host_field_line_size = actual_element_size;
3147 mhd_assert (opt_headers_size >= actual_element_size);
3148 opt_headers_size -= actual_element_size;
3149 }
3150 }
3151 if (0 == host_field_line_size)
3152 {
3153 static const size_t host_field_name_len =
3155 size_t host_field_name_value_len;
3159 host_field_name_len,
3160 NULL,
3161 &host_field_name_value_len))
3162 {
3163 /* Calculate the minimal size of the field line: no space between
3164 colon and the field value, line terminated by LR */
3165 host_field_line_size =
3166 host_field_name_len + host_field_name_value_len + 2; /* "2" for ':' and LF */
3167
3168 /* The "Host:" field could be added by application */
3169 if (opt_headers_size >= host_field_line_size)
3170 {
3171 opt_headers_size -= host_field_line_size;
3172 /* Take into account typical space after colon and CR at the end of the line */
3173 if (opt_headers_size >= 2)
3174 opt_headers_size -= 2;
3175 }
3176 else
3177 host_field_line_size = 0; /* No "Host:" field line set by the client */
3178 }
3179 }
3180
3181 uri_size = c->rq.req_target_len;
3183 method_size = 0; /* Do not recommend shorter request method */
3184 else
3185 {
3186 mhd_assert (NULL != c->rq.method);
3187 method_size = strlen (c->rq.method);
3188 }
3189
3190 if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3191 {
3192 /* Typically the easiest way to reduce request header size is
3193 a removal of some optional headers. */
3194 if (opt_headers_size > (uri_size / 8))
3195 {
3196 if ((opt_headers_size / 2) > method_size)
3198 else
3199 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3200 }
3201 else
3202 { /* Request target is MUCH larger than headers */
3203 if ((uri_size / 16) > method_size)
3204 return MHD_HTTP_URI_TOO_LONG;
3205 else
3206 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3207 }
3208 }
3209 if ((size_t) MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3210 {
3211 /* If request target size if larger than maximum reasonable size
3212 recommend client to reduce the request target size (length). */
3213 if ((uri_size / 16) > method_size)
3214 return MHD_HTTP_URI_TOO_LONG; /* Request target is MUCH larger than headers */
3215 else
3216 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3217 }
3218
3219 /* The read buffer is too small to handle reasonably large requests */
3220
3221 if ((size_t) MHD_MIN_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3222 {
3223 /* Recommend application to retry with minimal headers */
3224 if ((opt_headers_size * 4) > uri_size)
3225 {
3226 if (opt_headers_size > method_size)
3228 else
3229 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3230 }
3231 else
3232 { /* Request target is significantly larger than headers */
3233 if (uri_size > method_size * 4)
3234 return MHD_HTTP_URI_TOO_LONG;
3235 else
3236 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3237 }
3238 }
3239 if ((size_t) MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3240 {
3241 /* Recommend application to retry with a shorter request target */
3242 if (uri_size > method_size * 4)
3243 return MHD_HTTP_URI_TOO_LONG;
3244 else
3245 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3246 }
3247
3248 if ((size_t) MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ < method_size)
3249 {
3250 /* The request target (URI) and headers are (reasonably) very small.
3251 Some non-standard long request method is used. */
3252 /* The last resort response as it means "the method is not supported
3253 by the server for any URI". */
3255 }
3256
3257 /* The almost impossible situation: all elements are small, but cannot
3258 fit the buffer. The application set the buffer size to
3259 critically low value? */
3260
3261 if ((1 < opt_headers_size) || (1 < uri_size))
3262 {
3263 if (opt_headers_size >= uri_size)
3265 else
3266 return MHD_HTTP_URI_TOO_LONG;
3267 }
3268
3269 /* Nothing to reduce in the request.
3270 Reply with some status. */
3271 if (0 != host_field_line_size)
3273
3274 return MHD_HTTP_URI_TOO_LONG;
3275}
3276
3277
3288static void
3290 const char *add_header,
3291 size_t add_header_size)
3292{
3293 unsigned int err_code;
3294
3295 err_code = get_no_space_err_status_code (c,
3297 add_header,
3298 add_header_size);
3300 err_code,
3302}
3303
3304
3305#ifdef COOKIE_SUPPORT
3311static void
3312handle_req_cookie_no_space (struct MHD_Connection *c)
3313{
3314 unsigned int err_code;
3315
3316 err_code = get_no_space_err_status_code (c,
3318 NULL,
3319 0);
3321 err_code,
3323}
3324
3325
3326#endif /* COOKIE_SUPPORT */
3327
3328
3339static void
3341 const char *chunk_size_line,
3342 size_t chunk_size_line_size)
3343{
3344 unsigned int err_code;
3345
3346 if (NULL != chunk_size_line)
3347 {
3348 const char *semicol;
3349 /* Check for chunk extension */
3350 semicol = memchr (chunk_size_line, ';', chunk_size_line_size);
3351 if (NULL != semicol)
3352 { /* Chunk extension present. It could be removed without any loss of the
3353 details of the request. */
3357 }
3358 }
3359 err_code = get_no_space_err_status_code (c,
3361 chunk_size_line,
3362 chunk_size_line_size);
3364 err_code,
3366}
3367
3368
3379static void
3381 const char *add_footer,
3382 size_t add_footer_size)
3383{
3384 (void) add_footer; (void) add_footer_size; /* Unused */
3386
3387 /* Footers should be optional */
3391}
3392
3393
3404static void
3406 enum MHD_ProcRecvDataStage stage)
3407{
3408 mhd_assert (MHD_PROC_RECV_INIT <= stage);
3411 mhd_assert ((MHD_PROC_RECV_INIT != stage) || \
3412 (MHD_CONNECTION_INIT == c->state));
3413 mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \
3415 mhd_assert ((MHD_PROC_RECV_URI != stage) || \
3417 mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \
3419 mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \
3421 mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */
3422 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3424 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3426 mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \
3428 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3429 (! c->rq.have_chunked_upload));
3430 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3431 (c->rq.have_chunked_upload));
3432 switch (stage)
3433 {
3434 case MHD_PROC_RECV_INIT:
3436 /* Some data has been received, but it is not clear yet whether
3437 * the received data is an valid HTTP request */
3439 _ ("No space left in the read buffer when " \
3440 "receiving the initial part of " \
3441 "the request line."));
3442 return;
3443 case MHD_PROC_RECV_URI:
3445 /* Some data has been received, but the request line is incomplete */
3448 /* A quick simple check whether the incomplete line looks
3449 * like an HTTP request */
3450 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
3452 {
3456 return;
3457 }
3459 _ ("No space left in the read buffer when " \
3460 "receiving the URI in " \
3461 "the request line. " \
3462 "The request uses non-standard HTTP request " \
3463 "method token."));
3464 return;
3467 return;
3470 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3473 {
3474 /* The connection must not be in MHD_EVENT_LOOP_INFO_READ state
3475 when external polling is used and some data left unprocessed. */
3477 /* failed to grow the read buffer, and the
3478 client which is supposed to handle the
3479 received data in a *blocking* fashion
3480 (in this mode) did not handle the data as
3481 it was supposed to!
3482 => we would either have to do busy-waiting
3483 (on the client, which would likely fail),
3484 or if we do nothing, we would just timeout
3485 on the connection (if a timeout is even
3486 set!).
3487 Solution: we kill the connection with an error */
3491 }
3492 else
3493 {
3494 if (MHD_PROC_RECV_BODY_NORMAL == stage)
3495 {
3496 /* A header probably has been added to a suspended connection and
3497 it took precisely all the space in the buffer.
3498 Very low probability. */
3501 }
3502 else
3503 {
3506 { /* Receiving content of the chunk */
3507 /* A header probably has been added to a suspended connection and
3508 it took precisely all the space in the buffer.
3509 Very low probability. */
3511 }
3512 else
3513 {
3514 if (0 != c->rq.current_chunk_size)
3515 { /* Waiting for chunk-closing CRLF */
3516 /* Not really possible as some payload should be
3517 processed and the space used by payload should be available. */
3519 }
3520 else
3521 { /* Reading the line with the chunk size */
3523 c->read_buffer,
3525 }
3526 }
3527 }
3528 }
3529 return;
3532 return;
3533 /* The next cases should not be possible */
3535 default:
3536 break;
3537 }
3538 mhd_assert (0);
3539}
3540
3541
3555static bool
3557{
3561 bool rbuff_grow_desired;
3565 bool rbuff_grow_required;
3566
3569
3570 rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size);
3571 if (rbuff_grow_required)
3572 rbuff_grow_desired = true;
3573 else
3574 {
3575 rbuff_grow_desired = (c->read_buffer_offset + c->daemon->pool_increment >
3576 c->read_buffer_size);
3577
3578 if ((rbuff_grow_desired) &&
3580 {
3581 if (! c->rq.have_chunked_upload)
3582 {
3584 /* Do not grow read buffer more than necessary to process the current
3585 request. */
3586 rbuff_grow_desired =
3588 }
3589 else
3590 {
3592 if (0 == c->rq.current_chunk_size)
3593 rbuff_grow_desired = /* Reading value of the next chunk size */
3595 c->read_buffer_size);
3596 else
3597 {
3598 const uint64_t cur_chunk_left =
3600 /* Do not grow read buffer more than necessary to process the current
3601 chunk with terminating CRLF. */
3603 rbuff_grow_desired =
3604 ((cur_chunk_left + 2) > (uint64_t) (c->read_buffer_size));
3605 }
3606 }
3607 }
3608 }
3609
3610 if (! rbuff_grow_desired)
3611 return true; /* No need to increase the buffer */
3612
3613 if (try_grow_read_buffer (c, rbuff_grow_required))
3614 return true; /* Buffer increase succeed */
3615
3616 if (! rbuff_grow_required)
3617 return true; /* Can continue without buffer increase */
3618
3619 /* Failed to increase the read buffer size, but need to read the data
3620 from the network.
3621 No more space left in the buffer, no more space to increase the buffer. */
3622
3623 /* 'PROCESS_READ' event state flag must be set only if the last application
3624 callback has processed some data. If any data is processed then some
3625 space in the read buffer must be available. */
3627
3628 if ((! MHD_D_IS_USING_THREADS_ (c->daemon))
3631 {
3632 /* The application is handling processing cycles.
3633 The data could be processed later. */
3635 return true;
3636 }
3637 else
3638 {
3639 enum MHD_ProcRecvDataStage stage;
3640
3641 switch (c->state)
3642 {
3644 stage = MHD_PROC_RECV_INIT;
3645 break;
3648 stage = MHD_PROC_RECV_METHOD;
3649 else if (0 == c->rq.req_target_len)
3650 stage = MHD_PROC_RECV_URI;
3651 else
3652 stage = MHD_PROC_RECV_HTTPVER;
3653 break;
3655 stage = MHD_PROC_RECV_HEADERS;
3656 break;
3658 stage = c->rq.have_chunked_upload ?
3660 break;
3662 stage = MHD_PROC_RECV_FOOTERS;
3663 break;
3682#ifdef UPGRADE_SUPPORT
3683 case MHD_CONNECTION_UPGRADE:
3684#endif
3685 default:
3687 mhd_assert (0);
3688 }
3689
3690 handle_recv_no_space (c, stage);
3691 }
3692 return false;
3693}
3694
3695
3704static void
3706{
3707 /* Do not update states of suspended connection */
3708 if (connection->suspended)
3709 return; /* States will be updated after resume. */
3710#ifdef HTTPS_SUPPORT
3711 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
3712 { /* HTTPS connection. */
3713 switch (connection->tls_state)
3714 {
3715 case MHD_TLS_CONN_INIT:
3717 return;
3720 if (0 == gnutls_record_get_direction (connection->tls_session))
3722 else
3724 return;
3726 break; /* Do normal processing */
3730 return;
3731 case MHD_TLS_CONN_TLS_CLOSING: /* Not implemented yet */
3732 case MHD_TLS_CONN_TLS_CLOSED: /* Not implemented yet */
3734 case MHD_TLS_CONN_NO_TLS: /* Not possible */
3735 default:
3736 MHD_PANIC (_ ("Invalid TLS state value.\n"));
3737 }
3738 }
3739#endif /* HTTPS_SUPPORT */
3740 while (1)
3741 {
3742#if DEBUG_STATES
3743 MHD_DLOG (connection->daemon,
3744 _ ("In function %s handling connection at state: %s\n"),
3745 MHD_FUNC_,
3746 MHD_state_to_string (connection->state));
3747#endif
3748 switch (connection->state)
3749 {
3753 break;
3755 mhd_assert (0);
3756 break;
3759 break;
3762 mhd_assert (0);
3763 break;
3766 break;
3768 if ((connection->rq.some_payload_processed) &&
3770 {
3771 /* Some data was processed, the buffer must have some free space */
3772 mhd_assert (connection->read_buffer_offset < \
3773 connection->read_buffer_size);
3774 if (! connection->rq.have_chunked_upload)
3775 {
3776 /* Not a chunked upload. Do not read more than necessary to
3777 process the current request. */
3778 if (connection->rq.remaining_upload_size >=
3779 connection->read_buffer_offset)
3781 else
3783 }
3784 else
3785 {
3786 /* Chunked upload. The size of the current request is unknown.
3787 Continue reading as the space in the read buffer is available. */
3789 }
3790 }
3791 else
3793 break;
3795 mhd_assert (0);
3796 break;
3799 break;
3801 mhd_assert (0);
3802 break;
3805 break;
3807 mhd_assert (0);
3808 break;
3810 /* headers in buffer, keep writing */
3812 break;
3814 mhd_assert (0);
3815 break;
3818 break;
3821 break;
3824 break;
3827 break;
3829 mhd_assert (0);
3830 break;
3833 break;
3835 mhd_assert (0);
3836 break;
3839 return; /* do nothing, not even reading */
3840#ifdef UPGRADE_SUPPORT
3841 case MHD_CONNECTION_UPGRADE:
3842 mhd_assert (0);
3843 break;
3844#endif /* UPGRADE_SUPPORT */
3845 default:
3846 mhd_assert (0);
3847 }
3848
3849 if (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info))
3850 {
3851 /* Check whether the space is available to receive data */
3852 if (! check_and_grow_read_buffer_space (connection))
3853 {
3854 mhd_assert (connection->discard_request);
3855 continue;
3856 }
3857 }
3858 break; /* Everything was processed. */
3859 }
3860}
3861
3862
3875static enum MHD_Result
3877 const char *key,
3878 size_t key_size,
3879 const char *value,
3880 size_t value_size,
3881 enum MHD_ValueKind kind)
3882{
3883 struct MHD_Connection *connection = (struct MHD_Connection *) cls;
3884 if (MHD_NO ==
3885 MHD_set_connection_value_n (connection,
3886 kind,
3887 key,
3888 key_size,
3889 value,
3890 value_size))
3891 {
3892#ifdef HAVE_MESSAGES
3893 MHD_DLOG (connection->daemon,
3894 _ ("Not enough memory in pool to allocate header record!\n"));
3895#endif
3899 return MHD_NO;
3900 }
3901 return MHD_YES;
3902}
3903
3904
3905#ifdef COOKIE_SUPPORT
3906
3910enum _MHD_ParseCookie
3911{
3912 MHD_PARSE_COOKIE_OK = MHD_YES,
3913 MHD_PARSE_COOKIE_OK_LAX = 2,
3914 MHD_PARSE_COOKIE_MALFORMED = -1,
3915 MHD_PARSE_COOKIE_NO_MEMORY = MHD_NO
3916};
3917
3918
3931static enum _MHD_ParseCookie
3932parse_cookies_string (char *str,
3933 const size_t str_len,
3934 struct MHD_Connection *connection)
3935{
3936 size_t i;
3937 bool non_strict;
3938 /* Skip extra whitespaces and empty cookies */
3939 const bool allow_wsp_empty = (0 >= connection->daemon->client_discipline);
3940 /* Allow whitespaces around '=' character */
3941 const bool wsp_around_eq = (-3 >= connection->daemon->client_discipline);
3942 /* Allow whitespaces in quoted cookie value */
3943 const bool wsp_in_quoted = (-2 >= connection->daemon->client_discipline);
3944 /* Allow tab as space after semicolon between cookies */
3945 const bool tab_as_sp = (0 >= connection->daemon->client_discipline);
3946 /* Allow no space after semicolon between cookies */
3947 const bool allow_no_space = (0 >= connection->daemon->client_discipline);
3948
3949 non_strict = false;
3950 i = 0;
3951 while (i < str_len)
3952 {
3953 size_t name_start;
3954 size_t name_len;
3955 size_t value_start;
3956 size_t value_len;
3957 bool val_quoted;
3958 /* Skip any whitespaces and empty cookies */
3959 while (' ' == str[i] || '\t' == str[i] || ';' == str[i])
3960 {
3961 if (! allow_wsp_empty)
3962 return MHD_PARSE_COOKIE_MALFORMED;
3963 non_strict = true;
3964 i++;
3965 if (i == str_len)
3966 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
3967 }
3968 /* 'i' must point to the first char of cookie-name */
3969 name_start = i;
3970 /* Find the end of the cookie-name */
3971 do
3972 {
3973 const char l = str[i];
3974 if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) ||
3975 (';' == l) || (0 == l))
3976 break;
3977 } while (str_len > ++i);
3978 name_len = i - name_start;
3979 /* Skip any whitespaces */
3980 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
3981 {
3982 if (! wsp_around_eq)
3983 return MHD_PARSE_COOKIE_MALFORMED;
3984 non_strict = true;
3985 i++;
3986 }
3987 if ((str_len == i) || ('=' != str[i]) || (0 == name_len))
3988 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */
3989 /* 'i' must point to the '=' char */
3990 mhd_assert ('=' == str[i]);
3991 i++;
3992 /* Skip any whitespaces */
3993 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
3994 {
3995 if (! wsp_around_eq)
3996 return MHD_PARSE_COOKIE_MALFORMED;
3997 non_strict = true;
3998 i++;
3999 }
4000 /* 'i' must point to the first char of cookie-value */
4001 if (str_len == i)
4002 {
4003 value_start = 0;
4004 value_len = 0;
4005#ifdef _DEBUG
4006 val_quoted = false; /* This assignment used in assert */
4007#endif
4008 }
4009 else
4010 {
4011 bool valid_cookie;
4012 val_quoted = ('"' == str[i]);
4013 if (val_quoted)
4014 i++;
4015 value_start = i;
4016 /* Find the end of the cookie-value */
4017 while (str_len > i)
4018 {
4019 const char l = str[i];
4020 if ((';' == l) || ('"' == l) || (',' == l) || (';' == l) ||
4021 ('\\' == l) || (0 == l))
4022 break;
4023 if ((' ' == l) || ('\t' == l))
4024 {
4025 if (! val_quoted)
4026 break;
4027 if (! wsp_in_quoted)
4028 return MHD_PARSE_COOKIE_MALFORMED;
4029 non_strict = true;
4030 }
4031 i++;
4032 }
4033 value_len = i - value_start;
4034 if (val_quoted)
4035 {
4036 if ((str_len == i) || ('"' != str[i]))
4037 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */
4038 i++;
4039 }
4040 /* Skip any whitespaces */
4041 if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i])))
4042 {
4043 do
4044 {
4045 i++;
4046 } while (str_len > i && (' ' == str[i] || '\t' == str[i]));
4047 /* Whitespace at the end? */
4048 if (str_len > i)
4049 {
4050 if (! allow_wsp_empty)
4051 return MHD_PARSE_COOKIE_MALFORMED;
4052 non_strict = true;
4053 }
4054 }
4055 if (str_len == i)
4056 valid_cookie = true;
4057 else if (';' == str[i])
4058 valid_cookie = true;
4059 else
4060 valid_cookie = false;
4061
4062 if (! valid_cookie)
4063 return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */
4064 }
4065 mhd_assert (0 != name_len);
4066 str[name_start + name_len] = 0; /* Zero-terminate the name */
4067 if (0 != value_len)
4068 {
4069 mhd_assert (value_start + value_len <= str_len);
4070 str[value_start + value_len] = 0; /* Zero-terminate the value */
4071 if (MHD_NO ==
4074 str + name_start,
4075 name_len,
4076 str + value_start,
4077 value_len))
4078 return MHD_PARSE_COOKIE_NO_MEMORY;
4079 }
4080 else
4081 {
4082 if (MHD_NO ==
4085 str + name_start,
4086 name_len,
4087 "",
4088 0))
4089 return MHD_PARSE_COOKIE_NO_MEMORY;
4090 }
4091 if (str_len > i)
4092 {
4093 mhd_assert (0 == str[i] || ';' == str[i]);
4094 mhd_assert (! val_quoted || ';' == str[i]);
4095 mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len);
4096 i++;
4097 if (str_len == i)
4098 { /* No next cookie after semicolon */
4099 if (! allow_wsp_empty)
4100 return MHD_PARSE_COOKIE_MALFORMED;
4101 non_strict = true;
4102 }
4103 else if (' ' != str[i])
4104 {/* No space after semicolon */
4105 if (('\t' == str[i]) && tab_as_sp)
4106 i++;
4107 else if (! allow_no_space)
4108 return MHD_PARSE_COOKIE_MALFORMED;
4109 non_strict = true;
4110 }
4111 else
4112 {
4113 i++;
4114 if (str_len == i)
4115 {
4116 if (! allow_wsp_empty)
4117 return MHD_PARSE_COOKIE_MALFORMED;
4118 non_strict = true;
4119 }
4120 }
4121 }
4122 }
4123 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
4124}
4125
4126
4133static enum _MHD_ParseCookie
4134parse_cookie_header (struct MHD_Connection *connection)
4135{
4136 const char *hdr;
4137 size_t hdr_len;
4138 char *cpy;
4139 size_t i;
4140 enum _MHD_ParseCookie parse_res;
4141 struct MHD_HTTP_Req_Header *const saved_tail =
4142 connection->rq.headers_received_tail;
4143 const bool allow_partially_correct_cookie =
4144 (1 >= connection->daemon->client_discipline);
4145
4146 if (MHD_NO ==
4152 &hdr,
4153 &hdr_len))
4154 return MHD_PARSE_COOKIE_OK;
4155 if (0 == hdr_len)
4156 return MHD_PARSE_COOKIE_OK;
4157
4158 cpy = MHD_connection_alloc_memory_ (connection,
4159 hdr_len + 1);
4160 if (NULL == cpy)
4161 parse_res = MHD_PARSE_COOKIE_NO_MEMORY;
4162 else
4163 {
4164 memcpy (cpy,
4165 hdr,
4166 hdr_len);
4167 cpy[hdr_len] = '\0';
4168
4169 i = 0;
4170 /* Skip all initial whitespaces */
4171 while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i]))
4172 i++;
4173
4174 parse_res = parse_cookies_string (cpy + i, hdr_len - i, connection);
4175 }
4176
4177 switch (parse_res)
4178 {
4179 case MHD_PARSE_COOKIE_OK:
4180 break;
4181 case MHD_PARSE_COOKIE_OK_LAX:
4182#ifdef HAVE_MESSAGES
4183 if (saved_tail != connection->rq.headers_received_tail)
4184 MHD_DLOG (connection->daemon,
4185 _ ("The Cookie header has been parsed, but it is not fully "
4186 "compliant with the standard.\n"));
4187#endif /* HAVE_MESSAGES */
4188 break;
4189 case MHD_PARSE_COOKIE_MALFORMED:
4190#ifdef HAVE_MESSAGES
4191 if (saved_tail != connection->rq.headers_received_tail)
4192 {
4193 if (allow_partially_correct_cookie)
4194 MHD_DLOG (connection->daemon,
4195 _ ("The Cookie header has been only partially parsed as it "
4196 "contains malformed data.\n"));
4197 else
4198 {
4199 /* Remove extracted values from partially broken cookie */
4200 /* Memory remains allocated until the end of the request processing */
4201 connection->rq.headers_received_tail = saved_tail;
4202 saved_tail->next = NULL;
4203 MHD_DLOG (connection->daemon,
4204 _ ("The Cookie header has been ignored as it contains "
4205 "malformed data.\n"));
4206 }
4207 }
4208 else
4209 MHD_DLOG (connection->daemon,
4210 _ ("The Cookie header has malformed data.\n"));
4211#endif /* HAVE_MESSAGES */
4212 break;
4213 case MHD_PARSE_COOKIE_NO_MEMORY:
4214#ifdef HAVE_MESSAGES
4215 MHD_DLOG (connection->daemon,
4216 _ ("Not enough memory in the connection pool to "
4217 "parse client cookies!\n"));
4218#endif /* HAVE_MESSAGES */
4219 break;
4220 default:
4221 mhd_assert (0);
4222 break;
4223 }
4224#ifndef HAVE_MESSAGES
4225 (void) saved_tail; /* Mute compiler warning */
4226#endif /* ! HAVE_MESSAGES */
4227
4228 return parse_res;
4229}
4230
4231
4232#endif /* COOKIE_SUPPORT */
4233
4234
4238#define HTTP_VER_LEN (MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1))
4239
4249static bool
4251 const char *http_string,
4252 size_t len)
4253{
4254 const char *const h = http_string;
4255 mhd_assert (NULL != http_string);
4256
4257 /* String must start with 'HTTP/d.d', case-sensetive match.
4258 * See https://www.rfc-editor.org/rfc/rfc9112#name-http-version */
4259 if ((HTTP_VER_LEN != len) ||
4260 ('H' != h[0]) || ('T' != h[1]) || ('T' != h[2]) || ('P' != h[3]) ||
4261 ('/' != h[4])
4262 || ('.' != h[6]) ||
4263 (('0' > h[5]) || ('9' < h[5])) ||
4264 (('0' > h[7]) || ('9' < h[7])))
4265 {
4266 connection->rq.http_ver = MHD_HTTP_VER_INVALID;
4270 return false;
4271 }
4272 if (1 == h[5] - '0')
4273 {
4274 /* HTTP/1.x */
4275 if (1 == h[7] - '0')
4276 connection->rq.http_ver = MHD_HTTP_VER_1_1;
4277 else if (0 == h[7] - '0')
4278 connection->rq.http_ver = MHD_HTTP_VER_1_0;
4279 else
4280 connection->rq.http_ver = MHD_HTTP_VER_1_2__1_9;
4281
4282 return true;
4283 }
4284
4285 if (0 == h[5] - '0')
4286 {
4287 /* Too old major version */
4288 connection->rq.http_ver = MHD_HTTP_VER_TOO_OLD;
4292 return false;
4293 }
4294
4295 connection->rq.http_ver = MHD_HTTP_VER_FUTURE;
4299 return false;
4300}
4301
4302
4310static void
4312 const char *method,
4313 size_t len)
4314{
4315 const char *const m = method;
4316 mhd_assert (NULL != m);
4317 mhd_assert (0 != len);
4318
4319 if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET) == len) &&
4320 (0 == memcmp (m, MHD_HTTP_METHOD_GET, len)))
4321 connection->rq.http_mthd = MHD_HTTP_MTHD_GET;
4322 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_HEAD) == len) &&
4323 (0 == memcmp (m, MHD_HTTP_METHOD_HEAD, len)))
4324 connection->rq.http_mthd = MHD_HTTP_MTHD_HEAD;
4325 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_POST) == len) &&
4326 (0 == memcmp (m, MHD_HTTP_METHOD_POST, len)))
4327 connection->rq.http_mthd = MHD_HTTP_MTHD_POST;
4328 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_PUT) == len) &&
4329 (0 == memcmp (m, MHD_HTTP_METHOD_PUT, len)))
4330 connection->rq.http_mthd = MHD_HTTP_MTHD_PUT;
4331 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_DELETE) == len) &&
4332 (0 == memcmp (m, MHD_HTTP_METHOD_DELETE, len)))
4333 connection->rq.http_mthd = MHD_HTTP_MTHD_DELETE;
4334 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_CONNECT) == len) &&
4335 (0 == memcmp (m, MHD_HTTP_METHOD_CONNECT, len)))
4336 connection->rq.http_mthd = MHD_HTTP_MTHD_CONNECT;
4337 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_OPTIONS) == len) &&
4338 (0 == memcmp (m, MHD_HTTP_METHOD_OPTIONS, len)))
4339 connection->rq.http_mthd = MHD_HTTP_MTHD_OPTIONS;
4340 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_TRACE) == len) &&
4341 (0 == memcmp (m, MHD_HTTP_METHOD_TRACE, len)))
4342 connection->rq.http_mthd = MHD_HTTP_MTHD_TRACE;
4343 else
4344 connection->rq.http_mthd = MHD_HTTP_MTHD_OTHER;
4345}
4346
4347
4355static void
4357{
4358 struct MHD_Daemon *daemon = connection->daemon;
4359 size_t processed;
4360
4361 if (NULL != connection->rp.response)
4362 return; /* already queued a response */
4363 processed = 0;
4364 connection->rq.client_aware = true;
4365 connection->in_access_handler = true;
4366 if (MHD_NO ==
4367 daemon->default_handler (daemon->default_handler_cls,
4368 connection,
4369 connection->rq.url,
4370 connection->rq.method,
4371 connection->rq.version,
4372 NULL,
4373 &processed,
4374 &connection->rq.client_context))
4375 {
4376 connection->in_access_handler = false;
4377 /* serious internal error, close connection */
4378 CONNECTION_CLOSE_ERROR (connection,
4379 _ ("Application reported internal error, " \
4380 "closing connection."));
4381 return;
4382 }
4383 connection->in_access_handler = false;
4384}
4385
4386
4394static void
4396{
4397 struct MHD_Daemon *daemon = connection->daemon;
4398 size_t available;
4399 bool instant_retry;
4400 char *buffer_head;
4401 const int discp_lvl = daemon->client_discipline;
4402 /* Treat bare LF as the end of the line.
4403 RFC 9112, section 2.2-3
4404 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
4405 Bare LF is processed as end of the line or rejected as broken request. */
4406 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
4407 /* Allow "Bad WhiteSpace" in chunk extension.
4408 RFC 9112, Section 7.1.1, Paragraph 2 */
4409 const bool allow_bws = (2 < discp_lvl);
4410
4411 mhd_assert (NULL == connection->rp.response);
4412
4413 buffer_head = connection->read_buffer;
4414 available = connection->read_buffer_offset;
4415 do
4416 {
4417 size_t to_be_processed;
4418 size_t left_unprocessed;
4419 size_t processed_size;
4420
4421 instant_retry = false;
4422 if (connection->rq.have_chunked_upload)
4423 {
4425 if ( (connection->rq.current_chunk_offset ==
4426 connection->rq.current_chunk_size) &&
4427 (0 != connection->rq.current_chunk_size) )
4428 {
4429 size_t i;
4430 mhd_assert (0 != available);
4431 /* skip new line at the *end* of a chunk */
4432 i = 0;
4433 if ( (2 <= available) &&
4434 ('\r' == buffer_head[0]) &&
4435 ('\n' == buffer_head[1]) )
4436 i += 2; /* skip CRLF */
4437 else if (bare_lf_as_crlf && ('\n' == buffer_head[0]))
4438 i++; /* skip bare LF */
4439 else if (2 > available)
4440 break; /* need more upload data */
4441 if (0 == i)
4442 {
4443 /* malformed encoding */
4447 return;
4448 }
4449 available -= i;
4450 buffer_head += i;
4451 connection->rq.current_chunk_offset = 0;
4452 connection->rq.current_chunk_size = 0;
4453 if (0 == available)
4454 break;
4455 }
4456 if (0 != connection->rq.current_chunk_size)
4457 {
4458 uint64_t cur_chunk_left;
4459 mhd_assert (connection->rq.current_chunk_offset < \
4460 connection->rq.current_chunk_size);
4461 /* we are in the middle of a chunk, give
4462 as much as possible to the client (without
4463 crossing chunk boundaries) */
4464 cur_chunk_left
4465 = connection->rq.current_chunk_size
4466 - connection->rq.current_chunk_offset;
4467 if (cur_chunk_left > available)
4468 to_be_processed = available;
4469 else
4470 { /* cur_chunk_left <= (size_t)available */
4471 to_be_processed = (size_t) cur_chunk_left;
4472 if (available > to_be_processed)
4473 instant_retry = true;
4474 }
4475 }
4476 else
4477 { /* Need the parse the chunk size line */
4479 size_t num_dig;
4480 uint64_t chunk_size;
4481 bool broken;
4482 bool overflow;
4483
4484 mhd_assert (0 != available);
4485
4486 overflow = false;
4487 chunk_size = 0; /* Mute possible compiler warning.
4488 The real value will be set later. */
4489
4490 num_dig = MHD_strx_to_uint64_n_ (buffer_head,
4491 available,
4492 &chunk_size);
4493 mhd_assert (num_dig <= available);
4494 if (num_dig == available)
4495 continue; /* Need line delimiter */
4496
4497 broken = (0 == num_dig);
4498 if (broken)
4499 /* Check whether result is invalid due to uint64_t overflow */
4500 overflow = ((('0' <= buffer_head[0]) && ('9' >= buffer_head[0])) ||
4501 (('A' <= buffer_head[0]) && ('F' >= buffer_head[0])) ||
4502 (('a' <= buffer_head[0]) && ('f' >= buffer_head[0])));
4503 else
4504 {
4509 size_t chunk_size_line_len;
4510
4511 chunk_size_line_len = 0;
4512 if ((';' == buffer_head[num_dig]) ||
4513 (allow_bws &&
4514 ((' ' == buffer_head[num_dig]) ||
4515 ('\t' == buffer_head[num_dig]))))
4516 { /* Chunk extension */
4517 size_t i;
4518
4519 /* Skip bad whitespaces (if any) */
4520 for (i = num_dig; i < available; ++i)
4521 {
4522 if ((' ' != buffer_head[i]) && ('\t' != buffer_head[i]))
4523 break;
4524 }
4525 if (i == available)
4526 break; /* need more data */
4527 if (';' == buffer_head[i])
4528 {
4529 for (++i; i < available; ++i)
4530 {
4531 if ('\n' == buffer_head[i])
4532 break;
4533 }
4534 if (i == available)
4535 break; /* need more data */
4536 mhd_assert (i > num_dig);
4537 mhd_assert (1 <= i);
4538 /* Found LF position */
4539 if (bare_lf_as_crlf)
4540 chunk_size_line_len = i; /* Don't care about CR before LF */
4541 else if ('\r' == buffer_head[i - 1])
4542 chunk_size_line_len = i;
4543 }
4544 else
4545 { /* No ';' after "bad whitespace" */
4546 mhd_assert (allow_bws);
4547 mhd_assert (0 == chunk_size_line_len);
4548 }
4549 }
4550 else
4551 {
4552 mhd_assert (available >= num_dig);
4553 if ((2 <= (available - num_dig)) &&
4554 ('\r' == buffer_head[num_dig]) &&
4555 ('\n' == buffer_head[num_dig + 1]))
4556 chunk_size_line_len = num_dig + 2;
4557 else if (bare_lf_as_crlf &&
4558 ('\n' == buffer_head[num_dig]))
4559 chunk_size_line_len = num_dig + 1;
4560 else if (2 > (available - num_dig))
4561 break; /* need more data */
4562 }
4563
4564 if (0 != chunk_size_line_len)
4565 { /* Valid termination of the chunk size line */
4566 mhd_assert (chunk_size_line_len <= available);
4567 /* Start reading payload data of the chunk */
4568 connection->rq.current_chunk_offset = 0;
4569 connection->rq.current_chunk_size = chunk_size;
4570
4571 available -= chunk_size_line_len;
4572 buffer_head += chunk_size_line_len;
4573
4574 if (0 == chunk_size)
4575 { /* The final (termination) chunk */
4576 connection->rq.remaining_upload_size = 0;
4577 break;
4578 }
4579 if (available > 0)
4580 instant_retry = true;
4581 continue;
4582 }
4583 /* Invalid chunk size line */
4584 }
4585
4586 if (! overflow)
4590 else
4594 return;
4595 }
4596 }
4597 else
4598 {
4599 /* no chunked encoding, give all to the client */
4601 mhd_assert (0 != connection->rq.remaining_upload_size);
4602 if (connection->rq.remaining_upload_size < available)
4603 to_be_processed = (size_t) connection->rq.remaining_upload_size;
4604 else
4605 to_be_processed = available;
4606 }
4607 left_unprocessed = to_be_processed;
4608 connection->rq.client_aware = true;
4609 connection->in_access_handler = true;
4610 if (MHD_NO ==
4611 daemon->default_handler (daemon->default_handler_cls,
4612 connection,
4613 connection->rq.url,
4614 connection->rq.method,
4615 connection->rq.version,
4616 buffer_head,
4617 &left_unprocessed,
4618 &connection->rq.client_context))
4619 {
4620 connection->in_access_handler = false;
4621 /* serious internal error, close connection */
4622 CONNECTION_CLOSE_ERROR (connection,
4623 _ ("Application reported internal error, " \
4624 "closing connection."));
4625 return;
4626 }
4627 connection->in_access_handler = false;
4628
4629 if (left_unprocessed > to_be_processed)
4630 MHD_PANIC (_ ("libmicrohttpd API violation.\n"));
4631
4632 connection->rq.some_payload_processed =
4633 (left_unprocessed != to_be_processed);
4634
4635 if (0 != left_unprocessed)
4636 {
4637 instant_retry = false; /* client did not process everything */
4638#ifdef HAVE_MESSAGES
4639 if ((! connection->rq.some_payload_processed) &&
4640 (! connection->suspended))
4641 {
4642 /* client did not process any upload data, complain if
4643 the setup was incorrect, which may prevent us from
4644 handling the rest of the request */
4645 if (MHD_D_IS_USING_THREADS_ (daemon))
4646 MHD_DLOG (daemon,
4647 _ ("WARNING: Access Handler Callback has not processed " \
4648 "any upload data and connection is not suspended. " \
4649 "This may result in hung connection.\n"));
4650 }
4651#endif /* HAVE_MESSAGES */
4652 }
4653 processed_size = to_be_processed - left_unprocessed;
4654 /* dh left "processed" bytes in buffer for next time... */
4655 buffer_head += processed_size;
4656 available -= processed_size;
4657 if (! connection->rq.have_chunked_upload)
4658 {
4660 connection->rq.remaining_upload_size -= processed_size;
4661 }
4662 else
4663 {
4665 connection->rq.current_chunk_offset += processed_size;
4666 }
4667 } while (instant_retry);
4668 /* TODO: zero out reused memory region */
4669 if ( (available > 0) &&
4670 (buffer_head != connection->read_buffer) )
4671 memmove (connection->read_buffer,
4672 buffer_head,
4673 available);
4674 else
4675 mhd_assert ((0 == available) || \
4676 (connection->read_buffer_offset == available));
4677 connection->read_buffer_offset = available;
4678}
4679
4680
4689static enum MHD_Result
4691 enum MHD_CONNECTION_STATE next_state)
4692{
4693 if ( (connection->write_buffer_append_offset !=
4694 connection->write_buffer_send_offset)
4695 /* || data_in_tls_buffers == true */
4696 )
4697 return MHD_NO;
4698 connection->write_buffer_append_offset = 0;
4699 connection->write_buffer_send_offset = 0;
4700 connection->state = next_state;
4701 return MHD_YES;
4702}
4703
4704
4712static void
4714{
4715 const char *clen;
4716 const char *enc;
4717 size_t val_len;
4718
4719#ifdef COOKIE_SUPPORT
4720 if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (connection))
4721 {
4722 handle_req_cookie_no_space (connection);
4723 return;
4724 }
4725#endif /* COOKIE_SUPPORT */
4726 if ( (-3 < connection->daemon->client_discipline) &&
4727 (MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver)) &&
4728 (MHD_NO ==
4734 NULL,
4735 NULL)) )
4736 {
4737#ifdef HAVE_MESSAGES
4738 MHD_DLOG (connection->daemon,
4739 _ ("Received HTTP/1.1 request without `Host' header.\n"));
4740#endif
4744 return;
4745 }
4746
4747 /* The presence of the request body is indicated by "Content-Length:" or
4748 "Transfer-Encoding:" request headers.
4749 Unless one of these two headers is used, the request has no request body.
4750 See RFC9112, Section 6, paragraph 4. */
4751 connection->rq.remaining_upload_size = 0;
4752 if (MHD_NO !=
4758 &enc,
4759 NULL))
4760 {
4761 if (! MHD_str_equal_caseless_ (enc,
4762 "chunked"))
4763 {
4767 return;
4768 }
4769 else if (MHD_NO !=
4775 NULL,
4776 NULL))
4777 {
4778 /* TODO: add individual settings */
4779 if (1 <= connection->daemon->client_discipline)
4780 {
4784 return;
4785 }
4786 else
4787 {
4788 /* Must close connection after reply to prevent potential attack */
4789 connection->keepalive = MHD_CONN_MUST_CLOSE;
4790#ifdef HAVE_MESSAGES
4791 MHD_DLOG (connection->daemon,
4792 _ ("The 'Content-Length' request header is ignored "
4793 "as chunked Transfer-Encoding is used "
4794 "for this request.\n"));
4795#endif /* HAVE_MESSAGES */
4796 }
4797 }
4798 connection->rq.have_chunked_upload = true;
4800 }
4801 else if (MHD_NO !=
4807 &clen,
4808 &val_len))
4809 {
4810 size_t num_digits;
4811
4812 num_digits = MHD_str_to_uint64_n_ (clen,
4813 val_len,
4814 &connection->rq.remaining_upload_size);
4815
4816 if (((0 == num_digits) &&
4817 (0 != val_len) &&
4818 ('0' <= clen[0]) && ('9' >= clen[0]))
4819 || (MHD_SIZE_UNKNOWN == connection->rq.remaining_upload_size))
4820 {
4821 connection->rq.remaining_upload_size = 0;
4822#ifdef HAVE_MESSAGES
4823 MHD_DLOG (connection->daemon,
4824 _ ("Too large value of 'Content-Length' header. " \
4825 "Closing connection.\n"));
4826#endif
4830 }
4831 else if ((val_len != num_digits) ||
4832 (0 == num_digits))
4833 {
4834 connection->rq.remaining_upload_size = 0;
4835#ifdef HAVE_MESSAGES
4836 MHD_DLOG (connection->daemon,
4837 _ ("Failed to parse 'Content-Length' header. " \
4838 "Closing connection.\n"));
4839#endif
4843 }
4844 }
4845}
4846
4847
4855_MHD_static_inline void
4857{
4858 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
4859}
4860
4861
4866_MHD_static_inline void
4868{
4870 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
4872}
4873
4874
4875#ifndef MHD_MAX_EMPTY_LINES_SKIP
4880#define MHD_MAX_EMPTY_LINES_SKIP 1024
4881#endif /* ! MHD_MAX_EMPTY_LINES_SKIP */
4882
4890static bool
4892{
4893 size_t p;
4894 const int discp_lvl = c->daemon->client_discipline;
4895 /* Allow to skip one or more empty lines before the request line.
4896 RFC 9112, section 2.2 */
4897 const bool skip_empty_lines = (1 >= discp_lvl);
4898 /* Allow to skip more then one empty line before the request line.
4899 RFC 9112, section 2.2 */
4900 const bool skip_several_empty_lines = (skip_empty_lines && (0 >= discp_lvl));
4901 /* Allow to skip number of unlimited empty lines before the request line.
4902 RFC 9112, section 2.2 */
4903 const bool skip_unlimited_empty_lines =
4904 (skip_empty_lines && (-3 >= discp_lvl));
4905 /* Treat bare LF as the end of the line.
4906 RFC 9112, section 2.2 */
4907 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
4908 /* Treat tab as whitespace delimiter.
4909 RFC 9112, section 3 */
4910 const bool tab_as_wsp = (0 >= discp_lvl);
4911 /* Treat VT (vertical tab) and FF (form feed) as whitespace delimiters.
4912 RFC 9112, section 3 */
4913 const bool other_wsp_as_wsp = (-1 >= discp_lvl);
4914 /* Treat continuous whitespace block as a single space.
4915 RFC 9112, section 3 */
4916 const bool wsp_blocks = (-1 >= discp_lvl);
4917 /* Parse whitespace in URI, special parsing of the request line.
4918 RFC 9112, section 3.2 */
4919 const bool wsp_in_uri = (0 >= discp_lvl);
4920 /* Keep whitespace in URI, give app URI with whitespace instead of
4921 automatic redirect to fixed URI.
4922 Violates RFC 9112, section 3.2 */
4923 const bool wsp_in_uri_keep = (-2 >= discp_lvl);
4924 /* Keep bare CR character as is.
4925 Violates RFC 9112, section 2.2 */
4926 const bool bare_cr_keep = (wsp_in_uri_keep && (-3 >= discp_lvl));
4927 /* Treat bare CR as space; replace it with space before processing.
4928 RFC 9112, section 2.2 */
4929 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
4930
4933 mhd_assert (NULL == c->rq.method || \
4938 0 != c->rq.hdrs.rq_line.proc_pos);
4939
4940 if (0 == c->read_buffer_offset)
4941 {
4943 return false; /* No data to process */
4944 }
4945 p = c->rq.hdrs.rq_line.proc_pos;
4946 mhd_assert (p <= c->read_buffer_offset);
4947
4948 /* Skip empty lines, if any (and if allowed) */
4949 /* See RFC 9112, section 2.2 */
4950 if ((0 == p)
4951 && (skip_empty_lines))
4952 {
4953 /* Skip empty lines before the request line.
4954 See RFC 9112, section 2.2 */
4955 bool is_empty_line;
4957 mhd_assert (NULL == c->rq.method);
4958 mhd_assert (NULL == c->rq.url);
4959 mhd_assert (0 == c->rq.url_len);
4961 mhd_assert (0 == c->rq.req_target_len);
4962 mhd_assert (NULL == c->rq.version);
4963 do
4964 {
4965 is_empty_line = false;
4966 if ('\r' == c->read_buffer[0])
4967 {
4968 if (1 == c->read_buffer_offset)
4969 return false; /* Not enough data yet */
4970 if ('\n' == c->read_buffer[1])
4971 {
4972 is_empty_line = true;
4973 c->read_buffer += 2;
4974 c->read_buffer_size -= 2;
4975 c->read_buffer_offset -= 2;
4977 }
4978 }
4979 else if (('\n' == c->read_buffer[0]) &&
4980 (bare_lf_as_crlf))
4981 {
4982 is_empty_line = true;
4983 c->read_buffer += 1;
4984 c->read_buffer_size -= 1;
4985 c->read_buffer_offset -= 1;
4987 }
4988 if (is_empty_line)
4989 {
4990 if ((! skip_unlimited_empty_lines) &&
4991 (((unsigned int) ((skip_several_empty_lines) ?
4994 {
4996 _ ("Too many meaningless extra empty lines " \
4997 "received before the request"));
4998 return true; /* Process connection closure */
4999 }
5000 if (0 == c->read_buffer_offset)
5001 return false; /* No more data to process */
5002 }
5003 } while (is_empty_line);
5004 }
5005 /* All empty lines are skipped */
5006
5008 /* Read and parse the request line */
5010
5011 while (p < c->read_buffer_offset)
5012 {
5013 const char chr = c->read_buffer[p];
5014 bool end_of_line;
5015 /*
5016 The processing logic is different depending on the configured strictness:
5017
5018 When whitespace BLOCKS are NOT ALLOWED, the end of the whitespace is
5019 processed BEFORE processing of the current character.
5020 When whitespace BLOCKS are ALLOWED, the end of the whitespace is
5021 processed AFTER processing of the current character.
5022
5023 When space char in the URI is ALLOWED, the delimiter between the URI and
5024 the HTTP version string is processed only at the END of the line.
5025 When space in the URI is NOT ALLOWED, the delimiter between the URI and
5026 the HTTP version string is processed as soon as the FIRST whitespace is
5027 found after URI start.
5028 */
5029
5030 end_of_line = false;
5031
5032 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5033 (c->rq.hdrs.rq_line.last_ws_end > \
5035 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_start) || \
5036 (0 != c->rq.hdrs.rq_line.last_ws_end));
5037
5038 /* Check for the end of the line */
5039 if ('\r' == chr)
5040 {
5041 if (p + 1 == c->read_buffer_offset)
5042 {
5043 c->rq.hdrs.rq_line.proc_pos = p;
5044 return false; /* Not enough data yet */
5045 }
5046 else if ('\n' == c->read_buffer[p + 1])
5047 end_of_line = true;
5048 else
5049 {
5050 /* Bare CR alone */
5051 /* Must be rejected or replaced with space char.
5052 See RFC 9112, section 2.2 */
5053 if (bare_cr_as_sp)
5054 {
5055 c->read_buffer[p] = ' ';
5057 continue; /* Re-start processing of the current character */
5058 }
5059 else if (! bare_cr_keep)
5060 {
5061 /* A quick simple check whether this line looks like an HTTP request */
5062 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5064 {
5068 }
5069 else
5071 _ ("Bare CR characters are not allowed " \
5072 "in the request line.\n"));
5073 return true; /* Error in the request */
5074 }
5075 }
5076 }
5077 else if ('\n' == chr)
5078 {
5079 /* Bare LF may be recognised as a line delimiter.
5080 See RFC 9112, section 2.2 */
5081 if (bare_lf_as_crlf)
5082 end_of_line = true;
5083 else
5084 {
5085 /* While RFC does not enforce error for bare LF character,
5086 if this char is not treated as a line delimiter, it should be
5087 rejected to avoid any security weakness due to request smuggling. */
5088 /* A quick simple check whether this line looks like an HTTP request */
5089 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5091 {
5095 }
5096 else
5098 _ ("Bare LF characters are not allowed " \
5099 "in the request line.\n"));
5100 return true; /* Error in the request */
5101 }
5102 }
5103
5104 if (end_of_line)
5105 {
5106 /* Handle the end of the request line */
5107
5108 if (NULL != c->rq.method)
5109 {
5110 if (wsp_in_uri)
5111 {
5112 /* The end of the URI and the start of the HTTP version string
5113 should be determined now. */
5114 mhd_assert (NULL == c->rq.version);
5115 mhd_assert (0 == c->rq.req_target_len);
5116 if (0 != c->rq.hdrs.rq_line.last_ws_end)
5117 {
5118 /* Determine the end and the length of the URI */
5119 if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5120 {
5121 c->read_buffer [c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5122 c->rq.req_target_len =
5124 - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5125 }
5126 else if ((c->rq.hdrs.rq_line.last_ws_start + 1 <
5127 c->rq.hdrs.rq_line.last_ws_end) &&
5128 (HTTP_VER_LEN == (p - c->rq.hdrs.rq_line.last_ws_end)))
5129 {
5130 /* Found only HTTP method and HTTP version and more than one
5131 whitespace between them. Assume zero-length URI. */
5132 mhd_assert (wsp_blocks);
5134 c->read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5135 c->rq.hdrs.rq_line.rq_tgt =
5137 c->rq.req_target_len = 0;
5140 }
5141 /* Determine the start of the HTTP version string */
5142 if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5143 {
5145 }
5146 }
5147 }
5148 else
5149 {
5150 /* The end of the URI and the start of the HTTP version string
5151 should be already known. */
5152 if ((NULL == c->rq.version)
5153 && (NULL != c->rq.hdrs.rq_line.rq_tgt)
5154 && (HTTP_VER_LEN == p - (size_t) (c->rq.hdrs.rq_line.rq_tgt
5155 - c->read_buffer))
5156 && (0 != c->read_buffer[(size_t)
5157 (c->rq.hdrs.rq_line.rq_tgt
5158 - c->read_buffer) - 1]))
5159 {
5160 /* Found only HTTP method and HTTP version and more than one
5161 whitespace between them. Assume zero-length URI. */
5162 size_t uri_pos;
5163 mhd_assert (wsp_blocks);
5164 mhd_assert (0 == c->rq.req_target_len);
5165 uri_pos = (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer) - 1;
5166 mhd_assert (uri_pos < p);
5167 c->rq.version = c->rq.hdrs.rq_line.rq_tgt;
5168 c->read_buffer[uri_pos] = 0; /* Zero terminate the URI */
5169 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + uri_pos;
5170 c->rq.req_target_len = 0;
5173 }
5174 }
5175
5176 if (NULL != c->rq.version)
5177 {
5179 if (! parse_http_version (c, c->rq.version,
5180 p
5181 - (size_t) (c->rq.version
5182 - c->read_buffer)))
5183 {
5184 mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state);
5185 return true; /* Unsupported / broken HTTP version */
5186 }
5187 c->read_buffer[p] = 0; /* Zero terminate the HTTP version strings */
5188 if ('\r' == chr)
5189 {
5190 p++; /* Consume CR */
5191 mhd_assert (p < c->read_buffer_offset); /* The next character has been already checked */
5192 }
5193 p++; /* Consume LF */
5194 c->read_buffer += p;
5195 c->read_buffer_size -= p;
5196 c->read_buffer_offset -= p;
5198 c->rq.req_target_len);
5200 (0 != c->rq.req_target_len));
5202 ((size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5203 - c->rq.hdrs.rq_line.rq_tgt) < \
5204 c->rq.req_target_len));
5206 (c->rq.hdrs.rq_line.rq_tgt_qmark >= \
5207 c->rq.hdrs.rq_line.rq_tgt));
5208 return true; /* The request line is successfully parsed */
5209 }
5210 }
5211 /* Error in the request line */
5212
5213 /* A quick simple check whether this line looks like an HTTP request */
5214 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5216 {
5220 }
5221 else
5223 _ ("The request line is malformed.\n"));
5224
5225 return true;
5226 }
5227
5228 /* Process possible end of the previously found whitespace delimiter */
5229 if ((! wsp_blocks) &&
5230 (p == c->rq.hdrs.rq_line.last_ws_end) &&
5231 (0 != c->rq.hdrs.rq_line.last_ws_end))
5232 {
5233 /* Previous character was a whitespace char and whitespace blocks
5234 are not allowed. */
5235 /* The current position is the next character after
5236 a whitespace delimiter */
5237 if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5238 {
5239 /* The current position is the start of the URI */
5240 mhd_assert (0 == c->rq.req_target_len);
5241 mhd_assert (NULL == c->rq.version);
5242 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5243 /* Reset the whitespace marker */
5245 c->rq.hdrs.rq_line.last_ws_end = 0;
5246 }
5247 else
5248 {
5249 /* It was a whitespace after the start of the URI */
5250 if (! wsp_in_uri)
5251 {
5252 mhd_assert ((0 != c->rq.req_target_len) || \
5253 (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5254 mhd_assert (NULL == c->rq.version); /* Too many whitespaces? This error is handled at whitespace start */
5255 c->rq.version = c->read_buffer + p;
5256 /* Reset the whitespace marker */
5258 c->rq.hdrs.rq_line.last_ws_end = 0;
5259 }
5260 }
5261 }
5262
5263 /* Process the current character.
5264 Is it not the end of the line. */
5265 if ((' ' == chr)
5266 || (('\t' == chr) && (tab_as_wsp))
5267 || ((other_wsp_as_wsp) && ((0xb == chr) || (0xc == chr))))
5268 {
5269 /* A whitespace character */
5270 if ((0 == c->rq.hdrs.rq_line.last_ws_end) ||
5271 (p != c->rq.hdrs.rq_line.last_ws_end) ||
5272 (! wsp_blocks))
5273 {
5274 /* Found first whitespace char of the new whitespace block */
5275 if (NULL == c->rq.method)
5276 {
5277 /* Found the end of the HTTP method string */
5281 mhd_assert (0 == c->rq.req_target_len);
5282 mhd_assert (NULL == c->rq.version);
5283 if (0 == p)
5284 {
5286 _ ("The request line starts with "
5287 "a whitespace.\n"));
5288 return true; /* Error in the request */
5289 }
5290 c->read_buffer[p] = 0; /* Zero-terminate the request method string */
5291 c->rq.method = c->read_buffer;
5292 parse_http_std_method (c, c->rq.method, p);
5293 }
5294 else
5295 {
5296 /* A whitespace after the start of the URI */
5297 if (! wsp_in_uri)
5298 {
5299 /* Whitespace in URI is not allowed to be parsed */
5300 if (NULL == c->rq.version)
5301 {
5303 /* This is a delimiter between URI and HTTP version string */
5304 c->read_buffer[p] = 0; /* Zero-terminate request URI string */
5305 mhd_assert (((size_t) (c->rq.hdrs.rq_line.rq_tgt \
5306 - c->read_buffer)) <= p);
5307 c->rq.req_target_len =
5308 p - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5309 }
5310 else
5311 {
5312 /* This is a delimiter AFTER version string */
5313
5314 /* A quick simple check whether this line looks like an HTTP request */
5315 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5317 {
5321 }
5322 else
5324 _ ("The request line has more than "
5325 "two whitespaces.\n"));
5326 return true; /* Error in the request */
5327 }
5328 }
5329 else
5330 {
5331 /* Whitespace in URI is allowed to be parsed */
5332 if (0 != c->rq.hdrs.rq_line.last_ws_end)
5333 {
5334 /* The whitespace after the start of the URI has been found already */
5338 }
5339 }
5340 }
5342 c->rq.hdrs.rq_line.last_ws_end = p + 1; /* Will be updated on the next char parsing */
5343 }
5344 else
5345 {
5346 /* Continuation of the whitespace block */
5348 mhd_assert (0 != p);
5349 c->rq.hdrs.rq_line.last_ws_end = p + 1;
5350 }
5351 }
5352 else
5353 {
5354 /* Non-whitespace char, not the end of the line */
5355 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5356 (c->rq.hdrs.rq_line.last_ws_end == p) || \
5357 wsp_in_uri);
5358
5359 if ((p == c->rq.hdrs.rq_line.last_ws_end) &&
5360 (0 != c->rq.hdrs.rq_line.last_ws_end) &&
5361 (wsp_blocks))
5362 {
5363 /* The end of the whitespace block */
5364 if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5365 {
5366 /* This is the first character of the URI */
5367 mhd_assert (0 == c->rq.req_target_len);
5368 mhd_assert (NULL == c->rq.version);
5369 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5370 /* Reset the whitespace marker */
5372 c->rq.hdrs.rq_line.last_ws_end = 0;
5373 }
5374 else
5375 {
5376 if (! wsp_in_uri)
5377 {
5378 /* This is the first character of the HTTP version */
5380 mhd_assert ((0 != c->rq.req_target_len) || \
5381 (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5382 mhd_assert (NULL == c->rq.version); /* Handled at whitespace start */
5383 c->rq.version = c->read_buffer + p;
5384 /* Reset the whitespace marker */
5386 c->rq.hdrs.rq_line.last_ws_end = 0;
5387 }
5388 }
5389 }
5390
5391 /* Handle other special characters */
5392 if ('?' == chr)
5393 {
5394 if ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) &&
5395 (NULL != c->rq.hdrs.rq_line.rq_tgt))
5396 {
5398 }
5399 }
5400 else if ((0xb == chr) || (0xc == chr))
5401 {
5402 /* VT or LF characters */
5403 mhd_assert (! other_wsp_as_wsp);
5404 if ((NULL != c->rq.hdrs.rq_line.rq_tgt) &&
5405 (NULL == c->rq.version) &&
5406 (wsp_in_uri))
5407 {
5409 }
5410 else
5411 {
5413 _ ("Invalid character is in the "
5414 "request line.\n"));
5415 return true; /* Error in the request */
5416 }
5417 }
5418 else if (0 == chr)
5419 {
5420 /* NUL character */
5422 _ ("The NUL character is in the "
5423 "request line.\n"));
5424 return true; /* Error in the request */
5425 }
5426 }
5427
5428 p++;
5429 }
5430
5431 c->rq.hdrs.rq_line.proc_pos = p;
5432 return false; /* Not enough data yet */
5433}
5434
5435
5436#ifndef MHD_MAX_FIXED_URI_LEN
5440#define MHD_MAX_FIXED_URI_LEN (64 * 1024)
5441#endif /* ! MHD_MAX_FIXED_URI_LEN */
5442
5450static void
5452{
5453 char *b;
5454 size_t fixed_uri_len;
5455 size_t i;
5456 size_t o;
5457 char *hdr_name;
5458 size_t hdr_name_len;
5459
5463 c->rq.req_target_len);
5464 fixed_uri_len = c->rq.req_target_len
5465 + 2 * c->rq.hdrs.rq_line.num_ws_in_uri;
5466 if ( (fixed_uri_len + 200 > c->daemon->pool_size) ||
5467 (fixed_uri_len > MHD_MAX_FIXED_URI_LEN) ||
5468 (NULL == (b = malloc (fixed_uri_len + 1))) )
5469 {
5471 _ ("The request has whitespace character is " \
5472 "in the URI and the URI is too large to " \
5473 "send automatic redirect to fixed URI.\n"));
5474 return;
5475 }
5476 i = 0;
5477 o = 0;
5478
5479 do
5480 {
5481 const char chr = c->rq.hdrs.rq_line.rq_tgt[i++];
5482
5483 mhd_assert ('\r' != chr); /* Replaced during request line parsing */
5484 mhd_assert ('\n' != chr); /* Rejected during request line parsing */
5485 mhd_assert (0 != chr); /* Rejected during request line parsing */
5486 switch (chr)
5487 {
5488 case ' ':
5489 b[o++] = '%';
5490 b[o++] = '2';
5491 b[o++] = '0';
5492 break;
5493 case '\t':
5494 b[o++] = '%';
5495 b[o++] = '0';
5496 b[o++] = '9';
5497 break;
5498 case 0x0B: /* VT (vertical tab) */
5499 b[o++] = '%';
5500 b[o++] = '0';
5501 b[o++] = 'B';
5502 break;
5503 case 0x0C: /* FF (form feed) */
5504 b[o++] = '%';
5505 b[o++] = '0';
5506 b[o++] = 'C';
5507 break;
5508 default:
5509 b[o++] = chr;
5510 break;
5511 }
5512 } while (i < c->rq.req_target_len);
5513 mhd_assert (fixed_uri_len == o);
5514 b[o] = 0; /* Zero-terminate the result */
5515
5517 hdr_name = malloc (hdr_name_len + 1);
5518 if (NULL != hdr_name)
5519 {
5520 memcpy (hdr_name,
5522 hdr_name_len + 1);
5523 /* hdr_name and b are free()d within this call */
5527 hdr_name,
5528 hdr_name_len,
5529 b,
5530 o);
5531 return;
5532 }
5533 free (b);
5535 _ ("The request has whitespace character is in the " \
5536 "URI.\n"));
5537 return;
5538}
5539
5540
5547static bool
5549{
5550#ifdef _DEBUG
5551 size_t params_len;
5552#endif /* _DEBUG */
5554 mhd_assert (NULL == c->rq.url);
5555 mhd_assert (0 == c->rq.url_len);
5560 (c->rq.req_target_len > \
5561 (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5562 - c->rq.hdrs.rq_line.rq_tgt)));
5563
5564 /* Log callback before the request-target is modified/decoded */
5565 if (NULL != c->daemon->uri_log_callback)
5566 {
5567 c->rq.client_aware = true;
5568 c->rq.client_context =
5570 c->rq.hdrs.rq_line.rq_tgt,
5571 c);
5572 }
5573
5574 if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark)
5575 {
5576#ifdef _DEBUG
5577 params_len =
5579 - (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark - c->rq.hdrs.rq_line.rq_tgt);
5580#endif /* _DEBUG */
5581 c->rq.hdrs.rq_line.rq_tgt_qmark[0] = 0; /* Replace '?' with zero termination */
5582 if (MHD_NO == MHD_parse_arguments_ (c,
5584 c->rq.hdrs.rq_line.rq_tgt_qmark + 1,
5586 c))
5587 {
5589 return false;
5590 }
5591 }
5592#ifdef _DEBUG
5593 else
5594 params_len = 0;
5595#endif /* _DEBUG */
5596
5597 mhd_assert (strlen (c->rq.hdrs.rq_line.rq_tgt) == \
5598 c->rq.req_target_len - params_len);
5599
5600 /* Finally unescape URI itself */
5601 c->rq.url_len =
5603 c,
5604 c->rq.hdrs.rq_line.rq_tgt);
5605 c->rq.url = c->rq.hdrs.rq_line.rq_tgt;
5606
5607 return true;
5608}
5609
5610
5618static bool
5620{
5621 const int discp_lvl = c->daemon->client_discipline;
5622 /* Parse whitespace in URI, special parsing of the request line */
5623 const bool wsp_in_uri = (0 >= discp_lvl);
5624 /* Keep whitespace in URI, give app URI with whitespace instead of
5625 automatic redirect to fixed URI */
5626 const bool wsp_in_uri_keep = (-2 >= discp_lvl);
5627
5628 if (! get_request_line_inner (c))
5629 {
5630 /* End of the request line has not been found yet */
5631 mhd_assert ((! wsp_in_uri) || NULL == c->rq.version);
5632 if ((NULL != c->rq.version) &&
5633 (HTTP_VER_LEN <
5635 - (size_t) (c->rq.version - c->read_buffer))))
5636 {
5641 return true; /* Error in the request */
5642 }
5643 return false;
5644 }
5645 if (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state)
5646 return true; /* Error in the request */
5647
5649 mhd_assert (NULL == c->rq.url);
5650 mhd_assert (0 == c->rq.url_len);
5652 if (0 != c->rq.hdrs.rq_line.num_ws_in_uri)
5653 {
5654 if (! wsp_in_uri)
5655 {
5659 return true; /* Error in the request */
5660 }
5661 if (! wsp_in_uri_keep)
5662 {
5664 return true; /* Error in the request */
5665 }
5666 }
5667 if (! process_request_target (c))
5668 return true; /* Error in processing */
5669
5671 return true;
5672}
5673
5674
5679{
5697
5698
5710static enum MHD_HdrLineReadRes_
5712 bool process_footers,
5713 struct _MHD_str_w_len *hdr_name,
5714 struct _MHD_str_w_len *hdr_value)
5715{
5716 const int discp_lvl = c->daemon->client_discipline;
5717 /* Treat bare LF as the end of the line.
5718 RFC 9112, section 2.2-3
5719 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
5720 Bare LF is processed as end of the line or rejected as broken request. */
5721 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
5722 /* Keep bare CR character as is.
5723 Violates RFC 9112, section 2.2-4 */
5724 const bool bare_cr_keep = (-3 >= discp_lvl);
5725 /* Treat bare CR as space; replace it with space before processing.
5726 RFC 9112, section 2.2-4 */
5727 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
5728 /* Treat NUL as space; replace it with space before processing.
5729 RFC 9110, section 5.5-5 */
5730 const bool nul_as_sp = (-1 >= discp_lvl);
5731 /* Allow folded header lines.
5732 RFC 9112, section 5.2-4 */
5733 const bool allow_folded = (0 >= discp_lvl);
5734 /* Do not reject headers with the whitespace at the start of the first line.
5735 When allowed, the first line with whitespace character at the first
5736 position is ignored (as well as all possible line foldings of the first
5737 line).
5738 RFC 9112, section 2.2-8 */
5739 const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl);
5740 /* Allow whitespace in header (field) name.
5741 Violates RFC 9110, section 5.1-2 */
5742 const bool allow_wsp_in_name = (-2 >= discp_lvl);
5743 /* Allow zero-length header (field) name.
5744 Violates RFC 9110, section 5.1-2 */
5745 const bool allow_empty_name = (-2 >= discp_lvl);
5746 /* Allow whitespace before colon.
5747 Violates RFC 9112, section 5.1-2 */
5748 const bool allow_wsp_before_colon = (-3 >= discp_lvl);
5749 /* Do not abort the request when header line has no colon, just skip such
5750 bad lines.
5751 RFC 9112, section 5-1 */
5752 const bool allow_line_without_colon = (-2 >= discp_lvl);
5753
5754 size_t p;
5756#if ! defined (HAVE_MESSAGES) && ! defined(_DEBUG)
5757 (void) process_footers; /* Unused parameter */
5758#endif /* !HAVE_MESSAGES && !_DEBUG */
5759
5760 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
5762 c->state);
5763
5764 p = c->rq.hdrs.hdr.proc_pos;
5765
5766 mhd_assert (p <= c->read_buffer_offset);
5767 while (p < c->read_buffer_offset)
5768 {
5769 const char chr = c->read_buffer[p];
5770 bool end_of_line;
5771
5772 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
5773 (c->rq.hdrs.hdr.name_len < p));
5774 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p));
5775 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
5776 (c->rq.hdrs.hdr.name_end_found));
5777 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
5778 (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start));
5779 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
5780 (0 != c->rq.hdrs.hdr.name_len));
5781 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
5782 (0 == c->rq.hdrs.hdr.name_len) || \
5783 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len));
5784 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
5785 (0 == c->rq.hdrs.hdr.value_start) || \
5786 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start));
5787
5788 /* Check for the end of the line */
5789 if ('\r' == chr)
5790 {
5791 if (0 != p)
5792 {
5793 /* Line is not empty, need to check for possible line folding */
5794 if (p + 2 >= c->read_buffer_offset)
5795 break; /* Not enough data yet to check for folded line */
5796 }
5797 else
5798 {
5799 /* Line is empty, no need to check for possible line folding */
5800 if (p + 2 > c->read_buffer_offset)
5801 break; /* Not enough data yet to check for the end of the line */
5802 }
5803 if ('\n' == c->read_buffer[p + 1])
5804 end_of_line = true;
5805 else
5806 {
5807 /* Bare CR alone */
5808 /* Must be rejected or replaced with space char.
5809 See RFC 9112, section 2.2-4 */
5810 if (bare_cr_as_sp)
5811 {
5812 c->read_buffer[p] = ' ';
5814 continue; /* Re-start processing of the current character */
5815 }
5816 else if (! bare_cr_keep)
5817 {
5818 if (! process_footers)
5822 else
5826 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
5827 }
5828 end_of_line = false;
5829 }
5830 }
5831 else if ('\n' == chr)
5832 {
5833 /* Bare LF may be recognised as a line delimiter.
5834 See RFC 9112, section 2.2-3 */
5835 if (bare_lf_as_crlf)
5836 {
5837 if (0 != p)
5838 {
5839 /* Line is not empty, need to check for possible line folding */
5840 if (p + 1 >= c->read_buffer_offset)
5841 break; /* Not enough data yet to check for folded line */
5842 }
5843 end_of_line = true;
5844 }
5845 else
5846 {
5847 if (! process_footers)
5851 else
5855 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
5856 }
5857 }
5858 else
5859 end_of_line = false;
5860
5861 if (end_of_line)
5862 {
5863 /* Handle the end of the line */
5867 const size_t line_len = p + (('\r' == chr) ? 2 : 1);
5868 char next_line_char;
5869 mhd_assert (line_len <= c->read_buffer_offset);
5870
5871 if (0 == p)
5872 {
5873 /* Zero-length header line. This is the end of the request header
5874 section.
5875 RFC 9112, Section 2.1-1 */
5878 mhd_assert (0 == c->rq.hdrs.hdr.name_len);
5879 mhd_assert (0 == c->rq.hdrs.hdr.ws_start);
5880 mhd_assert (0 == c->rq.hdrs.hdr.value_start);
5881 /* Consume the line with CRLF (or bare LF) */
5882 c->read_buffer += line_len;
5883 c->read_buffer_offset -= line_len;
5884 c->read_buffer_size -= line_len;
5886 }
5887
5888 mhd_assert (line_len < c->read_buffer_offset);
5889 mhd_assert (0 != line_len);
5890 mhd_assert ('\n' == c->read_buffer[line_len - 1]);
5891 next_line_char = c->read_buffer[line_len];
5892 if ((' ' == next_line_char) ||
5893 ('\t' == next_line_char))
5894 {
5895 /* Folded line */
5896 if (! allow_folded)
5897 {
5898 if (! process_footers)
5902 else
5906
5907 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
5908 }
5909 /* Replace CRLF (or bare LF) character(s) with space characters.
5910 See RFC 9112, Section 5.2-4 */
5911 c->read_buffer[p] = ' ';
5912 if ('\r' == chr)
5913 c->read_buffer[p + 1] = ' ';
5914 continue; /* Re-start processing of the current character */
5915 }
5916 else
5917 {
5918 /* It is not a folded line, it's the real end of the non-empty line */
5919 bool skip_line = false;
5920 mhd_assert (0 != p);
5921 if (c->rq.hdrs.hdr.starts_with_ws)
5922 {
5923 /* This is the first line and it starts with whitespace. This line
5924 must be discarded completely.
5925 See RFC 9112, Section 2.2-8 */
5926 mhd_assert (allow_wsp_at_start);
5927#ifdef HAVE_MESSAGES
5928 MHD_DLOG (c->daemon,
5929 _ ("Whitespace-prefixed first header line " \
5930 "has been skipped.\n"));
5931#endif /* HAVE_MESSAGES */
5932 skip_line = true;
5933 }
5934 else if (! c->rq.hdrs.hdr.name_end_found)
5935 {
5936 if (! allow_line_without_colon)
5937 {
5938 if (! process_footers)
5941 ERR_RSP_HEADER_WITHOUT_COLON);
5942 else
5946
5947 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
5948 }
5949 /* Skip broken line completely */
5951 skip_line = true;
5952 }
5953 if (skip_line)
5954 {
5955 /* Skip the entire line */
5956 c->read_buffer += line_len;
5957 c->read_buffer_offset -= line_len;
5958 c->read_buffer_size -= line_len;
5959 p = 0;
5960 /* Reset processing state */
5961 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
5962 /* Start processing of the next line */
5963 continue;
5964 }
5965 else
5966 {
5967 /* This line should be valid header line */
5968 size_t value_len;
5969 mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name);
5970
5971 hdr_name->str = c->read_buffer + 0; /* The name always starts at the first character */
5972 hdr_name->len = c->rq.hdrs.hdr.name_len;
5973 mhd_assert (0 == hdr_name->str[hdr_name->len]);
5974
5975 if (0 == c->rq.hdrs.hdr.value_start)
5976 {
5977 c->rq.hdrs.hdr.value_start = p;
5978 c->read_buffer[p] = 0;
5979 value_len = 0;
5980 }
5981 else if (0 != c->rq.hdrs.hdr.ws_start)
5982 {
5983 mhd_assert (p > c->rq.hdrs.hdr.ws_start);
5985 c->read_buffer[c->rq.hdrs.hdr.ws_start] = 0;
5986 value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start;
5987 }
5988 else
5989 {
5990 mhd_assert (p > c->rq.hdrs.hdr.ws_start);
5991 c->read_buffer[p] = 0;
5992 value_len = p - c->rq.hdrs.hdr.value_start;
5993 }
5994 hdr_value->str = c->read_buffer + c->rq.hdrs.hdr.value_start;
5995 hdr_value->len = value_len;
5996 mhd_assert (0 == hdr_value->str[hdr_value->len]);
5997 /* Consume the entire line */
5998 c->read_buffer += line_len;
5999 c->read_buffer_offset -= line_len;
6000 c->read_buffer_size -= line_len;
6002 }
6003 }
6004 }
6005 else if ((' ' == chr) || ('\t' == chr))
6006 {
6007 if (0 == p)
6008 {
6009 if (! allow_wsp_at_start)
6010 {
6011 if (! process_footers)
6015 else
6019 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6020 }
6021 c->rq.hdrs.hdr.starts_with_ws = true;
6022 }
6023 else if ((! c->rq.hdrs.hdr.name_end_found) &&
6024 (! c->rq.hdrs.hdr.starts_with_ws))
6025 {
6026 /* Whitespace in header name / between header name and colon */
6027 if (allow_wsp_in_name || allow_wsp_before_colon)
6028 {
6029 if (0 == c->rq.hdrs.hdr.ws_start)
6030 c->rq.hdrs.hdr.ws_start = p;
6031 }
6032 else
6033 {
6034 if (! process_footers)
6038 else
6042
6043 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6044 }
6045 }
6046 else
6047 {
6048 /* Whitespace before/inside/after header (field) value */
6049 if (0 == c->rq.hdrs.hdr.ws_start)
6050 c->rq.hdrs.hdr.ws_start = p;
6051 }
6052 }
6053 else if (0 == chr)
6054 {
6055 if (! nul_as_sp)
6056 {
6057 if (! process_footers)
6061 else
6064 ERR_RSP_INVALID_CHR_IN_FOOTER);
6065
6066 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6067 }
6068 c->read_buffer[p] = ' ';
6069 continue; /* Re-start processing of the current character */
6070 }
6071 else
6072 {
6073 /* Not a whitespace, not the end of the header line */
6074 mhd_assert ('\r' != chr);
6075 mhd_assert ('\n' != chr);
6076 mhd_assert ('\0' != chr);
6077 if ((! c->rq.hdrs.hdr.name_end_found) &&
6078 (! c->rq.hdrs.hdr.starts_with_ws))
6079 {
6080 /* Processing the header (field) name */
6081 if (':' == chr)
6082 {
6083 if (0 == c->rq.hdrs.hdr.ws_start)
6084 c->rq.hdrs.hdr.name_len = p;
6085 else
6086 {
6087 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6088 if (! allow_wsp_before_colon)
6089 {
6090 if (! process_footers)
6094 else
6098 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6099 }
6101#ifndef MHD_FAVOR_SMALL_CODE
6102 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6103#endif /* ! MHD_FAVOR_SMALL_CODE */
6104 }
6105 if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name)
6106 {
6107 if (! process_footers)
6111 else
6115 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6116 }
6117 c->rq.hdrs.hdr.name_end_found = true;
6118 c->read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */
6119 }
6120 else
6121 {
6122 if (0 != c->rq.hdrs.hdr.ws_start)
6123 {
6124 /* End of the whitespace in header (field) name */
6125 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6126 if (! allow_wsp_in_name)
6127 {
6128 if (! process_footers)
6132 else
6136
6137 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6138 }
6139#ifndef MHD_FAVOR_SMALL_CODE
6140 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6141#endif /* ! MHD_FAVOR_SMALL_CODE */
6142 }
6143 }
6144 }
6145 else
6146 {
6147 /* Processing the header (field) value */
6148 if (0 == c->rq.hdrs.hdr.value_start)
6149 c->rq.hdrs.hdr.value_start = p;
6150#ifndef MHD_FAVOR_SMALL_CODE
6151 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6152#endif /* ! MHD_FAVOR_SMALL_CODE */
6153 }
6154#ifdef MHD_FAVOR_SMALL_CODE
6155 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6156#endif /* MHD_FAVOR_SMALL_CODE */
6157 }
6158 p++;
6159 }
6160 c->rq.hdrs.hdr.proc_pos = p;
6161 return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */
6162}
6163
6164
6175static bool
6176get_req_headers (struct MHD_Connection *c, bool process_footers)
6177{
6178 do
6179 {
6180 struct _MHD_str_w_len hdr_name;
6181 struct _MHD_str_w_len hdr_value;
6182 enum MHD_HdrLineReadRes_ res;
6183
6184 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6186 c->state);
6187
6188 #ifdef _DEBUG
6189 hdr_name.str = NULL;
6190 hdr_value.str = NULL;
6191#endif /* _DEBUG */
6192 res = get_req_header (c, process_footers, &hdr_name, &hdr_value);
6194 {
6195 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6197 c->state);
6198 mhd_assert (NULL != hdr_name.str);
6199 mhd_assert (NULL != hdr_value.str);
6200 /* Values must be zero-terminated and must not have binary zeros */
6201 mhd_assert (strlen (hdr_name.str) == hdr_name.len);
6202 mhd_assert (strlen (hdr_value.str) == hdr_value.len);
6203 /* Values must not have whitespaces at the start or at the end */
6204 mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != ' '));
6205 mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != '\t'));
6206 mhd_assert ((hdr_name.len == 0) || \
6207 (hdr_name.str[hdr_name.len - 1] != ' '));
6208 mhd_assert ((hdr_name.len == 0) || \
6209 (hdr_name.str[hdr_name.len - 1] != '\t'));
6210 mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != ' '));
6211 mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != '\t'));
6212 mhd_assert ((hdr_value.len == 0) || \
6213 (hdr_value.str[hdr_value.len - 1] != ' '));
6214 mhd_assert ((hdr_value.len == 0) || \
6215 (hdr_value.str[hdr_value.len - 1] != '\t'));
6216
6217 if (MHD_NO ==
6219 (! process_footers) ?
6222 hdr_name.str, hdr_name.len,
6223 hdr_value.str, hdr_value.len))
6224 {
6225 size_t add_element_size;
6226
6227 mhd_assert (hdr_name.str < hdr_value.str);
6228
6229#ifdef HAVE_MESSAGES
6230 MHD_DLOG (c->daemon,
6231 _ ("Failed to allocate memory in the connection memory " \
6232 "pool to store %s.\n"),
6233 (! process_footers) ? _ ("header") : _ ("footer"));
6234#endif /* HAVE_MESSAGES */
6235
6236 add_element_size = hdr_value.len
6237 + (size_t) (hdr_value.str - hdr_name.str);
6238
6239 if (! process_footers)
6240 handle_req_headers_no_space (c, hdr_name.str, add_element_size);
6241 else
6242 handle_req_footers_no_space (c, hdr_name.str, add_element_size);
6243
6244 mhd_assert (MHD_CONNECTION_FULL_REQ_RECEIVED < c->state);
6245 return true;
6246 }
6247 /* Reset processing state */
6249 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6251 c->state);
6252 /* Read the next header (field) line */
6253 continue;
6254 }
6256 {
6257 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6259 c->state);
6260 return false;
6261 }
6262 else if (MHD_HDR_LINE_READING_DATA_ERROR == res)
6263 {
6264 mhd_assert ((process_footers ? \
6269 return true;
6270 }
6272 break;
6273 } while (1);
6274
6275#ifdef HAVE_MESSAGES
6276 if (1 == c->rq.num_cr_sp_replaced)
6277 {
6278 MHD_DLOG (c->daemon,
6279 _ ("One bare CR character has been replaced with space " \
6280 "in %s.\n"),
6281 (! process_footers) ?
6282 _ ("the request line or in the request headers") :
6283 _ ("the request footers"));
6284 }
6285 else if (0 != c->rq.num_cr_sp_replaced)
6286 {
6287 MHD_DLOG (c->daemon,
6288 _ ("%" PRIu64 " bare CR characters have been replaced with " \
6289 "spaces in the request line and/or in the request %s.\n"),
6290 (uint64_t) c->rq.num_cr_sp_replaced,
6291 (! process_footers) ? _ ("headers") : _ ("footers"));
6292 }
6293 if (1 == c->rq.skipped_broken_lines)
6294 {
6295 MHD_DLOG (c->daemon,
6296 _ ("One %s line without colon has been skipped.\n"),
6297 (! process_footers) ? _ ("header") : _ ("footer"));
6298 }
6299 else if (0 != c->rq.skipped_broken_lines)
6300 {
6301 MHD_DLOG (c->daemon,
6302 _ ("%" PRIu64 " %s lines without colons has been skipped.\n"),
6303 (uint64_t) c->rq.skipped_broken_lines,
6304 (! process_footers) ? _ ("header") : _ ("footer"));
6305 }
6306#endif /* HAVE_MESSAGES */
6307
6308 mhd_assert (c->rq.method < c->read_buffer);
6309 if (! process_footers)
6310 {
6311 c->rq.header_size = (size_t) (c->read_buffer - c->rq.method);
6313 c->rq.field_lines.size =
6314 (size_t) ((c->read_buffer - c->rq.field_lines.start) - 1);
6315 if ('\r' == *(c->read_buffer - 2))
6316 c->rq.field_lines.size--;
6318
6320 {
6321 /* Try to re-use some of the last bytes of the request header */
6322 /* Do this only if space in the read buffer is limited AND
6323 amount of read ahead data is small. */
6328 const char *last_elmnt_end;
6329 size_t shift_back_size;
6330 if (NULL != c->rq.headers_received_tail)
6331 last_elmnt_end =
6333 + c->rq.headers_received_tail->value_size;
6334 else
6335 last_elmnt_end = c->rq.version + HTTP_VER_LEN;
6336 mhd_assert ((last_elmnt_end + 1) < c->read_buffer);
6337 shift_back_size = (size_t) (c->read_buffer - (last_elmnt_end + 1));
6338 if (0 != c->read_buffer_offset)
6339 memmove (c->read_buffer - shift_back_size,
6340 c->read_buffer,
6342 c->read_buffer -= shift_back_size;
6343 c->read_buffer_size += shift_back_size;
6344 }
6345 }
6346 else
6348
6349 return true;
6350}
6351
6352
6360void
6362{
6363 struct MHD_Daemon *daemon = connection->daemon;
6364#if defined(MHD_USE_THREADS)
6365 mhd_assert (NULL == daemon->worker_pool);
6366#endif /* MHD_USE_THREADS */
6367
6368 if (0 == connection->connection_timeout_ms)
6369 return; /* Skip update of activity for connections
6370 without timeout timer. */
6371 if (connection->suspended)
6372 return; /* no activity on suspended connections */
6373
6376 return; /* each connection has personal timeout */
6377
6378 if (connection->connection_timeout_ms != daemon->connection_timeout_ms)
6379 return; /* custom timeout, no need to move it in "normal" DLL */
6380#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6382#endif
6383 /* move connection to head of timeout list (by remove + add operation) */
6385 daemon->normal_timeout_tail,
6386 connection);
6388 daemon->normal_timeout_tail,
6389 connection);
6390#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6392#endif
6393}
6394
6395
6405void
6407 bool socket_error)
6408{
6409 ssize_t bytes_read;
6410
6411 if ( (MHD_CONNECTION_CLOSED == connection->state) ||
6412 (connection->suspended) )
6413 return;
6414#ifdef HTTPS_SUPPORT
6415 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6416 { /* HTTPS connection. */
6417 if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6418 {
6419 if (! MHD_run_tls_handshake_ (connection))
6420 return;
6421 }
6422 }
6423#endif /* HTTPS_SUPPORT */
6424
6425 mhd_assert (NULL != connection->read_buffer);
6426 if (connection->read_buffer_size == connection->read_buffer_offset)
6427 return; /* No space for receiving data. */
6428
6429 bytes_read = connection->recv_cls (connection,
6430 &connection->read_buffer
6431 [connection->read_buffer_offset],
6432 connection->read_buffer_size
6433 - connection->read_buffer_offset);
6434 if ((bytes_read < 0) || socket_error)
6435 {
6436 if ((MHD_ERR_AGAIN_ == bytes_read) && ! socket_error)
6437 return; /* No new data to process. */
6438 if ((bytes_read > 0) && connection->sk_nonblck)
6439 { /* Try to detect the socket error */
6440 int dummy;
6441 bytes_read = connection->recv_cls (connection, &dummy, sizeof (dummy));
6442 }
6443 if (MHD_ERR_CONNRESET_ == bytes_read)
6444 {
6445 if ( (MHD_CONNECTION_INIT < connection->state) &&
6446 (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6447 {
6448#ifdef HAVE_MESSAGES
6449 MHD_DLOG (connection->daemon,
6450 _ ("Socket has been disconnected when reading request.\n"));
6451#endif
6452 connection->discard_request = true;
6453 }
6454 MHD_connection_close_ (connection,
6456 return;
6457 }
6458
6459#ifdef HAVE_MESSAGES
6460 if (MHD_CONNECTION_INIT != connection->state)
6461 MHD_DLOG (connection->daemon,
6462 _ ("Connection socket is closed when reading " \
6463 "request due to the error: %s\n"),
6464 (bytes_read < 0) ? str_conn_error_ (bytes_read) :
6465 "detected connection closure");
6466#endif
6467 CONNECTION_CLOSE_ERROR (connection,
6468 NULL);
6469 return;
6470 }
6471
6472 if (0 == bytes_read)
6473 { /* Remote side closed connection. */
6474 connection->read_closed = true;
6475 if ( (MHD_CONNECTION_INIT < connection->state) &&
6476 (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6477 {
6478#ifdef HAVE_MESSAGES
6479 MHD_DLOG (connection->daemon,
6480 _ ("Connection was closed by remote side with incomplete "
6481 "request.\n"));
6482#endif
6483 connection->discard_request = true;
6484 MHD_connection_close_ (connection,
6486 }
6487 else if (MHD_CONNECTION_INIT == connection->state)
6488 /* This termination code cannot be reported to the application
6489 * because application has not been informed yet about this request */
6490 MHD_connection_close_ (connection,
6492 else
6493 MHD_connection_close_ (connection,
6495 return;
6496 }
6497 connection->read_buffer_offset += (size_t) bytes_read;
6498 MHD_update_last_activity_ (connection);
6499#if DEBUG_STATES
6500 MHD_DLOG (connection->daemon,
6501 _ ("In function %s handling connection at state: %s\n"),
6502 MHD_FUNC_,
6503 MHD_state_to_string (connection->state));
6504#endif
6505 /* TODO: check whether the next 'switch()' really needed */
6506 switch (connection->state)
6507 {
6514 /* nothing to do but default action */
6515 if (connection->read_closed)
6516 {
6517 /* TODO: check whether this really needed */
6518 MHD_connection_close_ (connection,
6520 }
6521 return;
6523 return;
6524#ifdef UPGRADE_SUPPORT
6525 case MHD_CONNECTION_UPGRADE:
6526 mhd_assert (0);
6527 return;
6528#endif /* UPGRADE_SUPPORT */
6530 /* shrink read buffer to how much is actually used */
6531 /* TODO: remove shrink as it handled in special function */
6532 if ((0 != connection->read_buffer_size) &&
6533 (connection->read_buffer_size != connection->read_buffer_offset))
6534 {
6535 mhd_assert (NULL != connection->read_buffer);
6536 connection->read_buffer =
6537 MHD_pool_reallocate (connection->pool,
6538 connection->read_buffer,
6539 connection->read_buffer_size,
6540 connection->read_buffer_offset);
6541 connection->read_buffer_size = connection->read_buffer_offset;
6542 }
6543 break;
6549 /* Milestone state, no data should be read */
6550 mhd_assert (0); /* Should not be possible */
6551 break;
6562 default:
6563 mhd_assert (0); /* Should not be possible */
6564 break;
6565 }
6566 return;
6567}
6568
6569
6578void
6580{
6581 struct MHD_Response *response;
6582 ssize_t ret;
6583 if (connection->suspended)
6584 return;
6585
6586#ifdef HTTPS_SUPPORT
6587 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6588 { /* HTTPS connection. */
6589 if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6590 {
6591 if (! MHD_run_tls_handshake_ (connection))
6592 return;
6593 }
6594 }
6595#endif /* HTTPS_SUPPORT */
6596
6597#if DEBUG_STATES
6598 MHD_DLOG (connection->daemon,
6599 _ ("In function %s handling connection at state: %s\n"),
6600 MHD_FUNC_,
6601 MHD_state_to_string (connection->state));
6602#endif
6603 switch (connection->state)
6604 {
6611 mhd_assert (0);
6612 return;
6614 ret = MHD_send_data_ (connection,
6616 [connection->continue_message_write_offset],
6618 - connection->continue_message_write_offset,
6619 true);
6620 if (ret < 0)
6621 {
6622 if (MHD_ERR_AGAIN_ == ret)
6623 return;
6624#ifdef HAVE_MESSAGES
6625 MHD_DLOG (connection->daemon,
6626 _ ("Failed to send data in request for %s.\n"),
6627 connection->rq.url);
6628#endif
6629 CONNECTION_CLOSE_ERROR (connection,
6630 NULL);
6631 return;
6632 }
6633#if _MHD_DEBUG_SEND_DATA
6634 fprintf (stderr,
6635 _ ("Sent 100 continue response: `%.*s'\n"),
6636 (int) ret,
6638#endif
6639 connection->continue_message_write_offset += (size_t) ret;
6640 MHD_update_last_activity_ (connection);
6641 return;
6647 mhd_assert (0);
6648 return;
6650 mhd_assert (0);
6651 return;
6653 {
6654 struct MHD_Response *const resp = connection->rp.response;
6655 const size_t wb_ready = connection->write_buffer_append_offset
6656 - connection->write_buffer_send_offset;
6657 mhd_assert (connection->write_buffer_append_offset >= \
6658 connection->write_buffer_send_offset);
6659 mhd_assert (NULL != resp);
6660 mhd_assert ( (0 == resp->data_size) || \
6661 (0 == resp->data_start) || \
6662 (NULL != resp->crc) );
6663 mhd_assert ( (0 == connection->rp.rsp_write_position) || \
6664 (resp->total_size ==
6665 connection->rp.rsp_write_position) );
6666 mhd_assert ((MHD_CONN_MUST_UPGRADE != connection->keepalive) || \
6667 (! connection->rp.props.send_reply_body));
6668
6669 if ( (connection->rp.props.send_reply_body) &&
6670 (NULL == resp->crc) &&
6671 (NULL == resp->data_iov) &&
6672 /* TODO: remove the next check as 'send_reply_body' is used */
6673 (0 == connection->rp.rsp_write_position) &&
6674 (! connection->rp.props.chunked) )
6675 {
6676 mhd_assert (resp->total_size >= resp->data_size);
6677 mhd_assert (0 == resp->data_start);
6678 /* Send response headers alongside the response body, if the body
6679 * data is available. */
6680 ret = MHD_send_hdr_and_body_ (connection,
6681 &connection->write_buffer
6682 [connection->write_buffer_send_offset],
6683 wb_ready,
6684 false,
6685 resp->data,
6686 resp->data_size,
6687 (resp->total_size == resp->data_size));
6688 }
6689 else
6690 {
6691 /* This is response for HEAD request or reply body is not allowed
6692 * for any other reason or reply body is dynamically generated. */
6693 /* Do not send the body data even if it's available. */
6694 ret = MHD_send_hdr_and_body_ (connection,
6695 &connection->write_buffer
6696 [connection->write_buffer_send_offset],
6697 wb_ready,
6698 false,
6699 NULL,
6700 0,
6701 ((0 == resp->total_size) ||
6702 (! connection->rp.props.send_reply_body)
6703 ));
6704 }
6705
6706 if (ret < 0)
6707 {
6708 if (MHD_ERR_AGAIN_ == ret)
6709 return;
6710#ifdef HAVE_MESSAGES
6711 MHD_DLOG (connection->daemon,
6712 _ ("Failed to send the response headers for the " \
6713 "request for `%s'. Error: %s\n"),
6714 connection->rq.url,
6715 str_conn_error_ (ret));
6716#endif
6717 CONNECTION_CLOSE_ERROR (connection,
6718 NULL);
6719 return;
6720 }
6721 /* 'ret' is not negative, it's safe to cast it to 'size_t'. */
6722 if (((size_t) ret) > wb_ready)
6723 {
6724 /* The complete header and some response data have been sent,
6725 * update both offsets. */
6726 mhd_assert (0 == connection->rp.rsp_write_position);
6727 mhd_assert (! connection->rp.props.chunked);
6728 mhd_assert (connection->rp.props.send_reply_body);
6729 connection->write_buffer_send_offset += wb_ready;
6730 connection->rp.rsp_write_position = ((size_t) ret) - wb_ready;
6731 }
6732 else
6733 connection->write_buffer_send_offset += (size_t) ret;
6734 MHD_update_last_activity_ (connection);
6735 if (MHD_CONNECTION_HEADERS_SENDING != connection->state)
6736 return;
6737 check_write_done (connection,
6739 return;
6740 }
6742 return;
6744 response = connection->rp.response;
6745 if (connection->rp.rsp_write_position <
6746 connection->rp.response->total_size)
6747 {
6748 uint64_t data_write_offset;
6749
6750#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6751 if (NULL != response->crc)
6752 MHD_mutex_lock_chk_ (&response->mutex);
6753#endif
6754 if (MHD_NO == try_ready_normal_body (connection))
6755 {
6756 /* mutex was already unlocked by try_ready_normal_body */
6757 return;
6758 }
6759#if defined(_MHD_HAVE_SENDFILE)
6760 if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
6761 {
6762 mhd_assert (NULL == response->data_iov);
6763 ret = MHD_send_sendfile_ (connection);
6764 }
6765 else /* combined with the next 'if' */
6766#endif /* _MHD_HAVE_SENDFILE */
6767 if (NULL != response->data_iov)
6768 {
6769 ret = MHD_send_iovec_ (connection,
6770 &connection->rp.resp_iov,
6771 true);
6772 }
6773 else
6774 {
6775 data_write_offset = connection->rp.rsp_write_position
6776 - response->data_start;
6777 if (data_write_offset > (uint64_t) SIZE_MAX)
6778 MHD_PANIC (_ ("Data offset exceeds limit.\n"));
6779 ret = MHD_send_data_ (connection,
6780 &response->data
6781 [(size_t) data_write_offset],
6782 response->data_size
6783 - (size_t) data_write_offset,
6784 true);
6785#if _MHD_DEBUG_SEND_DATA
6786 if (ret > 0)
6787 fprintf (stderr,
6788 _ ("Sent %d-byte DATA response: `%.*s'\n"),
6789 (int) ret,
6790 (int) ret,
6791 &rp.response->data[connection->rp.rsp_write_position
6792 - rp.response->data_start]);
6793#endif
6794 }
6795#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6796 if (NULL != response->crc)
6797 MHD_mutex_unlock_chk_ (&response->mutex);
6798#endif
6799 if (ret < 0)
6800 {
6801 if (MHD_ERR_AGAIN_ == ret)
6802 return;
6803#ifdef HAVE_MESSAGES
6804 MHD_DLOG (connection->daemon,
6805 _ ("Failed to send the response body for the " \
6806 "request for `%s'. Error: %s\n"),
6807 connection->rq.url,
6808 str_conn_error_ (ret));
6809#endif
6810 CONNECTION_CLOSE_ERROR (connection,
6811 NULL);
6812 return;
6813 }
6814 connection->rp.rsp_write_position += (size_t) ret;
6815 MHD_update_last_activity_ (connection);
6816 }
6817 if (connection->rp.rsp_write_position ==
6818 connection->rp.response->total_size)
6820 return;
6822 mhd_assert (0);
6823 return;
6825 ret = MHD_send_data_ (connection,
6826 &connection->write_buffer
6827 [connection->write_buffer_send_offset],
6828 connection->write_buffer_append_offset
6829 - connection->write_buffer_send_offset,
6830 true);
6831 if (ret < 0)
6832 {
6833 if (MHD_ERR_AGAIN_ == ret)
6834 return;
6835#ifdef HAVE_MESSAGES
6836 MHD_DLOG (connection->daemon,
6837 _ ("Failed to send the chunked response body for the " \
6838 "request for `%s'. Error: %s\n"),
6839 connection->rq.url,
6840 str_conn_error_ (ret));
6841#endif
6842 CONNECTION_CLOSE_ERROR (connection,
6843 NULL);
6844 return;
6845 }
6846 connection->write_buffer_send_offset += (size_t) ret;
6847 MHD_update_last_activity_ (connection);
6848 if (MHD_CONNECTION_CHUNKED_BODY_READY != connection->state)
6849 return;
6850 check_write_done (connection,
6851 (connection->rp.response->total_size ==
6852 connection->rp.rsp_write_position) ?
6855 return;
6858 mhd_assert (0);
6859 return;
6861 ret = MHD_send_data_ (connection,
6862 &connection->write_buffer
6863 [connection->write_buffer_send_offset],
6864 connection->write_buffer_append_offset
6865 - connection->write_buffer_send_offset,
6866 true);
6867 if (ret < 0)
6868 {
6869 if (MHD_ERR_AGAIN_ == ret)
6870 return;
6871#ifdef HAVE_MESSAGES
6872 MHD_DLOG (connection->daemon,
6873 _ ("Failed to send the footers for the " \
6874 "request for `%s'. Error: %s\n"),
6875 connection->rq.url,
6876 str_conn_error_ (ret));
6877#endif
6878 CONNECTION_CLOSE_ERROR (connection,
6879 NULL);
6880 return;
6881 }
6882 connection->write_buffer_send_offset += (size_t) ret;
6883 MHD_update_last_activity_ (connection);
6884 if (MHD_CONNECTION_FOOTERS_SENDING != connection->state)
6885 return;
6886 check_write_done (connection,
6888 return;
6890 mhd_assert (0);
6891 return;
6893 return;
6894#ifdef UPGRADE_SUPPORT
6895 case MHD_CONNECTION_UPGRADE:
6896 mhd_assert (0);
6897 return;
6898#endif /* UPGRADE_SUPPORT */
6899 default:
6900 mhd_assert (0);
6901 CONNECTION_CLOSE_ERROR (connection,
6902 _ ("Internal error.\n"));
6903 break;
6904 }
6905 return;
6906}
6907
6908
6915static bool
6917{
6918 const uint64_t timeout = c->connection_timeout_ms;
6919 uint64_t now;
6920 uint64_t since_actv;
6921
6922 if (c->suspended)
6923 return false;
6924 if (0 == timeout)
6925 return false;
6927 since_actv = now - c->last_activity;
6928 /* Keep the next lines in sync with #connection_get_wait() to avoid
6929 * undesired side-effects like busy-waiting. */
6930 if (timeout < since_actv)
6931 {
6932 if (UINT64_MAX / 2 < since_actv)
6933 {
6934 const uint64_t jump_back = c->last_activity - now;
6935 /* Very unlikely that it is more than quarter-million years pause.
6936 * More likely that system clock jumps back. */
6937 if (5000 >= jump_back)
6938 {
6939#ifdef HAVE_MESSAGES
6940 MHD_DLOG (c->daemon,
6941 _ ("Detected system clock %u milliseconds jump back.\n"),
6942 (unsigned int) jump_back);
6943#endif
6944 return false;
6945 }
6946#ifdef HAVE_MESSAGES
6947 MHD_DLOG (c->daemon,
6948 _ ("Detected too large system clock %" PRIu64 " milliseconds "
6949 "jump back.\n"),
6950 jump_back);
6951#endif
6952 }
6953 return true;
6954 }
6955 return false;
6956}
6957
6958
6967static void
6969{
6970 struct MHD_Daemon *daemon = connection->daemon;
6971#ifdef MHD_USE_THREADS
6972 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
6973 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
6974 mhd_assert (NULL == daemon->worker_pool);
6975#endif /* MHD_USE_THREADS */
6976
6977 if (connection->in_cleanup)
6978 return; /* Prevent double cleanup. */
6979 connection->in_cleanup = true;
6980 if (NULL != connection->rp.response)
6981 {
6982 MHD_destroy_response (connection->rp.response);
6983 connection->rp.response = NULL;
6984 }
6985#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6987#endif
6988 if (connection->suspended)
6989 {
6992 connection);
6993 connection->suspended = false;
6994 }
6995 else
6996 {
6997 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
6998 {
6999 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7001 daemon->normal_timeout_tail,
7002 connection);
7003 else
7005 daemon->manual_timeout_tail,
7006 connection);
7007 }
7009 daemon->connections_tail,
7010 connection);
7011 }
7012 DLL_insert (daemon->cleanup_head,
7013 daemon->cleanup_tail,
7014 connection);
7015 connection->resuming = false;
7016 connection->in_idle = false;
7017#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7019#endif
7021 {
7022 /* if we were at the connection limit before and are in
7023 thread-per-connection mode, signal the main thread
7024 to resume accepting connections */
7025 if ( (MHD_ITC_IS_VALID_ (daemon->itc)) &&
7026 (! MHD_itc_activate_ (daemon->itc, "c")) )
7027 {
7028#ifdef HAVE_MESSAGES
7029 MHD_DLOG (daemon,
7030 _ ("Failed to signal end of connection via inter-thread " \
7031 "communication channel.\n"));
7032#endif
7033 }
7034 }
7035}
7036
7037
7043void
7045{
7046 size_t read_buf_size;
7047
7048#ifdef HTTPS_SUPPORT
7049 mhd_assert ( (0 == (c->daemon->options & MHD_USE_TLS)) || \
7050 (MHD_TLS_CONN_INIT == c->tls_state) );
7051 mhd_assert ( (0 != (c->daemon->options & MHD_USE_TLS)) || \
7052 (MHD_TLS_CONN_NO_TLS == c->tls_state) );
7053#endif /* HTTPS_SUPPORT */
7055
7058
7059 memset (&c->rq, 0, sizeof(c->rq));
7060 memset (&c->rp, 0, sizeof(c->rp));
7061
7062 c->write_buffer = NULL;
7063 c->write_buffer_size = 0;
7066
7068
7069 c->read_buffer_offset = 0;
7070 read_buf_size = c->daemon->pool_size / 2;
7071 c->read_buffer
7072 = MHD_pool_allocate (c->pool,
7073 read_buf_size,
7074 false);
7075 c->read_buffer_size = read_buf_size;
7076}
7077
7078
7085static void
7087 bool reuse)
7088{
7089 struct MHD_Connection *const c = connection;
7090 struct MHD_Daemon *const d = connection->daemon;
7091
7092 if (! reuse)
7093 {
7094 /* Next function will destroy response, notify client,
7095 * destroy memory pool, and set connection state to "CLOSED" */
7097 c->stop_with_error ?
7100 c->read_buffer = NULL;
7101 c->read_buffer_size = 0;
7102 c->read_buffer_offset = 0;
7103 c->write_buffer = NULL;
7104 c->write_buffer_size = 0;
7107 }
7108 else
7109 {
7110 /* Reset connection to process the next request */
7111 size_t new_read_buf_size;
7114
7115 if ( (NULL != d->notify_completed) &&
7116 (c->rq.client_aware) )
7118 c,
7119 &c->rq.client_context,
7121 c->rq.client_aware = false;
7122
7123 if (NULL != c->rp.response)
7125 c->rp.response = NULL;
7126
7129 c->event_loop_info =
7130 (0 == c->read_buffer_offset) ?
7132
7133 memset (&c->rq, 0, sizeof(c->rq));
7134
7135 /* iov (if any) will be deallocated by MHD_pool_reset */
7136 memset (&c->rp, 0, sizeof(c->rp));
7137
7138 c->write_buffer = NULL;
7139 c->write_buffer_size = 0;
7143
7144 /* Reset the read buffer to the starting size,
7145 preserving the bytes we have already read. */
7146 new_read_buf_size = c->daemon->pool_size / 2;
7147 if (c->read_buffer_offset > new_read_buf_size)
7148 new_read_buf_size = c->read_buffer_offset;
7149
7150 c->read_buffer
7151 = MHD_pool_reset (c->pool,
7152 c->read_buffer,
7154 new_read_buf_size);
7155 c->read_buffer_size = new_read_buf_size;
7156 }
7157 c->rq.client_context = NULL;
7158}
7159
7160
7173enum MHD_Result
7175{
7176 struct MHD_Daemon *daemon = connection->daemon;
7177 enum MHD_Result ret;
7178#ifdef MHD_USE_THREADS
7179 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
7180 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
7181#endif /* MHD_USE_THREADS */
7182 /* 'daemon' is not used if epoll is not available and asserts are disabled */
7183 (void) daemon; /* Mute compiler warning */
7184
7185 connection->in_idle = true;
7186 while (! connection->suspended)
7187 {
7188#ifdef HTTPS_SUPPORT
7189 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
7190 { /* HTTPS connection. */
7191 if ((MHD_TLS_CONN_INIT <= connection->tls_state) &&
7192 (MHD_TLS_CONN_CONNECTED > connection->tls_state))
7193 break;
7194 }
7195#endif /* HTTPS_SUPPORT */
7196#if DEBUG_STATES
7197 MHD_DLOG (daemon,
7198 _ ("In function %s handling connection at state: %s\n"),
7199 MHD_FUNC_,
7200 MHD_state_to_string (connection->state));
7201#endif
7202 switch (connection->state)
7203 {
7206 if (get_request_line (connection))
7207 {
7208 mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING < connection->state);
7210 || (connection->discard_request));
7211 continue;
7212 }
7214 break;
7218 continue;
7220 if (get_req_headers (connection, false))
7221 {
7222 mhd_assert (MHD_CONNECTION_REQ_HEADERS_RECEIVING < connection->state);
7223 mhd_assert ((MHD_CONNECTION_HEADERS_RECEIVED == connection->state) || \
7224 (connection->discard_request));
7225 continue;
7226 }
7228 break;
7230 parse_connection_headers (connection);
7231 if (MHD_CONNECTION_HEADERS_RECEIVED != connection->state)
7232 continue;
7234 if (connection->suspended)
7235 break;
7236 continue;
7238 call_connection_handler (connection); /* first call */
7239 if (MHD_CONNECTION_HEADERS_PROCESSED != connection->state)
7240 continue;
7241 if (connection->suspended)
7242 continue;
7243
7244 if ( (NULL == connection->rp.response) &&
7245 (need_100_continue (connection)) &&
7246 /* If the client is already sending the payload (body)
7247 there is no need to send "100 Continue" */
7248 (0 == connection->read_buffer_offset) )
7249 {
7251 break;
7252 }
7253 if ( (NULL != connection->rp.response) &&
7254 (0 != connection->rq.remaining_upload_size) )
7255 {
7256 /* we refused (no upload allowed!) */
7257 connection->rq.remaining_upload_size = 0;
7258 /* force close, in case client still tries to upload... */
7259 connection->discard_request = true;
7260 }
7261 connection->state = (0 == connection->rq.remaining_upload_size)
7264 if (connection->suspended)
7265 break;
7266 continue;
7268 if (connection->continue_message_write_offset ==
7270 {
7272 continue;
7273 }
7274 break;
7276 mhd_assert (0 != connection->rq.remaining_upload_size);
7277 mhd_assert (! connection->discard_request);
7278 mhd_assert (NULL == connection->rp.response);
7279 if (0 != connection->read_buffer_offset)
7280 {
7281 process_request_body (connection); /* loop call */
7282 if (MHD_CONNECTION_BODY_RECEIVING != connection->state)
7283 continue;
7284 }
7285 /* Modify here when queueing of the response during data processing
7286 will be supported */
7287 mhd_assert (! connection->discard_request);
7288 mhd_assert (NULL == connection->rp.response);
7289 if (0 == connection->rq.remaining_upload_size)
7290 {
7291 connection->state = MHD_CONNECTION_BODY_RECEIVED;
7292 continue;
7293 }
7294 break;
7296 mhd_assert (! connection->discard_request);
7297 mhd_assert (NULL == connection->rp.response);
7298 if (0 == connection->rq.remaining_upload_size)
7299 {
7300 if (connection->rq.have_chunked_upload)
7301 {
7302 /* Reset counter variables reused for footers */
7303 connection->rq.num_cr_sp_replaced = 0;
7304 connection->rq.skipped_broken_lines = 0;
7307 }
7308 else
7310 continue;
7311 }
7312 break;
7314 if (get_req_headers (connection, true))
7315 {
7316 mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING < connection->state);
7317 mhd_assert ((MHD_CONNECTION_FOOTERS_RECEIVED == connection->state) || \
7318 (connection->discard_request));
7319 continue;
7320 }
7322 break;
7324 /* The header, the body, and the footers of the request has been received,
7325 * switch to the final processing of the request. */
7327 continue;
7329 call_connection_handler (connection); /* "final" call */
7330 if (connection->state != MHD_CONNECTION_FULL_REQ_RECEIVED)
7331 continue;
7332 if (NULL == connection->rp.response)
7333 break; /* try again next time */
7334 /* Response is ready, start reply */
7335 connection->state = MHD_CONNECTION_START_REPLY;
7336 continue;
7338 mhd_assert (NULL != connection->rp.response);
7340 if (MHD_NO == build_header_response (connection))
7341 {
7342 /* oops - close! */
7343 CONNECTION_CLOSE_ERROR (connection,
7344 _ ("Closing connection (failed to create "
7345 "response header).\n"));
7346 continue;
7347 }
7349 break;
7350
7352 /* no default action */
7353 break;
7355#ifdef UPGRADE_SUPPORT
7356 if (NULL != connection->rp.response->upgrade_handler)
7357 {
7358 connection->state = MHD_CONNECTION_UPGRADE;
7359 /* This connection is "upgraded". Pass socket to application. */
7360 if (MHD_NO ==
7362 connection))
7363 {
7364 /* upgrade failed, fail hard */
7365 CONNECTION_CLOSE_ERROR (connection,
7366 NULL);
7367 continue;
7368 }
7369 /* Response is not required anymore for this connection. */
7370 if (1)
7371 {
7372 struct MHD_Response *const resp = connection->rp.response;
7373
7374 connection->rp.response = NULL;
7375 MHD_destroy_response (resp);
7376 }
7377 continue;
7378 }
7379#endif /* UPGRADE_SUPPORT */
7380
7381 if (connection->rp.props.send_reply_body)
7382 {
7383 if (connection->rp.props.chunked)
7385 else
7387 }
7388 else
7390 continue;
7392 mhd_assert (connection->rp.props.send_reply_body);
7393 mhd_assert (! connection->rp.props.chunked);
7394 /* nothing to do here */
7395 break;
7397 mhd_assert (connection->rp.props.send_reply_body);
7398 mhd_assert (! connection->rp.props.chunked);
7399#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7400 if (NULL != connection->rp.response->crc)
7401 MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7402#endif
7403 if (0 == connection->rp.response->total_size)
7404 {
7405#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7406 if (NULL != connection->rp.response->crc)
7407 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7408#endif
7409 if (connection->rp.props.chunked)
7411 else
7413 continue;
7414 }
7415 if (MHD_NO != try_ready_normal_body (connection))
7416 {
7417#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7418 if (NULL != connection->rp.response->crc)
7419 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7420#endif
7422 /* Buffering for flushable socket was already enabled*/
7423
7424 break;
7425 }
7426 /* mutex was already unlocked by "try_ready_normal_body */
7427 /* not ready, no socket action */
7428 break;
7430 mhd_assert (connection->rp.props.send_reply_body);
7431 mhd_assert (connection->rp.props.chunked);
7432 /* nothing to do here */
7433 break;
7435 mhd_assert (connection->rp.props.send_reply_body);
7436 mhd_assert (connection->rp.props.chunked);
7437#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7438 if (NULL != connection->rp.response->crc)
7439 MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7440#endif
7441 if ( (0 == connection->rp.response->total_size) ||
7442 (connection->rp.rsp_write_position ==
7443 connection->rp.response->total_size) )
7444 {
7445#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7446 if (NULL != connection->rp.response->crc)
7447 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7448#endif
7450 continue;
7451 }
7452 if (1)
7453 { /* pseudo-branch for local variables scope */
7454 bool finished;
7455 if (MHD_NO != try_ready_chunked_body (connection, &finished))
7456 {
7457#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7458 if (NULL != connection->rp.response->crc)
7459 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7460#endif
7461 connection->state = finished ? MHD_CONNECTION_CHUNKED_BODY_SENT :
7463 continue;
7464 }
7465 /* mutex was already unlocked by try_ready_chunked_body */
7466 }
7467 break;
7469 mhd_assert (connection->rp.props.send_reply_body);
7470 mhd_assert (connection->rp.props.chunked);
7471 mhd_assert (connection->write_buffer_send_offset <= \
7472 connection->write_buffer_append_offset);
7473
7475 {
7476 /* oops - close! */
7477 CONNECTION_CLOSE_ERROR (connection,
7478 _ ("Closing connection (failed to create " \
7479 "response footer)."));
7480 continue;
7481 }
7482 mhd_assert (connection->write_buffer_send_offset < \
7483 connection->write_buffer_append_offset);
7485 continue;
7487 mhd_assert (connection->rp.props.send_reply_body);
7488 mhd_assert (connection->rp.props.chunked);
7489 /* no default action */
7490 break;
7492 if (MHD_HTTP_PROCESSING == connection->rp.responseCode)
7493 {
7494 /* After this type of response, we allow sending another! */
7496 MHD_destroy_response (connection->rp.response);
7497 connection->rp.response = NULL;
7498 /* FIXME: maybe partially reset memory pool? */
7499 continue;
7500 }
7501 /* Reset connection after complete reply */
7502 connection_reset (connection,
7503 MHD_CONN_USE_KEEPALIVE == connection->keepalive &&
7504 ! connection->read_closed &&
7505 ! connection->discard_request);
7506 continue;
7508 cleanup_connection (connection);
7509 connection->in_idle = false;
7510 return MHD_NO;
7511#ifdef UPGRADE_SUPPORT
7512 case MHD_CONNECTION_UPGRADE:
7513 connection->in_idle = false;
7514 return MHD_YES; /* keep open */
7515#endif /* UPGRADE_SUPPORT */
7516 default:
7517 mhd_assert (0);
7518 break;
7519 }
7520 break;
7521 }
7522 if (connection_check_timedout (connection))
7523 {
7524 MHD_connection_close_ (connection,
7526 connection->in_idle = false;
7527 return MHD_YES;
7528 }
7530 ret = MHD_YES;
7531#ifdef EPOLL_SUPPORT
7532 if ( (! connection->suspended) &&
7533 MHD_D_IS_USING_EPOLL_ (daemon) )
7534 {
7535 ret = MHD_connection_epoll_update_ (connection);
7536 }
7537#endif /* EPOLL_SUPPORT */
7538 connection->in_idle = false;
7539 return ret;
7540}
7541
7542
7543#ifdef EPOLL_SUPPORT
7552enum MHD_Result
7553MHD_connection_epoll_update_ (struct MHD_Connection *connection)
7554{
7555 struct MHD_Daemon *const daemon = connection->daemon;
7556
7558
7559 if ((0 != (MHD_EVENT_LOOP_INFO_PROCESS & connection->event_loop_info)) &&
7560 (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)))
7561 {
7562 /* Make sure that connection waiting for processing will be processed */
7563 EDLL_insert (daemon->eready_head,
7564 daemon->eready_tail,
7565 connection);
7566 connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
7567 }
7568
7569 if ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) &&
7570 (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
7571 ( ( (MHD_EVENT_LOOP_INFO_WRITE == connection->event_loop_info) &&
7572 (0 == (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY))) ||
7573 ( (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info)) &&
7574 (0 == (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) ) ) )
7575 {
7576 /* add to epoll set */
7577 struct epoll_event event;
7578
7579 event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
7580 event.data.ptr = connection;
7581 if (0 != epoll_ctl (daemon->epoll_fd,
7582 EPOLL_CTL_ADD,
7583 connection->socket_fd,
7584 &event))
7585 {
7586#ifdef HAVE_MESSAGES
7587 if (0 != (daemon->options & MHD_USE_ERROR_LOG))
7588 MHD_DLOG (daemon,
7589 _ ("Call to epoll_ctl failed: %s\n"),
7591#endif
7592 connection->state = MHD_CONNECTION_CLOSED;
7593 cleanup_connection (connection);
7594 return MHD_NO;
7595 }
7596 connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
7597 }
7598 return MHD_YES;
7599}
7600
7601
7602#endif
7603
7604
7610void
7612{
7613 connection->recv_cls = &recv_param_adapter;
7614}
7615
7616
7629_MHD_EXTERN const union MHD_ConnectionInfo *
7631 enum MHD_ConnectionInfoType info_type,
7632 ...)
7633{
7634 switch (info_type)
7635 {
7636#ifdef HTTPS_SUPPORT
7638 if (NULL == connection->tls_session)
7639 return NULL;
7640 if (1)
7641 { /* Workaround to mute compiler warning */
7642 gnutls_cipher_algorithm_t res;
7643 res = gnutls_cipher_get (connection->tls_session);
7644 connection->connection_info_dummy.cipher_algorithm = (int) res;
7645 }
7646 return &connection->connection_info_dummy;
7648 if (NULL == connection->tls_session)
7649 return NULL;
7650 if (1)
7651 { /* Workaround to mute compiler warning */
7652 gnutls_protocol_t res;
7653 res = gnutls_protocol_get_version (connection->tls_session);
7654 connection->connection_info_dummy.protocol = (int) res;
7655 }
7656 return &connection->connection_info_dummy;
7658 if (NULL == connection->tls_session)
7659 return NULL;
7660 connection->connection_info_dummy.tls_session = connection->tls_session;
7661 return &connection->connection_info_dummy;
7662#else /* ! HTTPS_SUPPORT */
7666#endif /* ! HTTPS_SUPPORT */
7668 return NULL; /* Not implemented */
7670 if (0 < connection->addr_len)
7671 {
7672 mhd_assert (sizeof (connection->addr) == \
7673 sizeof (connection->connection_info_dummy.client_addr));
7674 memcpy (&connection->connection_info_dummy.client_addr,
7675 &connection->addr,
7676 sizeof(connection->addr));
7677 return &connection->connection_info_dummy;
7678 }
7679 return NULL;
7681 connection->connection_info_dummy.daemon =
7682 MHD_get_master (connection->daemon);
7683 return &connection->connection_info_dummy;
7685 connection->connection_info_dummy.connect_fd = connection->socket_fd;
7686 return &connection->connection_info_dummy;
7689 connection->socket_context;
7690 return &connection->connection_info_dummy;
7692 connection->connection_info_dummy.suspended =
7693 connection->suspended ? MHD_YES : MHD_NO;
7694 return &connection->connection_info_dummy;
7696#if SIZEOF_UNSIGNED_INT <= (SIZEOF_UINT64_T - 2)
7697 if (UINT_MAX < connection->connection_timeout_ms / 1000)
7699 else
7700#endif /* SIZEOF_UNSIGNED_INT <=(SIZEOF_UINT64_T - 2) */
7702 (unsigned int) (connection->connection_timeout_ms / 1000);
7703 return &connection->connection_info_dummy;
7705 if ( (MHD_CONNECTION_HEADERS_RECEIVED > connection->state) ||
7706 (MHD_CONNECTION_CLOSED == connection->state) )
7707 return NULL; /* invalid, too early! */
7708 connection->connection_info_dummy.header_size = connection->rq.header_size;
7709 return &connection->connection_info_dummy;
7711 if (NULL == connection->rp.response)
7712 return NULL;
7713 connection->connection_info_dummy.http_status = connection->rp.responseCode;
7714 return &connection->connection_info_dummy;
7715 default:
7716 return NULL;
7717 }
7718}
7719
7720
7732 enum MHD_CONNECTION_OPTION option,
7733 ...)
7734{
7735 va_list ap;
7736 struct MHD_Daemon *daemon;
7737 unsigned int ui_val;
7738
7739 daemon = connection->daemon;
7740 switch (option)
7741 {
7743 if (0 == connection->connection_timeout_ms)
7745 va_start (ap, option);
7746 ui_val = va_arg (ap, unsigned int);
7747 va_end (ap);
7748#if (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT
7749 if ((UINT64_MAX / 4000 - 1) < ui_val)
7750 {
7751#ifdef HAVE_MESSAGES
7752 MHD_DLOG (connection->daemon,
7753 _ ("The specified connection timeout (%u) is too " \
7754 "large. Maximum allowed value (%" PRIu64 ") will be used " \
7755 "instead.\n"),
7756 ui_val,
7757 (UINT64_MAX / 4000 - 1));
7758#endif
7759 ui_val = UINT64_MAX / 4000 - 1;
7760 }
7761#endif /* (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT */
7762 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
7763 {
7764#if defined(MHD_USE_THREADS)
7766#endif
7767 if (! connection->suspended)
7768 {
7769 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7771 daemon->normal_timeout_tail,
7772 connection);
7773 else
7775 daemon->manual_timeout_tail,
7776 connection);
7777 connection->connection_timeout_ms = ((uint64_t) ui_val) * 1000;
7778 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7780 daemon->normal_timeout_tail,
7781 connection);
7782 else
7784 daemon->manual_timeout_tail,
7785 connection);
7786 }
7787#if defined(MHD_USE_THREADS)
7789#endif
7790 }
7791 return MHD_YES;
7792 default:
7793 return MHD_NO;
7794 }
7795}
7796
7797
7845 unsigned int status_code,
7846 struct MHD_Response *response)
7847{
7848 struct MHD_Daemon *daemon;
7849 bool reply_icy;
7850
7851 if ((NULL == connection) || (NULL == response))
7852 return MHD_NO;
7853
7854 daemon = connection->daemon;
7855 if ((! connection->in_access_handler) && (! connection->suspended) &&
7856 MHD_D_IS_USING_THREADS_ (daemon))
7857 return MHD_NO;
7858
7859 reply_icy = (0 != (status_code & MHD_ICY_FLAG));
7860 status_code &= ~MHD_ICY_FLAG;
7861
7862#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7863 if ( (! connection->suspended) &&
7864 MHD_D_IS_USING_THREADS_ (daemon) &&
7865 (! MHD_thread_handle_ID_is_current_thread_ (connection->tid)) )
7866 {
7867#ifdef HAVE_MESSAGES
7868 MHD_DLOG (daemon,
7869 _ ("Attempted to queue response on wrong thread!\n"));
7870#endif
7871 return MHD_NO;
7872 }
7873#endif
7874
7875 if (NULL != connection->rp.response)
7876 return MHD_NO; /* The response was already set */
7877
7878 if ( (MHD_CONNECTION_HEADERS_PROCESSED != connection->state) &&
7879 (MHD_CONNECTION_FULL_REQ_RECEIVED != connection->state) )
7880 return MHD_NO; /* Wrong connection state */
7881
7882 if (daemon->shutdown)
7883 return MHD_YES; /* If daemon was shut down in parallel,
7884 * response will be aborted now or on later stage. */
7885
7886#ifdef UPGRADE_SUPPORT
7887 if (NULL != response->upgrade_handler)
7888 {
7889 struct MHD_HTTP_Res_Header *conn_header;
7890 if (0 == (daemon->options & MHD_ALLOW_UPGRADE))
7891 {
7892#ifdef HAVE_MESSAGES
7893 MHD_DLOG (daemon,
7894 _ ("Attempted 'upgrade' connection on daemon without" \
7895 " MHD_ALLOW_UPGRADE option!\n"));
7896#endif
7897 return MHD_NO;
7898 }
7899 if (MHD_HTTP_SWITCHING_PROTOCOLS != status_code)
7900 {
7901#ifdef HAVE_MESSAGES
7902 MHD_DLOG (daemon,
7903 _ ("Application used invalid status code for" \
7904 " 'upgrade' response!\n"));
7905#endif
7906 return MHD_NO;
7907 }
7908 if (0 == (response->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
7909 {
7910#ifdef HAVE_MESSAGES
7911 MHD_DLOG (daemon,
7912 _ ("Application used invalid response" \
7913 " without \"Connection\" header!\n"));
7914#endif
7915 return MHD_NO;
7916 }
7917 conn_header = response->first_header;
7918 mhd_assert (NULL != conn_header);
7921 if (! MHD_str_has_s_token_caseless_ (conn_header->value,
7922 "upgrade"))
7923 {
7924#ifdef HAVE_MESSAGES
7925 MHD_DLOG (daemon,
7926 _ ("Application used invalid response" \
7927 " without \"upgrade\" token in" \
7928 " \"Connection\" header!\n"));
7929#endif
7930 return MHD_NO;
7931 }
7932 if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
7933 {
7934#ifdef HAVE_MESSAGES
7935 MHD_DLOG (daemon,
7936 _ ("Connection \"Upgrade\" can be used only " \
7937 "with HTTP/1.1 connections!\n"));
7938#endif
7939 return MHD_NO;
7940 }
7941 }
7942#endif /* UPGRADE_SUPPORT */
7943 if (MHD_HTTP_SWITCHING_PROTOCOLS == status_code)
7944 {
7945#ifdef UPGRADE_SUPPORT
7946 if (NULL == response->upgrade_handler)
7947 {
7948#ifdef HAVE_MESSAGES
7949 MHD_DLOG (daemon,
7950 _ ("Application used status code 101 \"Switching Protocols\" " \
7951 "with non-'upgrade' response!\n"));
7952#endif /* HAVE_MESSAGES */
7953 return MHD_NO;
7954 }
7955#else /* ! UPGRADE_SUPPORT */
7956#ifdef HAVE_MESSAGES
7957 MHD_DLOG (daemon,
7958 _ ("Application used status code 101 \"Switching Protocols\", " \
7959 "but this MHD was built without \"Upgrade\" support!\n"));
7960#endif /* HAVE_MESSAGES */
7961 return MHD_NO;
7962#endif /* ! UPGRADE_SUPPORT */
7963 }
7964 if ( (100 > status_code) ||
7965 (999 < status_code) )
7966 {
7967#ifdef HAVE_MESSAGES
7968 MHD_DLOG (daemon,
7969 _ ("Refused wrong status code (%u). " \
7970 "HTTP requires three digits status code!\n"),
7971 status_code);
7972#endif
7973 return MHD_NO;
7974 }
7975 if (200 > status_code)
7976 {
7977 if (MHD_HTTP_VER_1_0 == connection->rq.http_ver)
7978 {
7979#ifdef HAVE_MESSAGES
7980 MHD_DLOG (daemon,
7981 _ ("Wrong status code (%u) refused. " \
7982 "HTTP/1.0 clients do not support 1xx status codes!\n"),
7983 (status_code));
7984#endif
7985 return MHD_NO;
7986 }
7987 if (0 != (response->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
7989 {
7990#ifdef HAVE_MESSAGES
7991 MHD_DLOG (daemon,
7992 _ ("Wrong status code (%u) refused. " \
7993 "HTTP/1.0 reply mode does not support 1xx status codes!\n"),
7994 (status_code));
7995#endif
7996 return MHD_NO;
7997 }
7998 }
7999 if ( (MHD_HTTP_MTHD_CONNECT == connection->rq.http_mthd) &&
8000 (2 == status_code / 100) )
8001 {
8002#ifdef HAVE_MESSAGES
8003 MHD_DLOG (daemon,
8004 _ ("Successful (%u) response code cannot be used to answer " \
8005 "\"CONNECT\" request!\n"),
8006 (status_code));
8007#endif
8008 return MHD_NO;
8009 }
8010
8011 if ( (0 != (MHD_RF_HEAD_ONLY_RESPONSE & response->flags)) &&
8012 (RP_BODY_HEADERS_ONLY < is_reply_body_needed (connection, status_code)) )
8013 {
8014#ifdef HAVE_MESSAGES
8015 MHD_DLOG (daemon,
8016 _ ("HEAD-only response cannot be used when the request requires "
8017 "reply body to be sent!\n"));
8018#endif
8019 return MHD_NO;
8020 }
8021
8022#ifdef HAVE_MESSAGES
8023 if ( (0 != (MHD_RF_INSANITY_HEADER_CONTENT_LENGTH & response->flags)) &&
8024 (0 != (MHD_RAF_HAS_CONTENT_LENGTH & response->flags_auto)) )
8025 {
8026 MHD_DLOG (daemon,
8027 _ ("The response has application-defined \"Content-Length\" " \
8028 "header. The reply to the request will be not " \
8029 "HTTP-compliant and may result in hung connection or " \
8030 "other problems!\n"));
8031 }
8032#endif
8033
8034 MHD_increment_response_rc (response);
8035 connection->rp.response = response;
8036 connection->rp.responseCode = status_code;
8037 connection->rp.responseIcy = reply_icy;
8038#if defined(_MHD_HAVE_SENDFILE)
8039 if ( (response->fd == -1) ||
8040 (response->is_pipe) ||
8041 (0 != (connection->daemon->options & MHD_USE_TLS))
8042#if defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED) && \
8043 defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE)
8044 || (! daemon->sigpipe_blocked && ! connection->sk_spipe_suppress)
8045#endif /* MHD_SEND_SPIPE_SUPPRESS_NEEDED &&
8046 MHD_SEND_SPIPE_SUPPRESS_POSSIBLE */
8047 )
8048 connection->rp.resp_sender = MHD_resp_sender_std;
8049 else
8050 connection->rp.resp_sender = MHD_resp_sender_sendfile;
8051#endif /* _MHD_HAVE_SENDFILE */
8052 /* FIXME: if 'is_pipe' is set, TLS is off, and we have *splice*, we could use splice()
8053 to avoid two user-space copies... */
8054
8055 if ( (MHD_HTTP_MTHD_HEAD == connection->rq.http_mthd) ||
8056 (MHD_HTTP_OK > status_code) ||
8057 (MHD_HTTP_NO_CONTENT == status_code) ||
8058 (MHD_HTTP_NOT_MODIFIED == status_code) )
8059 {
8060 /* if this is a "HEAD" request, or a status code for
8061 which a body is not allowed, pretend that we
8062 have already sent the full message body. */
8063 /* TODO: remove the next assignment, use 'rp_props.send_reply_body' in
8064 * checks */
8065 connection->rp.rsp_write_position = response->total_size;
8066 }
8067 if (MHD_CONNECTION_HEADERS_PROCESSED == connection->state)
8068 {
8069 /* response was queued "early", refuse to read body / footers or
8070 further requests! */
8071 connection->discard_request = true;
8072 connection->state = MHD_CONNECTION_START_REPLY;
8073 connection->rq.remaining_upload_size = 0;
8074 }
8075 if (! connection->in_idle)
8076 (void) MHD_connection_handle_idle (connection);
8077 MHD_update_last_activity_ (connection);
8078 return MHD_YES;
8079}
8080
8081
8082/* 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)
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)
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
enum MHD_HdrLineReadRes_ _MHD_FIXED_ENUM
#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_connection_finish_forward_(conn)
Definition connection.h:174
#define MHD_ERR_TLS_
Definition connection.h:78
#define MHD_ERR_OPNOTSUPP_
Definition connection.h:68
#define MHD_ERR_PIPE_
Definition connection.h:73
static int parse_cookie_header(struct MHD_Request *request)
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:590
#define MHD_HTTP_HEADER_CONNECTION
Definition microhttpd.h:584
#define MHD_HTTP_HEADER_TRANSFER_ENCODING
Definition microhttpd.h:648
#define MHD_HTTP_HEADER_COOKIE
Definition microhttpd.h:748
#define MHD_HTTP_HEADER_EXPECT
Definition microhttpd.h:602
#define MHD_HTTP_HEADER_LOCATION
Definition microhttpd.h:622
#define MHD_HTTP_HEADER_HOST
Definition microhttpd.h:608
#define MHD_HTTP_INTERNAL_SERVER_ERROR
Definition microhttpd.h:447
#define MHD_HTTP_OK
Definition microhttpd.h:344
#define MHD_HTTP_MOVED_PERMANENTLY
Definition microhttpd.h:368
#define MHD_HTTP_URI_TOO_LONG
Definition microhttpd.h:413
#define MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE
Definition microhttpd.h:441
#define MHD_HTTP_PROCESSING
Definition microhttpd.h:339
#define MHD_HTTP_NOT_IMPLEMENTED
Definition microhttpd.h:449
#define MHD_HTTP_SWITCHING_PROTOCOLS
Definition microhttpd.h:337
#define MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED
Definition microhttpd.h:457
#define MHD_HTTP_CONTENT_TOO_LARGE
Definition microhttpd.h:411
#define MHD_HTTP_NOT_MODIFIED
Definition microhttpd.h:374
#define MHD_HTTP_NO_CONTENT
Definition microhttpd.h:352
#define MHD_HTTP_BAD_REQUEST
Definition microhttpd.h:385
#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
_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:550
_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
bool MHD_parse_arguments_(struct MHD_Request *request, enum MHD_ValueKind kind, char *args, MHD_ArgumentIterator_ cb, unsigned int *num_headers)
Definition internal.c:190
#define MHD_ERR_INVAL_
Definition internal.h:1889
#define MHD_ERR_CONNRESET_
Definition internal.h:1868
#define XDLL_insert(head, tail, element)
Definition internal.h:1786
#define MHD_ERR_NOMEM_
Definition internal.h:1879
MHD_EpollState
Definition internal.h:588
@ MHD_EPOLL_STATE_SUSPENDED
Definition internal.h:621
@ MHD_EPOLL_STATE_IN_EREADY_EDLL
Definition internal.h:611
@ MHD_EPOLL_STATE_READ_READY
Definition internal.h:600
@ MHD_EPOLL_STATE_IN_EPOLL_SET
Definition internal.h:616
@ MHD_EPOLL_STATE_WRITE_READY
Definition internal.h:606
#define DLL_insert(head, tail, element)
Definition internal.h:1743
#define EDLL_insert(head, tail, element)
Definition internal.h:1829
#define MHD_PANIC(msg)
Definition internal.h:69
#define MHD_MIN(a, b)
Definition internal.h:110
MHD_ConnKeepAlive
Definition internal.h:155
@ MHD_CONN_USE_KEEPALIVE
Definition internal.h:169
@ MHD_CONN_MUST_CLOSE
Definition internal.h:159
@ MHD_CONN_KEEPALIVE_UNKOWN
Definition internal.h:164
#define MHD_ERR_AGAIN_
Definition internal.h:1863
#define MHD_BUF_INC_SIZE
Definition internal.h:120
#define EDLL_remove(head, tail, element)
Definition internal.h:1847
#define XDLL_remove(head, tail, element)
Definition internal.h:1806
#define DLL_remove(head, tail, element)
Definition internal.h:1763
#define MHD_ERR_BADF_
Definition internal.h:1884
#define MHD_ERR_NOTCONN_
Definition internal.h:1874
void * MHD_pool_reallocate(struct MemoryPool *pool, void *old, size_t old_size, size_t new_size)
Definition memorypool.c:248
void MHD_pool_destroy(struct MemoryPool *pool)
Definition memorypool.c:157
size_t MHD_pool_get_free(struct MemoryPool *pool)
Definition memorypool.c:185
void * MHD_pool_reset(struct MemoryPool *pool, void *keep, size_t copy_bytes, size_t new_size)
Definition memorypool.c:314
void * MHD_pool_allocate(struct MemoryPool *pool, size_t size, int from_end)
Definition memorypool.c:203
#define mhd_assert(CHK)
Definition mhd_assert.h:39
#define UINT64_MAX
Definition mhd_limits.h:81
#define SIZE_MAX
Definition mhd_limits.h:99
#define UINT_MAX
Definition mhd_limits.h:45
#define MHD_mutex_unlock_chk_(pmutex)
Definition mhd_locks.h:180
#define MHD_mutex_lock_chk_(pmutex)
Definition mhd_locks.h:154
#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)
int MHD_str_equal_caseless_(const char *str1, const char *str2)
Definition mhd_str.c:346
size_t MHD_str_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition mhd_str.c:515
size_t MHD_strx_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition mhd_str.c:692
int MHD_str_equal_caseless_n_(const char *const str1, const char *const str2, size_t maxlen)
Definition mhd_str.c:378
bool MHD_str_has_token_caseless_(const char *str, const char *const token, size_t token_len)
Definition mhd_str.c:412
#define MHD_str_has_s_token_caseless_(str, tkn)
Definition mhd_str.h:115
#define MHD_STATICSTR_LEN_(macro)
Definition mhd_str.h:45
#define NULL
#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.
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
@ 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
@ 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
struct MHD_IoVec MHD_iovec_
Definition internal.h:440
#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_CONN_MUST_UPGRADE
Definition internal.h:837
#define MHD_IS_HTTP_VER_1_1_COMPAT(ver)
Definition internal.h:890
@ 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 PRIu64
Definition internal.h:53
#define MHD_D_IS_USING_THREAD_PER_CONN_(d)
Definition internal.h:2578
#define MHD_D_IS_USING_THREADS_(d)
Definition internal.h:2574
#define MHD_D_IS_USING_EPOLL_(d)
Definition internal.h:2550
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
memory pool; mostly used for efficient (de)allocation for each connection and bounding memory use for...
macros for mhd_assert()
Header for platform missing functions.
Header for platform-independent inter-thread communication.
limits values definitions
#define SSIZE_MAX
Definition mhd_limits.h:121
uint64_t MHD_monotonic_msec_counter(void)
internal monotonic clock functions implementations
#define MHD_SEND_SPIPE_SUPPRESS_NEEDED
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_uint64_to_str(uint64_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1591
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_thread_handle_ID_is_current_thread_(hndl_id)
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
#define MHD_SIZE_UNKNOWN
Definition microhttpd.h:178
MHD_Result
Definition microhttpd.h:158
@ MHD_YES
Definition microhttpd.h:167
@ MHD_NO
Definition microhttpd.h:162
#define MHD_CONTENT_READER_END_OF_STREAM
Definition microhttpd.h:181
_MHD_EXTERN size_t MHD_get_reason_phrase_len_for(unsigned int code)
#define MHD_INVALID_SOCKET
Definition microhttpd.h:202
_MHD_EXTERN const char * MHD_get_reason_phrase_for(unsigned int code)
#define MHD_CONTENT_READER_END_WITH_ERROR
Definition microhttpd.h:182
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
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:752
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:733
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
void * socket_context
Definition internal.h:694
bool discard_request
Definition internal.h:1535
ReceiveCallback recv_cls
Definition internal.h:706
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
struct sockaddr_storage addr
Definition internal.h:728
struct MemoryPool * pool
Definition internal.h:685
size_t read_buffer_offset
Definition internal.h:1436
struct MHD_Reply rp
Definition internal.h:1370
time_t last_activity
Definition internal.h:739
enum MHD_CONNECTION_STATE state
Definition internal.h:1565
char * read_buffer
Definition internal.h:1403
struct MHD_Daemon * daemon
Definition internal.h:675
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
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
int client_discipline
Definition internal.h:2265
void * unescape_callback_cls
Definition internal.h:2072
MHD_mutex_ cleanup_connection_mutex
Definition internal.h:1265
struct MHD_Connection * connections_head
Definition internal.h:1155
MHD_RequestCompletedCallback notify_completed
Definition internal.h:2032
struct MHD_itc_ itc
Definition internal.h:1410
uint64_t connection_timeout_ms
Definition internal.h:2253
struct MHD_Connection * manual_timeout_tail
Definition internal.h:1150
volatile bool shutdown
Definition internal.h:1526
enum MHD_FLAG options
Definition internal.h:1880
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 * cleanup_tail
Definition internal.h:1182
struct MHD_Daemon * worker_pool
Definition internal.h:1073
struct MHD_Connection * manual_timeout_head
Definition internal.h:1143
void * default_handler_cls
Definition internal.h:1872
struct MHD_Connection * suspended_connections_tail
Definition internal.h:1172
struct MHD_Connection * cleanup_head
Definition internal.h:1177
struct MHD_Connection * normal_timeout_head
Definition internal.h:1128
struct MHD_Connection * normal_timeout_tail
Definition internal.h:1135
size_t pool_increment
Definition internal.h:2141
void * uri_log_callback_cls
Definition internal.h:2062
struct MHD_Connection * suspended_connections_head
Definition internal.h:1166
struct MHD_Connection * connections_tail
Definition internal.h:1160
struct MHD_HTTP_Header * next
Definition internal.h:342
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
union MHD_HeadersProcessing hdrs
Definition internal.h:1241
uint64_t current_chunk_size
Definition internal.h:516
uint64_t current_chunk_offset
Definition internal.h:522
struct MHD_HTTP_Header * headers_received
Definition internal.h:388
struct MHD_HTTP_Header * headers_received_tail
Definition internal.h:393
bool some_payload_processed
Definition internal.h:1184
size_t skipped_broken_lines
Definition internal.h:1236
enum MHD_Method method
Definition internal.h:554
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
void * client_context
Definition internal.h:401
size_t req_target_len
Definition internal.h:1106
size_t header_size
Definition internal.h:502
const char * url
Definition internal.h:413
union MHD_StartOrSize field_lines
Definition internal.h:1134
enum MHD_HTTP_Method http_mthd
Definition internal.h:1090
bool client_aware
Definition internal.h:1199
bool have_chunked_upload
Definition internal.h:580
uint64_t remaining_upload_size
Definition internal.h:508
void * crc_cls
Definition internal.h:1594
size_t data_buffer_size
Definition internal.h:1664
MHD_iovec_ * data_iov
Definition internal.h:588
enum MHD_HTTP_StatusCode status_code
Definition internal.h:1669
uint64_t data_start
Definition internal.h:1648
MHD_ContentReaderCallback crc
Definition internal.h:1600
enum MHD_ResponseAutoFlags flags_auto
Definition internal.h:578
unsigned int data_iovcnt
Definition internal.h:593
size_t data_size
Definition internal.h:1659
enum MHD_ResponseFlags flags
Definition internal.h:573
char * data
Definition internal.h:1588
MHD_mutex_ mutex
Definition internal.h:1637
uint64_t total_size
Definition internal.h:1642
struct MHD_HTTP_Header * first_header
Definition internal.h:1582
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