GNU libmicrohttpd 1.0.4
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
361
366#ifdef HAVE_MESSAGES
367#define ERR_RSP_INVALID_CHAR_IN_FIELD_NAME \
368 "<html>" \
369 "<head><title>Request broken</title></head>" \
370 "<body>HTTP request has invalid character in field name.</body>" \
371 "</html>"
372#else
373#define ERR_RSP_INVALID_CHAR_IN_FIELD_NAME ""
374#endif
375
379#ifdef HAVE_MESSAGES
380#define ERR_RSP_INVALID_CHR_IN_HEADER \
381 "<html>" \
382 "<head><title>Request broken</title></head>" \
383 "<body>HTTP request has invalid character in header.</body>" \
384 "</html>"
385#else
386#define ERR_RSP_INVALID_CHR_IN_HEADER ""
387#endif
388
392#ifdef HAVE_MESSAGES
393#define ERR_RSP_INVALID_CHR_IN_FOOTER \
394 "<html>" \
395 "<head><title>Request broken</title></head>" \
396 "<body>HTTP request has invalid character in footer.</body>" \
397 "</html>"
398#else
399#define ERR_RSP_INVALID_CHR_IN_FOOTER ""
400#endif
401
405#ifdef HAVE_MESSAGES
406#define ERR_RSP_HEADER_WITHOUT_COLON \
407 "<html>" \
408 "<head><title>Request broken</title></head>" \
409 "<body>HTTP request header line has no colon character.</body>" \
410 "</html>"
411#else
412#define ERR_RSP_HEADER_WITHOUT_COLON ""
413#endif
414
418#ifdef HAVE_MESSAGES
419#define ERR_RSP_FOOTER_WITHOUT_COLON \
420 "<html>" \
421 "<head><title>Request broken</title></head>" \
422 "<body>HTTP request footer line has no colon character.</body>" \
423 "</html>"
424#else
425#define ERR_RSP_FOOTER_WITHOUT_COLON ""
426#endif
427
431#ifdef HAVE_MESSAGES
432#define ERR_RSP_EMPTY_HEADER_NAME \
433 "<html>" \
434 "<head><title>Request broken</title></head>" \
435 "<body>HTTP request header has empty header name.</body>" \
436 "</html>"
437#else
438#define ERR_RSP_EMPTY_HEADER_NAME ""
439#endif
440
444#ifdef HAVE_MESSAGES
445#define ERR_RSP_EMPTY_FOOTER_NAME \
446 "<html>" \
447 "<head><title>Request broken</title></head>" \
448 "<body>HTTP request footer has empty footer name.</body>" \
449 "</html>"
450#else
451#define ERR_RSP_EMPTY_FOOTER_NAME ""
452#endif
453
461#ifdef HAVE_MESSAGES
462#define REQUEST_LACKS_HOST \
463 "<html>" \
464 "<head><title>&quot;Host:&quot; header required</title></head>" \
465 "<body>HTTP/1.1 request without <b>&quot;Host:&quot;</b>.</body>" \
466 "</html>"
467
468#else
469#define REQUEST_LACKS_HOST ""
470#endif
471
472
477#ifdef HAVE_MESSAGES
478#define REQUEST_AMBIGUOUS_CONTENT_LENGTH \
479 "<html>" \
480 "<head><title>&quot;Content-Length:&quot; header must be unique</title></head>" \
481 "<body>HTTP/1.1 request with multiple <b>&quot;Content-Length:&quot;</b> headers.</body>" \
482 "</html>"
483
484#else
485#define REQUEST_AMBIGUOUS_CONTENT_LENGTH ""
486#endif
487
491#ifdef HAVE_MESSAGES
492#define REQUEST_UNSUPPORTED_TR_ENCODING \
493 "<html>" \
494 "<head><title>Unsupported Transfer-Encoding</title></head>" \
495 "<body>The Transfer-Encoding used in request is not supported.</body>" \
496 "</html>"
497#else
498#define REQUEST_UNSUPPORTED_TR_ENCODING ""
499#endif
500
505#ifdef HAVE_MESSAGES
506#define REQUEST_LENGTH_WITH_TR_ENCODING \
507 "<html>" \
508 "<head><title>Malformed request</title></head>" \
509 "<body>Wrong combination of the request headers: both Transfer-Encoding " \
510 "and Content-Length headers are used at the same time.</body>" \
511 "</html>"
512#else
513#define REQUEST_LENGTH_WITH_TR_ENCODING ""
514#endif
515
523#ifdef HAVE_MESSAGES
524#define REQUEST_MALFORMED \
525 "<html><head><title>Request malformed</title></head>" \
526 "<body>HTTP request is syntactically incorrect.</body></html>"
527#else
528#define REQUEST_MALFORMED ""
529#endif
530
534#define REQUEST_HAS_NUL_CHAR_IN_PATH \
535 "<html><head><title>Bad Request Path</title></head>" \
536 "<body>The request path contains invalid characters.</body></html>"
537
542#ifdef HAVE_MESSAGES
543#define REQUEST_CHUNKED_MALFORMED \
544 "<html><head><title>Request malformed</title></head>" \
545 "<body>HTTP chunked encoding is syntactically incorrect.</body></html>"
546#else
547#define REQUEST_CHUNKED_MALFORMED ""
548#endif
549
553#ifdef HAVE_MESSAGES
554#define REQUEST_CHUNK_TOO_LARGE \
555 "<html><head><title>Request content too large</title></head>" \
556 "<body>The chunk size used in HTTP chunked encoded " \
557 "request is too large.</body></html>"
558#else
559#define REQUEST_CHUNK_TOO_LARGE ""
560#endif
561
565#ifdef HAVE_MESSAGES
566#define REQUEST_CONTENTLENGTH_TOOLARGE \
567 "<html><head><title>Request content too large</title></head>" \
568 "<body>HTTP request has too large value for " \
569 "<b>Content-Length</b> header.</body></html>"
570#else
571#define REQUEST_CONTENTLENGTH_TOOLARGE ""
572#endif
573
578#ifdef HAVE_MESSAGES
579#define REQUEST_CONTENTLENGTH_MALFORMED \
580 "<html><head><title>Request malformed</title></head>" \
581 "<body>HTTP request has wrong value for " \
582 "<b>Content-Length</b> header.</body></html>"
583#else
584#define REQUEST_CONTENTLENGTH_MALFORMED ""
585#endif
586
593#ifdef HAVE_MESSAGES
594#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP \
595 "<html><head><title>Internal server error</title></head>" \
596 "<body>Please ask the developer of this Web server to carefully " \
597 "read the GNU libmicrohttpd documentation about connection " \
598 "management and blocking.</body></html>"
599#else
600#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP ""
601#endif
602
606#ifdef HAVE_MESSAGES
607#define REQ_HTTP_VER_IS_TOO_OLD \
608 "<html><head><title>Requested HTTP version is not supported</title></head>" \
609 "<body>Requested HTTP version is too old and not " \
610 "supported.</body></html>"
611#else
612#define REQ_HTTP_VER_IS_TOO_OLD ""
613#endif
614
618#ifdef HAVE_MESSAGES
619#define REQ_HTTP_VER_IS_NOT_SUPPORTED \
620 "<html><head><title>Requested HTTP version is not supported</title></head>" \
621 "<body>Requested HTTP version is not supported.</body></html>"
622#else
623#define REQ_HTTP_VER_IS_NOT_SUPPORTED ""
624#endif
625
626
630#define MHD_SENFILE_CHUNK_ (0x20000)
631
635#define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000)
636
637#ifdef HAVE_MESSAGES
643static const char *
644str_conn_error_ (ssize_t mhd_err_code)
645{
646 switch (mhd_err_code)
647 {
648 case MHD_ERR_AGAIN_:
649 return _ ("The operation would block, retry later");
651 return _ ("The connection was forcibly closed by remote peer");
652 case MHD_ERR_NOTCONN_:
653 return _ ("The socket is not connected");
654 case MHD_ERR_NOMEM_:
655 return _ ("Not enough system resources to serve the request");
656 case MHD_ERR_BADF_:
657 return _ ("Bad FD value");
658 case MHD_ERR_INVAL_:
659 return _ ("Argument value is invalid");
661 return _ ("Argument value is not supported");
662 case MHD_ERR_PIPE_:
663 return _ ("The socket is no longer available for sending");
664 case MHD_ERR_TLS_:
665 return _ ("TLS encryption or decryption error");
666 default:
667 break; /* Mute compiler warning */
668 }
669 if (0 <= mhd_err_code)
670 return _ ("Not an error code");
671
672 mhd_assert (0); /* Should never be reachable */
673 return _ ("Wrong error code value");
674}
675
676
677#endif /* HAVE_MESSAGES */
678
688void *
690 size_t size)
691{
692 struct MHD_Connection *const c = connection; /* a short alias */
693 struct MemoryPool *const pool = c->pool; /* a short alias */
694 size_t need_to_be_freed = 0;
695 void *res;
696
697 res = MHD_pool_try_alloc (pool,
698 size,
699 &need_to_be_freed);
700 if (NULL != res)
701 return res;
702
704 c->write_buffer,
706 {
708 need_to_be_freed)
709 {
710 char *buf;
711 const size_t new_buf_size = c->write_buffer_size - need_to_be_freed;
712 buf = MHD_pool_reallocate (pool,
713 c->write_buffer,
715 new_buf_size);
716 mhd_assert (c->write_buffer == buf);
717 mhd_assert (c->write_buffer_append_offset <= new_buf_size);
718 mhd_assert (c->write_buffer_send_offset <= new_buf_size);
719 c->write_buffer_size = new_buf_size;
720 c->write_buffer = buf;
721 }
722 else
723 return NULL;
724 }
725 else if (MHD_pool_is_resizable_inplace (pool,
726 c->read_buffer,
728 {
729 if (c->read_buffer_size - c->read_buffer_offset >= need_to_be_freed)
730 {
731 char *buf;
732 const size_t new_buf_size = c->read_buffer_size - need_to_be_freed;
733 buf = MHD_pool_reallocate (pool,
734 c->read_buffer,
736 new_buf_size);
737 mhd_assert (c->read_buffer == buf);
738 mhd_assert (c->read_buffer_offset <= new_buf_size);
739 c->read_buffer_size = new_buf_size;
740 c->read_buffer = buf;
741 }
742 else
743 return NULL;
744 }
745 else
746 return NULL;
747 res = MHD_pool_allocate (pool, size, true);
748 mhd_assert (NULL != res); /* It has been checked that pool has enough space */
749 return res;
750}
751
752
762static ssize_t
764 void *other,
765 size_t i)
766{
767 ssize_t ret;
768
769 if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
770 (MHD_CONNECTION_CLOSED == connection->state) )
771 {
772 return MHD_ERR_NOTCONN_;
773 }
775 i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */
776
777 ret = MHD_recv_ (connection->socket_fd,
778 other,
779 i);
780 if (0 > ret)
781 {
782 const int err = MHD_socket_get_error_ ();
783 if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
784 {
785#ifdef EPOLL_SUPPORT
786 /* Got EAGAIN --- no longer read-ready */
787 connection->epoll_state &=
789#endif /* EPOLL_SUPPORT */
790 return MHD_ERR_AGAIN_;
791 }
792 if (MHD_SCKT_ERR_IS_EINTR_ (err))
793 return MHD_ERR_AGAIN_;
795 return MHD_ERR_CONNRESET_;
797 return MHD_ERR_OPNOTSUPP_;
799 return MHD_ERR_NOTCONN_;
801 return MHD_ERR_INVAL_;
803 return MHD_ERR_NOMEM_;
805 return MHD_ERR_BADF_;
806 /* Treat any other error as a hard error. */
807 return MHD_ERR_NOTCONN_;
808 }
809#ifdef EPOLL_SUPPORT
810 else if (i > (size_t) ret)
811 connection->epoll_state &=
813#endif /* EPOLL_SUPPORT */
814 return ret;
815}
816
817
820 const char **uri,
821 size_t *uri_size)
822{
823 if (NULL != uri)
824 *uri = NULL;
825 if (NULL != uri_size)
826 *uri_size = 0u;
827
828 if (connection->state < MHD_CONNECTION_REQ_LINE_RECEIVED)
829 return MHD_NO;
830 if (connection->state >= MHD_CONNECTION_START_REPLY)
831 return MHD_NO;
832 if (NULL == connection->rq.url)
833 return MHD_NO;
834
835 if (NULL != uri)
836 *uri = connection->rq.url;
837 if (NULL != uri_size)
838 *uri_size = connection->rq.url_len;
839
840 return MHD_YES;
841}
842
843
856_MHD_EXTERN int
858 enum MHD_ValueKind kind,
859 MHD_KeyValueIterator iterator,
860 void *iterator_cls)
861{
862 int ret;
863 struct MHD_HTTP_Req_Header *pos;
864
865 if (NULL == connection)
866 return -1;
867 ret = 0;
868 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
869 if (0 != (pos->kind & kind))
870 {
871 ret++;
872 if ( (NULL != iterator) &&
873 (MHD_NO == iterator (iterator_cls,
874 pos->kind,
875 pos->header,
876 pos->value)) )
877 return ret;
878 }
879 return ret;
880}
881
882
895_MHD_EXTERN int
897 enum MHD_ValueKind kind,
898 MHD_KeyValueIteratorN iterator,
899 void *iterator_cls)
900{
901 int ret;
902 struct MHD_HTTP_Req_Header *pos;
903
904 if (NULL == connection)
905 return -1;
906 ret = 0;
907
908 if (NULL == iterator)
909 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
910 {
911 if (0 != (kind & pos->kind))
912 ret++;
913 }
914 else
915 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
916 if (0 != (kind & pos->kind))
917 {
918 ret++;
919 if (MHD_NO == iterator (iterator_cls,
920 pos->kind,
921 pos->header,
922 pos->header_size,
923 pos->value,
924 pos->value_size))
925 return ret;
926 }
927 return ret;
928}
929
930
948static enum MHD_Result
950 enum MHD_ValueKind kind,
951 const char *key,
952 size_t key_size,
953 const char *value,
954 size_t value_size)
955{
956 struct MHD_HTTP_Req_Header *pos;
957
958 pos = MHD_connection_alloc_memory_ (connection,
959 sizeof (struct MHD_HTTP_Res_Header));
960 if (NULL == pos)
961 return MHD_NO;
962 pos->header = key;
963 pos->header_size = key_size;
964 pos->value = value;
965 pos->value_size = value_size;
966 pos->kind = kind;
967 pos->next = NULL;
968 /* append 'pos' to the linked list of headers */
969 if (NULL == connection->rq.headers_received_tail)
970 {
971 connection->rq.headers_received = pos;
972 connection->rq.headers_received_tail = pos;
973 }
974 else
975 {
976 connection->rq.headers_received_tail->next = pos;
977 connection->rq.headers_received_tail = pos;
978 }
979 return MHD_YES;
980}
981
982
1010 enum MHD_ValueKind kind,
1011 const char *key,
1012 size_t key_size,
1013 const char *value,
1014 size_t value_size)
1015{
1016 if ( (MHD_GET_ARGUMENT_KIND != kind) &&
1017 ( ((key ? strlen (key) : 0) != key_size) ||
1018 ((value ? strlen (value) : 0) != value_size) ) )
1019 return MHD_NO; /* binary zero is allowed only in GET arguments */
1020
1021 return MHD_set_connection_value_n_nocheck_ (connection,
1022 kind,
1023 key,
1024 key_size,
1025 value,
1026 value_size);
1027}
1028
1029
1057 enum MHD_ValueKind kind,
1058 const char *key,
1059 const char *value)
1060{
1061 return MHD_set_connection_value_n_nocheck_ (connection,
1062 kind,
1063 key,
1064 NULL != key
1065 ? strlen (key)
1066 : 0,
1067 value,
1068 NULL != value
1069 ? strlen (value)
1070 : 0);
1071}
1072
1073
1084_MHD_EXTERN const char *
1086 enum MHD_ValueKind kind,
1087 const char *key)
1088{
1089 const char *value;
1090
1091 value = NULL;
1092 (void) MHD_lookup_connection_value_n (connection,
1093 kind,
1094 key,
1095 (NULL == key) ? 0 : strlen (key),
1096 &value,
1097 NULL);
1098 return value;
1099}
1100
1101
1123 enum MHD_ValueKind kind,
1124 const char *key,
1125 size_t key_size,
1126 const char **value_ptr,
1127 size_t *value_size_ptr)
1128{
1129 struct MHD_HTTP_Req_Header *pos;
1130
1131 if (NULL == connection)
1132 return MHD_NO;
1133
1134 if (NULL == key)
1135 {
1136 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1137 {
1138 if ( (0 != (kind & pos->kind)) &&
1139 (NULL == pos->header) )
1140 break;
1141 }
1142 }
1143 else
1144 {
1145 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1146 {
1147 if ( (0 != (kind & pos->kind)) &&
1148 (key_size == pos->header_size) &&
1149 ( (key == pos->header) ||
1151 pos->header,
1152 key_size) ) ) )
1153 break;
1154 }
1155 }
1156
1157 if (NULL == pos)
1158 return MHD_NO;
1159
1160 if (NULL != value_ptr)
1161 *value_ptr = pos->value;
1162
1163 if (NULL != value_size_ptr)
1164 *value_size_ptr = pos->value_size;
1165
1166 return MHD_YES;
1167}
1168
1169
1185static bool
1187 const char *header,
1188 size_t header_len,
1189 const char *token,
1190 size_t token_len)
1191{
1192 struct MHD_HTTP_Req_Header *pos;
1193
1194 if ((NULL == connection) || (NULL == header) || (0 == header[0]) ||
1195 (NULL == token) || (0 == token[0]))
1196 return false;
1197
1198 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1199 {
1200 if ((0 != (pos->kind & MHD_HEADER_KIND)) &&
1201 (header_len == pos->header_size) &&
1202 ( (header == pos->header) ||
1204 pos->header,
1205 header_len)) ) &&
1206 (MHD_str_has_token_caseless_ (pos->value, token, token_len)))
1207 return true;
1208 }
1209 return false;
1210}
1211
1212
1224#define MHD_lookup_header_s_token_ci(c,h,tkn) \
1225 MHD_lookup_header_token_ci ((c),(h),MHD_STATICSTR_LEN_ (h), \
1226 (tkn),MHD_STATICSTR_LEN_ (tkn))
1227
1228
1236static bool
1238{
1239 const char *expect;
1240
1241 if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
1242 return false;
1243
1244 if (0 == connection->rq.remaining_upload_size)
1245 return false;
1246
1247 if (MHD_NO ==
1253 &expect,
1254 NULL))
1255 return false;
1256
1257 if (MHD_str_equal_caseless_ (expect,
1258 "100-continue"))
1259 return true;
1260
1261 return false;
1262}
1263
1264
1271void
1273{
1274 const struct MHD_Daemon *daemon = connection->daemon;
1275
1276 if (0 == (daemon->options & MHD_USE_TURBO))
1277 {
1278#ifdef HTTPS_SUPPORT
1279 /* For TLS connection use shutdown of TLS layer
1280 * and do not shutdown TCP socket. This give more
1281 * chances to send TLS closure data to remote side.
1282 * Closure of TLS layer will be interpreted by
1283 * remote side as end of transmission. */
1284 if (0 != (daemon->options & MHD_USE_TLS))
1285 {
1286 if (! MHD_tls_connection_shutdown (connection))
1287 shutdown (connection->socket_fd,
1288 SHUT_WR);
1289 }
1290 else /* Combined with next 'shutdown()'. */
1291#endif /* HTTPS_SUPPORT */
1292 shutdown (connection->socket_fd,
1293 SHUT_WR);
1294 }
1295 connection->state = MHD_CONNECTION_CLOSED;
1297}
1298
1299
1309void
1311 enum MHD_RequestTerminationCode termination_code)
1312{
1313 struct MHD_Daemon *daemon = connection->daemon;
1314 struct MHD_Response *resp = connection->rp.response;
1315
1316 mhd_assert (! connection->suspended);
1317#ifdef MHD_USE_THREADS
1318 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
1319 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
1320#endif /* MHD_USE_THREADS */
1321 if ( (NULL != daemon->notify_completed) &&
1322 (connection->rq.client_aware) )
1323 daemon->notify_completed (daemon->notify_completed_cls,
1324 connection,
1325 &connection->rq.client_context,
1326 termination_code);
1327 connection->rq.client_aware = false;
1328 if (NULL != resp)
1329 {
1330 connection->rp.response = NULL;
1331 MHD_destroy_response (resp);
1332 }
1333 if (NULL != connection->pool)
1334 {
1335 MHD_pool_destroy (connection->pool);
1336 connection->pool = NULL;
1337 }
1338
1339 MHD_connection_mark_closed_ (connection);
1340}
1341
1342
1343#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
1354void
1356{
1357 struct MHD_Daemon *daemon = connection->daemon;
1358 struct MHD_UpgradeResponseHandle *urh = connection->urh;
1359
1360#ifdef MHD_USE_THREADS
1361 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
1364#endif /* MHD_USE_THREADS */
1365
1366 if (0 == (daemon->options & MHD_USE_TLS))
1367 return; /* Nothing to do with non-TLS connection. */
1368
1369 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
1370 DLL_remove (daemon->urh_head,
1371 daemon->urh_tail,
1372 urh);
1373#ifdef EPOLL_SUPPORT
1374 if (MHD_D_IS_USING_EPOLL_ (daemon) &&
1375 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
1376 EPOLL_CTL_DEL,
1377 connection->socket_fd,
1378 NULL)) )
1379 {
1380 MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
1381 }
1382 if (urh->in_eready_list)
1383 {
1384 EDLL_remove (daemon->eready_urh_head,
1385 daemon->eready_urh_tail,
1386 urh);
1387 urh->in_eready_list = false;
1388 }
1389#endif /* EPOLL_SUPPORT */
1390 if (MHD_INVALID_SOCKET != urh->mhd.socket)
1391 {
1392#ifdef EPOLL_SUPPORT
1393 if (MHD_D_IS_USING_EPOLL_ (daemon) &&
1394 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
1395 EPOLL_CTL_DEL,
1396 urh->mhd.socket,
1397 NULL)) )
1398 {
1399 MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
1400 }
1401#endif /* EPOLL_SUPPORT */
1402 /* Reflect remote disconnect to application by breaking
1403 * socketpair connection. */
1404 shutdown (urh->mhd.socket, SHUT_RDWR);
1405 }
1406 /* Socketpair sockets will remain open as they will be
1407 * used with MHD_UPGRADE_ACTION_CLOSE. They will be
1408 * closed by cleanup_upgraded_connection() during
1409 * connection's final cleanup.
1410 */
1411}
1412
1413
1414#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT*/
1415
1416
1424static void
1426 const char *emsg)
1427{
1428 connection->stop_with_error = true;
1429 connection->discard_request = true;
1430#ifdef HAVE_MESSAGES
1431 if (NULL != emsg)
1432 MHD_DLOG (connection->daemon,
1433 "%s\n",
1434 emsg);
1435#else /* ! HAVE_MESSAGES */
1436 (void) emsg; /* Mute compiler warning. */
1437#endif /* ! HAVE_MESSAGES */
1438 MHD_connection_close_ (connection,
1440}
1441
1442
1447#ifdef HAVE_MESSAGES
1448#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, emsg)
1449#else
1450#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, NULL)
1451#endif
1452
1453
1466static enum MHD_Result
1468{
1469 ssize_t ret;
1470 struct MHD_Response *response;
1471
1472 response = connection->rp.response;
1473 mhd_assert (connection->rp.props.send_reply_body);
1474
1475 if ( (0 == response->total_size) ||
1476 /* TODO: replace the next check with assert */
1477 (connection->rp.rsp_write_position == response->total_size) )
1478 return MHD_YES; /* 0-byte response is always ready */
1479 if (NULL != response->data_iov)
1480 {
1481 size_t copy_size;
1482
1483 if (NULL != connection->rp.resp_iov.iov)
1484 return MHD_YES;
1485 copy_size = response->data_iovcnt * sizeof(MHD_iovec_);
1486 connection->rp.resp_iov.iov = MHD_connection_alloc_memory_ (connection,
1487 copy_size);
1488 if (NULL == connection->rp.resp_iov.iov)
1489 {
1490 MHD_mutex_unlock_chk_ (&response->mutex);
1491 /* not enough memory */
1492 CONNECTION_CLOSE_ERROR (connection,
1493 _ ("Closing connection (out of memory)."));
1494 return MHD_NO;
1495 }
1496 memcpy (connection->rp.resp_iov.iov,
1497 response->data_iov,
1498 copy_size);
1499 connection->rp.resp_iov.cnt = response->data_iovcnt;
1500 connection->rp.resp_iov.sent = 0;
1501 return MHD_YES;
1502 }
1503 if (NULL == response->crc)
1504 return MHD_YES;
1505 if ( (response->data_start <=
1506 connection->rp.rsp_write_position) &&
1507 (response->data_size + response->data_start >
1508 connection->rp.rsp_write_position) )
1509 return MHD_YES; /* response already ready */
1510#if defined(_MHD_HAVE_SENDFILE)
1511 if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
1512 {
1513 /* will use sendfile, no need to bother response crc */
1514 return MHD_YES;
1515 }
1516#endif /* _MHD_HAVE_SENDFILE */
1517
1518 ret = response->crc (response->crc_cls,
1519 connection->rp.rsp_write_position,
1520 (char *) response->data,
1521 (size_t) MHD_MIN ((uint64_t) response->data_buffer_size,
1522 response->total_size
1523 - connection->rp.rsp_write_position));
1524 if (0 > ret)
1525 {
1526 /* either error or http 1.0 transfer, close socket! */
1527 /* TODO: do not update total size, check whether response
1528 * was really with unknown size */
1529 response->total_size = connection->rp.rsp_write_position;
1530#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1531 MHD_mutex_unlock_chk_ (&response->mutex);
1532#endif
1534 MHD_connection_close_ (connection,
1536 else
1537 CONNECTION_CLOSE_ERROR (connection,
1538 _ ("Closing connection (application reported " \
1539 "error generating data)."));
1540 return MHD_NO;
1541 }
1542 response->data_start = connection->rp.rsp_write_position;
1543 response->data_size = (size_t) ret;
1544 if (0 == ret)
1545 {
1547#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1548 MHD_mutex_unlock_chk_ (&response->mutex);
1549#endif
1550 return MHD_NO;
1551 }
1552 return MHD_YES;
1553}
1554
1555
1568static enum MHD_Result
1570 bool *p_finished)
1571{
1572 ssize_t ret;
1573 struct MHD_Response *response;
1574 static const size_t max_chunk = 0xFFFFFF;
1575 char chunk_hdr[6]; /* 6: max strlen of "FFFFFF" */
1576 /* "FFFFFF" + "\r\n" */
1577 static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2;
1578 /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */
1579 static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2;
1580 size_t chunk_hdr_len;
1581 uint64_t left_to_send;
1582 size_t size_to_fill;
1583
1584 response = connection->rp.response;
1585 mhd_assert (NULL != response->crc || NULL != response->data);
1586
1587 mhd_assert (0 == connection->write_buffer_append_offset);
1588
1589 /* The buffer must be reasonably large enough */
1590 if (128 > connection->write_buffer_size)
1591 {
1592 size_t size;
1593
1594 size = connection->write_buffer_size + MHD_pool_get_free (connection->pool);
1595 if (128 > size)
1596 {
1597#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1598 MHD_mutex_unlock_chk_ (&response->mutex);
1599#endif
1600 /* not enough memory */
1601 CONNECTION_CLOSE_ERROR (connection,
1602 _ ("Closing connection (out of memory)."));
1603 return MHD_NO;
1604 }
1605 /* Limit the buffer size to the largest usable size for chunks */
1606 if ( (max_chunk + max_chunk_overhead) < size)
1607 size = max_chunk + max_chunk_overhead;
1608 mhd_assert ((NULL == connection->write_buffer) || \
1609 MHD_pool_is_resizable_inplace (connection->pool, \
1610 connection->write_buffer, \
1611 connection->write_buffer_size));
1612 connection->write_buffer =
1613 MHD_pool_reallocate (connection->pool,
1614 connection->write_buffer,
1615 connection->write_buffer_size,
1616 size);
1617 mhd_assert (NULL != connection->write_buffer);
1618 connection->write_buffer_size = size;
1619 }
1620 mhd_assert (max_chunk_overhead < connection->write_buffer_size);
1621
1622 if (MHD_SIZE_UNKNOWN == response->total_size)
1623 left_to_send = MHD_SIZE_UNKNOWN;
1624 else
1625 left_to_send = response->total_size
1626 - connection->rp.rsp_write_position;
1627
1628 size_to_fill = connection->write_buffer_size - max_chunk_overhead;
1629 /* Limit size for the callback to the max usable size */
1630 if (max_chunk < size_to_fill)
1631 size_to_fill = max_chunk;
1632 if (left_to_send < size_to_fill)
1633 size_to_fill = (size_t) left_to_send;
1634
1635 if (0 == left_to_send)
1636 /* nothing to send, don't bother calling crc */
1638 else if ( (response->data_start <=
1639 connection->rp.rsp_write_position) &&
1640 (response->data_start + response->data_size >
1641 connection->rp.rsp_write_position) )
1642 {
1643 /* difference between rsp_write_position and data_start is less
1644 than data_size which is size_t type, no need to check for overflow */
1645 const size_t data_write_offset
1646 = (size_t) (connection->rp.rsp_write_position
1647 - response->data_start);
1648 /* buffer already ready, use what is there for the chunk */
1649 mhd_assert (SSIZE_MAX >= (response->data_size - data_write_offset));
1650 mhd_assert (response->data_size >= data_write_offset);
1651 ret = (ssize_t) (response->data_size - data_write_offset);
1652 if ( ((size_t) ret) > size_to_fill)
1653 ret = (ssize_t) size_to_fill;
1654 memcpy (&connection->write_buffer[max_chunk_hdr_len],
1655 &response->data[data_write_offset],
1656 (size_t) ret);
1657 }
1658 else
1659 {
1660 if (NULL == response->crc)
1661 { /* There is no way to reach this code */
1662#if defined(MHD_USE_THREADS)
1663 MHD_mutex_unlock_chk_ (&response->mutex);
1664#endif
1665 CONNECTION_CLOSE_ERROR (connection,
1666 _ ("No callback for the chunked data."));
1667 return MHD_NO;
1668 }
1669 ret = response->crc (response->crc_cls,
1670 connection->rp.rsp_write_position,
1671 &connection->write_buffer[max_chunk_hdr_len],
1672 size_to_fill);
1673 }
1675 {
1676 /* error, close socket! */
1677 /* TODO: remove update of the response size */
1678 response->total_size = connection->rp.rsp_write_position;
1679#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1680 MHD_mutex_unlock_chk_ (&response->mutex);
1681#endif
1682 CONNECTION_CLOSE_ERROR (connection,
1683 _ ("Closing connection (application error " \
1684 "generating response)."));
1685 return MHD_NO;
1686 }
1688 {
1689 *p_finished = true;
1690 /* TODO: remove update of the response size */
1691 response->total_size = connection->rp.rsp_write_position;
1692 return MHD_YES;
1693 }
1694 if (0 == ret)
1695 {
1697#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1698 MHD_mutex_unlock_chk_ (&response->mutex);
1699#endif
1700 return MHD_NO;
1701 }
1702 if (size_to_fill < (size_t) ret)
1703 {
1704#if defined(MHD_USE_THREADS)
1705 MHD_mutex_unlock_chk_ (&response->mutex);
1706#endif
1707 CONNECTION_CLOSE_ERROR (connection,
1708 _ ("Closing connection (application returned " \
1709 "more data than requested)."));
1710 return MHD_NO;
1711 }
1712 chunk_hdr_len = MHD_uint32_to_strx ((uint32_t) ret, chunk_hdr,
1713 sizeof(chunk_hdr));
1714 mhd_assert (chunk_hdr_len != 0);
1715 mhd_assert (chunk_hdr_len < sizeof(chunk_hdr));
1716 *p_finished = false;
1717 connection->write_buffer_send_offset =
1718 (max_chunk_hdr_len - (chunk_hdr_len + 2));
1719 memcpy (connection->write_buffer + connection->write_buffer_send_offset,
1720 chunk_hdr,
1721 chunk_hdr_len);
1722 connection->write_buffer[max_chunk_hdr_len - 2] = '\r';
1723 connection->write_buffer[max_chunk_hdr_len - 1] = '\n';
1724 connection->write_buffer[max_chunk_hdr_len + (size_t) ret] = '\r';
1725 connection->write_buffer[max_chunk_hdr_len + (size_t) ret + 1] = '\n';
1726 connection->rp.rsp_write_position += (size_t) ret;
1727 connection->write_buffer_append_offset = max_chunk_hdr_len + (size_t) ret + 2;
1728 return MHD_YES;
1729}
1730
1731
1754static enum MHD_ConnKeepAlive
1756{
1757 struct MHD_Connection *const c = connection;
1758 struct MHD_Response *const r = c->rp.response;
1759
1760 mhd_assert (NULL != r);
1762 return MHD_CONN_MUST_CLOSE;
1763
1764#ifdef UPGRADE_SUPPORT
1765 /* TODO: Move below the next check when MHD stops closing connections
1766 * when response is queued in first callback */
1767 if (NULL != r->upgrade_handler)
1768 {
1769 /* No "close" token is enforced by 'add_response_header_connection()' */
1771 /* Valid HTTP version is enforced by 'MHD_queue_response()' */
1774 return MHD_CONN_MUST_UPGRADE;
1775 }
1776#endif /* UPGRADE_SUPPORT */
1777
1778 mhd_assert ( (! c->stop_with_error) || (c->discard_request));
1779 if ((c->read_closed) || (c->discard_request))
1780 return MHD_CONN_MUST_CLOSE;
1781
1783 return MHD_CONN_MUST_CLOSE;
1785 return MHD_CONN_MUST_CLOSE;
1786
1788 return MHD_CONN_MUST_CLOSE;
1789
1792 "close"))
1793 return MHD_CONN_MUST_CLOSE;
1794
1795 if ((MHD_HTTP_VER_1_0 == connection->rq.http_ver) ||
1796 (0 != (connection->rp.response->flags & MHD_RF_HTTP_1_0_SERVER)))
1797 {
1798 if (MHD_lookup_header_s_token_ci (connection,
1800 "Keep-Alive"))
1802
1803 return MHD_CONN_MUST_CLOSE;
1804 }
1805
1808
1809 return MHD_CONN_MUST_CLOSE;
1810}
1811
1812
1822static bool
1823get_date_str (char *date)
1824{
1825 static const char *const days[] = {
1826 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1827 };
1828 static const char *const mons[] = {
1829 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1830 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1831 };
1832 static const size_t buf_len = 29;
1833 struct tm now;
1834 time_t t;
1835 const char *src;
1836#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
1837 ! defined(HAVE_GMTIME_R)
1838 struct tm *pNow;
1839#endif
1840
1841 if ((time_t) -1 == time (&t))
1842 return false;
1843#if defined(HAVE_C11_GMTIME_S)
1844 if (NULL == gmtime_s (&t,
1845 &now))
1846 return false;
1847#elif defined(HAVE_W32_GMTIME_S)
1848 if (0 != gmtime_s (&now,
1849 &t))
1850 return false;
1851#elif defined(HAVE_GMTIME_R)
1852 if (NULL == gmtime_r (&t,
1853 &now))
1854 return false;
1855#else
1856 pNow = gmtime (&t);
1857 if (NULL == pNow)
1858 return false;
1859 now = *pNow;
1860#endif
1861
1862 /* Day of the week */
1863 src = days[now.tm_wday % 7];
1864 date[0] = src[0];
1865 date[1] = src[1];
1866 date[2] = src[2];
1867 date[3] = ',';
1868 date[4] = ' ';
1869 /* Day of the month */
1870 if (2 != MHD_uint8_to_str_pad ((uint8_t) now.tm_mday, 2,
1871 date + 5, buf_len - 5))
1872 return false;
1873 date[7] = ' ';
1874 /* Month */
1875 src = mons[now.tm_mon % 12];
1876 date[8] = src[0];
1877 date[9] = src[1];
1878 date[10] = src[2];
1879 date[11] = ' ';
1880 /* Year */
1881 if (4 != MHD_uint16_to_str ((uint16_t) (1900 + now.tm_year), date + 12,
1882 buf_len - 12))
1883 return false;
1884 date[16] = ' ';
1885 /* Time */
1886 MHD_uint8_to_str_pad ((uint8_t) now.tm_hour, 2, date + 17, buf_len - 17);
1887 date[19] = ':';
1888 MHD_uint8_to_str_pad ((uint8_t) now.tm_min, 2, date + 20, buf_len - 20);
1889 date[22] = ':';
1890 MHD_uint8_to_str_pad ((uint8_t) now.tm_sec, 2, date + 23, buf_len - 23);
1891 date[25] = ' ';
1892 date[26] = 'G';
1893 date[27] = 'M';
1894 date[28] = 'T';
1895
1896 return true;
1897}
1898
1899
1907static bool
1908get_date_header (char *header)
1909{
1910 if (! get_date_str (header + 6))
1911 {
1912 header[0] = 0;
1913 return false;
1914 }
1915 header[0] = 'D';
1916 header[1] = 'a';
1917 header[2] = 't';
1918 header[3] = 'e';
1919 header[4] = ':';
1920 header[5] = ' ';
1921 header[35] = '\r';
1922 header[36] = '\n';
1923 header[37] = 0;
1924 return true;
1925}
1926
1927
1940static bool
1942 bool required)
1943{
1944 size_t new_size;
1945 size_t avail_size;
1946 const size_t def_grow_size = connection->daemon->pool_increment;
1947 void *rb;
1948
1949 avail_size = MHD_pool_get_free (connection->pool);
1950 if (0 == avail_size)
1951 return false; /* No more space available */
1952 if (0 == connection->read_buffer_size)
1953 new_size = avail_size / 2; /* Use half of available buffer for reading */
1954 else
1955 {
1956 size_t grow_size;
1957
1958 grow_size = avail_size / 8;
1959 if (def_grow_size > grow_size)
1960 { /* Shortage of space */
1961 const size_t left_free =
1962 connection->read_buffer_size - connection->read_buffer_offset;
1963 mhd_assert (connection->read_buffer_size >= \
1964 connection->read_buffer_offset);
1965 if ((def_grow_size <= grow_size + left_free)
1966 && (left_free < def_grow_size))
1967 grow_size = def_grow_size - left_free; /* Use precise 'def_grow_size' for new free space */
1968 else if (! required)
1969 return false; /* Grow is not mandatory, leave some space in pool */
1970 else
1971 {
1972 /* Shortage of space, but grow is mandatory */
1973 const size_t small_inc =
1974 ((MHD_BUF_INC_SIZE > def_grow_size) ?
1975 def_grow_size : MHD_BUF_INC_SIZE) / 8;
1976 if (small_inc < avail_size)
1977 grow_size = small_inc;
1978 else
1979 grow_size = avail_size;
1980 }
1981 }
1982 new_size = connection->read_buffer_size + grow_size;
1983 }
1984 /* Make sure that read buffer will not be moved */
1985 if ((NULL != connection->read_buffer) &&
1986 ! MHD_pool_is_resizable_inplace (connection->pool,
1987 connection->read_buffer,
1988 connection->read_buffer_size))
1989 {
1990 mhd_assert (0);
1991 return false;
1992 }
1993 /* we can actually grow the buffer, do it! */
1994 rb = MHD_pool_reallocate (connection->pool,
1995 connection->read_buffer,
1996 connection->read_buffer_size,
1997 new_size);
1998 if (NULL == rb)
1999 {
2000 /* This should NOT be possible: we just computed 'new_size' so that
2001 it should fit. If it happens, somehow our read buffer is not in
2002 the right position in the pool, say because someone called
2003 MHD_pool_allocate() without 'from_end' set to 'true'? Anyway,
2004 should be investigated! (Ideally provide all data from
2005 *pool and connection->read_buffer and new_size for debugging). */
2006 mhd_assert (0);
2007 return false;
2008 }
2009 mhd_assert (connection->read_buffer == rb);
2010 connection->read_buffer = rb;
2011 mhd_assert (NULL != connection->read_buffer);
2012 connection->read_buffer_size = new_size;
2013 return true;
2014}
2015
2016
2021static void
2023{
2024 struct MHD_Connection *const c = connection;
2025 void *new_buf;
2026
2027 if ((NULL == c->read_buffer) || (0 == c->read_buffer_size))
2028 {
2029 mhd_assert (0 == c->read_buffer_size);
2031 return;
2032 }
2033
2035 if (0 == c->read_buffer_offset)
2036 {
2038 c->read_buffer = NULL;
2039 c->read_buffer_size = 0;
2040 }
2041 else
2042 {
2044 c->read_buffer_size));
2047 mhd_assert (c->read_buffer == new_buf);
2048 c->read_buffer = new_buf;
2050 }
2051}
2052
2053
2060static size_t
2062{
2063 struct MHD_Connection *const c = connection;
2064 struct MemoryPool *const pool = connection->pool;
2065 void *new_buf;
2066 size_t new_size;
2067 size_t free_size;
2068
2069 mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
2072
2073 free_size = MHD_pool_get_free (pool);
2074 if (0 != free_size)
2075 {
2076 new_size = c->write_buffer_size + free_size;
2077 /* This function must not move the buffer position.
2078 * MHD_pool_reallocate () may return the new position only if buffer was
2079 * allocated 'from_end' or is not the last allocation,
2080 * which should not happen. */
2081 mhd_assert ((NULL == c->write_buffer) || \
2083 c->write_buffer_size));
2084 new_buf = MHD_pool_reallocate (pool,
2085 c->write_buffer,
2087 new_size);
2088 mhd_assert ((c->write_buffer == new_buf) || (NULL == c->write_buffer));
2089 c->write_buffer = new_buf;
2090 c->write_buffer_size = new_size;
2092 {
2093 /* All data have been sent, reset offsets to zero. */
2096 }
2097 }
2098
2100}
2101
2102
2103#if 0 /* disable unused function */
2112static void
2113connection_shrink_write_buffer (struct MHD_Connection *connection)
2114{
2115 struct MHD_Connection *const c = connection;
2116 struct MemoryPool *const pool = connection->pool;
2117 void *new_buf;
2118
2119 mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
2122
2123 if ( (NULL == c->write_buffer) || (0 == c->write_buffer_size))
2124 {
2127 c->write_buffer = NULL;
2128 return;
2129 }
2131 return;
2132
2133 new_buf = MHD_pool_reallocate (pool, c->write_buffer, c->write_buffer_size,
2135 mhd_assert ((c->write_buffer == new_buf) || \
2136 (0 == c->write_buffer_append_offset));
2138 if (0 == c->write_buffer_size)
2139 c->write_buffer = NULL;
2140 else
2141 c->write_buffer = new_buf;
2142}
2143
2144
2145#endif /* unused function */
2146
2147
2155static void
2157{
2158 /* Read buffer is not needed for this request, shrink it.*/
2159 connection_shrink_read_buffer (connection);
2160}
2161
2162
2190
2191
2203static enum replyBodyUse
2205 unsigned int rcode)
2206{
2207 struct MHD_Connection *const c = connection;
2208
2209 mhd_assert (100 <= rcode);
2210 mhd_assert (999 >= rcode);
2211
2212 if (199 >= rcode)
2213 return RP_BODY_NONE;
2214
2215 if (MHD_HTTP_NO_CONTENT == rcode)
2216 return RP_BODY_NONE;
2217
2218#if 0
2219 /* This check is not needed as upgrade handler is used only with code 101 */
2220#ifdef UPGRADE_SUPPORT
2221 if (NULL != rp.response->upgrade_handler)
2222 return RP_BODY_NONE;
2223#endif /* UPGRADE_SUPPORT */
2224#endif
2225
2226#if 0
2227 /* CONNECT is not supported by MHD */
2228 /* Successful responses for connect requests are filtered by
2229 * MHD_queue_response() */
2230 if ( (MHD_HTTP_MTHD_CONNECT == c->rq.http_mthd) &&
2231 (2 == rcode / 100) )
2232 return false; /* Actually pass-through CONNECT is not supported by MHD */
2233#endif
2234
2235 /* Reply body headers could be used.
2236 * Check whether reply body itself must be used. */
2237
2239 return RP_BODY_HEADERS_ONLY;
2240
2241 if (MHD_HTTP_NOT_MODIFIED == rcode)
2242 return RP_BODY_HEADERS_ONLY;
2243
2244 /* Reply body must be sent. The body may have zero length, but body size
2245 * must be indicated by headers ('Content-Length:' or
2246 * 'Transfer-Encoding: chunked'). */
2247 return RP_BODY_SEND;
2248}
2249
2250
2259static void
2261{
2262 struct MHD_Connection *const c = connection;
2263 struct MHD_Response *const r = c->rp.response;
2264 enum replyBodyUse use_rp_body;
2265 bool use_chunked;
2266
2267 mhd_assert (NULL != r);
2268
2269 /* ** Adjust reply properties ** */
2270
2272 use_rp_body = is_reply_body_needed (c, c->rp.responseCode);
2273 c->rp.props.send_reply_body = (use_rp_body > RP_BODY_HEADERS_ONLY);
2275
2276#ifdef UPGRADE_SUPPORT
2277 mhd_assert ( (NULL == r->upgrade_handler) ||
2278 (RP_BODY_NONE == use_rp_body) );
2279#endif /* UPGRADE_SUPPORT */
2280
2282 {
2283 if ((MHD_SIZE_UNKNOWN == r->total_size) ||
2285 { /* Use chunked reply encoding if possible */
2286
2287 /* Check whether chunked encoding is supported by the client */
2289 use_chunked = false;
2290 /* Check whether chunked encoding is allowed for the reply */
2291 else if (0 != (r->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
2293 use_chunked = false;
2294 else
2295 /* If chunked encoding is supported and allowed, and response size
2296 * is unknown, use chunked even for non-Keep-Alive connections.
2297 * See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3
2298 * Also use chunked if it is enforced by application and supported by
2299 * the client. */
2300 use_chunked = true;
2301 }
2302 else
2303 use_chunked = false;
2304
2305 if ( (MHD_SIZE_UNKNOWN == r->total_size) &&
2306 (! use_chunked) )
2307 {
2308 /* End of the stream is indicated by closure */
2310 }
2311 }
2312 else
2313 use_chunked = false; /* chunked encoding cannot be used without body */
2314
2315 c->rp.props.chunked = use_chunked;
2316#ifdef _DEBUG
2317 c->rp.props.set = true;
2318#endif /* _DEBUG */
2319}
2320
2321
2326static void
2328{
2329 struct MHD_Connection *const c = connection;
2330 struct MHD_Response *const r = c->rp.response;
2331
2332 mhd_assert (c->rp.props.set);
2333#ifdef HAVE_MESSAGES
2334 if ( (! c->rp.props.use_reply_body_headers) &&
2335 (0 != r->total_size) )
2336 {
2337 MHD_DLOG (c->daemon,
2338 _ ("This reply with response code %u cannot use reply body. "
2339 "Non-empty response body is ignored and not used.\n"),
2340 (unsigned) (c->rp.responseCode));
2341 }
2342 if ( (! c->rp.props.use_reply_body_headers) &&
2344 {
2345 MHD_DLOG (c->daemon,
2346 _ ("This reply with response code %u cannot use reply body. "
2347 "Application defined \"Content-Length\" header violates"
2348 "HTTP specification.\n"),
2349 (unsigned) (c->rp.responseCode));
2350 }
2351#else
2352 (void) c; /* Mute compiler warning */
2353 (void) r; /* Mute compiler warning */
2354#endif
2355}
2356
2357
2369static bool
2370buffer_append (char *buf,
2371 size_t *ppos,
2372 size_t buf_size,
2373 const char *append,
2374 size_t append_size)
2375{
2376 mhd_assert (NULL != buf); /* Mute static analyzer */
2377 if (buf_size < *ppos + append_size)
2378 return false;
2379 memcpy (buf + *ppos, append, append_size);
2380 *ppos += append_size;
2381 return true;
2382}
2383
2384
2395#define buffer_append_s(buf,ppos,buf_size,str) \
2396 buffer_append (buf,ppos,buf_size,str, MHD_STATICSTR_LEN_ (str))
2397
2398
2418static bool
2420 size_t *ppos,
2421 size_t buf_size,
2422 struct MHD_Response *response,
2423 bool filter_transf_enc,
2424 bool filter_content_len,
2425 bool add_close,
2426 bool add_keep_alive)
2427{
2428 struct MHD_Response *const r = response;
2429 struct MHD_HTTP_Res_Header *hdr;
2430 size_t el_size;
2431
2432 mhd_assert (! add_close || ! add_keep_alive);
2433
2435 filter_transf_enc = false; /* No such header */
2436 if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2437 filter_content_len = false; /* No such header */
2438 if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2439 {
2440 add_close = false; /* No such header */
2441 add_keep_alive = false; /* No such header */
2442 }
2443 else if (0 != (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE))
2444 add_close = false; /* "close" token was already set */
2445
2446 for (hdr = r->first_header; NULL != hdr; hdr = hdr->next)
2447 {
2448 size_t initial_pos = *ppos;
2449 if (MHD_HEADER_KIND != hdr->kind)
2450 continue;
2451 if (filter_transf_enc)
2452 { /* Need to filter-out "Transfer-Encoding" */
2454 hdr->header_size) &&
2456 hdr->header, hdr->header_size)) )
2457 {
2458 filter_transf_enc = false; /* There is the only one such header */
2459 continue; /* Skip "Transfer-Encoding" header */
2460 }
2461 }
2462 if (filter_content_len)
2463 { /* Need to filter-out "Content-Length" */
2465 hdr->header_size) &&
2467 hdr->header, hdr->header_size)) )
2468 {
2469 /* Reset filter flag if only one header is allowed */
2470 filter_transf_enc =
2472 continue; /* Skip "Content-Length" header */
2473 }
2474 }
2475
2476 /* Add user header */
2477 el_size = hdr->header_size + 2 + hdr->value_size + 2;
2478 if (buf_size < *ppos + el_size)
2479 return false;
2480 memcpy (buf + *ppos, hdr->header, hdr->header_size);
2481 (*ppos) += hdr->header_size;
2482 buf[(*ppos)++] = ':';
2483 buf[(*ppos)++] = ' ';
2484 if (add_close || add_keep_alive)
2485 {
2486 /* "Connection:" header must be always the first one */
2489 hdr->header_size));
2490
2491 if (add_close)
2492 {
2493 el_size += MHD_STATICSTR_LEN_ ("close, ");
2494 if (buf_size < initial_pos + el_size)
2495 return false;
2496 memcpy (buf + *ppos, "close, ",
2497 MHD_STATICSTR_LEN_ ("close, "));
2498 *ppos += MHD_STATICSTR_LEN_ ("close, ");
2499 }
2500 else
2501 {
2502 el_size += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2503 if (buf_size < initial_pos + el_size)
2504 return false;
2505 memcpy (buf + *ppos, "Keep-Alive, ",
2506 MHD_STATICSTR_LEN_ ("Keep-Alive, "));
2507 *ppos += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2508 }
2509 add_close = false;
2510 add_keep_alive = false;
2511 }
2512 if (0 != hdr->value_size)
2513 memcpy (buf + *ppos, hdr->value, hdr->value_size);
2514 *ppos += hdr->value_size;
2515 buf[(*ppos)++] = '\r';
2516 buf[(*ppos)++] = '\n';
2517 mhd_assert (initial_pos + el_size == (*ppos));
2518 }
2519 return true;
2520}
2521
2522
2531static enum MHD_Result
2533{
2534 struct MHD_Connection *const c = connection;
2535 struct MHD_Response *const r = c->rp.response;
2536 char *buf;
2537 size_t pos;
2538 size_t buf_size;
2539 size_t el_size;
2540 unsigned rcode;
2541 bool use_conn_close;
2542 bool use_conn_k_alive;
2543
2544 mhd_assert (NULL != r);
2545
2546 /* ** Adjust response properties ** */
2548
2549 mhd_assert (c->rp.props.set);
2553#ifdef UPGRADE_SUPPORT
2554 mhd_assert ((NULL == r->upgrade_handler) || \
2556#else /* ! UPGRADE_SUPPORT */
2558#endif /* ! UPGRADE_SUPPORT */
2560 mhd_assert ((! c->rp.props.send_reply_body) || \
2562#ifdef UPGRADE_SUPPORT
2563 mhd_assert (NULL == r->upgrade_handler || \
2565#endif /* UPGRADE_SUPPORT */
2566
2568
2569 rcode = (unsigned) c->rp.responseCode;
2571 {
2572 /* The closure of connection must be always indicated by header
2573 * to avoid hung connections */
2574 use_conn_close = true;
2575 use_conn_k_alive = false;
2576 }
2577 else if (MHD_CONN_USE_KEEPALIVE == c->keepalive)
2578 {
2579 use_conn_close = false;
2580 /* Add "Connection: keep-alive" if request is HTTP/1.0 or
2581 * if reply is HTTP/1.0
2582 * For HTTP/1.1 add header only if explicitly requested by app
2583 * (by response flag), as "Keep-Alive" is default for HTTP/1.1. */
2584 if ((0 != (r->flags & MHD_RF_SEND_KEEP_ALIVE_HEADER)) ||
2585 (MHD_HTTP_VER_1_0 == c->rq.http_ver) ||
2586 (0 != (r->flags & MHD_RF_HTTP_1_0_SERVER)))
2587 use_conn_k_alive = true;
2588 else
2589 use_conn_k_alive = false;
2590 }
2591 else
2592 {
2593 use_conn_close = false;
2594 use_conn_k_alive = false;
2595 }
2596
2597 /* ** Actually build the response header ** */
2598
2599 /* Get all space available */
2601 buf = c->write_buffer;
2603 buf_size = c->write_buffer_size;
2604 if (0 == buf_size)
2605 return MHD_NO;
2606 mhd_assert (NULL != buf);
2607
2608 /* * The status line * */
2609
2610 /* The HTTP version */
2611 if (! c->rp.responseIcy)
2612 { /* HTTP reply */
2613 if (0 == (r->flags & MHD_RF_HTTP_1_0_SERVER))
2614 { /* HTTP/1.1 reply */
2615 /* Use HTTP/1.1 responses for HTTP/1.0 clients.
2616 * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
2617 if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_1))
2618 return MHD_NO;
2619 }
2620 else
2621 { /* HTTP/1.0 reply */
2622 if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_0))
2623 return MHD_NO;
2624 }
2625 }
2626 else
2627 { /* ICY reply */
2628 if (! buffer_append_s (buf, &pos, buf_size, "ICY"))
2629 return MHD_NO;
2630 }
2631
2632 /* The response code */
2633 if (buf_size < pos + 5) /* space + code + space */
2634 return MHD_NO;
2635 buf[pos++] = ' ';
2636 pos += MHD_uint16_to_str ((uint16_t) rcode, buf + pos,
2637 buf_size - pos);
2638 buf[pos++] = ' ';
2639
2640 /* The reason phrase */
2641 el_size = MHD_get_reason_phrase_len_for (rcode);
2642 if (0 == el_size)
2643 {
2644 if (! buffer_append_s (buf, &pos, buf_size, "Non-Standard Status"))
2645 return MHD_NO;
2646 }
2647 else if (! buffer_append (buf, &pos, buf_size,
2649 el_size))
2650 return MHD_NO;
2651
2652 /* The linefeed */
2653 if (buf_size < pos + 2)
2654 return MHD_NO;
2655 buf[pos++] = '\r';
2656 buf[pos++] = '\n';
2657
2658 /* * The headers * */
2659
2660 /* Main automatic headers */
2661
2662 /* The "Date:" header */
2663 if ( (0 == (r->flags_auto & MHD_RAF_HAS_DATE_HDR)) &&
2665 {
2666 /* Additional byte for unused zero-termination */
2667 if (buf_size < pos + 38)
2668 return MHD_NO;
2669 if (get_date_header (buf + pos))
2670 pos += 37;
2671 }
2672 /* The "Connection:" header */
2673 mhd_assert (! use_conn_close || ! use_conn_k_alive);
2674 mhd_assert (! use_conn_k_alive || ! use_conn_close);
2675 if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2676 {
2677 if (use_conn_close)
2678 {
2679 if (! buffer_append_s (buf, &pos, buf_size,
2680 MHD_HTTP_HEADER_CONNECTION ": close\r\n"))
2681 return MHD_NO;
2682 }
2683 else if (use_conn_k_alive)
2684 {
2685 if (! buffer_append_s (buf, &pos, buf_size,
2686 MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n"))
2687 return MHD_NO;
2688 }
2689 }
2690
2691 /* User-defined headers */
2692
2693 if (! add_user_headers (buf, &pos, buf_size, r,
2694 ! c->rp.props.chunked,
2696 (0 ==
2698 use_conn_close,
2699 use_conn_k_alive))
2700 return MHD_NO;
2701
2702 /* Other automatic headers */
2703
2704 if ( (c->rp.props.use_reply_body_headers) &&
2705 (0 == (r->flags & MHD_RF_HEAD_ONLY_RESPONSE)) )
2706 {
2707 /* Body-specific headers */
2708
2709 if (c->rp.props.chunked)
2710 { /* Chunked encoding is used */
2712 { /* No chunked encoding header set by user */
2713 if (! buffer_append_s (buf, &pos, buf_size,
2715 "chunked\r\n"))
2716 return MHD_NO;
2717 }
2718 }
2719 else /* Chunked encoding is not used */
2720 {
2721 if (MHD_SIZE_UNKNOWN != r->total_size)
2722 { /* The size is known */
2723 if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2724 { /* The response does not have "Content-Length" header */
2725 if (! buffer_append_s (buf, &pos, buf_size,
2727 return MHD_NO;
2728 el_size = MHD_uint64_to_str (r->total_size, buf + pos,
2729 buf_size - pos);
2730 if (0 == el_size)
2731 return MHD_NO;
2732 pos += el_size;
2733
2734 if (buf_size < pos + 2)
2735 return MHD_NO;
2736 buf[pos++] = '\r';
2737 buf[pos++] = '\n';
2738 }
2739 }
2740 }
2741 }
2742
2743 /* * Header termination * */
2744 if (buf_size < pos + 2)
2745 return MHD_NO;
2746 buf[pos++] = '\r';
2747 buf[pos++] = '\n';
2748
2750 return MHD_YES;
2751}
2752
2753
2763static enum MHD_Result
2765{
2766 char *buf;
2767 size_t buf_size;
2768 size_t used_size;
2769 struct MHD_Connection *const c = connection;
2770 struct MHD_HTTP_Res_Header *pos;
2771
2772 mhd_assert (connection->rp.props.chunked);
2773 /* TODO: allow combining of the final footer with the last chunk,
2774 * modify the next assert. */
2776 mhd_assert (NULL != c->rp.response);
2777
2778 buf_size = connection_maximize_write_buffer (c);
2779 /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */
2780 if (buf_size < 5)
2781 return MHD_NO;
2784 mhd_assert (NULL != buf);
2785 used_size = 0;
2786 buf[used_size++] = '0';
2787 buf[used_size++] = '\r';
2788 buf[used_size++] = '\n';
2789
2790 for (pos = c->rp.response->first_header; NULL != pos; pos = pos->next)
2791 {
2792 if (MHD_FOOTER_KIND == pos->kind)
2793 {
2794 size_t new_used_size; /* resulting size with this header */
2795 /* '4' is colon, space, linefeeds */
2796 new_used_size = used_size + pos->header_size + pos->value_size + 4;
2797 if (new_used_size > buf_size)
2798 return MHD_NO;
2799 memcpy (buf + used_size, pos->header, pos->header_size);
2800 used_size += pos->header_size;
2801 buf[used_size++] = ':';
2802 buf[used_size++] = ' ';
2803 memcpy (buf + used_size, pos->value, pos->value_size);
2804 used_size += pos->value_size;
2805 buf[used_size++] = '\r';
2806 buf[used_size++] = '\n';
2807 mhd_assert (used_size == new_used_size);
2808 }
2809 }
2810 if (used_size + 2 > buf_size)
2811 return MHD_NO;
2812 buf[used_size++] = '\r';
2813 buf[used_size++] = '\n';
2814
2815 c->write_buffer_append_offset += used_size;
2817
2818 return MHD_YES;
2819}
2820
2821
2838static void
2840 unsigned int status_code,
2841 const char *message,
2842 size_t message_len,
2843 char *header_name,
2844 size_t header_name_len,
2845 char *header_value,
2846 size_t header_value_len)
2847{
2848 struct MHD_Response *response;
2849 enum MHD_Result iret;
2850
2851 mhd_assert (! connection->stop_with_error); /* Do not send error twice */
2852 if (connection->stop_with_error)
2853 { /* Should not happen */
2854 if (MHD_CONNECTION_CLOSED > connection->state)
2855 connection->state = MHD_CONNECTION_CLOSED;
2856 free (header_name);
2857 free (header_value);
2858 return;
2859 }
2860 connection->stop_with_error = true;
2861 connection->discard_request = true;
2862#ifdef HAVE_MESSAGES
2863 MHD_DLOG (connection->daemon,
2864 _ ("Error processing request (HTTP response code is %u ('%s')). " \
2865 "Closing connection.\n"),
2866 status_code,
2867 message);
2868#endif
2870 {
2871#ifdef HAVE_MESSAGES
2872 MHD_DLOG (connection->daemon,
2873 _ ("Too late to send an error response, " \
2874 "response is being sent already.\n"),
2875 status_code,
2876 message);
2877#endif
2878 CONNECTION_CLOSE_ERROR (connection,
2879 _ ("Too late for error response."));
2880 free (header_name);
2881 free (header_value);
2882 return;
2883 }
2884 /* TODO: remove when special error queue function is implemented */
2886 if (0 != connection->read_buffer_size)
2887 {
2888 /* Read buffer is not needed anymore, discard it
2889 * to free some space for error response. */
2890 MHD_pool_deallocate (connection->pool,
2891 connection->read_buffer,
2892 connection->read_buffer_size);
2893 connection->read_buffer = NULL;
2894 connection->read_buffer_size = 0;
2895 connection->read_buffer_offset = 0;
2896 }
2897 if (NULL != connection->rp.response)
2898 {
2899 MHD_destroy_response (connection->rp.response);
2900 connection->rp.response = NULL;
2901 }
2902 response = MHD_create_response_from_buffer_static (message_len,
2903 message);
2904 if (NULL == response)
2905 {
2906#ifdef HAVE_MESSAGES
2907 MHD_DLOG (connection->daemon,
2908 _ ("Failed to create error response.\n"),
2909 status_code,
2910 message);
2911#endif
2912 /* can't even send a reply, at least close the connection */
2913 connection->state = MHD_CONNECTION_CLOSED;
2914 free (header_name);
2915 free (header_value);
2916 return;
2917 }
2918 mhd_assert ((0 == header_name_len) || (NULL != header_name));
2919 mhd_assert ((NULL == header_name) || (0 != header_name_len));
2920 mhd_assert ((0 == header_value_len) || (NULL != header_value));
2921 mhd_assert ((NULL == header_value) || (0 != header_value_len));
2922 mhd_assert ((NULL == header_name) || (NULL != header_value));
2923 mhd_assert ((NULL != header_value) || (NULL == header_name));
2924 if (NULL != header_name)
2925 {
2926 iret = MHD_add_response_entry_no_alloc_ (response,
2928 header_name, header_name_len,
2929 header_value, header_value_len);
2930 if (MHD_NO == iret)
2931 {
2932 free (header_name);
2933 free (header_value);
2934 }
2935 }
2936 else
2937 iret = MHD_YES;
2938
2939 if (MHD_NO != iret)
2940 {
2941 bool before = connection->in_access_handler;
2942
2943 /* Fake the flag for the internal call */
2944 connection->in_access_handler = true;
2945 iret = MHD_queue_response (connection,
2946 status_code,
2947 response);
2948 connection->in_access_handler = before;
2949 }
2950 MHD_destroy_response (response);
2951 if (MHD_NO == iret)
2952 {
2953 /* can't even send a reply, at least close the connection */
2954 CONNECTION_CLOSE_ERROR (connection,
2955 _ ("Closing connection " \
2956 "(failed to queue error response)."));
2957 return;
2958 }
2959 mhd_assert (NULL != connection->rp.response);
2960 /* Do not reuse this connection. */
2961 connection->keepalive = MHD_CONN_MUST_CLOSE;
2962 if (MHD_NO == build_header_response (connection))
2963 {
2964 /* No memory. Release everything. */
2965 connection->rq.version = NULL;
2966 connection->rq.method = NULL;
2967 connection->rq.url = NULL;
2968 connection->rq.url_len = 0;
2969 connection->rq.url_for_callback = NULL;
2970 connection->rq.headers_received = NULL;
2971 connection->rq.headers_received_tail = NULL;
2972 connection->write_buffer = NULL;
2973 connection->write_buffer_size = 0;
2974 connection->write_buffer_send_offset = 0;
2975 connection->write_buffer_append_offset = 0;
2976 connection->read_buffer
2977 = MHD_pool_reset (connection->pool,
2978 NULL,
2979 0,
2980 0);
2981 connection->read_buffer_size = 0;
2982
2983 /* Retry with empty buffer */
2984 if (MHD_NO == build_header_response (connection))
2985 {
2986 CONNECTION_CLOSE_ERROR (connection,
2987 _ ("Closing connection " \
2988 "(failed to create error response header)."));
2989 return;
2990 }
2991 }
2993}
2994
2995
2999#ifdef HAVE_MESSAGES
3000# define transmit_error_response_static(c, code, msg) \
3001 transmit_error_response_len (c, code, \
3002 msg, MHD_STATICSTR_LEN_ (msg), \
3003 NULL, 0, NULL, 0)
3004#else /* ! HAVE_MESSAGES */
3005# define transmit_error_response_static(c, code, msg) \
3006 transmit_error_response_len (c, code, \
3007 "", 0, \
3008 NULL, 0, NULL, 0)
3009#endif /* ! HAVE_MESSAGES */
3010
3014#ifdef HAVE_MESSAGES
3015# define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l) \
3016 transmit_error_response_len (c, code, \
3017 m, MHD_STATICSTR_LEN_ (m), \
3018 hd_n, hd_n_l, \
3019 hd_v, hd_v_l)
3020#else /* ! HAVE_MESSAGES */
3021# define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l) \
3022 transmit_error_response_len (c, code, \
3023 "", 0, \
3024 hd_n, hd_n_l, \
3025 hd_v, hd_v_l)
3026#endif /* ! HAVE_MESSAGES */
3027
3028
3039static bool
3041{
3043 if (! c->rq.have_chunked_upload)
3044 return 0 != c->read_buffer_offset;
3045
3046 /* Chunked upload */
3047 mhd_assert (0 != c->rq.remaining_upload_size); /* Must not be possible in MHD_CONNECTION_BODY_RECEIVING state */
3049 {
3050 /* 0 == c->rq.current_chunk_size: Waiting the chunk size (chunk header).
3051 0 != c->rq.current_chunk_size: Waiting for chunk-closing CRLF. */
3052 return false;
3053 }
3054 return 0 != c->read_buffer_offset; /* Chunk payload data in the read buffer */
3055}
3056
3057
3074
3075
3076#ifndef MHD_MAX_REASONABLE_HEADERS_SIZE_
3086# define MHD_MAX_REASONABLE_HEADERS_SIZE_ (6 * 1024)
3087#endif /* ! MHD_MAX_REASONABLE_HEADERS_SIZE_ */
3088
3089#ifndef MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
3100# define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 8000
3101#endif /* ! MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ */
3102
3103#ifndef MHD_MIN_REASONABLE_HEADERS_SIZE_
3111# define MHD_MIN_REASONABLE_HEADERS_SIZE_ 26
3112#endif /* ! MHD_MIN_REASONABLE_HEADERS_SIZE_ */
3113
3114#ifndef MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
3122# define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 40
3123#endif /* ! MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ */
3124
3125#ifndef MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
3133# define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 16
3134#endif /* ! MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ */
3135
3136#ifndef MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
3142# define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 4
3143#endif /* ! MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ */
3144
3145
3157static unsigned int
3159 enum MHD_ProcRecvDataStage stage,
3160 const char *add_element,
3161 size_t add_element_size)
3162{
3163 size_t method_size;
3164 size_t uri_size;
3165 size_t opt_headers_size;
3166 size_t host_field_line_size;
3167
3170 mhd_assert ((0 == add_element_size) || (NULL != add_element));
3171
3173 {
3175 opt_headers_size =
3176 (size_t) ((c->read_buffer + c->read_buffer_offset)
3177 - c->rq.field_lines.start);
3178 }
3179 else
3180 opt_headers_size = c->rq.field_lines.size;
3181
3182 /* The read buffer is fully used by the request line, the field lines
3183 (headers) and internal information.
3184 The return status code works as a suggestion for the client to reduce
3185 one of the request elements. */
3186
3187 if ((MHD_PROC_RECV_BODY_CHUNKED == stage) &&
3188 (MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ < add_element_size))
3189 {
3190 /* Request could be re-tried easily with smaller chunk sizes */
3192 }
3193
3194 host_field_line_size = 0;
3195 /* The "Host:" field line is mandatory.
3196 The total size of the field lines (headers) cannot be smaller than
3197 the size of the "Host:" field line. */
3198 if ((MHD_PROC_RECV_HEADERS == stage)
3199 && (0 != add_element_size))
3200 {
3201 static const size_t header_host_key_len =
3203 const bool is_host_header =
3204 (header_host_key_len + 1 <= add_element_size)
3205 && ( (0 == add_element[header_host_key_len])
3206 || (':' == add_element[header_host_key_len]) )
3208 add_element,
3209 header_host_key_len);
3210 if (is_host_header)
3211 {
3212 const bool is_parsed = ! (
3214 (add_element_size == c->read_buffer_offset) &&
3215 (c->read_buffer == add_element) );
3216 size_t actual_element_size;
3217
3218 mhd_assert (! is_parsed || (0 == add_element[header_host_key_len]));
3219 /* The actual size should be larger due to CRLF or LF chars,
3220 however the exact termination sequence is not known here and
3221 as perfect precision is not required, to simplify the code
3222 assume the minimal length. */
3223 if (is_parsed)
3224 actual_element_size = add_element_size + 1; /* "1" for LF */
3225 else
3226 actual_element_size = add_element_size;
3227
3228 host_field_line_size = actual_element_size;
3229 mhd_assert (opt_headers_size >= actual_element_size);
3230 opt_headers_size -= actual_element_size;
3231 }
3232 }
3233 if (0 == host_field_line_size)
3234 {
3235 static const size_t host_field_name_len =
3237 size_t host_field_name_value_len;
3241 host_field_name_len,
3242 NULL,
3243 &host_field_name_value_len))
3244 {
3245 /* Calculate the minimal size of the field line: no space between
3246 colon and the field value, line terminated by LR */
3247 host_field_line_size =
3248 host_field_name_len + host_field_name_value_len + 2; /* "2" for ':' and LF */
3249
3250 /* The "Host:" field could be added by application */
3251 if (opt_headers_size >= host_field_line_size)
3252 {
3253 opt_headers_size -= host_field_line_size;
3254 /* Take into account typical space after colon and CR at the end of the line */
3255 if (opt_headers_size >= 2)
3256 opt_headers_size -= 2;
3257 }
3258 else
3259 host_field_line_size = 0; /* No "Host:" field line set by the client */
3260 }
3261 }
3262
3263 uri_size = c->rq.req_target_len;
3265 method_size = 0; /* Do not recommend shorter request method */
3266 else
3267 {
3268 mhd_assert (NULL != c->rq.method);
3269 method_size = strlen (c->rq.method);
3270 }
3271
3272 if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3273 {
3274 /* Typically the easiest way to reduce request header size is
3275 a removal of some optional headers. */
3276 if (opt_headers_size > (uri_size / 8))
3277 {
3278 if ((opt_headers_size / 2) > method_size)
3280 else
3281 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3282 }
3283 else
3284 { /* Request target is MUCH larger than headers */
3285 if ((uri_size / 16) > method_size)
3286 return MHD_HTTP_URI_TOO_LONG;
3287 else
3288 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3289 }
3290 }
3291 if ((size_t) MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3292 {
3293 /* If request target size if larger than maximum reasonable size
3294 recommend client to reduce the request target size (length). */
3295 if ((uri_size / 16) > method_size)
3296 return MHD_HTTP_URI_TOO_LONG; /* Request target is MUCH larger than headers */
3297 else
3298 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3299 }
3300
3301 /* The read buffer is too small to handle reasonably large requests */
3302
3303 if ((size_t) MHD_MIN_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3304 {
3305 /* Recommend application to retry with minimal headers */
3306 if ((opt_headers_size * 4) > uri_size)
3307 {
3308 if (opt_headers_size > method_size)
3310 else
3311 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3312 }
3313 else
3314 { /* Request target is significantly larger than headers */
3315 if (uri_size > method_size * 4)
3316 return MHD_HTTP_URI_TOO_LONG;
3317 else
3318 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3319 }
3320 }
3321 if ((size_t) MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3322 {
3323 /* Recommend application to retry with a shorter request target */
3324 if (uri_size > method_size * 4)
3325 return MHD_HTTP_URI_TOO_LONG;
3326 else
3327 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3328 }
3329
3330 if ((size_t) MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ < method_size)
3331 {
3332 /* The request target (URI) and headers are (reasonably) very small.
3333 Some non-standard long request method is used. */
3334 /* The last resort response as it means "the method is not supported
3335 by the server for any URI". */
3337 }
3338
3339 /* The almost impossible situation: all elements are small, but cannot
3340 fit the buffer. The application set the buffer size to
3341 critically low value? */
3342
3343 if ((1 < opt_headers_size) || (1 < uri_size))
3344 {
3345 if (opt_headers_size >= uri_size)
3347 else
3348 return MHD_HTTP_URI_TOO_LONG;
3349 }
3350
3351 /* Nothing to reduce in the request.
3352 Reply with some status. */
3353 if (0 != host_field_line_size)
3355
3356 return MHD_HTTP_URI_TOO_LONG;
3357}
3358
3359
3370static void
3372 const char *add_header,
3373 size_t add_header_size)
3374{
3375 unsigned int err_code;
3376
3377 err_code = get_no_space_err_status_code (c,
3379 add_header,
3380 add_header_size);
3382 err_code,
3384}
3385
3386
3387#ifdef COOKIE_SUPPORT
3393static void
3394handle_req_cookie_no_space (struct MHD_Connection *c)
3395{
3396 unsigned int err_code;
3397
3398 err_code = get_no_space_err_status_code (c,
3400 NULL,
3401 0);
3403 err_code,
3405}
3406
3407
3408#endif /* COOKIE_SUPPORT */
3409
3410
3421static void
3423 const char *chunk_size_line,
3424 size_t chunk_size_line_size)
3425{
3426 unsigned int err_code;
3427
3428 if (NULL != chunk_size_line)
3429 {
3430 const char *semicol;
3431 /* Check for chunk extension */
3432 semicol = memchr (chunk_size_line, ';', chunk_size_line_size);
3433 if (NULL != semicol)
3434 { /* Chunk extension present. It could be removed without any loss of the
3435 details of the request. */
3439 }
3440 }
3441 err_code = get_no_space_err_status_code (c,
3443 chunk_size_line,
3444 chunk_size_line_size);
3446 err_code,
3448}
3449
3450
3461static void
3463 const char *add_footer,
3464 size_t add_footer_size)
3465{
3466 (void) add_footer; (void) add_footer_size; /* Unused */
3468
3469 /* Footers should be optional */
3473}
3474
3475
3486static void
3488 enum MHD_ProcRecvDataStage stage)
3489{
3490 mhd_assert (MHD_PROC_RECV_INIT <= stage);
3493 mhd_assert ((MHD_PROC_RECV_INIT != stage) || \
3494 (MHD_CONNECTION_INIT == c->state));
3495 mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \
3497 mhd_assert ((MHD_PROC_RECV_URI != stage) || \
3499 mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \
3501 mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \
3503 mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */
3504 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3506 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3508 mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \
3510 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3511 (! c->rq.have_chunked_upload));
3512 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3513 (c->rq.have_chunked_upload));
3514 switch (stage)
3515 {
3516 case MHD_PROC_RECV_INIT:
3518 /* Some data has been received, but it is not clear yet whether
3519 * the received data is an valid HTTP request */
3521 _ ("No space left in the read buffer when " \
3522 "receiving the initial part of " \
3523 "the request line."));
3524 return;
3525 case MHD_PROC_RECV_URI:
3527 /* Some data has been received, but the request line is incomplete */
3530 /* A quick simple check whether the incomplete line looks
3531 * like an HTTP request */
3532 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
3534 {
3538 return;
3539 }
3541 _ ("No space left in the read buffer when " \
3542 "receiving the URI in " \
3543 "the request line. " \
3544 "The request uses non-standard HTTP request " \
3545 "method token."));
3546 return;
3549 return;
3552 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3555 {
3556 /* The connection must not be in MHD_EVENT_LOOP_INFO_READ state
3557 when external polling is used and some data left unprocessed. */
3559 /* failed to grow the read buffer, and the
3560 client which is supposed to handle the
3561 received data in a *blocking* fashion
3562 (in this mode) did not handle the data as
3563 it was supposed to!
3564 => we would either have to do busy-waiting
3565 (on the client, which would likely fail),
3566 or if we do nothing, we would just timeout
3567 on the connection (if a timeout is even
3568 set!).
3569 Solution: we kill the connection with an error */
3573 }
3574 else
3575 {
3576 if (MHD_PROC_RECV_BODY_NORMAL == stage)
3577 {
3578 /* A header probably has been added to a suspended connection and
3579 it took precisely all the space in the buffer.
3580 Very low probability. */
3583 }
3584 else
3585 {
3588 { /* Receiving content of the chunk */
3589 /* A header probably has been added to a suspended connection and
3590 it took precisely all the space in the buffer.
3591 Very low probability. */
3593 }
3594 else
3595 {
3596 if (0 != c->rq.current_chunk_size)
3597 { /* Waiting for chunk-closing CRLF */
3598 /* Not really possible as some payload should be
3599 processed and the space used by payload should be available. */
3601 }
3602 else
3603 { /* Reading the line with the chunk size */
3605 c->read_buffer,
3607 }
3608 }
3609 }
3610 }
3611 return;
3614 return;
3615 /* The next cases should not be possible */
3617 default:
3618 break;
3619 }
3620 mhd_assert (0);
3621}
3622
3623
3637static bool
3639{
3643 bool rbuff_grow_desired;
3647 bool rbuff_grow_required;
3648
3651
3652 rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size);
3653 if (rbuff_grow_required)
3654 rbuff_grow_desired = true;
3655 else
3656 {
3657 rbuff_grow_desired = (c->read_buffer_offset + c->daemon->pool_increment >
3658 c->read_buffer_size);
3659
3660 if ((rbuff_grow_desired) &&
3662 {
3663 if (! c->rq.have_chunked_upload)
3664 {
3666 /* Do not grow read buffer more than necessary to process the current
3667 request. */
3668 rbuff_grow_desired =
3670 }
3671 else
3672 {
3674 if (0 == c->rq.current_chunk_size)
3675 rbuff_grow_desired = /* Reading value of the next chunk size */
3677 c->read_buffer_size);
3678 else
3679 {
3680 const uint64_t cur_chunk_left =
3682 /* Do not grow read buffer more than necessary to process the current
3683 chunk with terminating CRLF. */
3685 rbuff_grow_desired =
3686 ((cur_chunk_left + 2) > (uint64_t) (c->read_buffer_size));
3687 }
3688 }
3689 }
3690 }
3691
3692 if (! rbuff_grow_desired)
3693 return true; /* No need to increase the buffer */
3694
3695 if (try_grow_read_buffer (c, rbuff_grow_required))
3696 return true; /* Buffer increase succeed */
3697
3698 if (! rbuff_grow_required)
3699 return true; /* Can continue without buffer increase */
3700
3701 /* Failed to increase the read buffer size, but need to read the data
3702 from the network.
3703 No more space left in the buffer, no more space to increase the buffer. */
3704
3705 /* 'PROCESS_READ' event state flag must be set only if the last application
3706 callback has processed some data. If any data is processed then some
3707 space in the read buffer must be available. */
3709
3710 if ((! MHD_D_IS_USING_THREADS_ (c->daemon))
3713 {
3714 /* The application is handling processing cycles.
3715 The data could be processed later. */
3717 return true;
3718 }
3719 else
3720 {
3721 enum MHD_ProcRecvDataStage stage;
3722
3723 switch (c->state)
3724 {
3726 stage = MHD_PROC_RECV_INIT;
3727 break;
3730 stage = MHD_PROC_RECV_METHOD;
3731 else if (0 == c->rq.req_target_len)
3732 stage = MHD_PROC_RECV_URI;
3733 else
3734 stage = MHD_PROC_RECV_HTTPVER;
3735 break;
3737 stage = MHD_PROC_RECV_HEADERS;
3738 break;
3740 stage = c->rq.have_chunked_upload ?
3742 break;
3744 stage = MHD_PROC_RECV_FOOTERS;
3745 break;
3764#ifdef UPGRADE_SUPPORT
3765 case MHD_CONNECTION_UPGRADE:
3766#endif
3767 default:
3769 mhd_assert (0);
3770 }
3771
3772 handle_recv_no_space (c, stage);
3773 }
3774 return false;
3775}
3776
3777
3786static void
3788{
3789 /* Do not update states of suspended connection */
3790 if (connection->suspended)
3791 return; /* States will be updated after resume. */
3792#ifdef HTTPS_SUPPORT
3793 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
3794 { /* HTTPS connection. */
3795 switch (connection->tls_state)
3796 {
3797 case MHD_TLS_CONN_INIT:
3799 return;
3802 if (0 == gnutls_record_get_direction (connection->tls_session))
3804 else
3806 return;
3808 break; /* Do normal processing */
3812 return;
3813 case MHD_TLS_CONN_TLS_CLOSING: /* Not implemented yet */
3814 case MHD_TLS_CONN_TLS_CLOSED: /* Not implemented yet */
3816 case MHD_TLS_CONN_NO_TLS: /* Not possible */
3817 default:
3818 MHD_PANIC (_ ("Invalid TLS state value.\n"));
3819 }
3820 }
3821#endif /* HTTPS_SUPPORT */
3822 while (1)
3823 {
3824#if DEBUG_STATES
3825 MHD_DLOG (connection->daemon,
3826 _ ("In function %s handling connection at state: %s\n"),
3827 MHD_FUNC_,
3828 MHD_state_to_string (connection->state));
3829#endif
3830 switch (connection->state)
3831 {
3835 break;
3837 mhd_assert (0);
3838 break;
3841 break;
3844 mhd_assert (0);
3845 break;
3848 break;
3850 if ((connection->rq.some_payload_processed) &&
3852 {
3853 /* Some data was processed, the buffer must have some free space */
3854 mhd_assert (connection->read_buffer_offset < \
3855 connection->read_buffer_size);
3856 if (! connection->rq.have_chunked_upload)
3857 {
3858 /* Not a chunked upload. Do not read more than necessary to
3859 process the current request. */
3860 if (connection->rq.remaining_upload_size >=
3861 connection->read_buffer_offset)
3863 else
3865 }
3866 else
3867 {
3868 /* Chunked upload. The size of the current request is unknown.
3869 Continue reading as the space in the read buffer is available. */
3871 }
3872 }
3873 else
3875 break;
3877 mhd_assert (0);
3878 break;
3881 break;
3883 mhd_assert (0);
3884 break;
3887 break;
3889 mhd_assert (0);
3890 break;
3892 /* headers in buffer, keep writing */
3894 break;
3896 mhd_assert (0);
3897 break;
3900 break;
3903 break;
3906 break;
3909 break;
3911 mhd_assert (0);
3912 break;
3915 break;
3917 mhd_assert (0);
3918 break;
3921 return; /* do nothing, not even reading */
3922#ifdef UPGRADE_SUPPORT
3923 case MHD_CONNECTION_UPGRADE:
3924 mhd_assert (0);
3925 break;
3926#endif /* UPGRADE_SUPPORT */
3927 default:
3928 mhd_assert (0);
3929 }
3930
3931 if (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info))
3932 {
3933 /* Check whether the space is available to receive data */
3934 if (! check_and_grow_read_buffer_space (connection))
3935 {
3936 mhd_assert (connection->discard_request);
3937 continue;
3938 }
3939 }
3940 break; /* Everything was processed. */
3941 }
3942}
3943
3944
3957static enum MHD_Result
3959 const char *key,
3960 size_t key_size,
3961 const char *value,
3962 size_t value_size,
3963 enum MHD_ValueKind kind)
3964{
3965 struct MHD_Connection *connection = (struct MHD_Connection *) cls;
3966 if (MHD_NO ==
3967 MHD_set_connection_value_n (connection,
3968 kind,
3969 key,
3970 key_size,
3971 value,
3972 value_size))
3973 {
3974#ifdef HAVE_MESSAGES
3975 MHD_DLOG (connection->daemon,
3976 _ ("Not enough memory in pool to allocate header record!\n"));
3977#endif
3981 return MHD_NO;
3982 }
3983 return MHD_YES;
3984}
3985
3986
3987#ifdef COOKIE_SUPPORT
3988
3992enum _MHD_ParseCookie
3993{
3994 MHD_PARSE_COOKIE_OK = MHD_YES,
3995 MHD_PARSE_COOKIE_OK_LAX = 2,
3996 MHD_PARSE_COOKIE_MALFORMED = -1,
3997 MHD_PARSE_COOKIE_NO_MEMORY = MHD_NO
3998};
3999
4000
4013static enum _MHD_ParseCookie
4014parse_cookies_string (char *str,
4015 const size_t str_len,
4016 struct MHD_Connection *connection)
4017{
4018 size_t i;
4019 bool non_strict;
4020 /* Skip extra whitespaces and empty cookies */
4021 const bool allow_wsp_empty = (0 >= connection->daemon->client_discipline);
4022 /* Allow whitespaces around '=' character */
4023 const bool wsp_around_eq = (-3 >= connection->daemon->client_discipline);
4024 /* Allow whitespaces in quoted cookie value */
4025 const bool wsp_in_quoted = (-2 >= connection->daemon->client_discipline);
4026 /* Allow tab as space after semicolon between cookies */
4027 const bool tab_as_sp = (0 >= connection->daemon->client_discipline);
4028 /* Allow no space after semicolon between cookies */
4029 const bool allow_no_space = (0 >= connection->daemon->client_discipline);
4030
4031 non_strict = false;
4032 i = 0;
4033 while (i < str_len)
4034 {
4035 size_t name_start;
4036 size_t name_len;
4037 size_t value_start;
4038 size_t value_len;
4039 bool val_quoted;
4040 /* Skip any whitespaces and empty cookies */
4041 while (' ' == str[i] || '\t' == str[i] || ';' == str[i])
4042 {
4043 if (! allow_wsp_empty)
4044 return MHD_PARSE_COOKIE_MALFORMED;
4045 non_strict = true;
4046 i++;
4047 if (i == str_len)
4048 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
4049 }
4050 /* 'i' must point to the first char of cookie-name */
4051 name_start = i;
4052 /* Find the end of the cookie-name */
4053 do
4054 {
4055 const char l = str[i];
4056 if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) ||
4057 (';' == l) || (0 == l))
4058 break;
4059 } while (str_len > ++i);
4060 name_len = i - name_start;
4061 /* Skip any whitespaces */
4062 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
4063 {
4064 if (! wsp_around_eq)
4065 return MHD_PARSE_COOKIE_MALFORMED;
4066 non_strict = true;
4067 i++;
4068 }
4069 if ((str_len == i) || ('=' != str[i]) || (0 == name_len))
4070 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */
4071 /* 'i' must point to the '=' char */
4072 mhd_assert ('=' == str[i]);
4073 i++;
4074 /* Skip any whitespaces */
4075 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
4076 {
4077 if (! wsp_around_eq)
4078 return MHD_PARSE_COOKIE_MALFORMED;
4079 non_strict = true;
4080 i++;
4081 }
4082 /* 'i' must point to the first char of cookie-value */
4083 if (str_len == i)
4084 {
4085 value_start = 0;
4086 value_len = 0;
4087#ifdef _DEBUG
4088 val_quoted = false; /* This assignment used in assert */
4089#endif
4090 }
4091 else
4092 {
4093 bool valid_cookie;
4094 val_quoted = ('"' == str[i]);
4095 if (val_quoted)
4096 i++;
4097 value_start = i;
4098 /* Find the end of the cookie-value */
4099 while (str_len > i)
4100 {
4101 const char l = str[i];
4102 if ((';' == l) || ('"' == l) || (',' == l) || (';' == l) ||
4103 ('\\' == l) || (0 == l))
4104 break;
4105 if ((' ' == l) || ('\t' == l))
4106 {
4107 if (! val_quoted)
4108 break;
4109 if (! wsp_in_quoted)
4110 return MHD_PARSE_COOKIE_MALFORMED;
4111 non_strict = true;
4112 }
4113 i++;
4114 }
4115 value_len = i - value_start;
4116 if (val_quoted)
4117 {
4118 if ((str_len == i) || ('"' != str[i]))
4119 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */
4120 i++;
4121 }
4122 /* Skip any whitespaces */
4123 if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i])))
4124 {
4125 do
4126 {
4127 i++;
4128 } while (str_len > i && (' ' == str[i] || '\t' == str[i]));
4129 /* Whitespace at the end? */
4130 if (str_len > i)
4131 {
4132 if (! allow_wsp_empty)
4133 return MHD_PARSE_COOKIE_MALFORMED;
4134 non_strict = true;
4135 }
4136 }
4137 if (str_len == i)
4138 valid_cookie = true;
4139 else if (';' == str[i])
4140 valid_cookie = true;
4141 else
4142 valid_cookie = false;
4143
4144 if (! valid_cookie)
4145 return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */
4146 }
4147 mhd_assert (0 != name_len);
4148 str[name_start + name_len] = 0; /* Zero-terminate the name */
4149 if (0 != value_len)
4150 {
4151 mhd_assert (value_start + value_len <= str_len);
4152 str[value_start + value_len] = 0; /* Zero-terminate the value */
4153 if (MHD_NO ==
4156 str + name_start,
4157 name_len,
4158 str + value_start,
4159 value_len))
4160 return MHD_PARSE_COOKIE_NO_MEMORY;
4161 }
4162 else
4163 {
4164 if (MHD_NO ==
4167 str + name_start,
4168 name_len,
4169 "",
4170 0))
4171 return MHD_PARSE_COOKIE_NO_MEMORY;
4172 }
4173 if (str_len > i)
4174 {
4175 mhd_assert (0 == str[i] || ';' == str[i]);
4176 mhd_assert (! val_quoted || ';' == str[i]);
4177 mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len);
4178 i++;
4179 if (str_len == i)
4180 { /* No next cookie after semicolon */
4181 if (! allow_wsp_empty)
4182 return MHD_PARSE_COOKIE_MALFORMED;
4183 non_strict = true;
4184 }
4185 else if (' ' != str[i])
4186 {/* No space after semicolon */
4187 if (('\t' == str[i]) && tab_as_sp)
4188 i++;
4189 else if (! allow_no_space)
4190 return MHD_PARSE_COOKIE_MALFORMED;
4191 non_strict = true;
4192 }
4193 else
4194 {
4195 i++;
4196 if (str_len == i)
4197 {
4198 if (! allow_wsp_empty)
4199 return MHD_PARSE_COOKIE_MALFORMED;
4200 non_strict = true;
4201 }
4202 }
4203 }
4204 }
4205 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
4206}
4207
4208
4215static enum _MHD_ParseCookie
4216parse_cookie_header (struct MHD_Connection *connection)
4217{
4218 const char *hdr;
4219 size_t hdr_len;
4220 char *cpy;
4221 size_t i;
4222 enum _MHD_ParseCookie parse_res;
4223 struct MHD_HTTP_Req_Header *const saved_tail =
4224 connection->rq.headers_received_tail;
4225 const bool allow_partially_correct_cookie =
4226 (1 >= connection->daemon->client_discipline);
4227
4228 if (MHD_NO ==
4234 &hdr,
4235 &hdr_len))
4236 return MHD_PARSE_COOKIE_OK;
4237 if (0 == hdr_len)
4238 return MHD_PARSE_COOKIE_OK;
4239
4240 cpy = MHD_connection_alloc_memory_ (connection,
4241 hdr_len + 1);
4242 if (NULL == cpy)
4243 parse_res = MHD_PARSE_COOKIE_NO_MEMORY;
4244 else
4245 {
4246 memcpy (cpy,
4247 hdr,
4248 hdr_len);
4249 cpy[hdr_len] = '\0';
4250
4251 i = 0;
4252 /* Skip all initial whitespaces */
4253 while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i]))
4254 i++;
4255
4256 parse_res = parse_cookies_string (cpy + i, hdr_len - i, connection);
4257 }
4258
4259 switch (parse_res)
4260 {
4261 case MHD_PARSE_COOKIE_OK:
4262 break;
4263 case MHD_PARSE_COOKIE_OK_LAX:
4264#ifdef HAVE_MESSAGES
4265 if (saved_tail != connection->rq.headers_received_tail)
4266 MHD_DLOG (connection->daemon,
4267 _ ("The Cookie header has been parsed, but it is not fully "
4268 "compliant with the standard.\n"));
4269#endif /* HAVE_MESSAGES */
4270 break;
4271 case MHD_PARSE_COOKIE_MALFORMED:
4272 if (saved_tail != connection->rq.headers_received_tail)
4273 {
4274 if (! allow_partially_correct_cookie)
4275 {
4276 /* Remove extracted values from partially broken cookie */
4277 /* Memory remains allocated until the end of the request processing */
4278 connection->rq.headers_received_tail = saved_tail;
4279 saved_tail->next = NULL;
4280#ifdef HAVE_MESSAGES
4281 MHD_DLOG (connection->daemon,
4282 _ ("The Cookie header has been ignored as it contains "
4283 "malformed data.\n"));
4284#endif /* HAVE_MESSAGES */
4285 }
4286#ifdef HAVE_MESSAGES
4287 else
4288 MHD_DLOG (connection->daemon,
4289 _ ("The Cookie header has been only partially parsed as it "
4290 "contains malformed data.\n"));
4291#endif /* HAVE_MESSAGES */
4292 }
4293#ifdef HAVE_MESSAGES
4294 else
4295 MHD_DLOG (connection->daemon,
4296 _ ("The Cookie header has malformed data.\n"));
4297#endif /* HAVE_MESSAGES */
4298 break;
4299 case MHD_PARSE_COOKIE_NO_MEMORY:
4300#ifdef HAVE_MESSAGES
4301 MHD_DLOG (connection->daemon,
4302 _ ("Not enough memory in the connection pool to "
4303 "parse client cookies!\n"));
4304#endif /* HAVE_MESSAGES */
4305 break;
4306 default:
4307 mhd_assert (0);
4308 break;
4309 }
4310#ifndef HAVE_MESSAGES
4311 (void) saved_tail; /* Mute compiler warning */
4312#endif /* ! HAVE_MESSAGES */
4313
4314 return parse_res;
4315}
4316
4317
4318#endif /* COOKIE_SUPPORT */
4319
4320
4324#define HTTP_VER_LEN (MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1))
4325
4335static bool
4337 const char *http_string,
4338 size_t len)
4339{
4340 const char *const h = http_string;
4341 mhd_assert (NULL != http_string);
4342
4343 /* String must start with 'HTTP/d.d', case-sensetive match.
4344 * See https://www.rfc-editor.org/rfc/rfc9112#name-http-version */
4345 if ((HTTP_VER_LEN != len) ||
4346 ('H' != h[0]) || ('T' != h[1]) || ('T' != h[2]) || ('P' != h[3]) ||
4347 ('/' != h[4])
4348 || ('.' != h[6]) ||
4349 (('0' > h[5]) || ('9' < h[5])) ||
4350 (('0' > h[7]) || ('9' < h[7])))
4351 {
4352 connection->rq.http_ver = MHD_HTTP_VER_INVALID;
4356 return false;
4357 }
4358 if (1 == h[5] - '0')
4359 {
4360 /* HTTP/1.x */
4361 if (1 == h[7] - '0')
4362 connection->rq.http_ver = MHD_HTTP_VER_1_1;
4363 else if (0 == h[7] - '0')
4364 connection->rq.http_ver = MHD_HTTP_VER_1_0;
4365 else
4366 connection->rq.http_ver = MHD_HTTP_VER_1_2__1_9;
4367
4368 return true;
4369 }
4370
4371 if (0 == h[5] - '0')
4372 {
4373 /* Too old major version */
4374 connection->rq.http_ver = MHD_HTTP_VER_TOO_OLD;
4378 return false;
4379 }
4380
4381 connection->rq.http_ver = MHD_HTTP_VER_FUTURE;
4385 return false;
4386}
4387
4388
4396static void
4398 const char *method,
4399 size_t len)
4400{
4401 const char *const m = method;
4402 mhd_assert (NULL != m);
4403 mhd_assert (0 != len);
4404
4405 if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET) == len) &&
4406 (0 == memcmp (m, MHD_HTTP_METHOD_GET, len)))
4407 connection->rq.http_mthd = MHD_HTTP_MTHD_GET;
4408 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_HEAD) == len) &&
4409 (0 == memcmp (m, MHD_HTTP_METHOD_HEAD, len)))
4410 connection->rq.http_mthd = MHD_HTTP_MTHD_HEAD;
4411 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_POST) == len) &&
4412 (0 == memcmp (m, MHD_HTTP_METHOD_POST, len)))
4413 connection->rq.http_mthd = MHD_HTTP_MTHD_POST;
4414 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_PUT) == len) &&
4415 (0 == memcmp (m, MHD_HTTP_METHOD_PUT, len)))
4416 connection->rq.http_mthd = MHD_HTTP_MTHD_PUT;
4417 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_DELETE) == len) &&
4418 (0 == memcmp (m, MHD_HTTP_METHOD_DELETE, len)))
4419 connection->rq.http_mthd = MHD_HTTP_MTHD_DELETE;
4420 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_CONNECT) == len) &&
4421 (0 == memcmp (m, MHD_HTTP_METHOD_CONNECT, len)))
4422 connection->rq.http_mthd = MHD_HTTP_MTHD_CONNECT;
4423 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_OPTIONS) == len) &&
4424 (0 == memcmp (m, MHD_HTTP_METHOD_OPTIONS, len)))
4425 connection->rq.http_mthd = MHD_HTTP_MTHD_OPTIONS;
4426 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_TRACE) == len) &&
4427 (0 == memcmp (m, MHD_HTTP_METHOD_TRACE, len)))
4428 connection->rq.http_mthd = MHD_HTTP_MTHD_TRACE;
4429 else
4430 connection->rq.http_mthd = MHD_HTTP_MTHD_OTHER;
4431}
4432
4433
4441static void
4443{
4444 struct MHD_Daemon *daemon = connection->daemon;
4445 size_t processed;
4446
4447 if (NULL != connection->rp.response)
4448 return; /* already queued a response */
4449 processed = 0;
4450 connection->rq.client_aware = true;
4451 connection->in_access_handler = true;
4452 if (MHD_NO ==
4453 daemon->default_handler (daemon->default_handler_cls,
4454 connection,
4455 connection->rq.url_for_callback,
4456 connection->rq.method,
4457 connection->rq.version,
4458 NULL,
4459 &processed,
4460 &connection->rq.client_context))
4461 {
4462 connection->in_access_handler = false;
4463 /* serious internal error, close connection */
4464 CONNECTION_CLOSE_ERROR (connection,
4465 _ ("Application reported internal error, " \
4466 "closing connection."));
4467 return;
4468 }
4469 connection->in_access_handler = false;
4470}
4471
4472
4480static void
4482{
4483 struct MHD_Daemon *daemon = connection->daemon;
4484 size_t available;
4485 bool instant_retry;
4486 char *buffer_head;
4487 const int discp_lvl = daemon->client_discipline;
4488 /* RFC does not allow LF as the line termination in chunk headers.
4489 See RFC 9112, section 7.1 and section 2.2-3 */
4490 const bool bare_lf_as_crlf = (-2 > discp_lvl);
4491 /* Allow "Bad WhiteSpace" in chunk extension.
4492 RFC 9112, Section 7.1.1, Paragraph 2 */
4493 const bool allow_bws = (2 > discp_lvl);
4494
4495 mhd_assert (NULL == connection->rp.response);
4496
4497 buffer_head = connection->read_buffer;
4498 available = connection->read_buffer_offset;
4499 do
4500 {
4501 size_t to_be_processed;
4502 size_t left_unprocessed;
4503 size_t processed_size;
4504
4505 instant_retry = false;
4506 if (connection->rq.have_chunked_upload)
4507 {
4509 if ( (connection->rq.current_chunk_offset ==
4510 connection->rq.current_chunk_size) &&
4511 (0 != connection->rq.current_chunk_size) )
4512 {
4513 /* Skip CRLF chunk termination */
4514 size_t i;
4515 mhd_assert (0 != available);
4516 /* skip new line at the *end* of a chunk */
4517 i = 0;
4518 if ( (2 <= available) &&
4519 ('\r' == buffer_head[0]) &&
4520 ('\n' == buffer_head[1]) )
4521 i += 2; /* skip CRLF */
4522 else if (bare_lf_as_crlf && ('\n' == buffer_head[0]))
4523 i++; /* skip bare LF */
4524 else if (2 > available)
4525 break; /* need more upload data */
4526 if (0 == i)
4527 {
4528 /* malformed encoding */
4532 return;
4533 }
4534 available -= i;
4535 buffer_head += i;
4536 connection->rq.current_chunk_offset = 0;
4537 connection->rq.current_chunk_size = 0;
4538 if (0 == available)
4539 break;
4540 }
4541 if (0 != connection->rq.current_chunk_size)
4542 {
4543 /* Process chunk "content" */
4544 uint64_t cur_chunk_left;
4545 mhd_assert (connection->rq.current_chunk_offset < \
4546 connection->rq.current_chunk_size);
4547 cur_chunk_left
4548 = connection->rq.current_chunk_size
4549 - connection->rq.current_chunk_offset;
4550 if (cur_chunk_left > available)
4551 to_be_processed = available;
4552 else
4553 { /* cur_chunk_left <= (size_t)available */
4554 to_be_processed = (size_t) cur_chunk_left;
4555 if (available > to_be_processed)
4556 instant_retry = true;
4557 }
4558 }
4559 else
4560 { /* Need the parse the chunk size line */
4562 size_t num_dig;
4563 uint64_t chunk_size;
4564 bool broken;
4565 bool overflow;
4566
4567 mhd_assert (0 != available);
4568
4569 overflow = false;
4570 chunk_size = 0; /* Mute possible compiler warning.
4571 The real value will be set later. */
4572
4573 num_dig = MHD_strx_to_uint64_n_ (buffer_head,
4574 available,
4575 &chunk_size);
4576 mhd_assert (num_dig <= available);
4577 if (num_dig == available)
4578 continue; /* Need line delimiter */
4579
4580 broken = (0 == num_dig);
4581 if (broken)
4582 {
4583 uint64_t dummy;
4584 /* Check whether result is invalid due to uint64_t overflow */
4585 overflow = (0 != MHD_strx_to_uint64_n_ (buffer_head,
4586 1,
4587 &dummy));
4588 }
4589 else
4590 {
4595 size_t chunk_size_line_len;
4596
4597 chunk_size_line_len = 0;
4598 if ((';' == buffer_head[num_dig]) ||
4599 (allow_bws &&
4600 ((' ' == buffer_head[num_dig]) ||
4601 ('\t' == buffer_head[num_dig]))))
4602 { /* Chunk extension or "bad whitespace" after chunk length */
4603 size_t i;
4604
4605 /* Skip bad whitespaces (if any) */
4606 for (i = num_dig; i < available; ++i)
4607 {
4608 if ((' ' != buffer_head[i]) && ('\t' != buffer_head[i]))
4609 break;
4610 }
4611 if (i == available)
4612 break; /* need more data */
4613 if (';' == buffer_head[i])
4614 {
4615 /* Chunk extension */
4616 for (++i; i < available; ++i)
4617 {
4618 if (('\r' == buffer_head[i]) ||
4619 ('\n' == buffer_head[i]))
4620 break;
4621 }
4622 if (i == available)
4623 break; /* need more data */
4624 mhd_assert (i > num_dig);
4625 mhd_assert (1 <= i);
4626 if ('\r' == buffer_head[i])
4627 {
4628 if (i + 1 == available)
4629 break; /* need more data */
4630 if ('\n' == buffer_head[i + 1])
4631 chunk_size_line_len = i; /* Valid chunk header */
4632 }
4633 else
4634 {
4635 mhd_assert ('\n' == buffer_head[i]);
4636 if (bare_lf_as_crlf)
4637 chunk_size_line_len = i; /* Valid chunk header */
4638 }
4639 /* The chunk header is broken
4640 if chunk_size_line_len is zero here. */
4641 }
4642 else
4643 { /* No ';' after "bad whitespace" */
4644 mhd_assert (allow_bws);
4645 mhd_assert (0 == chunk_size_line_len);
4646 }
4647 }
4648 else
4649 {
4650 /* No chunk extension */
4651 mhd_assert (available >= num_dig);
4652 if ((2 <= (available - num_dig)) &&
4653 ('\r' == buffer_head[num_dig]) &&
4654 ('\n' == buffer_head[num_dig + 1]))
4655 chunk_size_line_len = num_dig + 2;
4656 else if (bare_lf_as_crlf &&
4657 ('\n' == buffer_head[num_dig]))
4658 chunk_size_line_len = num_dig + 1;
4659 else if (2 > (available - num_dig))
4660 break; /* need more data */
4661 }
4662
4663 if (0 != chunk_size_line_len)
4664 { /* Valid termination of the chunk size line */
4665 mhd_assert (chunk_size_line_len <= available);
4666 /* Start reading payload data of the chunk */
4667 connection->rq.current_chunk_offset = 0;
4668 connection->rq.current_chunk_size = chunk_size;
4669
4670 available -= chunk_size_line_len;
4671 buffer_head += chunk_size_line_len;
4672
4673 if (0 == chunk_size)
4674 { /* The final (termination) chunk */
4675 connection->rq.remaining_upload_size = 0;
4676 break;
4677 }
4678 if (available > 0)
4679 instant_retry = true;
4680 continue;
4681 }
4682 /* Invalid chunk size line */
4683 }
4684
4685 if (! overflow)
4689 else
4693 return;
4694 }
4695 }
4696 else
4697 {
4698 /* no chunked encoding, give all to the client */
4700 mhd_assert (0 != connection->rq.remaining_upload_size);
4701 if (connection->rq.remaining_upload_size < available)
4702 to_be_processed = (size_t) connection->rq.remaining_upload_size;
4703 else
4704 to_be_processed = available;
4705 }
4706 left_unprocessed = to_be_processed;
4707 connection->rq.client_aware = true;
4708 connection->in_access_handler = true;
4709 if (MHD_NO ==
4710 daemon->default_handler (daemon->default_handler_cls,
4711 connection,
4712 connection->rq.url_for_callback,
4713 connection->rq.method,
4714 connection->rq.version,
4715 buffer_head,
4716 &left_unprocessed,
4717 &connection->rq.client_context))
4718 {
4719 connection->in_access_handler = false;
4720 /* serious internal error, close connection */
4721 CONNECTION_CLOSE_ERROR (connection,
4722 _ ("Application reported internal error, " \
4723 "closing connection."));
4724 return;
4725 }
4726 connection->in_access_handler = false;
4727
4728 if (left_unprocessed > to_be_processed)
4729 MHD_PANIC (_ ("libmicrohttpd API violation.\n"));
4730
4731 connection->rq.some_payload_processed =
4732 (left_unprocessed != to_be_processed);
4733
4734 if (0 != left_unprocessed)
4735 {
4736 instant_retry = false; /* client did not process everything */
4737#ifdef HAVE_MESSAGES
4738 if ((! connection->rq.some_payload_processed) &&
4739 (! connection->suspended))
4740 {
4741 /* client did not process any upload data, complain if
4742 the setup was incorrect, which may prevent us from
4743 handling the rest of the request */
4744 if (MHD_D_IS_USING_THREADS_ (daemon))
4745 MHD_DLOG (daemon,
4746 _ ("WARNING: Access Handler Callback has not processed " \
4747 "any upload data and connection is not suspended. " \
4748 "This may result in hung connection.\n"));
4749 }
4750#endif /* HAVE_MESSAGES */
4751 }
4752 processed_size = to_be_processed - left_unprocessed;
4753 /* dh left "processed" bytes in buffer for next time... */
4754 buffer_head += processed_size;
4755 available -= processed_size;
4756 if (! connection->rq.have_chunked_upload)
4757 {
4759 connection->rq.remaining_upload_size -= processed_size;
4760 }
4761 else
4762 {
4764 connection->rq.current_chunk_offset += processed_size;
4765 }
4766 } while (instant_retry);
4767 /* TODO: zero out reused memory region */
4768 if ( (available > 0) &&
4769 (buffer_head != connection->read_buffer) )
4770 memmove (connection->read_buffer,
4771 buffer_head,
4772 available);
4773 else
4774 mhd_assert ((0 == available) || \
4775 (connection->read_buffer_offset == available));
4776 connection->read_buffer_offset = available;
4777}
4778
4779
4788static enum MHD_Result
4790 enum MHD_CONNECTION_STATE next_state)
4791{
4792 if ( (connection->write_buffer_append_offset !=
4793 connection->write_buffer_send_offset)
4794 /* || data_in_tls_buffers == true */
4795 )
4796 return MHD_NO;
4797 connection->write_buffer_append_offset = 0;
4798 connection->write_buffer_send_offset = 0;
4799 connection->state = next_state;
4800 return MHD_YES;
4801}
4802
4803
4811static void
4813{
4814 const char *enc;
4815
4816#ifdef COOKIE_SUPPORT
4817 if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (connection))
4818 {
4819 handle_req_cookie_no_space (connection);
4820 return;
4821 }
4822#endif /* COOKIE_SUPPORT */
4823 if ( (-3 < connection->daemon->client_discipline) &&
4824 (MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver)) &&
4825 (MHD_NO ==
4831 NULL,
4832 NULL)) )
4833 {
4834#ifdef HAVE_MESSAGES
4835 MHD_DLOG (connection->daemon,
4836 _ ("Received HTTP/1.1 request without `Host' header.\n"));
4837#endif
4841 return;
4842 }
4843
4844 /* The presence of the request body is indicated by "Content-Length:" or
4845 "Transfer-Encoding:" request headers.
4846 Unless one of these two headers is used, the request has no request body.
4847 See RFC9112, Section 6, paragraph 4. */
4848 connection->rq.remaining_upload_size = 0;
4849 if (MHD_NO !=
4855 &enc,
4856 NULL))
4857 {
4858 if (! MHD_str_equal_caseless_ (enc,
4859 "chunked"))
4860 {
4864 return;
4865 }
4866 if (MHD_NO !=
4872 NULL,
4873 NULL))
4874 {
4875 /* TODO: add individual settings */
4876 if (1 <= connection->daemon->client_discipline)
4877 {
4881 return;
4882 }
4883 else
4884 {
4885 /* Must close connection after reply to prevent potential attack */
4886 connection->keepalive = MHD_CONN_MUST_CLOSE;
4887#ifdef HAVE_MESSAGES
4888 MHD_DLOG (connection->daemon,
4889 _ ("The 'Content-Length' request header is ignored "
4890 "as chunked Transfer-Encoding is used "
4891 "for this request.\n"));
4892#endif /* HAVE_MESSAGES */
4893 }
4894 }
4895 connection->rq.have_chunked_upload = true;
4897 }
4898 else /* was: transfer encoding set */
4899 {
4900 bool found = false;
4901 struct MHD_HTTP_Req_Header *pos;
4902
4903 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
4904 if ( (0 != (pos->kind & MHD_HEADER_KIND)) &&
4906 pos->header,
4909 {
4910 const char *clen;
4911 size_t val_len;
4912 size_t num_digits;
4913
4914 if (found &&
4915 (0 <= connection->daemon->client_discipline))
4916 {
4917 /* more than one header, bad */
4921 return;
4922 }
4923 found = true;
4924 val_len = pos->value_size;
4925 clen = pos->value;
4926 num_digits = MHD_str_to_uint64_n_ (clen,
4927 val_len,
4928 &connection->rq.remaining_upload_size
4929 );
4930
4931 if (((0 == num_digits) &&
4932 (0 != val_len) &&
4933 ('0' <= clen[0]) && ('9' >= clen[0]))
4934 || (MHD_SIZE_UNKNOWN == connection->rq.remaining_upload_size))
4935 {
4936 connection->rq.remaining_upload_size = 0;
4937#ifdef HAVE_MESSAGES
4938 MHD_DLOG (connection->daemon,
4939 _ ("Too large value of 'Content-Length' header. " \
4940 "Closing connection.\n"));
4941#endif
4945 return;
4946 }
4947 if ( (val_len != num_digits) ||
4948 (0 == num_digits) )
4949 {
4950 connection->rq.remaining_upload_size = 0;
4951#ifdef HAVE_MESSAGES
4952 MHD_DLOG (connection->daemon,
4953 _ ("Failed to parse 'Content-Length' header. " \
4954 "Closing connection.\n"));
4955#endif
4959 return;
4960 }
4961 } /* for all HTTP headers */
4962 } /* was: else: transfer encoding is not set */
4963}
4964
4965
4973_MHD_static_inline void
4975{
4976 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
4977}
4978
4979
4984_MHD_static_inline void
4986{
4988 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
4990}
4991
4992
4993#ifndef MHD_MAX_EMPTY_LINES_SKIP
4998#define MHD_MAX_EMPTY_LINES_SKIP 1024
4999#endif /* ! MHD_MAX_EMPTY_LINES_SKIP */
5000
5008static bool
5010{
5011 size_t p;
5012 const int discp_lvl = c->daemon->client_discipline;
5013 /* Allow to skip one or more empty lines before the request line.
5014 RFC 9112, section 2.2 */
5015 const bool skip_empty_lines = (1 >= discp_lvl);
5016 /* Allow to skip more then one empty line before the request line.
5017 RFC 9112, section 2.2 */
5018 const bool skip_several_empty_lines = (skip_empty_lines && (0 >= discp_lvl));
5019 /* Allow to skip number of unlimited empty lines before the request line.
5020 RFC 9112, section 2.2 */
5021 const bool skip_unlimited_empty_lines =
5022 (skip_empty_lines && (-3 >= discp_lvl));
5023 /* Treat bare LF as the end of the line.
5024 RFC 9112, section 2.2 */
5025 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
5026 /* Treat tab as whitespace delimiter.
5027 RFC 9112, section 3 */
5028 const bool tab_as_wsp = (0 >= discp_lvl);
5029 /* Treat VT (vertical tab) and FF (form feed) as whitespace delimiters.
5030 RFC 9112, section 3 */
5031 const bool other_wsp_as_wsp = (-1 >= discp_lvl);
5032 /* Treat continuous whitespace block as a single space.
5033 RFC 9112, section 3 */
5034 const bool wsp_blocks = (-1 >= discp_lvl);
5035 /* Parse whitespace in URI, special parsing of the request line.
5036 RFC 9112, section 3.2 */
5037 const bool wsp_in_uri = (0 >= discp_lvl);
5038 /* Keep whitespace in URI, give app URI with whitespace instead of
5039 automatic redirect to fixed URI.
5040 Violates RFC 9112, section 3.2 */
5041 const bool wsp_in_uri_keep = (-2 >= discp_lvl);
5042 /* Keep bare CR character as is.
5043 Violates RFC 9112, section 2.2 */
5044 const bool bare_cr_keep = (wsp_in_uri_keep && (-3 >= discp_lvl));
5045 /* Treat bare CR as space; replace it with space before processing.
5046 RFC 9112, section 2.2 */
5047 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
5048
5051 mhd_assert (NULL == c->rq.method || \
5056 0 != c->rq.hdrs.rq_line.proc_pos);
5057
5058 if (0 == c->read_buffer_offset)
5059 {
5061 return false; /* No data to process */
5062 }
5063 p = c->rq.hdrs.rq_line.proc_pos;
5064 mhd_assert (p <= c->read_buffer_offset);
5065
5066 /* Skip empty lines, if any (and if allowed) */
5067 /* See RFC 9112, section 2.2 */
5068 if ((0 == p)
5069 && (skip_empty_lines))
5070 {
5071 /* Skip empty lines before the request line.
5072 See RFC 9112, section 2.2 */
5073 bool is_empty_line;
5075 mhd_assert (NULL == c->rq.method);
5076 mhd_assert (NULL == c->rq.url);
5077 mhd_assert (0 == c->rq.url_len);
5079 mhd_assert (0 == c->rq.req_target_len);
5080 mhd_assert (NULL == c->rq.version);
5081 do
5082 {
5083 is_empty_line = false;
5084 if ('\r' == c->read_buffer[0])
5085 {
5086 if (1 == c->read_buffer_offset)
5087 return false; /* Not enough data yet */
5088 if ('\n' == c->read_buffer[1])
5089 {
5090 is_empty_line = true;
5091 c->read_buffer += 2;
5092 c->read_buffer_size -= 2;
5093 c->read_buffer_offset -= 2;
5095 }
5096 }
5097 else if (('\n' == c->read_buffer[0]) &&
5098 (bare_lf_as_crlf))
5099 {
5100 is_empty_line = true;
5101 c->read_buffer += 1;
5102 c->read_buffer_size -= 1;
5103 c->read_buffer_offset -= 1;
5105 }
5106 if (is_empty_line)
5107 {
5108 if ((! skip_unlimited_empty_lines) &&
5109 (((unsigned int) ((skip_several_empty_lines) ?
5112 {
5114 _ ("Too many meaningless extra empty lines " \
5115 "received before the request"));
5116 return true; /* Process connection closure */
5117 }
5118 if (0 == c->read_buffer_offset)
5119 return false; /* No more data to process */
5120 }
5121 } while (is_empty_line);
5122 }
5123 /* All empty lines are skipped */
5124
5126 /* Read and parse the request line */
5128
5129 while (p < c->read_buffer_offset)
5130 {
5131 const char chr = c->read_buffer[p];
5132 bool end_of_line;
5133 /*
5134 The processing logic is different depending on the configured strictness:
5135
5136 When whitespace BLOCKS are NOT ALLOWED, the end of the whitespace is
5137 processed BEFORE processing of the current character.
5138 When whitespace BLOCKS are ALLOWED, the end of the whitespace is
5139 processed AFTER processing of the current character.
5140
5141 When space char in the URI is ALLOWED, the delimiter between the URI and
5142 the HTTP version string is processed only at the END of the line.
5143 When space in the URI is NOT ALLOWED, the delimiter between the URI and
5144 the HTTP version string is processed as soon as the FIRST whitespace is
5145 found after URI start.
5146 */
5147
5148 end_of_line = false;
5149
5150 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5151 (c->rq.hdrs.rq_line.last_ws_end > \
5153 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_start) || \
5154 (0 != c->rq.hdrs.rq_line.last_ws_end));
5155
5156 /* Check for the end of the line */
5157 if ('\r' == chr)
5158 {
5159 if (p + 1 == c->read_buffer_offset)
5160 {
5161 c->rq.hdrs.rq_line.proc_pos = p;
5162 return false; /* Not enough data yet */
5163 }
5164 else if ('\n' == c->read_buffer[p + 1])
5165 end_of_line = true;
5166 else
5167 {
5168 /* Bare CR alone */
5169 /* Must be rejected or replaced with space char.
5170 See RFC 9112, section 2.2 */
5171 if (bare_cr_as_sp)
5172 {
5173 c->read_buffer[p] = ' ';
5175 continue; /* Re-start processing of the current character */
5176 }
5177 else if (! bare_cr_keep)
5178 {
5179 /* A quick simple check whether this line looks like an HTTP request */
5180 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5182 {
5186 }
5187 else
5189 _ ("Bare CR characters are not allowed " \
5190 "in the request line.\n"));
5191 return true; /* Error in the request */
5192 }
5193 }
5194 }
5195 else if ('\n' == chr)
5196 {
5197 /* Bare LF may be recognised as a line delimiter.
5198 See RFC 9112, section 2.2 */
5199 if (bare_lf_as_crlf)
5200 end_of_line = true;
5201 else
5202 {
5203 /* While RFC does not enforce error for bare LF character,
5204 if this char is not treated as a line delimiter, it should be
5205 rejected to avoid any security weakness due to request smuggling. */
5206 /* A quick simple check whether this line looks like an HTTP request */
5207 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5209 {
5213 }
5214 else
5216 _ ("Bare LF characters are not allowed " \
5217 "in the request line.\n"));
5218 return true; /* Error in the request */
5219 }
5220 }
5221
5222 if (end_of_line)
5223 {
5224 /* Handle the end of the request line */
5225
5226 if (NULL != c->rq.method)
5227 {
5228 if (wsp_in_uri)
5229 {
5230 /* The end of the URI and the start of the HTTP version string
5231 should be determined now. */
5232 mhd_assert (NULL == c->rq.version);
5233 mhd_assert (0 == c->rq.req_target_len);
5234 if (0 != c->rq.hdrs.rq_line.last_ws_end)
5235 {
5236 /* Determine the end and the length of the URI */
5237 if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5238 {
5239 c->read_buffer [c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5240 c->rq.req_target_len =
5242 - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5243 }
5244 else if ((c->rq.hdrs.rq_line.last_ws_start + 1 <
5245 c->rq.hdrs.rq_line.last_ws_end) &&
5246 (HTTP_VER_LEN == (p - c->rq.hdrs.rq_line.last_ws_end)))
5247 {
5248 /* Found only HTTP method and HTTP version and more than one
5249 whitespace between them. Assume zero-length URI. */
5250 mhd_assert (wsp_blocks);
5252 c->read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5253 c->rq.hdrs.rq_line.rq_tgt =
5255 c->rq.req_target_len = 0;
5258 }
5259 /* Determine the start of the HTTP version string */
5260 if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5261 {
5263 }
5264 }
5265 }
5266 else
5267 {
5268 /* The end of the URI and the start of the HTTP version string
5269 should be already known. */
5270 if ((NULL == c->rq.version)
5271 && (NULL != c->rq.hdrs.rq_line.rq_tgt)
5272 && (HTTP_VER_LEN == p - (size_t) (c->rq.hdrs.rq_line.rq_tgt
5273 - c->read_buffer))
5274 && (0 != c->read_buffer[(size_t)
5275 (c->rq.hdrs.rq_line.rq_tgt
5276 - c->read_buffer) - 1]))
5277 {
5278 /* Found only HTTP method and HTTP version and more than one
5279 whitespace between them. Assume zero-length URI. */
5280 size_t uri_pos;
5281 mhd_assert (wsp_blocks);
5282 mhd_assert (0 == c->rq.req_target_len);
5283 uri_pos = (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer) - 1;
5284 mhd_assert (uri_pos < p);
5285 c->rq.version = c->rq.hdrs.rq_line.rq_tgt;
5286 c->read_buffer[uri_pos] = 0; /* Zero terminate the URI */
5287 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + uri_pos;
5288 c->rq.req_target_len = 0;
5291 }
5292 }
5293
5294 if (NULL != c->rq.version)
5295 {
5297 if (! parse_http_version (c, c->rq.version,
5298 p
5299 - (size_t) (c->rq.version
5300 - c->read_buffer)))
5301 {
5303 return true; /* Unsupported / broken HTTP version */
5304 }
5305 c->read_buffer[p] = 0; /* Zero terminate the HTTP version strings */
5306 if ('\r' == chr)
5307 {
5308 p++; /* Consume CR */
5309 mhd_assert (p < c->read_buffer_offset); /* The next character has been already checked */
5310 }
5311 p++; /* Consume LF */
5312 c->read_buffer += p;
5313 c->read_buffer_size -= p;
5314 c->read_buffer_offset -= p;
5316 c->rq.req_target_len);
5318 (0 != c->rq.req_target_len));
5320 ((size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5321 - c->rq.hdrs.rq_line.rq_tgt) < \
5322 c->rq.req_target_len));
5324 (c->rq.hdrs.rq_line.rq_tgt_qmark >= \
5325 c->rq.hdrs.rq_line.rq_tgt));
5326 return true; /* The request line is successfully parsed */
5327 }
5328 }
5329 /* Error in the request line */
5330
5331 /* A quick simple check whether this line looks like an HTTP request */
5332 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5334 {
5338 }
5339 else
5341 _ ("The request line is malformed.\n"));
5342
5343 return true;
5344 }
5345
5346 /* Process possible end of the previously found whitespace delimiter */
5347 if ((! wsp_blocks) &&
5348 (p == c->rq.hdrs.rq_line.last_ws_end) &&
5349 (0 != c->rq.hdrs.rq_line.last_ws_end))
5350 {
5351 /* Previous character was a whitespace char and whitespace blocks
5352 are not allowed. */
5353 /* The current position is the next character after
5354 a whitespace delimiter */
5355 if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5356 {
5357 /* The current position is the start of the URI */
5358 mhd_assert (0 == c->rq.req_target_len);
5359 mhd_assert (NULL == c->rq.version);
5360 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5361 /* Reset the whitespace marker */
5363 c->rq.hdrs.rq_line.last_ws_end = 0;
5364 }
5365 else
5366 {
5367 /* It was a whitespace after the start of the URI */
5368 if (! wsp_in_uri)
5369 {
5370 mhd_assert ((0 != c->rq.req_target_len) || \
5371 (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5372 mhd_assert (NULL == c->rq.version); /* Too many whitespaces? This error is handled at whitespace start */
5373 c->rq.version = c->read_buffer + p;
5374 /* Reset the whitespace marker */
5376 c->rq.hdrs.rq_line.last_ws_end = 0;
5377 }
5378 }
5379 }
5380
5381 /* Process the current character.
5382 Is it not the end of the line. */
5383 if ((' ' == chr)
5384 || (('\t' == chr) && (tab_as_wsp))
5385 || ((other_wsp_as_wsp) && ((0xb == chr) || (0xc == chr))))
5386 {
5387 /* A whitespace character */
5388 if ((0 == c->rq.hdrs.rq_line.last_ws_end) ||
5389 (p != c->rq.hdrs.rq_line.last_ws_end) ||
5390 (! wsp_blocks))
5391 {
5392 /* Found first whitespace char of the new whitespace block */
5393 if (NULL == c->rq.method)
5394 {
5395 /* Found the end of the HTTP method string */
5399 mhd_assert (0 == c->rq.req_target_len);
5400 mhd_assert (NULL == c->rq.version);
5401 if (0 == p)
5402 {
5404 _ ("The request line starts with "
5405 "a whitespace.\n"));
5406 return true; /* Error in the request */
5407 }
5408 c->read_buffer[p] = 0; /* Zero-terminate the request method string */
5409 c->rq.method = c->read_buffer;
5410 parse_http_std_method (c, c->rq.method, p);
5411 }
5412 else
5413 {
5414 /* A whitespace after the start of the URI */
5415 if (! wsp_in_uri)
5416 {
5417 /* Whitespace in URI is not allowed to be parsed */
5418 if (NULL == c->rq.version)
5419 {
5421 /* This is a delimiter between URI and HTTP version string */
5422 c->read_buffer[p] = 0; /* Zero-terminate request URI string */
5423 mhd_assert (((size_t) (c->rq.hdrs.rq_line.rq_tgt \
5424 - c->read_buffer)) <= p);
5425 c->rq.req_target_len =
5426 p - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5427 }
5428 else
5429 {
5430 /* This is a delimiter AFTER version string */
5431
5432 /* A quick simple check whether this line looks like an HTTP request */
5433 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5435 {
5439 }
5440 else
5442 _ ("The request line has more than "
5443 "two whitespaces.\n"));
5444 return true; /* Error in the request */
5445 }
5446 }
5447 else
5448 {
5449 /* Whitespace in URI is allowed to be parsed */
5450 if (0 != c->rq.hdrs.rq_line.last_ws_end)
5451 {
5452 /* The whitespace after the start of the URI has been found already */
5456 }
5457 }
5458 }
5460 c->rq.hdrs.rq_line.last_ws_end = p + 1; /* Will be updated on the next char parsing */
5461 }
5462 else
5463 {
5464 /* Continuation of the whitespace block */
5466 mhd_assert (0 != p);
5467 c->rq.hdrs.rq_line.last_ws_end = p + 1;
5468 }
5469 }
5470 else
5471 {
5472 /* Non-whitespace char, not the end of the line */
5473 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5474 (c->rq.hdrs.rq_line.last_ws_end == p) || \
5475 wsp_in_uri);
5476
5477 if ((p == c->rq.hdrs.rq_line.last_ws_end) &&
5478 (0 != c->rq.hdrs.rq_line.last_ws_end) &&
5479 (wsp_blocks))
5480 {
5481 /* The end of the whitespace block */
5482 if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5483 {
5484 /* This is the first character of the URI */
5485 mhd_assert (0 == c->rq.req_target_len);
5486 mhd_assert (NULL == c->rq.version);
5487 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5488 /* Reset the whitespace marker */
5490 c->rq.hdrs.rq_line.last_ws_end = 0;
5491 }
5492 else
5493 {
5494 if (! wsp_in_uri)
5495 {
5496 /* This is the first character of the HTTP version */
5498 mhd_assert ((0 != c->rq.req_target_len) || \
5499 (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5500 mhd_assert (NULL == c->rq.version); /* Handled at whitespace start */
5501 c->rq.version = c->read_buffer + p;
5502 /* Reset the whitespace marker */
5504 c->rq.hdrs.rq_line.last_ws_end = 0;
5505 }
5506 }
5507 }
5508
5509 /* Handle other special characters */
5510 if ('?' == chr)
5511 {
5512 if ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) &&
5513 (NULL != c->rq.hdrs.rq_line.rq_tgt))
5514 {
5516 }
5517 }
5518 else if ((0xb == chr) || (0xc == chr))
5519 {
5520 /* VT or LF characters */
5521 mhd_assert (! other_wsp_as_wsp);
5522 if ((NULL != c->rq.hdrs.rq_line.rq_tgt) &&
5523 (NULL == c->rq.version) &&
5524 (wsp_in_uri))
5525 {
5527 }
5528 else
5529 {
5531 _ ("Invalid character is in the "
5532 "request line.\n"));
5533 return true; /* Error in the request */
5534 }
5535 }
5536 else if (0 == chr)
5537 {
5538 /* NUL character */
5540 _ ("The NUL character is in the "
5541 "request line.\n"));
5542 return true; /* Error in the request */
5543 }
5544 }
5545
5546 p++;
5547 }
5548
5549 c->rq.hdrs.rq_line.proc_pos = p;
5550 return false; /* Not enough data yet */
5551}
5552
5553
5554#ifndef MHD_MAX_FIXED_URI_LEN
5558#define MHD_MAX_FIXED_URI_LEN (64 * 1024)
5559#endif /* ! MHD_MAX_FIXED_URI_LEN */
5560
5568static void
5570{
5571 char *b;
5572 size_t fixed_uri_len;
5573 size_t i;
5574 size_t o;
5575 char *hdr_name;
5576 size_t hdr_name_len;
5577
5581 c->rq.req_target_len);
5582 fixed_uri_len = c->rq.req_target_len
5583 + 2 * c->rq.hdrs.rq_line.num_ws_in_uri;
5584 if ( (fixed_uri_len + 200 > c->daemon->pool_size) ||
5585 (fixed_uri_len > MHD_MAX_FIXED_URI_LEN) ||
5586 (NULL == (b = malloc (fixed_uri_len + 1))) )
5587 {
5589 _ ("The request has whitespace character is " \
5590 "in the URI and the URI is too large to " \
5591 "send automatic redirect to fixed URI.\n"));
5592 return;
5593 }
5594 i = 0;
5595 o = 0;
5596
5597 do
5598 {
5599 const char chr = c->rq.hdrs.rq_line.rq_tgt[i++];
5600
5601 mhd_assert ('\r' != chr); /* Replaced during request line parsing */
5602 mhd_assert ('\n' != chr); /* Rejected during request line parsing */
5603 mhd_assert (0 != chr); /* Rejected during request line parsing */
5604 switch (chr)
5605 {
5606 case ' ':
5607 b[o++] = '%';
5608 b[o++] = '2';
5609 b[o++] = '0';
5610 break;
5611 case '\t':
5612 b[o++] = '%';
5613 b[o++] = '0';
5614 b[o++] = '9';
5615 break;
5616 case 0x0B: /* VT (vertical tab) */
5617 b[o++] = '%';
5618 b[o++] = '0';
5619 b[o++] = 'B';
5620 break;
5621 case 0x0C: /* FF (form feed) */
5622 b[o++] = '%';
5623 b[o++] = '0';
5624 b[o++] = 'C';
5625 break;
5626 default:
5627 b[o++] = chr;
5628 break;
5629 }
5630 } while (i < c->rq.req_target_len);
5631 mhd_assert (fixed_uri_len == o);
5632 b[o] = 0; /* Zero-terminate the result */
5633
5635 hdr_name = malloc (hdr_name_len + 1);
5636 if (NULL != hdr_name)
5637 {
5638 memcpy (hdr_name,
5640 hdr_name_len + 1);
5641 /* hdr_name and b are free()d within this call */
5645 hdr_name,
5646 hdr_name_len,
5647 b,
5648 o);
5649 return;
5650 }
5651 free (b);
5653 _ ("The request has whitespace character is in the " \
5654 "URI.\n"));
5655 return;
5656}
5657
5658
5665static bool
5667{
5668#ifdef _DEBUG
5669 size_t params_len;
5670#endif /* _DEBUG */
5672 mhd_assert (NULL == c->rq.url);
5673 mhd_assert (0 == c->rq.url_len);
5679 (c->rq.req_target_len > \
5680 (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5681 - c->rq.hdrs.rq_line.rq_tgt)));
5682
5683 /* Log callback before the request-target is modified/decoded */
5684 if (NULL != c->daemon->uri_log_callback)
5685 {
5686 c->rq.client_aware = true;
5687 c->rq.client_context =
5689 c->rq.hdrs.rq_line.rq_tgt,
5690 c);
5691 }
5692
5693 if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark)
5694 {
5695#ifdef _DEBUG
5696 params_len =
5698 - (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark - c->rq.hdrs.rq_line.rq_tgt);
5699#endif /* _DEBUG */
5700 c->rq.hdrs.rq_line.rq_tgt_qmark[0] = 0; /* Replace '?' with zero termination */
5701 if (MHD_NO == MHD_parse_arguments_ (c,
5703 c->rq.hdrs.rq_line.rq_tgt_qmark + 1,
5705 c))
5706 {
5708 return false;
5709 }
5710 }
5711#ifdef _DEBUG
5712 else
5713 params_len = 0;
5714#endif /* _DEBUG */
5715
5717 mhd_assert (strlen (c->rq.hdrs.rq_line.rq_tgt) == \
5718 c->rq.req_target_len - params_len);
5719
5720 /* Finally unescape URI itself */
5721 c->rq.url_len =
5723 c,
5724 c->rq.hdrs.rq_line.rq_tgt);
5725 c->rq.url = c->rq.hdrs.rq_line.rq_tgt;
5726
5727 if (2 == c->daemon->allow_bzero_in_url)
5728 c->rq.url_for_callback = c->rq.url;
5729 else if (strlen (c->rq.url) == c->rq.url_len)
5730 c->rq.url_for_callback = c->rq.url;
5731 else if (0 == c->daemon->allow_bzero_in_url)
5732 {
5736 return false;
5737 }
5738
5739 return true;
5740}
5741
5742
5750static bool
5752{
5753 const int discp_lvl = c->daemon->client_discipline;
5754 /* Parse whitespace in URI, special parsing of the request line */
5755 const bool wsp_in_uri = (0 >= discp_lvl);
5756 /* Keep whitespace in URI, give app URI with whitespace instead of
5757 automatic redirect to fixed URI */
5758 const bool wsp_in_uri_keep = (-2 >= discp_lvl);
5759
5760 if (! get_request_line_inner (c))
5761 {
5762 /* End of the request line has not been found yet */
5763 mhd_assert ((! wsp_in_uri) || NULL == c->rq.version);
5764 if ((NULL != c->rq.version) &&
5765 (HTTP_VER_LEN <
5767 - (size_t) (c->rq.version - c->read_buffer))))
5768 {
5773 return true; /* Error in the request */
5774 }
5775 return false;
5776 }
5778 return true; /* Error in the request */
5779
5781 mhd_assert (NULL == c->rq.url);
5782 mhd_assert (0 == c->rq.url_len);
5785 if (0 != c->rq.hdrs.rq_line.num_ws_in_uri)
5786 {
5787 if (! wsp_in_uri)
5788 {
5792 return true; /* Error in the request */
5793 }
5794 if (! wsp_in_uri_keep)
5795 {
5797 return true; /* Error in the request */
5798 }
5799 }
5800 if (! process_request_target (c))
5801 return true; /* Error in processing */
5802
5804 return true;
5805}
5806
5807
5830
5831
5839static bool
5841{
5842 switch (chr)
5843 {
5844 case '!':
5845 case '#':
5846 case '$':
5847 case '%':
5848 case '&':
5849 case '\'':
5850 case '*':
5851 case '+':
5852 case '-':
5853 case '.':
5854 case '^':
5855 case '_':
5856 case '`':
5857 case '|':
5858 case '~':
5859 case 'a':
5860 case 'b':
5861 case 'c':
5862 case 'd':
5863 case 'e':
5864 case 'f':
5865 case 'g':
5866 case 'h':
5867 case 'i':
5868 case 'j':
5869 case 'k':
5870 case 'l':
5871 case 'm':
5872 case 'n':
5873 case 'o':
5874 case 'p':
5875 case 'q':
5876 case 'r':
5877 case 's':
5878 case 't':
5879 case 'u':
5880 case 'v':
5881 case 'w':
5882 case 'x':
5883 case 'y':
5884 case 'z':
5885 case 'A':
5886 case 'B':
5887 case 'C':
5888 case 'D':
5889 case 'E':
5890 case 'F':
5891 case 'G':
5892 case 'H':
5893 case 'I':
5894 case 'J':
5895 case 'K':
5896 case 'L':
5897 case 'M':
5898 case 'N':
5899 case 'O':
5900 case 'P':
5901 case 'Q':
5902 case 'R':
5903 case 'S':
5904 case 'T':
5905 case 'U':
5906 case 'V':
5907 case 'W':
5908 case 'X':
5909 case 'Y':
5910 case 'Z':
5911 case '0':
5912 case '1':
5913 case '2':
5914 case '3':
5915 case '4':
5916 case '5':
5917 case '6':
5918 case '7':
5919 case '8':
5920 case '9':
5921 return true;
5922 default:
5923 return false;
5924 }
5925}
5926
5927
5939static enum MHD_HdrLineReadRes_
5941 bool process_footers,
5942 struct _MHD_str_w_len *hdr_name,
5943 struct _MHD_str_w_len *hdr_value)
5944{
5945 const int discp_lvl = c->daemon->client_discipline;
5946 /* Treat bare LF as the end of the line.
5947 RFC 9112, section 2.2-3
5948 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
5949 Bare LF is processed as end of the line or rejected as broken request. */
5950 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
5951 /* Keep bare CR character as is.
5952 Violates RFC 9112, section 2.2-4 */
5953 const bool bare_cr_keep = (-3 >= discp_lvl);
5954 /* Treat bare CR as space; replace it with space before processing.
5955 RFC 9112, section 2.2-4 */
5956 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
5957 /* Treat NUL as space; replace it with space before processing.
5958 RFC 9110, section 5.5-5 */
5959 const bool nul_as_sp = (-1 >= discp_lvl);
5960 /* Allow folded header lines.
5961 RFC 9112, section 5.2-4 */
5962 const bool allow_folded = (0 >= discp_lvl);
5963 /* Do not reject headers with the whitespace at the start of the first line.
5964 When allowed, the first line with whitespace character at the first
5965 position is ignored (as well as all possible line foldings of the first
5966 line).
5967 RFC 9112, section 2.2-8 */
5968 const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl);
5969 /* Allow whitespace in header (field) name.
5970 Violates RFC 9110, section 5.1-2 */
5971 const bool allow_wsp_in_name = (-2 >= discp_lvl);
5972 /* Allow zero-length header (field) name.
5973 Violates RFC 9110, section 5.1-2 */
5974 const bool allow_empty_name = (-2 >= discp_lvl);
5975 /* Allow non-tchar characters in header (field) name.
5976 Violates RFC 9110, section 5.1 */
5977 const bool allow_extended_charset = (-2 >= discp_lvl);
5978 /* Allow whitespace before colon.
5979 Violates RFC 9112, section 5.1-2 */
5980 const bool allow_wsp_before_colon = (-3 >= discp_lvl);
5981 /* Do not abort the request when header line has no colon, just skip such
5982 bad lines.
5983 RFC 9112, section 5-1 */
5984 const bool allow_line_without_colon = (-2 >= discp_lvl);
5985
5986 size_t p;
5987
5988#if ! defined (HAVE_MESSAGES) && ! defined(_DEBUG)
5989 (void) process_footers; /* Unused parameter */
5990#endif /* !HAVE_MESSAGES && !_DEBUG */
5991
5992 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
5994 c->state);
5995
5996 p = c->rq.hdrs.hdr.proc_pos;
5997
5998 mhd_assert (p <= c->read_buffer_offset);
5999 while (p < c->read_buffer_offset)
6000 {
6001 const char chr = c->read_buffer[p];
6002 bool end_of_line;
6003
6004 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
6005 (c->rq.hdrs.hdr.name_len < p));
6006 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p));
6007 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
6008 (c->rq.hdrs.hdr.name_end_found));
6009 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
6010 (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start));
6011 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
6012 (0 != c->rq.hdrs.hdr.name_len));
6013 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
6014 (0 == c->rq.hdrs.hdr.name_len) || \
6015 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len));
6016 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
6017 (0 == c->rq.hdrs.hdr.value_start) || \
6018 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start));
6019
6020 /* Check for the end of the line */
6021 if ('\r' == chr)
6022 {
6023 if (0 != p)
6024 {
6025 /* Line is not empty, need to check for possible line folding */
6026 if (p + 2 >= c->read_buffer_offset)
6027 break; /* Not enough data yet to check for folded line */
6028 }
6029 else
6030 {
6031 /* Line is empty, no need to check for possible line folding */
6032 if (p + 2 > c->read_buffer_offset)
6033 break; /* Not enough data yet to check for the end of the line */
6034 }
6035 if ('\n' == c->read_buffer[p + 1])
6036 end_of_line = true;
6037 else
6038 {
6039 /* Bare CR alone */
6040 /* Must be rejected or replaced with space char.
6041 See RFC 9112, section 2.2-4 */
6042 if (bare_cr_as_sp)
6043 {
6044 c->read_buffer[p] = ' ';
6046 continue; /* Re-start processing of the current character */
6047 }
6048 else if (! bare_cr_keep)
6049 {
6050 if (! process_footers)
6054 else
6058 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6059 }
6060 end_of_line = false;
6061 }
6062 }
6063 else if ('\n' == chr)
6064 {
6065 /* Bare LF may be recognised as a line delimiter.
6066 See RFC 9112, section 2.2-3 */
6067 if (bare_lf_as_crlf)
6068 {
6069 if (0 != p)
6070 {
6071 /* Line is not empty, need to check for possible line folding */
6072 if (p + 1 >= c->read_buffer_offset)
6073 break; /* Not enough data yet to check for folded line */
6074 }
6075 end_of_line = true;
6076 }
6077 else
6078 {
6079 if (! process_footers)
6083 else
6087 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6088 }
6089 }
6090 else
6091 end_of_line = false;
6092
6093 if (end_of_line)
6094 {
6095 /* Handle the end of the line */
6099 const size_t line_len = p + (('\r' == chr) ? 2 : 1);
6100 char next_line_char;
6101 mhd_assert (line_len <= c->read_buffer_offset);
6102
6103 if (0 == p)
6104 {
6105 /* Zero-length header line. This is the end of the request header
6106 section.
6107 RFC 9112, Section 2.1-1 */
6110 mhd_assert (0 == c->rq.hdrs.hdr.name_len);
6111 mhd_assert (0 == c->rq.hdrs.hdr.ws_start);
6112 mhd_assert (0 == c->rq.hdrs.hdr.value_start);
6113 /* Consume the line with CRLF (or bare LF) */
6114 c->read_buffer += line_len;
6115 c->read_buffer_offset -= line_len;
6116 c->read_buffer_size -= line_len;
6118 }
6119
6120 mhd_assert (line_len < c->read_buffer_offset);
6121 mhd_assert (0 != line_len);
6122 mhd_assert ('\n' == c->read_buffer[line_len - 1]);
6123 next_line_char = c->read_buffer[line_len];
6124 if ((' ' == next_line_char) ||
6125 ('\t' == next_line_char))
6126 {
6127 /* Folded line */
6128 if (! allow_folded)
6129 {
6130 if (! process_footers)
6134 else
6138
6139 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6140 }
6141 /* Replace CRLF (or bare LF) character(s) with space characters.
6142 See RFC 9112, Section 5.2-4 */
6143 c->read_buffer[p] = ' ';
6144 if ('\r' == chr)
6145 c->read_buffer[p + 1] = ' ';
6146 continue; /* Re-start processing of the current character */
6147 }
6148 else
6149 {
6150 /* It is not a folded line, it's the real end of the non-empty line */
6151 bool skip_line = false;
6152 mhd_assert (0 != p);
6153 if (c->rq.hdrs.hdr.starts_with_ws)
6154 {
6155 /* This is the first line and it starts with whitespace. This line
6156 must be discarded completely.
6157 See RFC 9112, Section 2.2-8 */
6158 mhd_assert (allow_wsp_at_start);
6159#ifdef HAVE_MESSAGES
6160 MHD_DLOG (c->daemon,
6161 _ ("Whitespace-prefixed first header line " \
6162 "has been skipped.\n"));
6163#endif /* HAVE_MESSAGES */
6164 skip_line = true;
6165 }
6166 else if (! c->rq.hdrs.hdr.name_end_found)
6167 {
6168 if (! allow_line_without_colon)
6169 {
6170 if (! process_footers)
6174 else
6178
6179 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6180 }
6181 /* Skip broken line completely */
6183 skip_line = true;
6184 }
6185 if (skip_line)
6186 {
6187 /* Skip the entire line */
6188 c->read_buffer += line_len;
6189 c->read_buffer_offset -= line_len;
6190 c->read_buffer_size -= line_len;
6191 p = 0;
6192 /* Reset processing state */
6193 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
6194 /* Start processing of the next line */
6195 continue;
6196 }
6197 else
6198 {
6199 /* This line should be valid header line */
6200 size_t value_len;
6201 mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name);
6202
6203 hdr_name->str = c->read_buffer + 0; /* The name always starts at the first character */
6204 hdr_name->len = c->rq.hdrs.hdr.name_len;
6205 mhd_assert (0 == hdr_name->str[hdr_name->len]);
6206
6207 if (0 == c->rq.hdrs.hdr.value_start)
6208 {
6209 c->rq.hdrs.hdr.value_start = p;
6210 c->read_buffer[p] = 0;
6211 value_len = 0;
6212 }
6213 else if (0 != c->rq.hdrs.hdr.ws_start)
6214 {
6215 mhd_assert (p > c->rq.hdrs.hdr.ws_start);
6217 c->read_buffer[c->rq.hdrs.hdr.ws_start] = 0;
6218 value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start;
6219 }
6220 else
6221 {
6222 mhd_assert (p > c->rq.hdrs.hdr.ws_start);
6223 c->read_buffer[p] = 0;
6224 value_len = p - c->rq.hdrs.hdr.value_start;
6225 }
6226 hdr_value->str = c->read_buffer + c->rq.hdrs.hdr.value_start;
6227 hdr_value->len = value_len;
6228 mhd_assert (0 == hdr_value->str[hdr_value->len]);
6229 /* Consume the entire line */
6230 c->read_buffer += line_len;
6231 c->read_buffer_offset -= line_len;
6232 c->read_buffer_size -= line_len;
6234 }
6235 }
6236 }
6237 else if ((' ' == chr) || ('\t' == chr))
6238 {
6239 if (0 == p)
6240 {
6241 if (! allow_wsp_at_start)
6242 {
6243 if (! process_footers)
6247 else
6251 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6252 }
6253 c->rq.hdrs.hdr.starts_with_ws = true;
6254 }
6255 else if ((! c->rq.hdrs.hdr.name_end_found) &&
6256 (! c->rq.hdrs.hdr.starts_with_ws))
6257 {
6258 /* Whitespace in header name / between header name and colon */
6259 if (allow_wsp_in_name || allow_wsp_before_colon)
6260 {
6261 if (0 == c->rq.hdrs.hdr.ws_start)
6262 c->rq.hdrs.hdr.ws_start = p;
6263 }
6264 else
6265 {
6266 if (! process_footers)
6270 else
6274
6275 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6276 }
6277 }
6278 else
6279 {
6280 /* Whitespace before/inside/after header (field) value */
6281 if (0 == c->rq.hdrs.hdr.ws_start)
6282 c->rq.hdrs.hdr.ws_start = p;
6283 }
6284 }
6285 else if (0 == chr)
6286 {
6287 if (! nul_as_sp)
6288 {
6289 if (! process_footers)
6293 else
6297
6298 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6299 }
6300 c->read_buffer[p] = ' ';
6301 continue; /* Re-start processing of the current character */
6302 }
6303 else
6304 {
6305 /* Not a whitespace, not the end of the header line */
6306 mhd_assert ('\r' != chr);
6307 mhd_assert ('\n' != chr);
6308 mhd_assert ('\0' != chr);
6309 if ( (! c->rq.hdrs.hdr.name_end_found) &&
6310 (! c->rq.hdrs.hdr.starts_with_ws) )
6311 {
6312 /* Processing the header (field) name */
6313 if ( (! allow_extended_charset) &&
6314 (':' != chr) &&
6315 (! char_legal_in_field_name (chr)) )
6316 {
6321 }
6322
6323 if (':' == chr)
6324 {
6325 if (0 == c->rq.hdrs.hdr.ws_start)
6326 c->rq.hdrs.hdr.name_len = p;
6327 else
6328 {
6329 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6330 if (! allow_wsp_before_colon)
6331 {
6332 if (! process_footers)
6336 else
6340 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6341 }
6343#ifndef MHD_FAVOR_SMALL_CODE
6344 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6345#endif /* ! MHD_FAVOR_SMALL_CODE */
6346 }
6347 if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name)
6348 {
6349 if (! process_footers)
6353 else
6357 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6358 }
6359 c->rq.hdrs.hdr.name_end_found = true;
6360 c->read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */
6361 }
6362 else
6363 {
6364 if (0 != c->rq.hdrs.hdr.ws_start)
6365 {
6366 /* End of the whitespace in header (field) name */
6367 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6368 if (! allow_wsp_in_name)
6369 {
6370 if (! process_footers)
6374 else
6378
6379 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6380 }
6381#ifndef MHD_FAVOR_SMALL_CODE
6382 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6383#endif /* ! MHD_FAVOR_SMALL_CODE */
6384 }
6385 }
6386 }
6387 else
6388 {
6389 /* Processing the header (field) value */
6390 if (0 == c->rq.hdrs.hdr.value_start)
6391 c->rq.hdrs.hdr.value_start = p;
6392#ifndef MHD_FAVOR_SMALL_CODE
6393 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6394#endif /* ! MHD_FAVOR_SMALL_CODE */
6395 }
6396#ifdef MHD_FAVOR_SMALL_CODE
6397 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6398#endif /* MHD_FAVOR_SMALL_CODE */
6399 }
6400 p++;
6401 }
6402 c->rq.hdrs.hdr.proc_pos = p;
6403 return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */
6404}
6405
6406
6417static bool
6418get_req_headers (struct MHD_Connection *c, bool process_footers)
6419{
6420 do
6421 {
6422 struct _MHD_str_w_len hdr_name;
6423 struct _MHD_str_w_len hdr_value;
6424 enum MHD_HdrLineReadRes_ res;
6425
6426 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6428 c->state);
6429
6430 #ifdef _DEBUG
6431 hdr_name.str = NULL;
6432 hdr_value.str = NULL;
6433#endif /* _DEBUG */
6434 res = get_req_header (c, process_footers, &hdr_name, &hdr_value);
6436 {
6437 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6439 c->state);
6440 mhd_assert (NULL != hdr_name.str);
6441 mhd_assert (NULL != hdr_value.str);
6442 /* Values must be zero-terminated and must not have binary zeros */
6443 mhd_assert (strlen (hdr_name.str) == hdr_name.len);
6444 mhd_assert (strlen (hdr_value.str) == hdr_value.len);
6445 /* Values must not have whitespaces at the start or at the end */
6446 mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != ' '));
6447 mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != '\t'));
6448 mhd_assert ((hdr_name.len == 0) || \
6449 (hdr_name.str[hdr_name.len - 1] != ' '));
6450 mhd_assert ((hdr_name.len == 0) || \
6451 (hdr_name.str[hdr_name.len - 1] != '\t'));
6452 mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != ' '));
6453 mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != '\t'));
6454 mhd_assert ((hdr_value.len == 0) || \
6455 (hdr_value.str[hdr_value.len - 1] != ' '));
6456 mhd_assert ((hdr_value.len == 0) || \
6457 (hdr_value.str[hdr_value.len - 1] != '\t'));
6458
6459 if (MHD_NO ==
6461 (! process_footers) ?
6464 hdr_name.str, hdr_name.len,
6465 hdr_value.str, hdr_value.len))
6466 {
6467 size_t add_element_size;
6468
6469 mhd_assert (hdr_name.str < hdr_value.str);
6470
6471#ifdef HAVE_MESSAGES
6472 MHD_DLOG (c->daemon,
6473 _ ("Failed to allocate memory in the connection memory " \
6474 "pool to store %s.\n"),
6475 (! process_footers) ? _ ("header") : _ ("footer"));
6476#endif /* HAVE_MESSAGES */
6477
6478 add_element_size = hdr_value.len
6479 + (size_t) (hdr_value.str - hdr_name.str);
6480
6481 if (! process_footers)
6482 handle_req_headers_no_space (c, hdr_name.str, add_element_size);
6483 else
6484 handle_req_footers_no_space (c, hdr_name.str, add_element_size);
6485
6487 return true;
6488 }
6489 /* Reset processing state */
6491 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6493 c->state);
6494 /* Read the next header (field) line */
6495 continue;
6496 }
6498 {
6499 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6501 c->state);
6502 return false;
6503 }
6504 else if (MHD_HDR_LINE_READING_DATA_ERROR == res)
6505 {
6506 mhd_assert ((process_footers ? \
6511 return true;
6512 }
6514 break;
6515 } while (1);
6516
6517#ifdef HAVE_MESSAGES
6518 if (1 == c->rq.num_cr_sp_replaced)
6519 {
6520 MHD_DLOG (c->daemon,
6521 _ ("One bare CR character has been replaced with space " \
6522 "in %s.\n"),
6523 (! process_footers) ?
6524 _ ("the request line or in the request headers") :
6525 _ ("the request footers"));
6526 }
6527 else if (0 != c->rq.num_cr_sp_replaced)
6528 {
6529 MHD_DLOG (c->daemon,
6530 _ ("%" PRIu64 " bare CR characters have been replaced with " \
6531 "spaces in the request line and/or in the request %s.\n"),
6532 (uint64_t) c->rq.num_cr_sp_replaced,
6533 (! process_footers) ? _ ("headers") : _ ("footers"));
6534 }
6535 if (1 == c->rq.skipped_broken_lines)
6536 {
6537 MHD_DLOG (c->daemon,
6538 _ ("One %s line without colon has been skipped.\n"),
6539 (! process_footers) ? _ ("header") : _ ("footer"));
6540 }
6541 else if (0 != c->rq.skipped_broken_lines)
6542 {
6543 MHD_DLOG (c->daemon,
6544 _ ("%" PRIu64 " %s lines without colons has been skipped.\n"),
6545 (uint64_t) c->rq.skipped_broken_lines,
6546 (! process_footers) ? _ ("header") : _ ("footer"));
6547 }
6548#endif /* HAVE_MESSAGES */
6549
6550 mhd_assert (c->rq.method < c->read_buffer);
6551 if (! process_footers)
6552 {
6553 c->rq.header_size = (size_t) (c->read_buffer - c->rq.method);
6555 c->rq.field_lines.size =
6556 (size_t) ((c->read_buffer - c->rq.field_lines.start) - 1);
6557 if ('\r' == *(c->read_buffer - 2))
6558 c->rq.field_lines.size--;
6560
6562 {
6563 /* Try to re-use some of the last bytes of the request header */
6564 /* Do this only if space in the read buffer is limited AND
6565 amount of read ahead data is small. */
6570 const char *last_elmnt_end;
6571 size_t shift_back_size;
6572 if (NULL != c->rq.headers_received_tail)
6573 last_elmnt_end =
6576 else
6577 last_elmnt_end = c->rq.version + HTTP_VER_LEN;
6578 mhd_assert ((last_elmnt_end + 1) < c->read_buffer);
6579 shift_back_size = (size_t) (c->read_buffer - (last_elmnt_end + 1));
6580 if (0 != c->read_buffer_offset)
6581 memmove (c->read_buffer - shift_back_size,
6582 c->read_buffer,
6584 c->read_buffer -= shift_back_size;
6585 c->read_buffer_size += shift_back_size;
6586 }
6587 }
6588 else
6590
6591 return true;
6592}
6593
6594
6602void
6604{
6605 struct MHD_Daemon *daemon = connection->daemon;
6606#if defined(MHD_USE_THREADS)
6607 mhd_assert (NULL == daemon->worker_pool);
6608#endif /* MHD_USE_THREADS */
6609
6610 if (0 == connection->connection_timeout_ms)
6611 return; /* Skip update of activity for connections
6612 without timeout timer. */
6613 if (connection->suspended)
6614 return; /* no activity on suspended connections */
6615
6618 return; /* each connection has personal timeout */
6619
6620 if (connection->connection_timeout_ms != daemon->connection_timeout_ms)
6621 return; /* custom timeout, no need to move it in "normal" DLL */
6622#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6623 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
6624#endif
6625 /* move connection to head of timeout list (by remove + add operation) */
6627 daemon->normal_timeout_tail,
6628 connection);
6630 daemon->normal_timeout_tail,
6631 connection);
6632#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6633 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
6634#endif
6635}
6636
6637
6647void
6649 bool socket_error)
6650{
6651 ssize_t bytes_read;
6652
6653 if ( (MHD_CONNECTION_CLOSED == connection->state) ||
6654 (connection->suspended) )
6655 return;
6656#ifdef HTTPS_SUPPORT
6657 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6658 { /* HTTPS connection. */
6659 if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6660 {
6661 if (! MHD_run_tls_handshake_ (connection))
6662 return;
6663 }
6664 }
6665#endif /* HTTPS_SUPPORT */
6666
6667 mhd_assert (NULL != connection->read_buffer);
6668 if (connection->read_buffer_size == connection->read_buffer_offset)
6669 return; /* No space for receiving data. */
6670
6671 bytes_read = connection->recv_cls (connection,
6672 &connection->read_buffer
6673 [connection->read_buffer_offset],
6674 connection->read_buffer_size
6675 - connection->read_buffer_offset);
6676 if ((bytes_read < 0) || socket_error)
6677 {
6678 if ((MHD_ERR_AGAIN_ == bytes_read) && ! socket_error)
6679 return; /* No new data to process. */
6680 if ((bytes_read > 0) && connection->sk_nonblck)
6681 { /* Try to detect the socket error */
6682 int dummy;
6683 bytes_read = connection->recv_cls (connection, &dummy, sizeof (dummy));
6684 }
6685 if (MHD_ERR_CONNRESET_ == bytes_read)
6686 {
6688 (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6689 {
6690#ifdef HAVE_MESSAGES
6691 MHD_DLOG (connection->daemon,
6692 _ ("Socket has been disconnected when reading request.\n"));
6693#endif
6694 connection->discard_request = true;
6695 }
6696 MHD_connection_close_ (connection,
6698 return;
6699 }
6700
6701#ifdef HAVE_MESSAGES
6702 if (MHD_CONNECTION_INIT != connection->state)
6703 MHD_DLOG (connection->daemon,
6704 _ ("Connection socket is closed when reading " \
6705 "request due to the error: %s\n"),
6706 (bytes_read < 0) ? str_conn_error_ (bytes_read) :
6707 "detected connection closure");
6708#endif
6709 CONNECTION_CLOSE_ERROR (connection,
6710 NULL);
6711 return;
6712 }
6713
6714 if (0 == bytes_read)
6715 { /* Remote side closed connection. */
6716 connection->read_closed = true;
6718 (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6719 {
6720#ifdef HAVE_MESSAGES
6721 MHD_DLOG (connection->daemon,
6722 _ ("Connection was closed by remote side with incomplete "
6723 "request.\n"));
6724#endif
6725 connection->discard_request = true;
6726 MHD_connection_close_ (connection,
6728 }
6729 else if (MHD_CONNECTION_INIT == connection->state)
6730 /* This termination code cannot be reported to the application
6731 * because application has not been informed yet about this request */
6732 MHD_connection_close_ (connection,
6734 else
6735 MHD_connection_close_ (connection,
6737 return;
6738 }
6739 connection->read_buffer_offset += (size_t) bytes_read;
6740 MHD_update_last_activity_ (connection);
6741#if DEBUG_STATES
6742 MHD_DLOG (connection->daemon,
6743 _ ("In function %s handling connection at state: %s\n"),
6744 MHD_FUNC_,
6745 MHD_state_to_string (connection->state));
6746#endif
6747 /* TODO: check whether the next 'switch()' really needed */
6748 switch (connection->state)
6749 {
6756 /* nothing to do but default action */
6757 if (connection->read_closed)
6758 {
6759 /* TODO: check whether this really needed */
6760 MHD_connection_close_ (connection,
6762 }
6763 return;
6765 return;
6766#ifdef UPGRADE_SUPPORT
6767 case MHD_CONNECTION_UPGRADE:
6768 mhd_assert (0);
6769 return;
6770#endif /* UPGRADE_SUPPORT */
6772 /* shrink read buffer to how much is actually used */
6773 /* TODO: remove shrink as it handled in special function */
6774 if ((0 != connection->read_buffer_size) &&
6775 (connection->read_buffer_size != connection->read_buffer_offset))
6776 {
6777 mhd_assert (NULL != connection->read_buffer);
6778 connection->read_buffer =
6779 MHD_pool_reallocate (connection->pool,
6780 connection->read_buffer,
6781 connection->read_buffer_size,
6782 connection->read_buffer_offset);
6783 connection->read_buffer_size = connection->read_buffer_offset;
6784 }
6785 break;
6791 /* Milestone state, no data should be read */
6792 mhd_assert (0); /* Should not be possible */
6793 break;
6804 default:
6805 mhd_assert (0); /* Should not be possible */
6806 break;
6807 }
6808 return;
6809}
6810
6811
6820void
6822{
6823 struct MHD_Response *response;
6824 ssize_t ret;
6825 if (connection->suspended)
6826 return;
6827
6828#ifdef HTTPS_SUPPORT
6829 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6830 { /* HTTPS connection. */
6831 if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6832 {
6833 if (! MHD_run_tls_handshake_ (connection))
6834 return;
6835 }
6836 }
6837#endif /* HTTPS_SUPPORT */
6838
6839#if DEBUG_STATES
6840 MHD_DLOG (connection->daemon,
6841 _ ("In function %s handling connection at state: %s\n"),
6842 MHD_FUNC_,
6843 MHD_state_to_string (connection->state));
6844#endif
6845 switch (connection->state)
6846 {
6853 mhd_assert (0);
6854 return;
6856 ret = MHD_send_data_ (connection,
6858 [connection->continue_message_write_offset],
6860 - connection->continue_message_write_offset,
6861 true);
6862 if (ret < 0)
6863 {
6864 if (MHD_ERR_AGAIN_ == ret)
6865 return;
6866#ifdef HAVE_MESSAGES
6867 MHD_DLOG (connection->daemon,
6868 _ ("Failed to send data in request for %s.\n"),
6869 connection->rq.url);
6870#endif
6871 CONNECTION_CLOSE_ERROR (connection,
6872 NULL);
6873 return;
6874 }
6875#if _MHD_DEBUG_SEND_DATA
6876 fprintf (stderr,
6877 _ ("Sent 100 continue response: `%.*s'\n"),
6878 (int) ret,
6880#endif
6881 connection->continue_message_write_offset += (size_t) ret;
6882 MHD_update_last_activity_ (connection);
6883 return;
6889 mhd_assert (0);
6890 return;
6892 mhd_assert (0);
6893 return;
6895 {
6896 struct MHD_Response *const resp = connection->rp.response;
6897 const size_t wb_ready = connection->write_buffer_append_offset
6898 - connection->write_buffer_send_offset;
6899 mhd_assert (connection->write_buffer_append_offset >= \
6900 connection->write_buffer_send_offset);
6901 mhd_assert (NULL != resp);
6902 mhd_assert ( (0 == resp->data_size) || \
6903 (0 == resp->data_start) || \
6904 (NULL != resp->crc) );
6905 mhd_assert ( (0 == connection->rp.rsp_write_position) || \
6906 (resp->total_size ==
6907 connection->rp.rsp_write_position) );
6908 mhd_assert ((MHD_CONN_MUST_UPGRADE != connection->keepalive) || \
6909 (! connection->rp.props.send_reply_body));
6910
6911 if ( (connection->rp.props.send_reply_body) &&
6912 (NULL == resp->crc) &&
6913 (NULL == resp->data_iov) &&
6914 /* TODO: remove the next check as 'send_reply_body' is used */
6915 (0 == connection->rp.rsp_write_position) &&
6916 (! connection->rp.props.chunked) )
6917 {
6918 mhd_assert (resp->total_size >= resp->data_size);
6919 mhd_assert (0 == resp->data_start);
6920 /* Send response headers alongside the response body, if the body
6921 * data is available. */
6922 ret = MHD_send_hdr_and_body_ (connection,
6923 &connection->write_buffer
6924 [connection->write_buffer_send_offset],
6925 wb_ready,
6926 false,
6927 resp->data,
6928 resp->data_size,
6929 (resp->total_size == resp->data_size));
6930 }
6931 else
6932 {
6933 /* This is response for HEAD request or reply body is not allowed
6934 * for any other reason or reply body is dynamically generated. */
6935 /* Do not send the body data even if it's available. */
6936 ret = MHD_send_hdr_and_body_ (connection,
6937 &connection->write_buffer
6938 [connection->write_buffer_send_offset],
6939 wb_ready,
6940 false,
6941 NULL,
6942 0,
6943 ((0 == resp->total_size) ||
6944 (! connection->rp.props.send_reply_body)
6945 ));
6946 }
6947
6948 if (ret < 0)
6949 {
6950 if (MHD_ERR_AGAIN_ == ret)
6951 return;
6952#ifdef HAVE_MESSAGES
6953 MHD_DLOG (connection->daemon,
6954 _ ("Failed to send the response headers for the " \
6955 "request for `%s'. Error: %s\n"),
6956 connection->rq.url,
6957 str_conn_error_ (ret));
6958#endif
6959 CONNECTION_CLOSE_ERROR (connection,
6960 NULL);
6961 return;
6962 }
6963 /* 'ret' is not negative, it's safe to cast it to 'size_t'. */
6964 if (((size_t) ret) > wb_ready)
6965 {
6966 /* The complete header and some response data have been sent,
6967 * update both offsets. */
6968 mhd_assert (0 == connection->rp.rsp_write_position);
6969 mhd_assert (! connection->rp.props.chunked);
6970 mhd_assert (connection->rp.props.send_reply_body);
6971 connection->write_buffer_send_offset += wb_ready;
6972 connection->rp.rsp_write_position = ((size_t) ret) - wb_ready;
6973 }
6974 else
6975 connection->write_buffer_send_offset += (size_t) ret;
6976 MHD_update_last_activity_ (connection);
6977 if (MHD_CONNECTION_HEADERS_SENDING != connection->state)
6978 return;
6979 check_write_done (connection,
6981 return;
6982 }
6984 return;
6986 response = connection->rp.response;
6987 if (connection->rp.rsp_write_position <
6988 connection->rp.response->total_size)
6989 {
6990 uint64_t data_write_offset;
6991
6992#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6993 if (NULL != response->crc)
6994 MHD_mutex_lock_chk_ (&response->mutex);
6995#endif
6996 if (MHD_NO == try_ready_normal_body (connection))
6997 {
6998 /* mutex was already unlocked by try_ready_normal_body */
6999 return;
7000 }
7001#if defined(_MHD_HAVE_SENDFILE)
7002 if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
7003 {
7004 mhd_assert (NULL == response->data_iov);
7005 ret = MHD_send_sendfile_ (connection);
7006 }
7007 else /* combined with the next 'if' */
7008#endif /* _MHD_HAVE_SENDFILE */
7009 if (NULL != response->data_iov)
7010 {
7011 ret = MHD_send_iovec_ (connection,
7012 &connection->rp.resp_iov,
7013 true);
7014 }
7015 else
7016 {
7017 data_write_offset = connection->rp.rsp_write_position
7018 - response->data_start;
7019 if (data_write_offset > (uint64_t) SIZE_MAX)
7020 MHD_PANIC (_ ("Data offset exceeds limit.\n"));
7021 ret = MHD_send_data_ (connection,
7022 &response->data
7023 [(size_t) data_write_offset],
7024 response->data_size
7025 - (size_t) data_write_offset,
7026 true);
7027#if _MHD_DEBUG_SEND_DATA
7028 if (ret > 0)
7029 fprintf (stderr,
7030 _ ("Sent %d-byte DATA response: `%.*s'\n"),
7031 (int) ret,
7032 (int) ret,
7033 &rp.response->data[connection->rp.rsp_write_position
7034 - rp.response->data_start]);
7035#endif
7036 }
7037#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7038 if (NULL != response->crc)
7039 MHD_mutex_unlock_chk_ (&response->mutex);
7040#endif
7041 if (ret < 0)
7042 {
7043 if (MHD_ERR_AGAIN_ == ret)
7044 return;
7045#ifdef HAVE_MESSAGES
7046 MHD_DLOG (connection->daemon,
7047 _ ("Failed to send the response body for the " \
7048 "request for `%s'. Error: %s\n"),
7049 connection->rq.url,
7050 str_conn_error_ (ret));
7051#endif
7052 CONNECTION_CLOSE_ERROR (connection,
7053 NULL);
7054 return;
7055 }
7056 connection->rp.rsp_write_position += (size_t) ret;
7057 MHD_update_last_activity_ (connection);
7058 }
7059 if (connection->rp.rsp_write_position ==
7060 connection->rp.response->total_size)
7062 return;
7064 mhd_assert (0);
7065 return;
7067 ret = MHD_send_data_ (connection,
7068 &connection->write_buffer
7069 [connection->write_buffer_send_offset],
7070 connection->write_buffer_append_offset
7071 - connection->write_buffer_send_offset,
7072 true);
7073 if (ret < 0)
7074 {
7075 if (MHD_ERR_AGAIN_ == ret)
7076 return;
7077#ifdef HAVE_MESSAGES
7078 MHD_DLOG (connection->daemon,
7079 _ ("Failed to send the chunked response body for the " \
7080 "request for `%s'. Error: %s\n"),
7081 connection->rq.url,
7082 str_conn_error_ (ret));
7083#endif
7084 CONNECTION_CLOSE_ERROR (connection,
7085 NULL);
7086 return;
7087 }
7088 connection->write_buffer_send_offset += (size_t) ret;
7089 MHD_update_last_activity_ (connection);
7090 if (MHD_CONNECTION_CHUNKED_BODY_READY != connection->state)
7091 return;
7092 check_write_done (connection,
7093 (connection->rp.response->total_size ==
7094 connection->rp.rsp_write_position) ?
7097 return;
7100 mhd_assert (0);
7101 return;
7103 ret = MHD_send_data_ (connection,
7104 &connection->write_buffer
7105 [connection->write_buffer_send_offset],
7106 connection->write_buffer_append_offset
7107 - connection->write_buffer_send_offset,
7108 true);
7109 if (ret < 0)
7110 {
7111 if (MHD_ERR_AGAIN_ == ret)
7112 return;
7113#ifdef HAVE_MESSAGES
7114 MHD_DLOG (connection->daemon,
7115 _ ("Failed to send the footers for the " \
7116 "request for `%s'. Error: %s\n"),
7117 connection->rq.url,
7118 str_conn_error_ (ret));
7119#endif
7120 CONNECTION_CLOSE_ERROR (connection,
7121 NULL);
7122 return;
7123 }
7124 connection->write_buffer_send_offset += (size_t) ret;
7125 MHD_update_last_activity_ (connection);
7126 if (MHD_CONNECTION_FOOTERS_SENDING != connection->state)
7127 return;
7128 check_write_done (connection,
7130 return;
7132 mhd_assert (0);
7133 return;
7135 return;
7136#ifdef UPGRADE_SUPPORT
7137 case MHD_CONNECTION_UPGRADE:
7138 mhd_assert (0);
7139 return;
7140#endif /* UPGRADE_SUPPORT */
7141 default:
7142 mhd_assert (0);
7143 CONNECTION_CLOSE_ERROR (connection,
7144 _ ("Internal error.\n"));
7145 break;
7146 }
7147 return;
7148}
7149
7150
7157static bool
7159{
7160 const uint64_t timeout = c->connection_timeout_ms;
7161 uint64_t now;
7162 uint64_t since_actv;
7163
7164 if (c->suspended)
7165 return false;
7166 if (0 == timeout)
7167 return false;
7169 since_actv = now - c->last_activity;
7170 /* Keep the next lines in sync with #connection_get_wait() to avoid
7171 * undesired side-effects like busy-waiting. */
7172 if (timeout < since_actv)
7173 {
7174 if (UINT64_MAX / 2 < since_actv)
7175 {
7176 const uint64_t jump_back = c->last_activity - now;
7177 /* Very unlikely that it is more than quarter-million years pause.
7178 * More likely that system clock jumps back. */
7179 if (5000 >= jump_back)
7180 {
7181#ifdef HAVE_MESSAGES
7182 MHD_DLOG (c->daemon,
7183 _ ("Detected system clock %u milliseconds jump back.\n"),
7184 (unsigned int) jump_back);
7185#endif
7186 return false;
7187 }
7188#ifdef HAVE_MESSAGES
7189 MHD_DLOG (c->daemon,
7190 _ ("Detected too large system clock %" PRIu64 " milliseconds "
7191 "jump back.\n"),
7192 jump_back);
7193#endif
7194 }
7195 return true;
7196 }
7197 return false;
7198}
7199
7200
7209static void
7211{
7212 struct MHD_Daemon *daemon = connection->daemon;
7213#ifdef MHD_USE_THREADS
7214 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
7215 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
7216 mhd_assert (NULL == daemon->worker_pool);
7217#endif /* MHD_USE_THREADS */
7218
7219 if (connection->in_cleanup)
7220 return; /* Prevent double cleanup. */
7221 connection->in_cleanup = true;
7222 if (NULL != connection->rp.response)
7223 {
7224 MHD_destroy_response (connection->rp.response);
7225 connection->rp.response = NULL;
7226 }
7227#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7228 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
7229#endif
7230 if (connection->suspended)
7231 {
7234 connection);
7235 connection->suspended = false;
7236 }
7237 else
7238 {
7239 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
7240 {
7241 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7243 daemon->normal_timeout_tail,
7244 connection);
7245 else
7247 daemon->manual_timeout_tail,
7248 connection);
7249 }
7251 daemon->connections_tail,
7252 connection);
7253 }
7254 DLL_insert (daemon->cleanup_head,
7255 daemon->cleanup_tail,
7256 connection);
7257 connection->resuming = false;
7258 connection->in_idle = false;
7259#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7260 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
7261#endif
7263 {
7264 /* if we were at the connection limit before and are in
7265 thread-per-connection mode, signal the main thread
7266 to resume accepting connections */
7267 if ( (MHD_ITC_IS_VALID_ (daemon->itc)) &&
7268 (! MHD_itc_activate_ (daemon->itc, "c")) )
7269 {
7270#ifdef HAVE_MESSAGES
7271 MHD_DLOG (daemon,
7272 _ ("Failed to signal end of connection via inter-thread " \
7273 "communication channel.\n"));
7274#endif
7275 }
7276 }
7277}
7278
7279
7285void
7287{
7288 size_t read_buf_size;
7289
7290#ifdef HTTPS_SUPPORT
7291 mhd_assert ( (0 == (c->daemon->options & MHD_USE_TLS)) || \
7292 (MHD_TLS_CONN_INIT == c->tls_state) );
7293 mhd_assert ( (0 != (c->daemon->options & MHD_USE_TLS)) || \
7294 (MHD_TLS_CONN_NO_TLS == c->tls_state) );
7295#endif /* HTTPS_SUPPORT */
7297
7300
7301 memset (&c->rq, 0, sizeof(c->rq));
7302 memset (&c->rp, 0, sizeof(c->rp));
7303
7304 c->write_buffer = NULL;
7305 c->write_buffer_size = 0;
7308
7310
7311 c->read_buffer_offset = 0;
7312 read_buf_size = c->daemon->pool_size / 2;
7313 c->read_buffer
7314 = MHD_pool_allocate (c->pool,
7315 read_buf_size,
7316 false);
7317 c->read_buffer_size = read_buf_size;
7318}
7319
7320
7327static void
7329 bool reuse)
7330{
7331 struct MHD_Connection *const c = connection;
7332 struct MHD_Daemon *const d = connection->daemon;
7333
7334 if (! reuse)
7335 {
7336 /* Next function will destroy response, notify client,
7337 * destroy memory pool, and set connection state to "CLOSED" */
7339 c->stop_with_error ?
7342 c->read_buffer = NULL;
7343 c->read_buffer_size = 0;
7344 c->read_buffer_offset = 0;
7345 c->write_buffer = NULL;
7346 c->write_buffer_size = 0;
7349 }
7350 else
7351 {
7352 /* Reset connection to process the next request */
7353 size_t new_read_buf_size;
7356
7357 if ( (NULL != d->notify_completed) &&
7358 (c->rq.client_aware) )
7360 c,
7361 &c->rq.client_context,
7363 c->rq.client_aware = false;
7364
7365 if (NULL != c->rp.response)
7367 c->rp.response = NULL;
7368
7371 c->event_loop_info =
7372 (0 == c->read_buffer_offset) ?
7374
7375 memset (&c->rq, 0, sizeof(c->rq));
7376
7377 /* iov (if any) will be deallocated by MHD_pool_reset */
7378 memset (&c->rp, 0, sizeof(c->rp));
7379
7380 c->write_buffer = NULL;
7381 c->write_buffer_size = 0;
7385
7386 /* Reset the read buffer to the starting size,
7387 preserving the bytes we have already read. */
7388 new_read_buf_size = c->daemon->pool_size / 2;
7389 if (c->read_buffer_offset > new_read_buf_size)
7390 new_read_buf_size = c->read_buffer_offset;
7391
7392 c->read_buffer
7393 = MHD_pool_reset (c->pool,
7394 c->read_buffer,
7396 new_read_buf_size);
7397 c->read_buffer_size = new_read_buf_size;
7398 }
7399 c->rq.client_context = NULL;
7400}
7401
7402
7415enum MHD_Result
7417{
7418 struct MHD_Daemon *daemon = connection->daemon;
7419 enum MHD_Result ret;
7420#ifdef MHD_USE_THREADS
7421 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
7422 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
7423#endif /* MHD_USE_THREADS */
7424 /* 'daemon' is not used if epoll is not available and asserts are disabled */
7425 (void) daemon; /* Mute compiler warning */
7426
7427 connection->in_idle = true;
7428 while (! connection->suspended)
7429 {
7430#ifdef HTTPS_SUPPORT
7431 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
7432 { /* HTTPS connection. */
7433 if ((MHD_TLS_CONN_INIT <= connection->tls_state) &&
7434 (MHD_TLS_CONN_CONNECTED > connection->tls_state))
7435 break;
7436 }
7437#endif /* HTTPS_SUPPORT */
7438#if DEBUG_STATES
7439 MHD_DLOG (daemon,
7440 _ ("In function %s handling connection at state: %s\n"),
7441 MHD_FUNC_,
7442 MHD_state_to_string (connection->state));
7443#endif
7444 switch (connection->state)
7445 {
7448 if (get_request_line (connection))
7449 {
7452 || (connection->discard_request));
7453 continue;
7454 }
7456 break;
7460 continue;
7462 if (get_req_headers (connection, false))
7463 {
7465 mhd_assert ((MHD_CONNECTION_HEADERS_RECEIVED == connection->state) || \
7466 (connection->discard_request));
7467 continue;
7468 }
7470 break;
7472 parse_connection_headers (connection);
7473 if (MHD_CONNECTION_HEADERS_RECEIVED != connection->state)
7474 continue;
7476 if (connection->suspended)
7477 break;
7478 continue;
7480 call_connection_handler (connection); /* first call */
7481 if (MHD_CONNECTION_HEADERS_PROCESSED != connection->state)
7482 continue;
7483 if (connection->suspended)
7484 continue;
7485
7486 if ( (NULL == connection->rp.response) &&
7487 (need_100_continue (connection)) &&
7488 /* If the client is already sending the payload (body)
7489 there is no need to send "100 Continue" */
7490 (0 == connection->read_buffer_offset) )
7491 {
7493 break;
7494 }
7495 if ( (NULL != connection->rp.response) &&
7496 (0 != connection->rq.remaining_upload_size) )
7497 {
7498 /* we refused (no upload allowed!) */
7499 connection->rq.remaining_upload_size = 0;
7500 /* force close, in case client still tries to upload... */
7501 connection->discard_request = true;
7502 }
7503 connection->state = (0 == connection->rq.remaining_upload_size)
7506 if (connection->suspended)
7507 break;
7508 continue;
7510 if (connection->continue_message_write_offset ==
7512 {
7514 continue;
7515 }
7516 break;
7518 mhd_assert (0 != connection->rq.remaining_upload_size);
7519 mhd_assert (! connection->discard_request);
7520 mhd_assert (NULL == connection->rp.response);
7521 if (0 != connection->read_buffer_offset)
7522 {
7523 process_request_body (connection); /* loop call */
7524 if (MHD_CONNECTION_BODY_RECEIVING != connection->state)
7525 continue;
7526 }
7527 /* Modify here when queueing of the response during data processing
7528 will be supported */
7529 mhd_assert (! connection->discard_request);
7530 mhd_assert (NULL == connection->rp.response);
7531 if (0 == connection->rq.remaining_upload_size)
7532 {
7533 connection->state = MHD_CONNECTION_BODY_RECEIVED;
7534 continue;
7535 }
7536 break;
7538 mhd_assert (! connection->discard_request);
7539 mhd_assert (NULL == connection->rp.response);
7540 if (0 == connection->rq.remaining_upload_size)
7541 {
7542 if (connection->rq.have_chunked_upload)
7543 {
7544 /* Reset counter variables reused for footers */
7545 connection->rq.num_cr_sp_replaced = 0;
7546 connection->rq.skipped_broken_lines = 0;
7549 }
7550 else
7552 continue;
7553 }
7554 break;
7556 if (get_req_headers (connection, true))
7557 {
7559 mhd_assert ((MHD_CONNECTION_FOOTERS_RECEIVED == connection->state) || \
7560 (connection->discard_request));
7561 continue;
7562 }
7564 break;
7566 /* The header, the body, and the footers of the request has been received,
7567 * switch to the final processing of the request. */
7569 continue;
7571 call_connection_handler (connection); /* "final" call */
7572 if (connection->state != MHD_CONNECTION_FULL_REQ_RECEIVED)
7573 continue;
7574 if (NULL == connection->rp.response)
7575 break; /* try again next time */
7576 /* Response is ready, start reply */
7577 connection->state = MHD_CONNECTION_START_REPLY;
7578 continue;
7580 mhd_assert (NULL != connection->rp.response);
7582 if (MHD_NO == build_header_response (connection))
7583 {
7584 /* oops - close! */
7585 CONNECTION_CLOSE_ERROR (connection,
7586 _ ("Closing connection (failed to create "
7587 "response header).\n"));
7588 continue;
7589 }
7591 break;
7592
7594 /* no default action */
7595 break;
7597#ifdef UPGRADE_SUPPORT
7598 if (NULL != connection->rp.response->upgrade_handler)
7599 {
7600 connection->state = MHD_CONNECTION_UPGRADE;
7601 /* This connection is "upgraded". Pass socket to application. */
7602 if (MHD_NO ==
7604 connection))
7605 {
7606 /* upgrade failed, fail hard */
7607 CONNECTION_CLOSE_ERROR (connection,
7608 NULL);
7609 continue;
7610 }
7611 /* Response is not required anymore for this connection. */
7612 if (1)
7613 {
7614 struct MHD_Response *const resp = connection->rp.response;
7615
7616 connection->rp.response = NULL;
7617 MHD_destroy_response (resp);
7618 }
7619 continue;
7620 }
7621#endif /* UPGRADE_SUPPORT */
7622
7623 if (connection->rp.props.send_reply_body)
7624 {
7625 if (connection->rp.props.chunked)
7627 else
7629 }
7630 else
7632 continue;
7634 mhd_assert (connection->rp.props.send_reply_body);
7635 mhd_assert (! connection->rp.props.chunked);
7636 /* nothing to do here */
7637 break;
7639 mhd_assert (connection->rp.props.send_reply_body);
7640 mhd_assert (! connection->rp.props.chunked);
7641#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7642 if (NULL != connection->rp.response->crc)
7643 MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7644#endif
7645 if (0 == connection->rp.response->total_size)
7646 {
7647#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7648 if (NULL != connection->rp.response->crc)
7649 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7650#endif
7651 if (connection->rp.props.chunked)
7653 else
7655 continue;
7656 }
7657 if (MHD_NO != try_ready_normal_body (connection))
7658 {
7659#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7660 if (NULL != connection->rp.response->crc)
7661 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7662#endif
7664 /* Buffering for flushable socket was already enabled*/
7665
7666 break;
7667 }
7668 /* mutex was already unlocked by "try_ready_normal_body */
7669 /* not ready, no socket action */
7670 break;
7672 mhd_assert (connection->rp.props.send_reply_body);
7673 mhd_assert (connection->rp.props.chunked);
7674 /* nothing to do here */
7675 break;
7677 mhd_assert (connection->rp.props.send_reply_body);
7678 mhd_assert (connection->rp.props.chunked);
7679#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7680 if (NULL != connection->rp.response->crc)
7681 MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7682#endif
7683 if ( (0 == connection->rp.response->total_size) ||
7684 (connection->rp.rsp_write_position ==
7685 connection->rp.response->total_size) )
7686 {
7687#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7688 if (NULL != connection->rp.response->crc)
7689 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7690#endif
7692 continue;
7693 }
7694 if (1)
7695 { /* pseudo-branch for local variables scope */
7696 bool finished;
7697 if (MHD_NO != try_ready_chunked_body (connection, &finished))
7698 {
7699#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7700 if (NULL != connection->rp.response->crc)
7701 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7702#endif
7703 connection->state = finished ? MHD_CONNECTION_CHUNKED_BODY_SENT :
7705 continue;
7706 }
7707 /* mutex was already unlocked by try_ready_chunked_body */
7708 }
7709 break;
7711 mhd_assert (connection->rp.props.send_reply_body);
7712 mhd_assert (connection->rp.props.chunked);
7713 mhd_assert (connection->write_buffer_send_offset <= \
7714 connection->write_buffer_append_offset);
7715
7717 {
7718 /* oops - close! */
7719 CONNECTION_CLOSE_ERROR (connection,
7720 _ ("Closing connection (failed to create " \
7721 "response footer)."));
7722 continue;
7723 }
7724 mhd_assert (connection->write_buffer_send_offset < \
7725 connection->write_buffer_append_offset);
7727 continue;
7729 mhd_assert (connection->rp.props.send_reply_body);
7730 mhd_assert (connection->rp.props.chunked);
7731 /* no default action */
7732 break;
7734 if (MHD_HTTP_PROCESSING == connection->rp.responseCode)
7735 {
7736 /* After this type of response, we allow sending another! */
7738 MHD_destroy_response (connection->rp.response);
7739 connection->rp.response = NULL;
7740 /* FIXME: maybe partially reset memory pool? */
7741 continue;
7742 }
7743 /* Reset connection after complete reply */
7744 connection_reset (connection,
7745 MHD_CONN_USE_KEEPALIVE == connection->keepalive &&
7746 ! connection->read_closed &&
7747 ! connection->discard_request);
7748 continue;
7750 cleanup_connection (connection);
7751 connection->in_idle = false;
7752 return MHD_NO;
7753#ifdef UPGRADE_SUPPORT
7754 case MHD_CONNECTION_UPGRADE:
7755 connection->in_idle = false;
7756 return MHD_YES; /* keep open */
7757#endif /* UPGRADE_SUPPORT */
7758 default:
7759 mhd_assert (0);
7760 break;
7761 }
7762 break;
7763 }
7764 if (connection_check_timedout (connection))
7765 {
7766 MHD_connection_close_ (connection,
7768 connection->in_idle = false;
7769 return MHD_YES;
7770 }
7772 ret = MHD_YES;
7773#ifdef EPOLL_SUPPORT
7774 if ( (! connection->suspended) &&
7775 MHD_D_IS_USING_EPOLL_ (daemon) )
7776 {
7777 ret = MHD_connection_epoll_update_ (connection);
7778 }
7779#endif /* EPOLL_SUPPORT */
7780 connection->in_idle = false;
7781 return ret;
7782}
7783
7784
7785#ifdef EPOLL_SUPPORT
7794enum MHD_Result
7795MHD_connection_epoll_update_ (struct MHD_Connection *connection)
7796{
7797 struct MHD_Daemon *const daemon = connection->daemon;
7798
7800
7801 if ((0 != (MHD_EVENT_LOOP_INFO_PROCESS & connection->event_loop_info)) &&
7802 (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)))
7803 {
7804 /* Make sure that connection waiting for processing will be processed */
7805 EDLL_insert (daemon->eready_head,
7806 daemon->eready_tail,
7807 connection);
7808 connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
7809 }
7810
7811 if ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) &&
7812 (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
7813 ( ( (MHD_EVENT_LOOP_INFO_WRITE == connection->event_loop_info) &&
7814 (0 == (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY))) ||
7815 ( (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info)) &&
7816 (0 == (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) ) ) )
7817 {
7818 /* add to epoll set */
7819 struct epoll_event event;
7820
7821 event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
7822 event.data.ptr = connection;
7823 if (0 != epoll_ctl (daemon->epoll_fd,
7824 EPOLL_CTL_ADD,
7825 connection->socket_fd,
7826 &event))
7827 {
7828#ifdef HAVE_MESSAGES
7829 if (0 != (daemon->options & MHD_USE_ERROR_LOG))
7830 MHD_DLOG (daemon,
7831 _ ("Call to epoll_ctl failed: %s\n"),
7833#endif
7834 connection->state = MHD_CONNECTION_CLOSED;
7835 cleanup_connection (connection);
7836 return MHD_NO;
7837 }
7838 connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
7839 }
7840 return MHD_YES;
7841}
7842
7843
7844#endif
7845
7846
7852void
7854{
7855 connection->recv_cls = &recv_param_adapter;
7856}
7857
7858
7871_MHD_EXTERN const union MHD_ConnectionInfo *
7873 enum MHD_ConnectionInfoType info_type,
7874 ...)
7875{
7876 switch (info_type)
7877 {
7878#ifdef HTTPS_SUPPORT
7880 if (NULL == connection->tls_session)
7881 return NULL;
7882 if (1)
7883 { /* Workaround to mute compiler warning */
7884 gnutls_cipher_algorithm_t res;
7885 res = gnutls_cipher_get (connection->tls_session);
7886 connection->connection_info_dummy.cipher_algorithm = (int) res;
7887 }
7888 return &connection->connection_info_dummy;
7890 if (NULL == connection->tls_session)
7891 return NULL;
7892 if (1)
7893 { /* Workaround to mute compiler warning */
7894 gnutls_protocol_t res;
7895 res = gnutls_protocol_get_version (connection->tls_session);
7896 connection->connection_info_dummy.protocol = (int) res;
7897 }
7898 return &connection->connection_info_dummy;
7900 if (NULL == connection->tls_session)
7901 return NULL;
7902 connection->connection_info_dummy.tls_session = connection->tls_session;
7903 return &connection->connection_info_dummy;
7904#else /* ! HTTPS_SUPPORT */
7908#endif /* ! HTTPS_SUPPORT */
7910 return NULL; /* Not implemented */
7912 if (0 < connection->addr_len)
7913 {
7914 mhd_assert (sizeof (connection->addr) == \
7915 sizeof (connection->connection_info_dummy.client_addr));
7916 memcpy (&connection->connection_info_dummy.client_addr,
7917 &connection->addr,
7918 sizeof(connection->addr));
7919 return &connection->connection_info_dummy;
7920 }
7921 return NULL;
7923 connection->connection_info_dummy.daemon =
7924 MHD_get_master (connection->daemon);
7925 return &connection->connection_info_dummy;
7927 connection->connection_info_dummy.connect_fd = connection->socket_fd;
7928 return &connection->connection_info_dummy;
7931 connection->socket_context;
7932 return &connection->connection_info_dummy;
7934 connection->connection_info_dummy.suspended =
7935 connection->suspended ? MHD_YES : MHD_NO;
7936 return &connection->connection_info_dummy;
7938#if SIZEOF_UNSIGNED_INT <= (SIZEOF_UINT64_T - 2)
7939 if (UINT_MAX < connection->connection_timeout_ms / 1000)
7941 else
7942#endif /* SIZEOF_UNSIGNED_INT <=(SIZEOF_UINT64_T - 2) */
7944 (unsigned int) (connection->connection_timeout_ms / 1000);
7945 return &connection->connection_info_dummy;
7947 if ( (MHD_CONNECTION_HEADERS_RECEIVED > connection->state) ||
7948 (MHD_CONNECTION_CLOSED == connection->state) )
7949 return NULL; /* invalid, too early! */
7950 connection->connection_info_dummy.header_size = connection->rq.header_size;
7951 return &connection->connection_info_dummy;
7953 if (NULL == connection->rp.response)
7954 return NULL;
7955 connection->connection_info_dummy.http_status = connection->rp.responseCode;
7956 return &connection->connection_info_dummy;
7957 default:
7958 return NULL;
7959 }
7960}
7961
7962
7974 enum MHD_CONNECTION_OPTION option,
7975 ...)
7976{
7977 va_list ap;
7978 struct MHD_Daemon *daemon;
7979 unsigned int ui_val;
7980
7981 daemon = connection->daemon;
7982 switch (option)
7983 {
7985 if (0 == connection->connection_timeout_ms)
7987 va_start (ap, option);
7988 ui_val = va_arg (ap, unsigned int);
7989 va_end (ap);
7990#if (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT
7991 if ((UINT64_MAX / 4000 - 1) < ui_val)
7992 {
7993#ifdef HAVE_MESSAGES
7994 MHD_DLOG (connection->daemon,
7995 _ ("The specified connection timeout (%u) is too " \
7996 "large. Maximum allowed value (%" PRIu64 ") will be used " \
7997 "instead.\n"),
7998 ui_val,
7999 (UINT64_MAX / 4000 - 1));
8000#endif
8001 ui_val = UINT64_MAX / 4000 - 1;
8002 }
8003#endif /* (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT */
8004 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
8005 {
8006#if defined(MHD_USE_THREADS)
8007 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
8008#endif
8009 if (! connection->suspended)
8010 {
8011 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
8013 daemon->normal_timeout_tail,
8014 connection);
8015 else
8017 daemon->manual_timeout_tail,
8018 connection);
8019 connection->connection_timeout_ms = ((uint64_t) ui_val) * 1000;
8020 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
8022 daemon->normal_timeout_tail,
8023 connection);
8024 else
8026 daemon->manual_timeout_tail,
8027 connection);
8028 }
8029#if defined(MHD_USE_THREADS)
8030 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
8031#endif
8032 }
8033 return MHD_YES;
8034 default:
8035 return MHD_NO;
8036 }
8037}
8038
8039
8087 unsigned int status_code,
8088 struct MHD_Response *response)
8089{
8090 struct MHD_Daemon *daemon;
8091 bool reply_icy;
8092
8093 if ((NULL == connection) || (NULL == response))
8094 return MHD_NO;
8095
8096 daemon = connection->daemon;
8097 if ((! connection->in_access_handler) && (! connection->suspended) &&
8098 MHD_D_IS_USING_THREADS_ (daemon))
8099 return MHD_NO;
8100
8101 reply_icy = (0 != (status_code & MHD_ICY_FLAG));
8102 status_code &= ~MHD_ICY_FLAG;
8103
8104#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
8105 if ( (! connection->suspended) &&
8106 MHD_D_IS_USING_THREADS_ (daemon) &&
8107 (! MHD_thread_handle_ID_is_current_thread_ (connection->tid)) )
8108 {
8109#ifdef HAVE_MESSAGES
8110 MHD_DLOG (daemon,
8111 _ ("Attempted to queue response on wrong thread!\n"));
8112#endif
8113 return MHD_NO;
8114 }
8115#endif
8116
8117 if (NULL != connection->rp.response)
8118 return MHD_NO; /* The response was already set */
8119
8120 if ( (MHD_CONNECTION_HEADERS_PROCESSED != connection->state) &&
8121 (MHD_CONNECTION_FULL_REQ_RECEIVED != connection->state) )
8122 return MHD_NO; /* Wrong connection state */
8123
8124 if (daemon->shutdown)
8125 return MHD_NO;
8126
8127#ifdef UPGRADE_SUPPORT
8128 if (NULL != response->upgrade_handler)
8129 {
8130 struct MHD_HTTP_Res_Header *conn_header;
8131 if (0 == (daemon->options & MHD_ALLOW_UPGRADE))
8132 {
8133#ifdef HAVE_MESSAGES
8134 MHD_DLOG (daemon,
8135 _ ("Attempted 'upgrade' connection on daemon without" \
8136 " MHD_ALLOW_UPGRADE option!\n"));
8137#endif
8138 return MHD_NO;
8139 }
8140 if (MHD_HTTP_SWITCHING_PROTOCOLS != status_code)
8141 {
8142#ifdef HAVE_MESSAGES
8143 MHD_DLOG (daemon,
8144 _ ("Application used invalid status code for" \
8145 " 'upgrade' response!\n"));
8146#endif
8147 return MHD_NO;
8148 }
8149 if (0 == (response->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
8150 {
8151#ifdef HAVE_MESSAGES
8152 MHD_DLOG (daemon,
8153 _ ("Application used invalid response" \
8154 " without \"Connection\" header!\n"));
8155#endif
8156 return MHD_NO;
8157 }
8158 conn_header = response->first_header;
8159 mhd_assert (NULL != conn_header);
8162 if (! MHD_str_has_s_token_caseless_ (conn_header->value,
8163 "upgrade"))
8164 {
8165#ifdef HAVE_MESSAGES
8166 MHD_DLOG (daemon,
8167 _ ("Application used invalid response" \
8168 " without \"upgrade\" token in" \
8169 " \"Connection\" header!\n"));
8170#endif
8171 return MHD_NO;
8172 }
8173 if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
8174 {
8175#ifdef HAVE_MESSAGES
8176 MHD_DLOG (daemon,
8177 _ ("Connection \"Upgrade\" can be used only " \
8178 "with HTTP/1.1 connections!\n"));
8179#endif
8180 return MHD_NO;
8181 }
8182 }
8183#endif /* UPGRADE_SUPPORT */
8184 if (MHD_HTTP_SWITCHING_PROTOCOLS == status_code)
8185 {
8186#ifdef UPGRADE_SUPPORT
8187 if (NULL == response->upgrade_handler)
8188 {
8189#ifdef HAVE_MESSAGES
8190 MHD_DLOG (daemon,
8191 _ ("Application used status code 101 \"Switching Protocols\" " \
8192 "with non-'upgrade' response!\n"));
8193#endif /* HAVE_MESSAGES */
8194 return MHD_NO;
8195 }
8196#else /* ! UPGRADE_SUPPORT */
8197#ifdef HAVE_MESSAGES
8198 MHD_DLOG (daemon,
8199 _ ("Application used status code 101 \"Switching Protocols\", " \
8200 "but this MHD was built without \"Upgrade\" support!\n"));
8201#endif /* HAVE_MESSAGES */
8202 return MHD_NO;
8203#endif /* ! UPGRADE_SUPPORT */
8204 }
8205 if ( (100 > status_code) ||
8206 (999 < status_code) )
8207 {
8208#ifdef HAVE_MESSAGES
8209 MHD_DLOG (daemon,
8210 _ ("Refused wrong status code (%u). " \
8211 "HTTP requires three digits status code!\n"),
8212 status_code);
8213#endif
8214 return MHD_NO;
8215 }
8216 if (200 > status_code)
8217 {
8218 if (MHD_HTTP_VER_1_0 == connection->rq.http_ver)
8219 {
8220#ifdef HAVE_MESSAGES
8221 MHD_DLOG (daemon,
8222 _ ("Wrong status code (%u) refused. " \
8223 "HTTP/1.0 clients do not support 1xx status codes!\n"),
8224 (status_code));
8225#endif
8226 return MHD_NO;
8227 }
8228 if (0 != (response->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
8230 {
8231#ifdef HAVE_MESSAGES
8232 MHD_DLOG (daemon,
8233 _ ("Wrong status code (%u) refused. " \
8234 "HTTP/1.0 reply mode does not support 1xx status codes!\n"),
8235 (status_code));
8236#endif
8237 return MHD_NO;
8238 }
8239 }
8240 if ( (MHD_HTTP_MTHD_CONNECT == connection->rq.http_mthd) &&
8241 (2 == status_code / 100) )
8242 {
8243#ifdef HAVE_MESSAGES
8244 MHD_DLOG (daemon,
8245 _ ("Successful (%u) response code cannot be used to answer " \
8246 "\"CONNECT\" request!\n"),
8247 (status_code));
8248#endif
8249 return MHD_NO;
8250 }
8251
8252 if ( (0 != (MHD_RF_HEAD_ONLY_RESPONSE & response->flags)) &&
8253 (RP_BODY_HEADERS_ONLY < is_reply_body_needed (connection, status_code)) )
8254 {
8255#ifdef HAVE_MESSAGES
8256 MHD_DLOG (daemon,
8257 _ ("HEAD-only response cannot be used when the request requires "
8258 "reply body to be sent!\n"));
8259#endif
8260 return MHD_NO;
8261 }
8262
8263#ifdef HAVE_MESSAGES
8264 if ( (0 != (MHD_RF_INSANITY_HEADER_CONTENT_LENGTH & response->flags)) &&
8265 (0 != (MHD_RAF_HAS_CONTENT_LENGTH & response->flags_auto)) )
8266 {
8267 MHD_DLOG (daemon,
8268 _ ("The response has application-defined \"Content-Length\" " \
8269 "header. The reply to the request will be not " \
8270 "HTTP-compliant and may result in hung connection or " \
8271 "other problems!\n"));
8272 }
8273#endif
8274
8275 MHD_increment_response_rc (response);
8276 connection->rp.response = response;
8277 connection->rp.responseCode = status_code;
8278 connection->rp.responseIcy = reply_icy;
8279#if defined(_MHD_HAVE_SENDFILE)
8280 if ( (response->fd == -1) ||
8281 (response->is_pipe) ||
8282 (0 != (connection->daemon->options & MHD_USE_TLS))
8283#if defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED) && \
8284 defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE)
8285 || (! daemon->sigpipe_blocked && ! connection->sk_spipe_suppress)
8286#endif /* MHD_SEND_SPIPE_SUPPRESS_NEEDED &&
8287 MHD_SEND_SPIPE_SUPPRESS_POSSIBLE */
8288 )
8289 connection->rp.resp_sender = MHD_resp_sender_std;
8290 else
8291 connection->rp.resp_sender = MHD_resp_sender_sendfile;
8292#endif /* _MHD_HAVE_SENDFILE */
8293 /* FIXME: if 'is_pipe' is set, TLS is off, and we have *splice*, we could use splice()
8294 to avoid two user-space copies... */
8295
8296 if ( (MHD_HTTP_MTHD_HEAD == connection->rq.http_mthd) ||
8297 (MHD_HTTP_OK > status_code) ||
8298 (MHD_HTTP_NO_CONTENT == status_code) ||
8299 (MHD_HTTP_NOT_MODIFIED == status_code) )
8300 {
8301 /* if this is a "HEAD" request, or a status code for
8302 which a body is not allowed, pretend that we
8303 have already sent the full message body. */
8304 /* TODO: remove the next assignment, use 'rp_props.send_reply_body' in
8305 * checks */
8306 connection->rp.rsp_write_position = response->total_size;
8307 }
8308 if (MHD_CONNECTION_HEADERS_PROCESSED == connection->state)
8309 {
8310 /* response was queued "early", refuse to read body / footers or
8311 further requests! */
8312 connection->discard_request = true;
8313 connection->state = MHD_CONNECTION_START_REPLY;
8314 connection->rq.remaining_upload_size = 0;
8315 }
8316 if (! connection->in_idle)
8317 (void) MHD_connection_handle_idle (connection);
8318 MHD_update_last_activity_ (connection);
8319 return MHD_YES;
8320}
8321
8322
8323/* end of connection.c */
#define ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG
Definition connection.c:167
#define REQUEST_CONTENTLENGTH_TOOLARGE
Definition connection.c:571
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:763
#define ERR_RSP_EMPTY_FOOTER_NAME
Definition connection.c:451
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 char_legal_in_field_name(char chr)
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:559
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:689
#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:547
#define REQ_HTTP_VER_IS_NOT_SUPPORTED
Definition connection.c:623
#define MHD_MAX_REASONABLE_HEADERS_SIZE_
#define RQ_LINE_TOO_MANY_WSP
Definition connection.c:198
static void call_connection_handler(struct MHD_Connection *connection)
static enum MHD_Result connection_add_header(void *cls, const char *key, size_t key_size, const char *value, size_t value_size, enum MHD_ValueKind kind)
static void process_request_body(struct MHD_Connection *connection)
#define ERR_RSP_INVALID_CHR_IN_FOOTER
Definition connection.c:399
static void setup_reply_properties(struct MHD_Connection *connection)
#define ERR_RSP_INVALID_CHAR_IN_FIELD_NAME
Definition connection.c:373
#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:528
static bool get_request_line(struct MHD_Connection *c)
MHD_HdrLineReadRes_
@ MHD_HDR_LINE_READING_NEED_MORE_DATA
@ MHD_HDR_LINE_READING_GOT_END_OF_HEADER
@ MHD_HDR_LINE_READING_GOT_HEADER
@ MHD_HDR_LINE_READING_DATA_ERROR
_MHD_static_inline void reset_rq_header_processing_state(struct MHD_Connection *c)
#define ERR_RSP_HEADER_WITHOUT_COLON
Definition connection.c:412
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:612
#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:469
#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:438
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:600
#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:498
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:513
#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)
#define REQUEST_AMBIGUOUS_CONTENT_LENGTH
Definition connection.c:485
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:386
void MHD_update_last_activity_(struct MHD_Connection *connection)
#define MHD_MAX_EMPTY_LINES_SKIP
#define MHD_MIN_REASONABLE_HEADERS_SIZE_
#define REQUEST_HAS_NUL_CHAR_IN_PATH
Definition connection.c:534
#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:584
static enum MHD_HdrLineReadRes_ get_req_header(struct MHD_Connection *c, bool process_footers, struct _MHD_str_w_len *hdr_name, struct _MHD_str_w_len *hdr_value)
static void check_connection_reply(struct MHD_Connection *connection)
static void handle_req_headers_no_space(struct MHD_Connection *c, const char *add_header, size_t add_header_size)
void MHD_connection_close_(struct MHD_Connection *connection, enum MHD_RequestTerminationCode termination_code)
static size_t connection_maximize_write_buffer(struct MHD_Connection *connection)
static bool has_unprocessed_upload_body_data_in_buffer(struct MHD_Connection *c)
void MHD_connection_mark_closed_(struct MHD_Connection *connection)
#define CONNECTION_CLOSE_ERROR(c, emsg)
#define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l)
static bool buffer_append(char *buf, size_t *ppos, size_t buf_size, const char *append, size_t append_size)
static enum MHD_Result check_write_done(struct MHD_Connection *connection, enum MHD_CONNECTION_STATE next_state)
#define ERR_RSP_WSP_BEFORE_HEADER
Definition connection.c:313
#define ERR_MSG_REQUEST_HEADER_WITH_COOKIES_TOO_BIG
Definition connection.c:129
_MHD_static_inline void switch_to_rq_headers_processing(struct MHD_Connection *c)
static bool get_date_header(char *header)
static bool MHD_lookup_header_token_ci(const struct MHD_Connection *connection, const char *header, size_t header_len, const char *token, size_t token_len)
#define ERR_RSP_FOOTER_WITHOUT_COLON
Definition connection.c:425
#define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
Methods for managing connections.
#define MHD_ERR_INVAL_
Definition connection.h:63
#define MHD_ERR_CONNRESET_
Definition connection.h:42
#define MHD_ERR_NOMEM_
Definition connection.h:53
#define MHD_connection_finish_forward_(conn)
Definition connection.h:174
#define MHD_ERR_TLS_
Definition connection.h:78
#define MHD_ERR_AGAIN_
Definition connection.h:37
#define MHD_ERR_OPNOTSUPP_
Definition connection.h:68
#define MHD_ERR_BADF_
Definition connection.h:58
#define MHD_ERR_PIPE_
Definition connection.h:73
#define MHD_ERR_NOTCONN_
Definition connection.h:48
bool MHD_tls_connection_shutdown(struct MHD_Connection *connection)
bool MHD_run_tls_handshake_(struct MHD_Connection *connection)
Methods for managing connections.
#define MHD_HTTP_HEADER_CONTENT_LENGTH
Definition microhttpd.h:596
#define MHD_HTTP_HEADER_CONNECTION
Definition microhttpd.h:590
#define MHD_HTTP_HEADER_TRANSFER_ENCODING
Definition microhttpd.h:654
#define MHD_HTTP_HEADER_COOKIE
Definition microhttpd.h:754
#define MHD_HTTP_HEADER_EXPECT
Definition microhttpd.h:608
#define MHD_HTTP_HEADER_LOCATION
Definition microhttpd.h:628
#define MHD_HTTP_HEADER_HOST
Definition microhttpd.h:614
#define MHD_HTTP_INTERNAL_SERVER_ERROR
Definition microhttpd.h:453
#define MHD_HTTP_OK
Definition microhttpd.h:350
#define MHD_HTTP_MOVED_PERMANENTLY
Definition microhttpd.h:374
#define MHD_HTTP_URI_TOO_LONG
Definition microhttpd.h:419
#define MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE
Definition microhttpd.h:447
#define MHD_HTTP_PROCESSING
Definition microhttpd.h:345
#define MHD_HTTP_NOT_IMPLEMENTED
Definition microhttpd.h:455
#define MHD_HTTP_SWITCHING_PROTOCOLS
Definition microhttpd.h:343
#define MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED
Definition microhttpd.h:463
#define MHD_HTTP_CONTENT_TOO_LARGE
Definition microhttpd.h:417
#define MHD_HTTP_NOT_MODIFIED
Definition microhttpd.h:380
#define MHD_HTTP_NO_CONTENT
Definition microhttpd.h:358
#define MHD_HTTP_BAD_REQUEST
Definition microhttpd.h:391
#define MHD_HTTP_METHOD_TRACE
#define MHD_HTTP_METHOD_OPTIONS
#define MHD_HTTP_METHOD_GET
#define MHD_HTTP_METHOD_HEAD
#define MHD_HTTP_METHOD_POST
#define MHD_HTTP_METHOD_PUT
#define MHD_HTTP_METHOD_CONNECT
#define MHD_HTTP_METHOD_DELETE
_MHD_EXTERN enum MHD_Result MHD_set_connection_value(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key, const char *value)
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:896
_MHD_EXTERN int MHD_get_connection_values(struct MHD_Connection *connection, enum MHD_ValueKind kind, MHD_KeyValueIterator iterator, void *iterator_cls)
Definition connection.c:857
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:949
_MHD_EXTERN enum MHD_Result MHD_get_connection_URI_path_n(struct MHD_Connection *connection, const char **uri, size_t *uri_size)
Definition connection.c:819
_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)
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)
#define _MHD_FIXED_ENUM
Definition microhttpd.h:154
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:556
_MHD_EXTERN const union MHD_ConnectionInfo * MHD_get_connection_info(struct MHD_Connection *connection, enum MHD_ConnectionInfoType info_type,...)
#define MHD_HTTP_VERSION_1_0
#define MHD_HTTP_VERSION_1_1
enum MHD_Result MHD_parse_arguments_(struct MHD_Connection *connection, enum MHD_ValueKind kind, char *args, MHD_ArgumentIterator_ cb, void *cls)
Definition internal.c:169
MHD internal shared structures.
MHD_CONNECTION_STATE
Definition internal.h:613
@ MHD_CONNECTION_BODY_RECEIVED
Definition internal.h:663
@ MHD_CONNECTION_CHUNKED_BODY_SENT
Definition internal.h:725
@ MHD_CONNECTION_REQ_HEADERS_RECEIVING
Definition internal.h:636
@ MHD_CONNECTION_BODY_RECEIVING
Definition internal.h:656
@ MHD_CONNECTION_HEADERS_SENDING
Definition internal.h:694
@ MHD_CONNECTION_FOOTERS_SENDING
Definition internal.h:730
@ MHD_CONNECTION_FOOTERS_RECEIVED
Definition internal.h:675
@ MHD_CONNECTION_FULL_REPLY_SENT
Definition internal.h:736
@ MHD_CONNECTION_HEADERS_SENT
Definition internal.h:699
@ MHD_CONNECTION_HEADERS_PROCESSED
Definition internal.h:646
@ MHD_CONNECTION_INIT
Definition internal.h:618
@ MHD_CONNECTION_CLOSED
Definition internal.h:741
@ MHD_CONNECTION_REQ_LINE_RECEIVED
Definition internal.h:631
@ MHD_CONNECTION_NORMAL_BODY_UNREADY
Definition internal.h:705
@ MHD_CONNECTION_HEADERS_RECEIVED
Definition internal.h:641
@ MHD_CONNECTION_NORMAL_BODY_READY
Definition internal.h:710
@ MHD_CONNECTION_START_REPLY
Definition internal.h:688
@ MHD_CONNECTION_FOOTERS_RECEIVING
Definition internal.h:668
@ MHD_CONNECTION_CHUNKED_BODY_READY
Definition internal.h:720
@ MHD_CONNECTION_FULL_REQ_RECEIVED
Definition internal.h:681
@ MHD_CONNECTION_CHUNKED_BODY_UNREADY
Definition internal.h:715
@ MHD_CONNECTION_CONTINUE_SENDING
Definition internal.h:651
@ MHD_CONNECTION_REQ_LINE_RECEIVING
Definition internal.h:624
#define XDLL_insert(head, tail, element)
Definition internal.h:2718
MHD_EpollState
Definition internal.h:169
@ MHD_EPOLL_STATE_SUSPENDED
Definition internal.h:202
@ MHD_EPOLL_STATE_IN_EREADY_EDLL
Definition internal.h:192
@ MHD_EPOLL_STATE_READ_READY
Definition internal.h:181
@ MHD_EPOLL_STATE_IN_EPOLL_SET
Definition internal.h:197
@ MHD_EPOLL_STATE_WRITE_READY
Definition internal.h:187
@ MHD_TLS_CONN_TLS_CLOSING
Definition internal.h:766
@ MHD_TLS_CONN_WR_CLOSING
Definition internal.h:764
@ MHD_TLS_CONN_INVALID_STATE
Definition internal.h:769
@ MHD_TLS_CONN_WR_CLOSED
Definition internal.h:765
@ MHD_TLS_CONN_NO_TLS
Definition internal.h:760
@ MHD_TLS_CONN_INIT
Definition internal.h:761
@ MHD_TLS_CONN_TLS_CLOSED
Definition internal.h:767
@ MHD_TLS_CONN_TLS_FAILED
Definition internal.h:768
@ MHD_TLS_CONN_CONNECTED
Definition internal.h:763
@ MHD_TLS_CONN_HANDSHAKING
Definition internal.h:762
#define DLL_insert(head, tail, element)
Definition internal.h:2671
@ MHD_EVENT_LOOP_INFO_PROCESS_READ
Definition internal.h:235
@ MHD_EVENT_LOOP_INFO_PROCESS
Definition internal.h:229
@ MHD_EVENT_LOOP_INFO_READ
Definition internal.h:219
@ MHD_EVENT_LOOP_INFO_WRITE
Definition internal.h:224
@ MHD_EVENT_LOOP_INFO_CLEANUP
Definition internal.h:241
#define EDLL_insert(head, tail, element)
Definition internal.h:2765
struct MHD_IoVec MHD_iovec_
Definition internal.h:440
#define MHD_MIN(a, b)
Definition internal.h:130
#define MHD_IS_HTTP_VER_SUPPORTED(ver)
Definition internal.h:881
_MHD_static_inline struct MHD_Daemon * MHD_get_master(struct MHD_Daemon *const daemon)
Definition internal.h:2913
@ MHD_RAF_HAS_DATE_HDR
Definition internal.h:411
@ MHD_RAF_HAS_CONTENT_LENGTH
Definition internal.h:410
@ MHD_RAF_HAS_CONNECTION_CLOSE
Definition internal.h:408
@ MHD_RAF_HAS_TRANS_ENC_CHUNKED
Definition internal.h:409
@ MHD_RAF_HAS_CONNECTION_HDR
Definition internal.h:407
@ MHD_HTTP_VER_1_0
Definition internal.h:860
@ MHD_HTTP_VER_1_1
Definition internal.h:865
@ MHD_HTTP_VER_TOO_OLD
Definition internal.h:855
@ MHD_HTTP_VER_INVALID
Definition internal.h:845
@ MHD_HTTP_VER_UNKNOWN
Definition internal.h:850
@ MHD_HTTP_VER_1_2__1_9
Definition internal.h:870
@ MHD_HTTP_VER_FUTURE
Definition internal.h:875
MHD_ConnKeepAlive
Definition internal.h:818
@ MHD_CONN_USE_KEEPALIVE
Definition internal.h:832
@ MHD_CONN_MUST_UPGRADE
Definition internal.h:837
@ MHD_CONN_MUST_CLOSE
Definition internal.h:822
@ MHD_CONN_KEEPALIVE_UNKOWN
Definition internal.h:827
#define MHD_IS_HTTP_VER_1_1_COMPAT(ver)
Definition internal.h:890
#define MHD_BUF_INC_SIZE
Definition internal.h:142
@ MHD_HTTP_MTHD_GET
Definition internal.h:907
@ MHD_HTTP_MTHD_CONNECT
Definition internal.h:927
@ MHD_HTTP_MTHD_DELETE
Definition internal.h:923
@ MHD_HTTP_MTHD_OPTIONS
Definition internal.h:931
@ MHD_HTTP_MTHD_TRACE
Definition internal.h:935
@ MHD_HTTP_MTHD_HEAD
Definition internal.h:911
@ MHD_HTTP_MTHD_POST
Definition internal.h:915
@ MHD_HTTP_MTHD_OTHER
Definition internal.h:939
@ MHD_HTTP_MTHD_NO_METHOD
Definition internal.h:903
@ MHD_HTTP_MTHD_PUT
Definition internal.h:919
#define EDLL_remove(head, tail, element)
Definition internal.h:2785
#define PRIu64
Definition internal.h:53
#define XDLL_remove(head, tail, element)
Definition internal.h:2740
#define MHD_D_IS_USING_THREAD_PER_CONN_(d)
Definition internal.h:2591
#define DLL_remove(head, tail, element)
Definition internal.h:2693
#define MHD_D_IS_USING_THREADS_(d)
Definition internal.h:2587
#define MHD_D_IS_USING_EPOLL_(d)
Definition internal.h:2563
void * MHD_pool_reallocate(struct MemoryPool *pool, void *old, size_t old_size, size_t new_size)
Definition memorypool.c:533
void MHD_pool_destroy(struct MemoryPool *pool)
Definition memorypool.c:341
bool MHD_pool_is_resizable_inplace(struct MemoryPool *pool, void *block, size_t block_size)
Definition memorypool.c:440
void MHD_pool_deallocate(struct MemoryPool *pool, void *block, size_t block_size)
Definition memorypool.c:625
void * MHD_pool_try_alloc(struct MemoryPool *pool, size_t size, size_t *required_bytes)
Definition memorypool.c:481
size_t MHD_pool_get_free(struct MemoryPool *pool)
Definition memorypool.c:374
void * MHD_pool_reset(struct MemoryPool *pool, void *keep, size_t copy_bytes, size_t new_size)
Definition memorypool.c:729
void * MHD_pool_allocate(struct MemoryPool *pool, size_t size, bool from_end)
Definition memorypool.c:399
memory pool; mostly used for efficient (de)allocation for each connection and bounding memory use for...
macros for mhd_assert()
#define mhd_assert(ignore)
Definition mhd_assert.h:45
Header for platform missing functions.
Header for platform-independent inter-thread communication.
#define MHD_PANIC(msg)
Definition mhd_itc.h:45
limits values definitions
#define SSIZE_MAX
Definition mhd_limits.h:121
#define UINT64_MAX
Definition mhd_limits.h:93
#define SIZE_MAX
Definition mhd_limits.h:111
#define UINT_MAX
Definition mhd_limits.h:53
Header for platform-independent locks abstraction.
#define MHD_mutex_unlock_chk_(ignore)
Definition mhd_locks.h:198
#define MHD_mutex_lock_chk_(ignore)
Definition mhd_locks.h:196
#define NULL
uint64_t MHD_monotonic_msec_counter(void)
internal monotonic clock functions implementations
#define _(String)
Definition mhd_options.h:42
#define _MHD_EXTERN
Definition mhd_options.h:53
#define MHD_FUNC_
ssize_t MHD_send_hdr_and_body_(struct MHD_Connection *connection, const char *header, size_t header_size, bool never_push_hdr, const char *body, size_t body_size, bool complete_response)
Definition mhd_send.c:905
ssize_t MHD_send_iovec_(struct MHD_Connection *connection, struct MHD_iovec_track_ *const r_iov, bool push_data)
Definition mhd_send.c:1622
ssize_t MHD_send_data_(struct MHD_Connection *connection, const char *buffer, size_t buffer_size, bool push_data)
Definition mhd_send.c:753
Declarations of send() wrappers.
#define MHD_SCKT_ERR_IS_(err, code)
#define MHD_SCKT_ERR_IS_EAGAIN_(err)
#define MHD_SCKT_ERR_IS_LOW_RESOURCES_(err)
#define MHD_socket_last_strerr_()
#define MHD_SCKT_EOPNOTSUPP_
#define MHD_SCKT_EBADF_
#define MHD_socket_get_error_()
#define MHD_SCKT_ERR_IS_REMOTE_DISCNN_(err)
#define MHD_SCKT_EINVAL_
#define MHD_SCKT_ERR_IS_EINTR_(err)
#define MHD_SCKT_SEND_MAX_SIZE_
#define MHD_SCKT_ENOTCONN_
#define MHD_recv_(s, b, l)
#define MHD_SEND_SPIPE_SUPPRESS_NEEDED
int MHD_str_equal_caseless_(const char *str1, const char *str2)
Definition mhd_str.c:683
size_t MHD_uint8_to_str_pad(uint8_t val, uint8_t min_digits, char *buf, size_t buf_size)
Definition mhd_str.c:1629
size_t MHD_uint16_to_str(uint16_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1550
size_t MHD_str_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition mhd_str.c:1238
size_t MHD_uint64_to_str(uint64_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1591
size_t MHD_strx_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition mhd_str.c:1415
int MHD_str_equal_caseless_n_(const char *const str1, const char *const str2, size_t maxlen)
Definition mhd_str.c:717
bool MHD_str_has_token_caseless_(const char *str, const char *const token, size_t token_len)
Definition mhd_str.c:782
bool MHD_str_equal_caseless_bin_n_(const char *const str1, const char *const str2, size_t len)
Definition mhd_str.c:749
size_t MHD_uint32_to_strx(uint32_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1516
Header for string manipulating helpers.
#define MHD_str_has_s_token_caseless_(str, tkn)
Definition mhd_str.h:154
#define MHD_STATICSTR_LEN_(macro)
#define MHD_thread_handle_ID_is_current_thread_(hndl_id)
#define MHD_SIZE_UNKNOWN
Definition microhttpd.h:183
MHD_Result
Definition microhttpd.h:163
@ MHD_YES
Definition microhttpd.h:172
@ MHD_NO
Definition microhttpd.h:167
#define MHD_CONTENT_READER_END_OF_STREAM
Definition microhttpd.h:186
_MHD_EXTERN size_t MHD_get_reason_phrase_len_for(unsigned int code)
#define MHD_INVALID_SOCKET
Definition microhttpd.h:207
_MHD_EXTERN const char * MHD_get_reason_phrase_for(unsigned int code)
#define MHD_CONTENT_READER_END_WITH_ERROR
Definition microhttpd.h:187
MHD_ValueKind
@ MHD_FOOTER_KIND
@ MHD_COOKIE_KIND
@ MHD_HEADER_KIND
@ MHD_GET_ARGUMENT_KIND
@ MHD_USE_TURBO
@ MHD_USE_SUPPRESS_DATE_NO_CLOCK
@ MHD_USE_TLS
@ MHD_ALLOW_UPGRADE
@ MHD_USE_ERROR_LOG
@ MHD_RF_SEND_KEEP_ALIVE_HEADER
@ MHD_RF_HEAD_ONLY_RESPONSE
@ MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
@ MHD_RF_HTTP_1_0_SERVER
@ MHD_RF_INSANITY_HEADER_CONTENT_LENGTH
MHD_CONNECTION_OPTION
@ MHD_CONNECTION_OPTION_TIMEOUT
bool MHD_add_response_entry_no_alloc_(struct MHD_Response *response, enum MHD_ValueKind kind, char *header, size_t header_len, char *content, size_t content_len)
Definition response.c:167
void MHD_increment_response_rc(struct MHD_Response *response)
Definition response.c:2335
Methods for managing response objects.
enum MHD_Result MHD_response_execute_upgrade_(struct MHD_Response *response, struct MHD_Connection *connection)
MHD_socket socket_fd
Definition internal.h:1489
size_t write_buffer_size
Definition internal.h:1447
size_t write_buffer_send_offset
Definition internal.h:1452
socklen_t addr_len
Definition internal.h:1469
enum MHD_ConnectionEventLoopInfo event_loop_info
Definition internal.h:1576
size_t write_buffer_append_offset
Definition internal.h:1458
char * write_buffer
Definition internal.h:1415
bool stop_with_error
Definition internal.h:1532
bool discard_request
Definition internal.h:1541
ReceiveCallback recv_cls
Definition internal.h:1581
volatile bool resuming
Definition internal.h:1627
void * socket_context
Definition internal.h:1395
uint64_t last_activity
Definition internal.h:1475
enum MHD_ConnKeepAlive keepalive
Definition internal.h:1402
struct MHD_Request rq
Definition internal.h:1371
union MHD_ConnectionInfo connection_info_dummy
Definition internal.h:1632
size_t continue_message_write_offset
Definition internal.h:1464
size_t read_buffer_offset
Definition internal.h:1442
struct MHD_Reply rp
Definition internal.h:1376
struct MemoryPool * pool
Definition internal.h:1386
enum MHD_CONNECTION_STATE state
Definition internal.h:1571
char * read_buffer
Definition internal.h:1409
struct MHD_Daemon * daemon
Definition internal.h:1366
bool in_access_handler
Definition internal.h:1622
bool sk_spipe_suppress
Definition internal.h:1505
uint64_t connection_timeout_ms
Definition internal.h:1482
struct sockaddr_storage * addr
Definition internal.h:1421
size_t read_buffer_size
Definition internal.h:1436
size_t pool_size
Definition internal.h:2142
MHD_AccessHandlerCallback default_handler
Definition internal.h:1873
LogCallback uri_log_callback
Definition internal.h:2063
struct MHD_Connection * normal_timeout_tail
Definition internal.h:2006
int client_discipline
Definition internal.h:2271
void * unescape_callback_cls
Definition internal.h:2078
struct MHD_Connection * connections_tail
Definition internal.h:1906
struct MHD_Connection * suspended_connections_tail
Definition internal.h:1916
struct MHD_Connection * manual_timeout_head
Definition internal.h:2014
struct MHD_Connection * normal_timeout_head
Definition internal.h:1999
MHD_RequestCompletedCallback notify_completed
Definition internal.h:2038
struct MHD_Connection * cleanup_head
Definition internal.h:1921
struct MHD_itc_ itc
Definition internal.h:2204
uint64_t connection_timeout_ms
Definition internal.h:2259
struct MHD_Connection * manual_timeout_tail
Definition internal.h:2021
volatile bool shutdown
Definition internal.h:2209
enum MHD_FLAG options
Definition internal.h:1886
struct MHD_Connection * cleanup_tail
Definition internal.h:1926
bool sigpipe_blocked
Definition internal.h:2299
UnescapeCallback unescape_callback
Definition internal.h:2073
void * notify_completed_cls
Definition internal.h:2043
struct MHD_Connection * suspended_connections_head
Definition internal.h:1911
int allow_bzero_in_url
Definition internal.h:2278
void * default_handler_cls
Definition internal.h:1878
struct MHD_Connection * connections_head
Definition internal.h:1901
size_t pool_increment
Definition internal.h:2147
void * uri_log_callback_cls
Definition internal.h:2068
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:1301
bool responseIcy
Definition internal.h:1294
struct MHD_iovec_track_ resp_iov
Definition internal.h:1309
struct MHD_Response * response
Definition internal.h:1282
unsigned int responseCode
Definition internal.h:1288
struct MHD_Reply_Properties props
Definition internal.h:1318
unsigned int skipped_empty_lines
Definition internal.h:955
struct MHD_HTTP_Req_Header * headers_received
Definition internal.h:1117
union MHD_HeadersProcessing hdrs
Definition internal.h:1247
uint64_t current_chunk_size
Definition internal.h:1167
uint64_t current_chunk_offset
Definition internal.h:1173
bool some_payload_processed
Definition internal.h:1190
size_t skipped_broken_lines
Definition internal.h:1242
void * client_context
Definition internal.h:1198
const char * url
Definition internal.h:1096
size_t url_len
Definition internal.h:1101
const char * version
Definition internal.h:1075
size_t num_cr_sp_replaced
Definition internal.h:1237
enum MHD_HTTP_Version http_ver
Definition internal.h:1080
size_t req_target_len
Definition internal.h:1106
const char * method
Definition internal.h:1085
size_t header_size
Definition internal.h:1130
union MHD_StartOrSize field_lines
Definition internal.h:1140
struct MHD_HTTP_Req_Header * headers_received_tail
Definition internal.h:1122
enum MHD_HTTP_Method http_mthd
Definition internal.h:1090
bool client_aware
Definition internal.h:1205
bool have_chunked_upload
Definition internal.h:1158
const char * url_for_callback
Definition internal.h:1112
uint64_t remaining_upload_size
Definition internal.h:1146
size_t data_buffer_size
Definition internal.h:557
MHD_iovec_ * data_iov
Definition internal.h:588
const char * data
Definition internal.h:489
uint64_t data_start
Definition internal.h:541
MHD_ContentReaderCallback crc
Definition internal.h:501
enum MHD_ResponseAutoFlags flags_auto
Definition internal.h:578
unsigned int data_iovcnt
Definition internal.h:593
size_t data_size
Definition internal.h:552
enum MHD_ResponseFlags flags
Definition internal.h:573
void * crc_cls
Definition internal.h:495
struct MHD_HTTP_Res_Header * first_header
Definition internal.h:478
uint64_t total_size
Definition internal.h:535
MHD_iovec_ * iov
Definition internal.h:453
const char * str
unsigned int connection_timeout
struct MHD_Daemon * daemon
unsigned int http_status
struct sockaddr * client_addr
MHD_socket connect_fd
struct MHD_RequestLineProcessing rq_line
Definition internal.h:1036
struct MHD_HeaderProcessing hdr
Definition internal.h:1041
const char * start
Definition internal.h:1055