GNU libmicrohttpd 1.0.6
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-2026 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
475#define REQUEST_MULTIPLE_HOST_HDR \
476 "<html>" \
477 "<head><title>Multiple &quot;Host:&quot; headers</title></head>" \
478 "<body>Request contains several <b>&quot;Host:&quot;</b> headers." \
479 "</body></html>"
480
485#ifdef HAVE_MESSAGES
486#define REQUEST_AMBIGUOUS_CONTENT_LENGTH \
487 "<html>" \
488 "<head><title>&quot;Content-Length:&quot; header must be unique</title></head>" \
489 "<body>HTTP/1.1 request with multiple <b>&quot;Content-Length:&quot;</b> headers.</body>" \
490 "</html>"
491
492#else
493#define REQUEST_AMBIGUOUS_CONTENT_LENGTH ""
494#endif
495
499#ifdef HAVE_MESSAGES
500#define REQUEST_UNSUPPORTED_TR_ENCODING \
501 "<html>" \
502 "<head><title>Unsupported Transfer-Encoding</title></head>" \
503 "<body>The Transfer-Encoding used in request is not supported.</body>" \
504 "</html>"
505#else
506#define REQUEST_UNSUPPORTED_TR_ENCODING ""
507#endif
508
513#ifdef HAVE_MESSAGES
514#define REQUEST_LENGTH_WITH_TR_ENCODING \
515 "<html>" \
516 "<head><title>Malformed request</title></head>" \
517 "<body>Wrong combination of the request headers: both Transfer-Encoding " \
518 "and Content-Length headers are used at the same time.</body>" \
519 "</html>"
520#else
521#define REQUEST_LENGTH_WITH_TR_ENCODING ""
522#endif
523
527#define REQUEST_HTTP1_0_TR_ENCODING \
528 "<html><head><title>Malformed request</title></head>" \
529 "<body><b>&quot;Transfer-Encoding:&quot;</b> must not be used " \
530 "with HTTP/1.0.</body></html>"
531
539#ifdef HAVE_MESSAGES
540#define REQUEST_MALFORMED \
541 "<html><head><title>Request malformed</title></head>" \
542 "<body>HTTP request is syntactically incorrect.</body></html>"
543#else
544#define REQUEST_MALFORMED ""
545#endif
546
550#define REQUEST_HAS_NUL_CHAR_IN_PATH \
551 "<html><head><title>Bad Request Path</title></head>" \
552 "<body>The request path contains invalid characters.</body></html>"
553
558#ifdef HAVE_MESSAGES
559#define REQUEST_CHUNKED_MALFORMED \
560 "<html><head><title>Request malformed</title></head>" \
561 "<body>HTTP chunked encoding is syntactically incorrect.</body></html>"
562#else
563#define REQUEST_CHUNKED_MALFORMED ""
564#endif
565
569#ifdef HAVE_MESSAGES
570#define REQUEST_CHUNK_TOO_LARGE \
571 "<html><head><title>Request content too large</title></head>" \
572 "<body>The chunk size used in HTTP chunked encoded " \
573 "request is too large.</body></html>"
574#else
575#define REQUEST_CHUNK_TOO_LARGE ""
576#endif
577
581#ifdef HAVE_MESSAGES
582#define REQUEST_CONTENTLENGTH_TOOLARGE \
583 "<html><head><title>Request content too large</title></head>" \
584 "<body>HTTP request has too large value for " \
585 "<b>Content-Length</b> header.</body></html>"
586#else
587#define REQUEST_CONTENTLENGTH_TOOLARGE ""
588#endif
589
594#ifdef HAVE_MESSAGES
595#define REQUEST_CONTENTLENGTH_MALFORMED \
596 "<html><head><title>Request malformed</title></head>" \
597 "<body>HTTP request has wrong value for " \
598 "<b>Content-Length</b> header.</body></html>"
599#else
600#define REQUEST_CONTENTLENGTH_MALFORMED ""
601#endif
602
609#ifdef HAVE_MESSAGES
610#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP \
611 "<html><head><title>Internal server error</title></head>" \
612 "<body>Please ask the developer of this Web server to carefully " \
613 "read the GNU libmicrohttpd documentation about connection " \
614 "management and blocking.</body></html>"
615#else
616#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP ""
617#endif
618
622#ifdef HAVE_MESSAGES
623#define REQ_HTTP_VER_IS_TOO_OLD \
624 "<html><head><title>Requested HTTP version is not supported</title></head>" \
625 "<body>Requested HTTP version is too old and not " \
626 "supported.</body></html>"
627#else
628#define REQ_HTTP_VER_IS_TOO_OLD ""
629#endif
630
634#ifdef HAVE_MESSAGES
635#define REQ_HTTP_VER_IS_NOT_SUPPORTED \
636 "<html><head><title>Requested HTTP version is not supported</title></head>" \
637 "<body>Requested HTTP version is not supported.</body></html>"
638#else
639#define REQ_HTTP_VER_IS_NOT_SUPPORTED ""
640#endif
641
642
646#define MHD_SENFILE_CHUNK_ (0x20000)
647
651#define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000)
652
653#ifdef HAVE_MESSAGES
659static const char *
660str_conn_error_ (ssize_t mhd_err_code)
661{
662 switch (mhd_err_code)
663 {
664 case MHD_ERR_AGAIN_:
665 return _ ("The operation would block, retry later");
667 return _ ("The connection was forcibly closed by remote peer");
668 case MHD_ERR_NOTCONN_:
669 return _ ("The socket is not connected");
670 case MHD_ERR_NOMEM_:
671 return _ ("Not enough system resources to serve the request");
672 case MHD_ERR_BADF_:
673 return _ ("Bad FD value");
674 case MHD_ERR_INVAL_:
675 return _ ("Argument value is invalid");
677 return _ ("Argument value is not supported");
678 case MHD_ERR_PIPE_:
679 return _ ("The socket is no longer available for sending");
680 case MHD_ERR_TLS_:
681 return _ ("TLS encryption or decryption error");
682 default:
683 break; /* Mute compiler warning */
684 }
685 if (0 <= mhd_err_code)
686 return _ ("Not an error code");
687
688 mhd_assert (0); /* Should never be reachable */
689 return _ ("Wrong error code value");
690}
691
692
693#endif /* HAVE_MESSAGES */
694
704void *
706 size_t size)
707{
708 struct MHD_Connection *const c = connection; /* a short alias */
709 struct MemoryPool *const pool = c->pool; /* a short alias */
710 size_t need_to_be_freed = 0;
711 void *res;
712
713 res = MHD_pool_try_alloc (pool,
714 size,
715 &need_to_be_freed);
716 if (NULL != res)
717 return res;
718
720 c->write_buffer,
722 {
724 need_to_be_freed)
725 {
726 char *buf;
727 const size_t new_buf_size = c->write_buffer_size - need_to_be_freed;
728 buf = MHD_pool_reallocate (pool,
729 c->write_buffer,
731 new_buf_size);
732 mhd_assert (c->write_buffer == buf);
733 mhd_assert (c->write_buffer_append_offset <= new_buf_size);
734 mhd_assert (c->write_buffer_send_offset <= new_buf_size);
735 c->write_buffer_size = new_buf_size;
736 c->write_buffer = buf;
737 }
738 else
739 return NULL;
740 }
741 else if (MHD_pool_is_resizable_inplace (pool,
742 c->read_buffer,
744 {
745 if (c->read_buffer_size - c->read_buffer_offset >= need_to_be_freed)
746 {
747 char *buf;
748 const size_t new_buf_size = c->read_buffer_size - need_to_be_freed;
749 buf = MHD_pool_reallocate (pool,
750 c->read_buffer,
752 new_buf_size);
753 mhd_assert (c->read_buffer == buf);
754 mhd_assert (c->read_buffer_offset <= new_buf_size);
755 c->read_buffer_size = new_buf_size;
756 c->read_buffer = buf;
757 }
758 else
759 return NULL;
760 }
761 else
762 return NULL;
763 res = MHD_pool_allocate (pool, size, true);
764 mhd_assert (NULL != res); /* It has been checked that pool has enough space */
765 return res;
766}
767
768
778static ssize_t
780 void *other,
781 size_t i)
782{
783 ssize_t ret;
784
785 if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
786 (MHD_CONNECTION_CLOSED == connection->state) )
787 {
788 return MHD_ERR_NOTCONN_;
789 }
791 i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */
792
793 ret = MHD_recv_ (connection->socket_fd,
794 other,
795 i);
796 if (0 > ret)
797 {
798 const int err = MHD_socket_get_error_ ();
799 if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
800 {
801#ifdef EPOLL_SUPPORT
802 /* Got EAGAIN --- no longer read-ready */
803 connection->epoll_state &=
805#endif /* EPOLL_SUPPORT */
806 return MHD_ERR_AGAIN_;
807 }
808 if (MHD_SCKT_ERR_IS_EINTR_ (err))
809 return MHD_ERR_AGAIN_;
811 return MHD_ERR_CONNRESET_;
813 return MHD_ERR_OPNOTSUPP_;
815 return MHD_ERR_NOTCONN_;
817 return MHD_ERR_INVAL_;
819 return MHD_ERR_NOMEM_;
821 return MHD_ERR_BADF_;
822 /* Treat any other error as a hard error. */
823 return MHD_ERR_NOTCONN_;
824 }
825#ifdef EPOLL_SUPPORT
826 else if (i > (size_t) ret)
827 connection->epoll_state &=
829#endif /* EPOLL_SUPPORT */
830 return ret;
831}
832
833
836 const char **uri,
837 size_t *uri_size)
838{
839 if (NULL != uri)
840 *uri = NULL;
841 if (NULL != uri_size)
842 *uri_size = 0u;
843
844 if (connection->state < MHD_CONNECTION_REQ_LINE_RECEIVED)
845 return MHD_NO;
846 if (connection->state >= MHD_CONNECTION_START_REPLY)
847 return MHD_NO;
848 if (NULL == connection->rq.url)
849 return MHD_NO;
850
851 if (NULL != uri)
852 *uri = connection->rq.url;
853 if (NULL != uri_size)
854 *uri_size = connection->rq.url_len;
855
856 return MHD_YES;
857}
858
859
872_MHD_EXTERN int
874 enum MHD_ValueKind kind,
875 MHD_KeyValueIterator iterator,
876 void *iterator_cls)
877{
878 int ret;
879 struct MHD_HTTP_Req_Header *pos;
880
881 if (NULL == connection)
882 return -1;
883 ret = 0;
884 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
885 if (0 != (pos->kind & kind))
886 {
887 ret++;
888 if ( (NULL != iterator) &&
889 (MHD_NO == iterator (iterator_cls,
890 pos->kind,
891 pos->header,
892 pos->value)) )
893 return ret;
894 }
895 return ret;
896}
897
898
911_MHD_EXTERN int
913 enum MHD_ValueKind kind,
914 MHD_KeyValueIteratorN iterator,
915 void *iterator_cls)
916{
917 int ret;
918 struct MHD_HTTP_Req_Header *pos;
919
920 if (NULL == connection)
921 return -1;
922 ret = 0;
923
924 if (NULL == iterator)
925 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
926 {
927 if (0 != (kind & pos->kind))
928 ret++;
929 }
930 else
931 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
932 if (0 != (kind & pos->kind))
933 {
934 ret++;
935 if (MHD_NO == iterator (iterator_cls,
936 pos->kind,
937 pos->header,
938 pos->header_size,
939 pos->value,
940 pos->value_size))
941 return ret;
942 }
943 return ret;
944}
945
946
964static enum MHD_Result
966 enum MHD_ValueKind kind,
967 const char *key,
968 size_t key_size,
969 const char *value,
970 size_t value_size)
971{
972 struct MHD_HTTP_Req_Header *pos;
973
974 pos = MHD_connection_alloc_memory_ (connection,
975 sizeof (struct MHD_HTTP_Req_Header));
976 if (NULL == pos)
977 return MHD_NO;
978 pos->header = key;
979 pos->header_size = key_size;
980 pos->value = value;
981 pos->value_size = value_size;
982 pos->kind = kind;
983 pos->next = NULL;
984 pos->prev = NULL;
985 /* append 'pos' to the linked list of headers */
986 if (NULL == connection->rq.headers_received_tail)
987 {
988 mhd_assert (NULL == connection->rq.headers_received);
989 connection->rq.headers_received = pos;
990 connection->rq.headers_received_tail = pos;
991 }
992 else
993 {
994 mhd_assert (NULL != connection->rq.headers_received);
995 mhd_assert (NULL == connection->rq.headers_received_tail->next);
996 mhd_assert (pos != connection->rq.headers_received_tail);
997 mhd_assert (pos != connection->rq.headers_received);
998 connection->rq.headers_received_tail->next = pos;
999 connection->rq.headers_received_tail = pos;
1000 }
1001 return MHD_YES;
1002}
1003
1004
1032 enum MHD_ValueKind kind,
1033 const char *key,
1034 size_t key_size,
1035 const char *value,
1036 size_t value_size)
1037{
1038 if ( (MHD_GET_ARGUMENT_KIND != kind) &&
1039 ( ((key ? strlen (key) : 0) != key_size) ||
1040 ((value ? strlen (value) : 0) != value_size) ) )
1041 return MHD_NO; /* binary zero is allowed only in GET arguments */
1042
1043 return MHD_set_connection_value_n_nocheck_ (connection,
1044 kind,
1045 key,
1046 key_size,
1047 value,
1048 value_size);
1049}
1050
1051
1079 enum MHD_ValueKind kind,
1080 const char *key,
1081 const char *value)
1082{
1083 return MHD_set_connection_value_n_nocheck_ (connection,
1084 kind,
1085 key,
1086 NULL != key
1087 ? strlen (key)
1088 : 0,
1089 value,
1090 NULL != value
1091 ? strlen (value)
1092 : 0);
1093}
1094
1095
1106_MHD_EXTERN const char *
1108 enum MHD_ValueKind kind,
1109 const char *key)
1110{
1111 const char *value;
1112
1113 value = NULL;
1114 (void) MHD_lookup_connection_value_n (connection,
1115 kind,
1116 key,
1117 (NULL == key) ? 0 : strlen (key),
1118 &value,
1119 NULL);
1120 return value;
1121}
1122
1123
1145 enum MHD_ValueKind kind,
1146 const char *key,
1147 size_t key_size,
1148 const char **value_ptr,
1149 size_t *value_size_ptr)
1150{
1151 struct MHD_HTTP_Req_Header *pos;
1152
1153 if (NULL == connection)
1154 return MHD_NO;
1155
1156 if (NULL == key)
1157 {
1158 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1159 {
1160 if ( (0 != (kind & pos->kind)) &&
1161 (NULL == pos->header) )
1162 break;
1163 }
1164 }
1165 else
1166 {
1167 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1168 {
1169 if ( (0 != (kind & pos->kind)) &&
1170 (key_size == pos->header_size) &&
1171 ( (key == pos->header) ||
1173 pos->header,
1174 key_size) ) ) )
1175 break;
1176 }
1177 }
1178
1179 if (NULL == pos)
1180 return MHD_NO;
1181
1182 if (NULL != value_ptr)
1183 *value_ptr = pos->value;
1184
1185 if (NULL != value_size_ptr)
1186 *value_size_ptr = pos->value_size;
1187
1188 return MHD_YES;
1189}
1190
1191
1207static bool
1209 const char *header,
1210 size_t header_len,
1211 const char *token,
1212 size_t token_len)
1213{
1214 struct MHD_HTTP_Req_Header *pos;
1215
1216 if ((NULL == connection) || (NULL == header) || (0 == header[0]) ||
1217 (NULL == token) || (0 == token[0]))
1218 return false;
1219
1220 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1221 {
1222 if ((0 != (pos->kind & MHD_HEADER_KIND)) &&
1223 (header_len == pos->header_size) &&
1224 ( (header == pos->header) ||
1226 pos->header,
1227 header_len)) ) &&
1228 (MHD_str_has_token_caseless_ (pos->value, token, token_len)))
1229 return true;
1230 }
1231 return false;
1232}
1233
1234
1246#define MHD_lookup_header_s_token_ci(c,h,tkn) \
1247 MHD_lookup_header_token_ci ((c),(h),MHD_STATICSTR_LEN_ (h), \
1248 (tkn),MHD_STATICSTR_LEN_ (tkn))
1249
1250
1258static bool
1260{
1261 const char *expect;
1262
1263 if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
1264 return false;
1265
1266 if (0 == connection->rq.remaining_upload_size)
1267 return false;
1268
1269 if (MHD_NO ==
1275 &expect,
1276 NULL))
1277 return false;
1278
1279 if (MHD_str_equal_caseless_ (expect,
1280 "100-continue"))
1281 return true;
1282
1283 return false;
1284}
1285
1286
1293void
1295{
1296 const struct MHD_Daemon *daemon = connection->daemon;
1297
1298 if (0 == (daemon->options & MHD_USE_TURBO))
1299 {
1300#ifdef HTTPS_SUPPORT
1301 /* For TLS connection use shutdown of TLS layer
1302 * and do not shutdown TCP socket. This give more
1303 * chances to send TLS closure data to remote side.
1304 * Closure of TLS layer will be interpreted by
1305 * remote side as end of transmission. */
1306 if (0 != (daemon->options & MHD_USE_TLS))
1307 {
1308 if (! MHD_tls_connection_shutdown (connection))
1309 shutdown (connection->socket_fd,
1310 SHUT_WR);
1311 }
1312 else /* Combined with next 'shutdown()'. */
1313#endif /* HTTPS_SUPPORT */
1314 shutdown (connection->socket_fd,
1315 SHUT_WR);
1316 }
1317 connection->state = MHD_CONNECTION_CLOSED;
1319}
1320
1321
1331void
1333 enum MHD_RequestTerminationCode termination_code)
1334{
1335 struct MHD_Daemon *daemon = connection->daemon;
1336 struct MHD_Response *resp = connection->rp.response;
1337
1338 mhd_assert (! connection->suspended);
1339#ifdef MHD_USE_THREADS
1340 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
1341 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
1342#endif /* MHD_USE_THREADS */
1343 if ( (NULL != daemon->notify_completed) &&
1344 (connection->rq.client_aware) )
1345 daemon->notify_completed (daemon->notify_completed_cls,
1346 connection,
1347 &connection->rq.client_context,
1348 termination_code);
1349 connection->rq.client_aware = false;
1350 if (NULL != resp)
1351 {
1352 connection->rp.response = NULL;
1353 MHD_destroy_response (resp);
1354 }
1355 if (NULL != connection->pool)
1356 {
1357 MHD_pool_destroy (connection->pool);
1358 connection->pool = NULL;
1359 }
1360
1361 MHD_connection_mark_closed_ (connection);
1362}
1363
1364
1365#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
1376void
1378{
1379 struct MHD_Daemon *daemon = connection->daemon;
1380 struct MHD_UpgradeResponseHandle *urh = connection->urh;
1381
1382#ifdef MHD_USE_THREADS
1383 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
1386#endif /* MHD_USE_THREADS */
1387
1388 if (0 == (daemon->options & MHD_USE_TLS))
1389 return; /* Nothing to do with non-TLS connection. */
1390
1391 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
1392 DLL_remove (daemon->urh_head,
1393 daemon->urh_tail,
1394 urh);
1395#ifdef EPOLL_SUPPORT
1396 if (MHD_D_IS_USING_EPOLL_ (daemon) &&
1397 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
1398 EPOLL_CTL_DEL,
1399 connection->socket_fd,
1400 NULL)) )
1401 {
1402 MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
1403 }
1404 if (urh->in_eready_list)
1405 {
1406 EDLL_remove (daemon->eready_urh_head,
1407 daemon->eready_urh_tail,
1408 urh);
1409 urh->in_eready_list = false;
1410 }
1411#endif /* EPOLL_SUPPORT */
1412 if (MHD_INVALID_SOCKET != urh->mhd.socket)
1413 {
1414#ifdef EPOLL_SUPPORT
1415 if (MHD_D_IS_USING_EPOLL_ (daemon) &&
1416 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
1417 EPOLL_CTL_DEL,
1418 urh->mhd.socket,
1419 NULL)) )
1420 {
1421 MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
1422 }
1423#endif /* EPOLL_SUPPORT */
1424 /* Reflect remote disconnect to application by breaking
1425 * socketpair connection. */
1426 shutdown (urh->mhd.socket, SHUT_RDWR);
1427 }
1428 /* Socketpair sockets will remain open as they will be
1429 * used with MHD_UPGRADE_ACTION_CLOSE. They will be
1430 * closed by cleanup_upgraded_connection() during
1431 * connection's final cleanup.
1432 */
1433}
1434
1435
1436#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT*/
1437
1438
1446static void
1448 const char *emsg)
1449{
1450 connection->stop_with_error = true;
1451 connection->discard_request = true;
1452#ifdef HAVE_MESSAGES
1453 if (NULL != emsg)
1454 MHD_DLOG (connection->daemon,
1455 "%s\n",
1456 emsg);
1457#else /* ! HAVE_MESSAGES */
1458 (void) emsg; /* Mute compiler warning. */
1459#endif /* ! HAVE_MESSAGES */
1460 MHD_connection_close_ (connection,
1462}
1463
1464
1469#ifdef HAVE_MESSAGES
1470#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, emsg)
1471#else
1472#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, NULL)
1473#endif
1474
1475
1488static enum MHD_Result
1490{
1491 ssize_t ret;
1492 struct MHD_Response *response;
1493
1494 response = connection->rp.response;
1495 mhd_assert (connection->rp.props.send_reply_body);
1496
1497 if ( (0 == response->total_size) ||
1498 /* TODO: replace the next check with assert */
1499 (connection->rp.rsp_write_position == response->total_size) )
1500 return MHD_YES; /* 0-byte response is always ready */
1501 if (NULL != response->data_iov)
1502 {
1503 size_t copy_size;
1504
1505 if (NULL != connection->rp.resp_iov.iov)
1506 return MHD_YES;
1507 copy_size = response->data_iovcnt * sizeof(MHD_iovec_);
1508 connection->rp.resp_iov.iov = MHD_connection_alloc_memory_ (connection,
1509 copy_size);
1510 if (NULL == connection->rp.resp_iov.iov)
1511 {
1512 MHD_mutex_unlock_chk_ (&response->mutex);
1513 /* not enough memory */
1514 CONNECTION_CLOSE_ERROR (connection,
1515 _ ("Closing connection (out of memory)."));
1516 return MHD_NO;
1517 }
1518 memcpy (connection->rp.resp_iov.iov,
1519 response->data_iov,
1520 copy_size);
1521 connection->rp.resp_iov.cnt = response->data_iovcnt;
1522 connection->rp.resp_iov.sent = 0;
1523 return MHD_YES;
1524 }
1525 if (NULL == response->crc)
1526 return MHD_YES;
1527 if ( (response->data_start <=
1528 connection->rp.rsp_write_position) &&
1529 (response->data_size + response->data_start >
1530 connection->rp.rsp_write_position) )
1531 return MHD_YES; /* response already ready */
1532#if defined(_MHD_HAVE_SENDFILE)
1533 if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
1534 {
1535 /* will use sendfile, no need to bother response crc */
1536 return MHD_YES;
1537 }
1538#endif /* _MHD_HAVE_SENDFILE */
1539
1540 ret = response->crc (response->crc_cls,
1541 connection->rp.rsp_write_position,
1542 (char *) response->data,
1543 (size_t) MHD_MIN ((uint64_t) response->data_buffer_size,
1544 response->total_size
1545 - connection->rp.rsp_write_position));
1546 if (0 > ret)
1547 {
1548 /* either error or http 1.0 transfer, close socket! */
1549 /* TODO: do not update total size, check whether response
1550 * was really with unknown size */
1551 response->total_size = connection->rp.rsp_write_position;
1552#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1553 MHD_mutex_unlock_chk_ (&response->mutex);
1554#endif
1556 MHD_connection_close_ (connection,
1558 else
1559 CONNECTION_CLOSE_ERROR (connection,
1560 _ ("Closing connection (application reported " \
1561 "error generating data)."));
1562 return MHD_NO;
1563 }
1564 response->data_start = connection->rp.rsp_write_position;
1565 response->data_size = (size_t) ret;
1566 if (0 == ret)
1567 {
1569#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1570 MHD_mutex_unlock_chk_ (&response->mutex);
1571#endif
1572 return MHD_NO;
1573 }
1574 return MHD_YES;
1575}
1576
1577
1590static enum MHD_Result
1592 bool *p_finished)
1593{
1594 ssize_t ret;
1595 struct MHD_Response *response;
1596 static const size_t max_chunk = 0xFFFFFF;
1597 char chunk_hdr[7]; /* 6: max strlen of "FFFFFF" */
1598 /* "FFFFFF" + "\r\n" */
1599 static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2;
1600 /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */
1601 static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2;
1602 size_t chunk_hdr_len;
1603 uint64_t left_to_send;
1604 size_t size_to_fill;
1605
1606 response = connection->rp.response;
1607 mhd_assert (NULL != response->crc || NULL != response->data);
1608
1609 mhd_assert (0 == connection->write_buffer_append_offset);
1610
1611 /* The buffer must be reasonably large enough */
1612 if (128 > connection->write_buffer_size)
1613 {
1614 size_t size;
1615
1616 size = connection->write_buffer_size + MHD_pool_get_free (connection->pool);
1617 if (128 > size)
1618 {
1619#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1620 MHD_mutex_unlock_chk_ (&response->mutex);
1621#endif
1622 /* not enough memory */
1623 CONNECTION_CLOSE_ERROR (connection,
1624 _ ("Closing connection (out of memory)."));
1625 return MHD_NO;
1626 }
1627 /* Limit the buffer size to the largest usable size for chunks */
1628 if ( (max_chunk + max_chunk_overhead) < size)
1629 size = max_chunk + max_chunk_overhead;
1630 mhd_assert ((NULL == connection->write_buffer) || \
1631 MHD_pool_is_resizable_inplace (connection->pool, \
1632 connection->write_buffer, \
1633 connection->write_buffer_size));
1634 connection->write_buffer =
1635 MHD_pool_reallocate (connection->pool,
1636 connection->write_buffer,
1637 connection->write_buffer_size,
1638 size);
1639 mhd_assert (NULL != connection->write_buffer);
1640 connection->write_buffer_size = size;
1641 }
1642 mhd_assert (max_chunk_overhead < connection->write_buffer_size);
1643
1644 if (MHD_SIZE_UNKNOWN == response->total_size)
1645 left_to_send = MHD_SIZE_UNKNOWN;
1646 else
1647 left_to_send = response->total_size
1648 - connection->rp.rsp_write_position;
1649
1650 size_to_fill = connection->write_buffer_size - max_chunk_overhead;
1651 /* Limit size for the callback to the max usable size */
1652 if (max_chunk < size_to_fill)
1653 size_to_fill = max_chunk;
1654 if (left_to_send < size_to_fill)
1655 size_to_fill = (size_t) left_to_send;
1656
1657 if (0 == left_to_send)
1658 /* nothing to send, don't bother calling crc */
1660 else if ( (response->data_start <=
1661 connection->rp.rsp_write_position) &&
1662 (response->data_start + response->data_size >
1663 connection->rp.rsp_write_position) )
1664 {
1665 /* difference between rsp_write_position and data_start is less
1666 than data_size which is size_t type, no need to check for overflow */
1667 const size_t data_write_offset
1668 = (size_t) (connection->rp.rsp_write_position
1669 - response->data_start);
1670 /* buffer already ready, use what is there for the chunk */
1671 mhd_assert (SSIZE_MAX >= (response->data_size - data_write_offset));
1672 mhd_assert (response->data_size >= data_write_offset);
1673 ret = (ssize_t) (response->data_size - data_write_offset);
1674 if ( ((size_t) ret) > size_to_fill)
1675 ret = (ssize_t) size_to_fill;
1676 memcpy (&connection->write_buffer[max_chunk_hdr_len],
1677 &response->data[data_write_offset],
1678 (size_t) ret);
1679 }
1680 else
1681 {
1682 if (NULL == response->crc)
1683 { /* There is no way to reach this code */
1684#if defined(MHD_USE_THREADS)
1685 MHD_mutex_unlock_chk_ (&response->mutex);
1686#endif
1687 CONNECTION_CLOSE_ERROR (connection,
1688 _ ("No callback for the chunked data."));
1689 return MHD_NO;
1690 }
1691 ret = response->crc (response->crc_cls,
1692 connection->rp.rsp_write_position,
1693 &connection->write_buffer[max_chunk_hdr_len],
1694 size_to_fill);
1695 }
1697 {
1698 /* error, close socket! */
1699 /* TODO: remove update of the response size */
1700 response->total_size = connection->rp.rsp_write_position;
1701#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1702 MHD_mutex_unlock_chk_ (&response->mutex);
1703#endif
1704 CONNECTION_CLOSE_ERROR (connection,
1705 _ ("Closing connection (application error " \
1706 "generating response)."));
1707 return MHD_NO;
1708 }
1710 {
1711 *p_finished = true;
1712 /* TODO: remove update of the response size */
1713 response->total_size = connection->rp.rsp_write_position;
1714 return MHD_YES;
1715 }
1716 if (0 == ret)
1717 {
1719#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1720 MHD_mutex_unlock_chk_ (&response->mutex);
1721#endif
1722 return MHD_NO;
1723 }
1724 if (size_to_fill < (size_t) ret)
1725 {
1726#if defined(MHD_USE_THREADS)
1727 MHD_mutex_unlock_chk_ (&response->mutex);
1728#endif
1729 CONNECTION_CLOSE_ERROR (connection,
1730 _ ("Closing connection (application returned " \
1731 "more data than requested)."));
1732 return MHD_NO;
1733 }
1734 chunk_hdr_len = MHD_uint32_to_strx ((uint32_t) ret,
1735 chunk_hdr,
1736 sizeof(chunk_hdr));
1737 mhd_assert (chunk_hdr_len != 0);
1738 mhd_assert (chunk_hdr_len < sizeof(chunk_hdr));
1739 *p_finished = false;
1740 connection->write_buffer_send_offset =
1741 (max_chunk_hdr_len - (chunk_hdr_len + 2));
1742 memcpy (connection->write_buffer + connection->write_buffer_send_offset,
1743 chunk_hdr,
1744 chunk_hdr_len);
1745 connection->write_buffer[max_chunk_hdr_len - 2] = '\r';
1746 connection->write_buffer[max_chunk_hdr_len - 1] = '\n';
1747 connection->write_buffer[max_chunk_hdr_len + (size_t) ret] = '\r';
1748 connection->write_buffer[max_chunk_hdr_len + (size_t) ret + 1] = '\n';
1749 connection->rp.rsp_write_position += (size_t) ret;
1750 connection->write_buffer_append_offset = max_chunk_hdr_len + (size_t) ret + 2;
1751 return MHD_YES;
1752}
1753
1754
1777static enum MHD_ConnKeepAlive
1779{
1780 struct MHD_Connection *const c = connection;
1781 struct MHD_Response *const r = c->rp.response;
1782
1783 mhd_assert (NULL != r);
1785 return MHD_CONN_MUST_CLOSE;
1786
1787#ifdef UPGRADE_SUPPORT
1788 /* TODO: Move below the next check when MHD stops closing connections
1789 * when response is queued in first callback */
1790 if (NULL != r->upgrade_handler)
1791 {
1792 /* No "close" token is enforced by 'add_response_header_connection()' */
1794 /* Valid HTTP version is enforced by 'MHD_queue_response()' */
1797 return MHD_CONN_MUST_UPGRADE;
1798 }
1799#endif /* UPGRADE_SUPPORT */
1800
1801 mhd_assert ( (! c->stop_with_error) || (c->discard_request));
1802 if ((c->read_closed) || (c->discard_request))
1803 return MHD_CONN_MUST_CLOSE;
1804
1806 return MHD_CONN_MUST_CLOSE;
1808 return MHD_CONN_MUST_CLOSE;
1809
1811 return MHD_CONN_MUST_CLOSE;
1812
1815 "close"))
1816 return MHD_CONN_MUST_CLOSE;
1817
1818 if ((MHD_HTTP_VER_1_0 == connection->rq.http_ver) ||
1819 (0 != (connection->rp.response->flags & MHD_RF_HTTP_1_0_SERVER)))
1820 {
1821 if (MHD_lookup_header_s_token_ci (connection,
1823 "Keep-Alive"))
1825
1826 return MHD_CONN_MUST_CLOSE;
1827 }
1828
1831
1832 return MHD_CONN_MUST_CLOSE;
1833}
1834
1835
1845static bool
1846get_date_str (char *date)
1847{
1848 static const char *const days[] = {
1849 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1850 };
1851 static const char *const mons[] = {
1852 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1853 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1854 };
1855 static const size_t buf_len = 29;
1856 struct tm now;
1857 time_t t;
1858 const char *src;
1859#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
1860 ! defined(HAVE_GMTIME_R)
1861 struct tm *pNow;
1862#endif
1863
1864 if ((time_t) -1 == time (&t))
1865 return false;
1866#if defined(HAVE_C11_GMTIME_S)
1867 if (NULL == gmtime_s (&t,
1868 &now))
1869 return false;
1870#elif defined(HAVE_W32_GMTIME_S)
1871 if (0 != gmtime_s (&now,
1872 &t))
1873 return false;
1874#elif defined(HAVE_GMTIME_R)
1875 if (NULL == gmtime_r (&t,
1876 &now))
1877 return false;
1878#else
1879 pNow = gmtime (&t);
1880 if (NULL == pNow)
1881 return false;
1882 now = *pNow;
1883#endif
1884
1885 /* Day of the week */
1886 src = days[now.tm_wday % 7];
1887 date[0] = src[0];
1888 date[1] = src[1];
1889 date[2] = src[2];
1890 date[3] = ',';
1891 date[4] = ' ';
1892 /* Day of the month */
1893 if (2 != MHD_uint8_to_str_pad ((uint8_t) now.tm_mday, 2,
1894 date + 5, buf_len - 5))
1895 return false;
1896 date[7] = ' ';
1897 /* Month */
1898 src = mons[now.tm_mon % 12];
1899 date[8] = src[0];
1900 date[9] = src[1];
1901 date[10] = src[2];
1902 date[11] = ' ';
1903 /* Year */
1904 if (4 != MHD_uint16_to_str ((uint16_t) (1900 + now.tm_year), date + 12,
1905 buf_len - 12))
1906 return false;
1907 date[16] = ' ';
1908 /* Time */
1909 MHD_uint8_to_str_pad ((uint8_t) now.tm_hour, 2, date + 17, buf_len - 17);
1910 date[19] = ':';
1911 MHD_uint8_to_str_pad ((uint8_t) now.tm_min, 2, date + 20, buf_len - 20);
1912 date[22] = ':';
1913 MHD_uint8_to_str_pad ((uint8_t) now.tm_sec, 2, date + 23, buf_len - 23);
1914 date[25] = ' ';
1915 date[26] = 'G';
1916 date[27] = 'M';
1917 date[28] = 'T';
1918
1919 return true;
1920}
1921
1922
1930static bool
1931get_date_header (char *header)
1932{
1933 if (! get_date_str (header + 6))
1934 {
1935 header[0] = 0;
1936 return false;
1937 }
1938 header[0] = 'D';
1939 header[1] = 'a';
1940 header[2] = 't';
1941 header[3] = 'e';
1942 header[4] = ':';
1943 header[5] = ' ';
1944 header[35] = '\r';
1945 header[36] = '\n';
1946 header[37] = 0;
1947 return true;
1948}
1949
1950
1963static bool
1965 bool required)
1966{
1967 size_t new_size;
1968 size_t avail_size;
1969 const size_t def_grow_size = connection->daemon->pool_increment;
1970 void *rb;
1971
1972 avail_size = MHD_pool_get_free (connection->pool);
1973 if (0 == avail_size)
1974 return false; /* No more space available */
1975 if (0 == connection->read_buffer_size)
1976 new_size = avail_size / 2; /* Use half of available buffer for reading */
1977 else
1978 {
1979 size_t grow_size;
1980
1981 grow_size = avail_size / 8;
1982 if (def_grow_size > grow_size)
1983 { /* Shortage of space */
1984 const size_t left_free =
1985 connection->read_buffer_size - connection->read_buffer_offset;
1986 mhd_assert (connection->read_buffer_size >= \
1987 connection->read_buffer_offset);
1988 if ((def_grow_size <= grow_size + left_free)
1989 && (left_free < def_grow_size))
1990 grow_size = def_grow_size - left_free; /* Use precise 'def_grow_size' for new free space */
1991 else if (! required)
1992 return false; /* Grow is not mandatory, leave some space in pool */
1993 else
1994 {
1995 /* Shortage of space, but grow is mandatory */
1996 const size_t small_inc =
1997 ((MHD_BUF_INC_SIZE > def_grow_size) ?
1998 def_grow_size : MHD_BUF_INC_SIZE) / 8;
1999 if (small_inc < avail_size)
2000 grow_size = small_inc;
2001 else
2002 grow_size = avail_size;
2003 }
2004 }
2005 new_size = connection->read_buffer_size + grow_size;
2006 }
2007 /* Make sure that read buffer will not be moved */
2008 if ((NULL != connection->read_buffer) &&
2009 ! MHD_pool_is_resizable_inplace (connection->pool,
2010 connection->read_buffer,
2011 connection->read_buffer_size))
2012 {
2013 mhd_assert (0);
2014 return false;
2015 }
2016 /* we can actually grow the buffer, do it! */
2017 rb = MHD_pool_reallocate (connection->pool,
2018 connection->read_buffer,
2019 connection->read_buffer_size,
2020 new_size);
2021 if (NULL == rb)
2022 {
2023 /* This should NOT be possible: we just computed 'new_size' so that
2024 it should fit. If it happens, somehow our read buffer is not in
2025 the right position in the pool, say because someone called
2026 MHD_pool_allocate() without 'from_end' set to 'true'? Anyway,
2027 should be investigated! (Ideally provide all data from
2028 *pool and connection->read_buffer and new_size for debugging). */
2029 mhd_assert (0);
2030 return false;
2031 }
2032 mhd_assert (connection->read_buffer == rb);
2033 connection->read_buffer = rb;
2034 mhd_assert (NULL != connection->read_buffer);
2035 connection->read_buffer_size = new_size;
2036 return true;
2037}
2038
2039
2044static void
2046{
2047 struct MHD_Connection *const c = connection;
2048 void *new_buf;
2049
2050 if ((NULL == c->read_buffer) || (0 == c->read_buffer_size))
2051 {
2052 mhd_assert (0 == c->read_buffer_size);
2054 return;
2055 }
2056
2058 if (0 == c->read_buffer_offset)
2059 {
2061 c->read_buffer = NULL;
2062 c->read_buffer_size = 0;
2063 }
2064 else
2065 {
2067 c->read_buffer_size));
2070 mhd_assert (c->read_buffer == new_buf);
2071 c->read_buffer = new_buf;
2073 }
2074}
2075
2076
2083static size_t
2085{
2086 struct MHD_Connection *const c = connection;
2087 struct MemoryPool *const pool = connection->pool;
2088 void *new_buf;
2089 size_t new_size;
2090 size_t free_size;
2091
2092 mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
2095
2096 free_size = MHD_pool_get_free (pool);
2097 if (0 != free_size)
2098 {
2099 new_size = c->write_buffer_size + free_size;
2100 /* This function must not move the buffer position.
2101 * MHD_pool_reallocate () may return the new position only if buffer was
2102 * allocated 'from_end' or is not the last allocation,
2103 * which should not happen. */
2104 mhd_assert ((NULL == c->write_buffer) || \
2106 c->write_buffer_size));
2107 new_buf = MHD_pool_reallocate (pool,
2108 c->write_buffer,
2110 new_size);
2111 mhd_assert ((c->write_buffer == new_buf) || (NULL == c->write_buffer));
2112 c->write_buffer = new_buf;
2113 c->write_buffer_size = new_size;
2115 {
2116 /* All data have been sent, reset offsets to zero. */
2119 }
2120 }
2121
2123}
2124
2125
2126#if 0 /* disable unused function */
2135static void
2136connection_shrink_write_buffer (struct MHD_Connection *connection)
2137{
2138 struct MHD_Connection *const c = connection;
2139 struct MemoryPool *const pool = connection->pool;
2140 void *new_buf;
2141
2142 mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
2145
2146 if ( (NULL == c->write_buffer) || (0 == c->write_buffer_size))
2147 {
2150 c->write_buffer = NULL;
2151 return;
2152 }
2154 return;
2155
2156 new_buf = MHD_pool_reallocate (pool, c->write_buffer, c->write_buffer_size,
2158 mhd_assert ((c->write_buffer == new_buf) || \
2159 (0 == c->write_buffer_append_offset));
2161 if (0 == c->write_buffer_size)
2162 c->write_buffer = NULL;
2163 else
2164 c->write_buffer = new_buf;
2165}
2166
2167
2168#endif /* unused function */
2169
2170
2178static void
2180{
2181 /* Read buffer is not needed for this request, shrink it.*/
2182 connection_shrink_read_buffer (connection);
2183}
2184
2185
2213
2214
2226static enum replyBodyUse
2228 unsigned int rcode)
2229{
2230 struct MHD_Connection *const c = connection;
2231
2232 mhd_assert (100 <= rcode);
2233 mhd_assert (999 >= rcode);
2234
2235 if (199 >= rcode)
2236 return RP_BODY_NONE;
2237
2238 if (MHD_HTTP_NO_CONTENT == rcode)
2239 return RP_BODY_NONE;
2240
2241#if 0
2242 /* This check is not needed as upgrade handler is used only with code 101 */
2243#ifdef UPGRADE_SUPPORT
2244 if (NULL != rp.response->upgrade_handler)
2245 return RP_BODY_NONE;
2246#endif /* UPGRADE_SUPPORT */
2247#endif
2248
2249#if 0
2250 /* CONNECT is not supported by MHD */
2251 /* Successful responses for connect requests are filtered by
2252 * MHD_queue_response() */
2253 if ( (MHD_HTTP_MTHD_CONNECT == c->rq.http_mthd) &&
2254 (2 == rcode / 100) )
2255 return false; /* Actually pass-through CONNECT is not supported by MHD */
2256#endif
2257
2258 /* Reply body headers could be used.
2259 * Check whether reply body itself must be used. */
2260
2262 return RP_BODY_HEADERS_ONLY;
2263
2264 if (MHD_HTTP_NOT_MODIFIED == rcode)
2265 return RP_BODY_HEADERS_ONLY;
2266
2267 /* Reply body must be sent. The body may have zero length, but body size
2268 * must be indicated by headers ('Content-Length:' or
2269 * 'Transfer-Encoding: chunked'). */
2270 return RP_BODY_SEND;
2271}
2272
2273
2282static void
2284{
2285 struct MHD_Connection *const c = connection;
2286 struct MHD_Response *const r = c->rp.response;
2287 enum replyBodyUse use_rp_body;
2288 bool use_chunked;
2289
2290 mhd_assert (NULL != r);
2291
2292 /* ** Adjust reply properties ** */
2293
2295 use_rp_body = is_reply_body_needed (c, c->rp.responseCode);
2296 c->rp.props.send_reply_body = (use_rp_body > RP_BODY_HEADERS_ONLY);
2298
2299#ifdef UPGRADE_SUPPORT
2300 mhd_assert ( (NULL == r->upgrade_handler) ||
2301 (RP_BODY_NONE == use_rp_body) );
2302#endif /* UPGRADE_SUPPORT */
2303
2305 {
2306 if ((MHD_SIZE_UNKNOWN == r->total_size) ||
2308 { /* Use chunked reply encoding if possible */
2309
2310 /* Check whether chunked encoding is supported by the client */
2312 use_chunked = false;
2313 /* Check whether chunked encoding is allowed for the reply */
2314 else if (0 != (r->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
2316 use_chunked = false;
2317 else
2318 /* If chunked encoding is supported and allowed, and response size
2319 * is unknown, use chunked even for non-Keep-Alive connections.
2320 * See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3
2321 * Also use chunked if it is enforced by application and supported by
2322 * the client. */
2323 use_chunked = true;
2324 }
2325 else
2326 use_chunked = false;
2327
2328 if ( (MHD_SIZE_UNKNOWN == r->total_size) &&
2329 (! use_chunked) )
2330 {
2331 /* End of the stream is indicated by closure */
2333 }
2334 }
2335 else
2336 use_chunked = false; /* chunked encoding cannot be used without body */
2337
2338 c->rp.props.chunked = use_chunked;
2339#ifdef _DEBUG
2340 c->rp.props.set = true;
2341#endif /* _DEBUG */
2342}
2343
2344
2349static void
2351{
2352 struct MHD_Connection *const c = connection;
2353 struct MHD_Response *const r = c->rp.response;
2354
2355 mhd_assert (c->rp.props.set);
2356#ifdef HAVE_MESSAGES
2357 if ( (! c->rp.props.use_reply_body_headers) &&
2358 (0 != r->total_size) )
2359 {
2360 MHD_DLOG (c->daemon,
2361 _ ("This reply with response code %u cannot use reply body. "
2362 "Non-empty response body is ignored and not used.\n"),
2363 (unsigned) (c->rp.responseCode));
2364 }
2365 if ( (! c->rp.props.use_reply_body_headers) &&
2367 {
2368 MHD_DLOG (c->daemon,
2369 _ ("This reply with response code %u cannot use reply body. "
2370 "Application defined \"Content-Length\" header violates"
2371 "HTTP specification.\n"),
2372 (unsigned) (c->rp.responseCode));
2373 }
2374#else
2375 (void) c; /* Mute compiler warning */
2376 (void) r; /* Mute compiler warning */
2377#endif
2378}
2379
2380
2392static bool
2393buffer_append (char *buf,
2394 size_t *ppos,
2395 size_t buf_size,
2396 const char *append,
2397 size_t append_size)
2398{
2399 mhd_assert (NULL != buf); /* Mute static analyzer */
2400 if (buf_size < *ppos + append_size)
2401 return false;
2402 memcpy (buf + *ppos, append, append_size);
2403 *ppos += append_size;
2404 return true;
2405}
2406
2407
2418#define buffer_append_s(buf,ppos,buf_size,str) \
2419 buffer_append (buf,ppos,buf_size,str, MHD_STATICSTR_LEN_ (str))
2420
2421
2441static bool
2443 size_t *ppos,
2444 size_t buf_size,
2445 struct MHD_Response *response,
2446 bool filter_transf_enc,
2447 bool filter_content_len,
2448 bool add_close,
2449 bool add_keep_alive)
2450{
2451 struct MHD_Response *const r = response;
2452 struct MHD_HTTP_Res_Header *hdr;
2453 size_t el_size;
2454
2455 mhd_assert (! add_close || ! add_keep_alive);
2456
2458 filter_transf_enc = false; /* No such header */
2459 if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2460 filter_content_len = false; /* No such header */
2461 if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2462 {
2463 add_close = false; /* No such header */
2464 add_keep_alive = false; /* No such header */
2465 }
2466 else if (0 != (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE))
2467 add_close = false; /* "close" token was already set */
2468
2469 for (hdr = r->first_header; NULL != hdr; hdr = hdr->next)
2470 {
2471 size_t initial_pos = *ppos;
2472 if (MHD_HEADER_KIND != hdr->kind)
2473 continue;
2474 if (filter_transf_enc)
2475 { /* Need to filter-out "Transfer-Encoding" */
2477 hdr->header_size) &&
2479 hdr->header, hdr->header_size)) )
2480 {
2481 filter_transf_enc = false; /* There is the only one such header */
2482 continue; /* Skip "Transfer-Encoding" header */
2483 }
2484 }
2485 if (filter_content_len)
2486 { /* Need to filter-out "Content-Length" */
2488 hdr->header_size) &&
2490 hdr->header, hdr->header_size)) )
2491 {
2492 /* Reset filter flag if only one header is allowed */
2493 filter_transf_enc =
2495 continue; /* Skip "Content-Length" header */
2496 }
2497 }
2498
2499 /* Add user header */
2500 /* Check available space using subtractions to avoid integer overflow.
2501 '4' is two colons/space plus the final CRLF. */
2502 if ( (buf_size - *ppos < 4) ||
2503 (buf_size - *ppos - 4 < hdr->header_size) ||
2504 (buf_size - *ppos - 4 - hdr->header_size < hdr->value_size) )
2505 return false;
2506 el_size = hdr->header_size + 2 + hdr->value_size + 2;
2507 memcpy (buf + *ppos, hdr->header, hdr->header_size);
2508 (*ppos) += hdr->header_size;
2509 buf[(*ppos)++] = ':';
2510 buf[(*ppos)++] = ' ';
2511 if (add_close || add_keep_alive)
2512 {
2513 /* "Connection:" header must be always the first one */
2516 hdr->header_size));
2517
2518 if (add_close)
2519 {
2520 el_size += MHD_STATICSTR_LEN_ ("close, ");
2521 if (buf_size < initial_pos + el_size)
2522 return false;
2523 memcpy (buf + *ppos, "close, ",
2524 MHD_STATICSTR_LEN_ ("close, "));
2525 *ppos += MHD_STATICSTR_LEN_ ("close, ");
2526 }
2527 else
2528 {
2529 el_size += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2530 if (buf_size < initial_pos + el_size)
2531 return false;
2532 memcpy (buf + *ppos, "Keep-Alive, ",
2533 MHD_STATICSTR_LEN_ ("Keep-Alive, "));
2534 *ppos += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2535 }
2536 add_close = false;
2537 add_keep_alive = false;
2538 }
2539 if (0 != hdr->value_size)
2540 memcpy (buf + *ppos, hdr->value, hdr->value_size);
2541 *ppos += hdr->value_size;
2542 buf[(*ppos)++] = '\r';
2543 buf[(*ppos)++] = '\n';
2544 mhd_assert (initial_pos + el_size == (*ppos));
2545 }
2546 return true;
2547}
2548
2549
2558static enum MHD_Result
2560{
2561 struct MHD_Connection *const c = connection;
2562 struct MHD_Response *const r = c->rp.response;
2563 char *buf;
2564 size_t pos;
2565 size_t buf_size;
2566 size_t el_size;
2567 unsigned rcode;
2568 bool use_conn_close;
2569 bool use_conn_k_alive;
2570
2571 mhd_assert (NULL != r);
2572
2573 /* ** Adjust response properties ** */
2575
2576 mhd_assert (c->rp.props.set);
2580#ifdef UPGRADE_SUPPORT
2581 mhd_assert ((NULL == r->upgrade_handler) || \
2583#else /* ! UPGRADE_SUPPORT */
2585#endif /* ! UPGRADE_SUPPORT */
2587 mhd_assert ((! c->rp.props.send_reply_body) || \
2589#ifdef UPGRADE_SUPPORT
2590 mhd_assert (NULL == r->upgrade_handler || \
2592#endif /* UPGRADE_SUPPORT */
2593
2595
2596 rcode = (unsigned) c->rp.responseCode;
2598 {
2599 /* The closure of connection must be always indicated by header
2600 * to avoid hung connections */
2601 use_conn_close = true;
2602 use_conn_k_alive = false;
2603 }
2604 else if (MHD_CONN_USE_KEEPALIVE == c->keepalive)
2605 {
2606 use_conn_close = false;
2607 /* Add "Connection: keep-alive" if request is HTTP/1.0 or
2608 * if reply is HTTP/1.0
2609 * For HTTP/1.1 add header only if explicitly requested by app
2610 * (by response flag), as "Keep-Alive" is default for HTTP/1.1. */
2611 if ((0 != (r->flags & MHD_RF_SEND_KEEP_ALIVE_HEADER)) ||
2612 (MHD_HTTP_VER_1_0 == c->rq.http_ver) ||
2613 (0 != (r->flags & MHD_RF_HTTP_1_0_SERVER)))
2614 use_conn_k_alive = true;
2615 else
2616 use_conn_k_alive = false;
2617 }
2618 else
2619 {
2620 use_conn_close = false;
2621 use_conn_k_alive = false;
2622 }
2623
2624 /* ** Actually build the response header ** */
2625
2626 /* Get all space available */
2628 buf = c->write_buffer;
2630 buf_size = c->write_buffer_size;
2631 if (0 == buf_size)
2632 return MHD_NO;
2633 mhd_assert (NULL != buf);
2634
2635 /* * The status line * */
2636
2637 /* The HTTP version */
2638 if (! c->rp.responseIcy)
2639 { /* HTTP reply */
2640 if (0 == (r->flags & MHD_RF_HTTP_1_0_SERVER))
2641 { /* HTTP/1.1 reply */
2642 /* Use HTTP/1.1 responses for HTTP/1.0 clients.
2643 * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
2644 if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_1))
2645 return MHD_NO;
2646 }
2647 else
2648 { /* HTTP/1.0 reply */
2649 if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_0))
2650 return MHD_NO;
2651 }
2652 }
2653 else
2654 { /* ICY reply */
2655 if (! buffer_append_s (buf, &pos, buf_size, "ICY"))
2656 return MHD_NO;
2657 }
2658
2659 /* The response code */
2660 if (buf_size < pos + 5) /* space + code + space */
2661 return MHD_NO;
2662 buf[pos++] = ' ';
2663 pos += MHD_uint16_to_str ((uint16_t) rcode, buf + pos,
2664 buf_size - pos);
2665 buf[pos++] = ' ';
2666
2667 /* The reason phrase */
2668 el_size = MHD_get_reason_phrase_len_for (rcode);
2669 if (0 == el_size)
2670 {
2671 if (! buffer_append_s (buf, &pos, buf_size, "Non-Standard Status"))
2672 return MHD_NO;
2673 }
2674 else if (! buffer_append (buf, &pos, buf_size,
2676 el_size))
2677 return MHD_NO;
2678
2679 /* The linefeed */
2680 if (buf_size < pos + 2)
2681 return MHD_NO;
2682 buf[pos++] = '\r';
2683 buf[pos++] = '\n';
2684
2685 /* * The headers * */
2686
2687 /* Main automatic headers */
2688
2689 /* The "Date:" header */
2690 if ( (0 == (r->flags_auto & MHD_RAF_HAS_DATE_HDR)) &&
2692 {
2693 /* Additional byte for unused zero-termination */
2694 if (buf_size < pos + 38)
2695 return MHD_NO;
2696 if (get_date_header (buf + pos))
2697 pos += 37;
2698 }
2699 /* The "Connection:" header */
2700 mhd_assert (! use_conn_close || ! use_conn_k_alive);
2701 mhd_assert (! use_conn_k_alive || ! use_conn_close);
2702 if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2703 {
2704 if (use_conn_close)
2705 {
2706 if (! buffer_append_s (buf, &pos, buf_size,
2707 MHD_HTTP_HEADER_CONNECTION ": close\r\n"))
2708 return MHD_NO;
2709 }
2710 else if (use_conn_k_alive)
2711 {
2712 if (! buffer_append_s (buf, &pos, buf_size,
2713 MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n"))
2714 return MHD_NO;
2715 }
2716 }
2717
2718 /* User-defined headers */
2719
2720 if (! add_user_headers (buf, &pos, buf_size, r,
2721 ! c->rp.props.chunked,
2723 (0 ==
2725 use_conn_close,
2726 use_conn_k_alive))
2727 return MHD_NO;
2728
2729 /* Other automatic headers */
2730
2731 if ( (c->rp.props.use_reply_body_headers) &&
2732 (0 == (r->flags & MHD_RF_HEAD_ONLY_RESPONSE)) )
2733 {
2734 /* Body-specific headers */
2735
2736 if (c->rp.props.chunked)
2737 { /* Chunked encoding is used */
2739 { /* No chunked encoding header set by user */
2740 if (! buffer_append_s (buf, &pos, buf_size,
2742 "chunked\r\n"))
2743 return MHD_NO;
2744 }
2745 }
2746 else /* Chunked encoding is not used */
2747 {
2748 if (MHD_SIZE_UNKNOWN != r->total_size)
2749 { /* The size is known */
2750 if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2751 { /* The response does not have "Content-Length" header */
2752 if (! buffer_append_s (buf, &pos, buf_size,
2754 return MHD_NO;
2755 el_size = MHD_uint64_to_str (r->total_size, buf + pos,
2756 buf_size - pos);
2757 if (0 == el_size)
2758 return MHD_NO;
2759 pos += el_size;
2760
2761 if (buf_size < pos + 2)
2762 return MHD_NO;
2763 buf[pos++] = '\r';
2764 buf[pos++] = '\n';
2765 }
2766 }
2767 }
2768 }
2769
2770 /* * Header termination * */
2771 if (buf_size < pos + 2)
2772 return MHD_NO;
2773 buf[pos++] = '\r';
2774 buf[pos++] = '\n';
2775
2777 return MHD_YES;
2778}
2779
2780
2790static enum MHD_Result
2792{
2793 char *buf;
2794 size_t buf_size;
2795 size_t used_size;
2796 struct MHD_Connection *const c = connection;
2797 struct MHD_HTTP_Res_Header *pos;
2798
2799 mhd_assert (connection->rp.props.chunked);
2800 /* TODO: allow combining of the final footer with the last chunk,
2801 * modify the next assert. */
2803 mhd_assert (NULL != c->rp.response);
2804
2805 buf_size = connection_maximize_write_buffer (c);
2806 /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */
2807 if (buf_size < 5)
2808 return MHD_NO;
2811 mhd_assert (NULL != buf);
2812 used_size = 0;
2813 buf[used_size++] = '0';
2814 buf[used_size++] = '\r';
2815 buf[used_size++] = '\n';
2816
2817 for (pos = c->rp.response->first_header; NULL != pos; pos = pos->next)
2818 {
2819 if (MHD_FOOTER_KIND == pos->kind)
2820 {
2821 size_t new_used_size; /* resulting size with this header */
2822 /* '4' is colon, space, linefeeds */
2823 /* Check available space using subtractions to avoid integer overflow. */
2824 if ( (buf_size - used_size < 4) ||
2825 (buf_size - used_size - 4 < pos->header_size) ||
2826 (buf_size - used_size - 4 - pos->header_size < pos->value_size) )
2827 return MHD_NO;
2828 new_used_size = used_size + pos->header_size + pos->value_size + 4;
2829 memcpy (buf + used_size, pos->header, pos->header_size);
2830 used_size += pos->header_size;
2831 buf[used_size++] = ':';
2832 buf[used_size++] = ' ';
2833 memcpy (buf + used_size, pos->value, pos->value_size);
2834 used_size += pos->value_size;
2835 buf[used_size++] = '\r';
2836 buf[used_size++] = '\n';
2837 mhd_assert (used_size == new_used_size);
2838 }
2839 }
2840 if (used_size + 2 > buf_size)
2841 return MHD_NO;
2842 buf[used_size++] = '\r';
2843 buf[used_size++] = '\n';
2844
2845 c->write_buffer_append_offset += used_size;
2847
2848 return MHD_YES;
2849}
2850
2851
2868static void
2870 unsigned int status_code,
2871 const char *message,
2872 size_t message_len,
2873 char *header_name,
2874 size_t header_name_len,
2875 char *header_value,
2876 size_t header_value_len)
2877{
2878 struct MHD_Response *response;
2879 enum MHD_Result iret;
2880
2881 mhd_assert (! connection->stop_with_error); /* Do not send error twice */
2882 if (connection->stop_with_error)
2883 { /* Should not happen */
2884 if (MHD_CONNECTION_CLOSED > connection->state)
2885 connection->state = MHD_CONNECTION_CLOSED;
2886 free (header_name);
2887 free (header_value);
2888 return;
2889 }
2890 connection->stop_with_error = true;
2891 connection->discard_request = true;
2892#ifdef HAVE_MESSAGES
2893 MHD_DLOG (connection->daemon,
2894 _ ("Error processing request (HTTP response code is %u ('%s')). " \
2895 "Closing connection.\n"),
2896 status_code,
2897 message);
2898#endif
2900 {
2901#ifdef HAVE_MESSAGES
2902 MHD_DLOG (connection->daemon,
2903 _ ("Too late to send an error response, " \
2904 "response is being sent already.\n"),
2905 status_code,
2906 message);
2907#endif
2908 CONNECTION_CLOSE_ERROR (connection,
2909 _ ("Too late for error response."));
2910 free (header_name);
2911 free (header_value);
2912 return;
2913 }
2914 /* TODO: remove when special error queue function is implemented */
2916 if (0 != connection->read_buffer_size)
2917 {
2918 /* Read buffer is not needed anymore, discard it
2919 * to free some space for error response. */
2920 MHD_pool_deallocate (connection->pool,
2921 connection->read_buffer,
2922 connection->read_buffer_size);
2923 connection->read_buffer = NULL;
2924 connection->read_buffer_size = 0;
2925 connection->read_buffer_offset = 0;
2926 }
2927 if (NULL != connection->rp.response)
2928 {
2929 MHD_destroy_response (connection->rp.response);
2930 connection->rp.response = NULL;
2931 }
2932 response = MHD_create_response_from_buffer_static (message_len,
2933 message);
2934 if (NULL == response)
2935 {
2936#ifdef HAVE_MESSAGES
2937 MHD_DLOG (connection->daemon,
2938 _ ("Failed to create error response.\n"),
2939 status_code,
2940 message);
2941#endif
2942 /* can't even send a reply, at least close the connection */
2943 connection->state = MHD_CONNECTION_CLOSED;
2944 free (header_name);
2945 free (header_value);
2946 return;
2947 }
2948 mhd_assert ((0 == header_name_len) || (NULL != header_name));
2949 mhd_assert ((NULL == header_name) || (0 != header_name_len));
2950 mhd_assert ((0 == header_value_len) || (NULL != header_value));
2951 mhd_assert ((NULL == header_value) || (0 != header_value_len));
2952 mhd_assert ((NULL == header_name) || (NULL != header_value));
2953 mhd_assert ((NULL != header_value) || (NULL == header_name));
2954 if (NULL != header_name)
2955 {
2956 iret = MHD_add_response_entry_no_alloc_ (response,
2958 header_name, header_name_len,
2959 header_value, header_value_len);
2960 if (MHD_NO == iret)
2961 {
2962 free (header_name);
2963 free (header_value);
2964 }
2965 }
2966 else
2967 iret = MHD_YES;
2968
2969 if (MHD_NO != iret)
2970 {
2971 bool before = connection->in_access_handler;
2972
2973 /* Fake the flag for the internal call */
2974 connection->in_access_handler = true;
2975 iret = MHD_queue_response (connection,
2976 status_code,
2977 response);
2978 connection->in_access_handler = before;
2979 }
2980 MHD_destroy_response (response);
2981 if (MHD_NO == iret)
2982 {
2983 /* can't even send a reply, at least close the connection */
2984 CONNECTION_CLOSE_ERROR (connection,
2985 _ ("Closing connection " \
2986 "(failed to queue error response)."));
2987 return;
2988 }
2989 mhd_assert (NULL != connection->rp.response);
2990 /* Do not reuse this connection. */
2991 connection->keepalive = MHD_CONN_MUST_CLOSE;
2992 if (MHD_NO == build_header_response (connection))
2993 {
2994 /* No memory. Release everything. */
2995 connection->rq.version = NULL;
2996 connection->rq.method = NULL;
2997 connection->rq.url = NULL;
2998 connection->rq.url_len = 0;
2999 connection->rq.url_for_callback = NULL;
3000 connection->rq.headers_received = NULL;
3001 connection->rq.headers_received_tail = NULL;
3002 connection->write_buffer = NULL;
3003 connection->write_buffer_size = 0;
3004 connection->write_buffer_send_offset = 0;
3005 connection->write_buffer_append_offset = 0;
3006 connection->read_buffer
3007 = MHD_pool_reset (connection->pool,
3008 NULL,
3009 0,
3010 0);
3011 connection->read_buffer_size = 0;
3012
3013 /* Retry with empty buffer */
3014 if (MHD_NO == build_header_response (connection))
3015 {
3016 CONNECTION_CLOSE_ERROR (connection,
3017 _ ("Closing connection " \
3018 "(failed to create error response header)."));
3019 return;
3020 }
3021 }
3023}
3024
3025
3029#ifdef HAVE_MESSAGES
3030# define transmit_error_response_static(c, code, msg) \
3031 transmit_error_response_len (c, code, \
3032 msg, MHD_STATICSTR_LEN_ (msg), \
3033 NULL, 0, NULL, 0)
3034#else /* ! HAVE_MESSAGES */
3035# define transmit_error_response_static(c, code, msg) \
3036 transmit_error_response_len (c, code, \
3037 "", 0, \
3038 NULL, 0, NULL, 0)
3039#endif /* ! HAVE_MESSAGES */
3040
3044#ifdef HAVE_MESSAGES
3045# define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l) \
3046 transmit_error_response_len (c, code, \
3047 m, MHD_STATICSTR_LEN_ (m), \
3048 hd_n, hd_n_l, \
3049 hd_v, hd_v_l)
3050#else /* ! HAVE_MESSAGES */
3051# define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l) \
3052 transmit_error_response_len (c, code, \
3053 "", 0, \
3054 hd_n, hd_n_l, \
3055 hd_v, hd_v_l)
3056#endif /* ! HAVE_MESSAGES */
3057
3058
3069static bool
3071{
3073 if (! c->rq.have_chunked_upload)
3074 return 0 != c->read_buffer_offset;
3075
3076 /* Chunked upload */
3077 mhd_assert (0 != c->rq.remaining_upload_size); /* Must not be possible in MHD_CONNECTION_BODY_RECEIVING state */
3079 {
3080 /* 0 == c->rq.current_chunk_size: Waiting the chunk size (chunk header).
3081 0 != c->rq.current_chunk_size: Waiting for chunk-closing CRLF. */
3082 return false;
3083 }
3084 return 0 != c->read_buffer_offset; /* Chunk payload data in the read buffer */
3085}
3086
3087
3104
3105
3106#ifndef MHD_MAX_REASONABLE_HEADERS_SIZE_
3116# define MHD_MAX_REASONABLE_HEADERS_SIZE_ (6 * 1024)
3117#endif /* ! MHD_MAX_REASONABLE_HEADERS_SIZE_ */
3118
3119#ifndef MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
3130# define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 8000
3131#endif /* ! MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ */
3132
3133#ifndef MHD_MIN_REASONABLE_HEADERS_SIZE_
3141# define MHD_MIN_REASONABLE_HEADERS_SIZE_ 26
3142#endif /* ! MHD_MIN_REASONABLE_HEADERS_SIZE_ */
3143
3144#ifndef MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
3152# define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 40
3153#endif /* ! MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ */
3154
3155#ifndef MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
3163# define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 16
3164#endif /* ! MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ */
3165
3166#ifndef MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
3172# define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 4
3173#endif /* ! MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ */
3174
3175
3187static unsigned int
3189 enum MHD_ProcRecvDataStage stage,
3190 const char *add_element,
3191 size_t add_element_size)
3192{
3193 size_t method_size;
3194 size_t uri_size;
3195 size_t opt_headers_size;
3196 size_t host_field_line_size;
3197
3200 mhd_assert ((0 == add_element_size) || (NULL != add_element));
3201
3203 {
3205 opt_headers_size =
3206 (size_t) ((c->read_buffer + c->read_buffer_offset)
3207 - c->rq.field_lines.start);
3208 }
3209 else
3210 opt_headers_size = c->rq.field_lines.size;
3211
3212 /* The read buffer is fully used by the request line, the field lines
3213 (headers) and internal information.
3214 The return status code works as a suggestion for the client to reduce
3215 one of the request elements. */
3216
3217 if ((MHD_PROC_RECV_BODY_CHUNKED == stage) &&
3218 (MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ < add_element_size))
3219 {
3220 /* Request could be re-tried easily with smaller chunk sizes */
3222 }
3223
3224 host_field_line_size = 0;
3225 /* The "Host:" field line is mandatory.
3226 The total size of the field lines (headers) cannot be smaller than
3227 the size of the "Host:" field line. */
3228 if ((MHD_PROC_RECV_HEADERS == stage)
3229 && (0 != add_element_size))
3230 {
3231 static const size_t header_host_key_len =
3233 const bool is_host_header =
3234 (header_host_key_len + 1 <= add_element_size)
3235 && ( (0 == add_element[header_host_key_len])
3236 || (':' == add_element[header_host_key_len]) )
3238 add_element,
3239 header_host_key_len);
3240 if (is_host_header)
3241 {
3242 const bool is_parsed = ! (
3244 (add_element_size == c->read_buffer_offset) &&
3245 (c->read_buffer == add_element) );
3246 size_t actual_element_size;
3247
3248 mhd_assert (! is_parsed || (0 == add_element[header_host_key_len]));
3249 /* The actual size should be larger due to CRLF or LF chars,
3250 however the exact termination sequence is not known here and
3251 as perfect precision is not required, to simplify the code
3252 assume the minimal length. */
3253 if (is_parsed)
3254 actual_element_size = add_element_size + 1; /* "1" for LF */
3255 else
3256 actual_element_size = add_element_size;
3257
3258 host_field_line_size = actual_element_size;
3259 mhd_assert (opt_headers_size >= actual_element_size);
3260 opt_headers_size -= actual_element_size;
3261 }
3262 }
3263 if (0 == host_field_line_size)
3264 {
3265 static const size_t host_field_name_len =
3267 size_t host_field_name_value_len;
3271 host_field_name_len,
3272 NULL,
3273 &host_field_name_value_len))
3274 {
3275 /* Calculate the minimal size of the field line: no space between
3276 colon and the field value, line terminated by LR */
3277 host_field_line_size =
3278 host_field_name_len + host_field_name_value_len + 2; /* "2" for ':' and LF */
3279
3280 /* The "Host:" field could be added by application */
3281 if (opt_headers_size >= host_field_line_size)
3282 {
3283 opt_headers_size -= host_field_line_size;
3284 /* Take into account typical space after colon and CR at the end of the line */
3285 if (opt_headers_size >= 2)
3286 opt_headers_size -= 2;
3287 }
3288 else
3289 host_field_line_size = 0; /* No "Host:" field line set by the client */
3290 }
3291 }
3292
3293 uri_size = c->rq.req_target_len;
3295 method_size = 0; /* Do not recommend shorter request method */
3296 else
3297 {
3298 mhd_assert (NULL != c->rq.method);
3299 method_size = strlen (c->rq.method);
3300 }
3301
3302 if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3303 {
3304 /* Typically the easiest way to reduce request header size is
3305 a removal of some optional headers. */
3306 if (opt_headers_size > (uri_size / 8))
3307 {
3308 if ((opt_headers_size / 2) > 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 MUCH larger than headers */
3315 if ((uri_size / 16) > method_size)
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_MAX_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3322 {
3323 /* If request target size if larger than maximum reasonable size
3324 recommend client to reduce the request target size (length). */
3325 if ((uri_size / 16) > method_size)
3326 return MHD_HTTP_URI_TOO_LONG; /* Request target is MUCH larger than headers */
3327 else
3328 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3329 }
3330
3331 /* The read buffer is too small to handle reasonably large requests */
3332
3333 if ((size_t) MHD_MIN_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3334 {
3335 /* Recommend application to retry with minimal headers */
3336 if ((opt_headers_size * 4) > uri_size)
3337 {
3338 if (opt_headers_size > method_size)
3340 else
3341 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3342 }
3343 else
3344 { /* Request target is significantly larger than headers */
3345 if (uri_size > method_size * 4)
3346 return MHD_HTTP_URI_TOO_LONG;
3347 else
3348 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3349 }
3350 }
3351 if ((size_t) MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3352 {
3353 /* Recommend application to retry with a shorter request target */
3354 if (uri_size > method_size * 4)
3355 return MHD_HTTP_URI_TOO_LONG;
3356 else
3357 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3358 }
3359
3360 if ((size_t) MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ < method_size)
3361 {
3362 /* The request target (URI) and headers are (reasonably) very small.
3363 Some non-standard long request method is used. */
3364 /* The last resort response as it means "the method is not supported
3365 by the server for any URI". */
3367 }
3368
3369 /* The almost impossible situation: all elements are small, but cannot
3370 fit the buffer. The application set the buffer size to
3371 critically low value? */
3372
3373 if ((1 < opt_headers_size) || (1 < uri_size))
3374 {
3375 if (opt_headers_size >= uri_size)
3377 else
3378 return MHD_HTTP_URI_TOO_LONG;
3379 }
3380
3381 /* Nothing to reduce in the request.
3382 Reply with some status. */
3383 if (0 != host_field_line_size)
3385
3386 return MHD_HTTP_URI_TOO_LONG;
3387}
3388
3389
3400static void
3402 const char *add_header,
3403 size_t add_header_size)
3404{
3405 unsigned int err_code;
3406
3407 err_code = get_no_space_err_status_code (c,
3409 add_header,
3410 add_header_size);
3412 err_code,
3414}
3415
3416
3417#ifdef COOKIE_SUPPORT
3423static void
3424handle_req_cookie_no_space (struct MHD_Connection *c)
3425{
3426 unsigned int err_code;
3427
3428 err_code = get_no_space_err_status_code (c,
3430 NULL,
3431 0);
3433 err_code,
3435}
3436
3437
3438#endif /* COOKIE_SUPPORT */
3439
3440
3451static void
3453 const char *chunk_size_line,
3454 size_t chunk_size_line_size)
3455{
3456 unsigned int err_code;
3457
3458 if (NULL != chunk_size_line)
3459 {
3460 const char *semicol;
3461 /* Check for chunk extension */
3462 semicol = memchr (chunk_size_line, ';', chunk_size_line_size);
3463 if (NULL != semicol)
3464 { /* Chunk extension present. It could be removed without any loss of the
3465 details of the request. */
3469 }
3470 }
3471 err_code = get_no_space_err_status_code (c,
3473 chunk_size_line,
3474 chunk_size_line_size);
3476 err_code,
3478}
3479
3480
3491static void
3493 const char *add_footer,
3494 size_t add_footer_size)
3495{
3496 (void) add_footer; (void) add_footer_size; /* Unused */
3498
3499 /* Footers should be optional */
3503}
3504
3505
3516static void
3518 enum MHD_ProcRecvDataStage stage)
3519{
3520 mhd_assert (MHD_PROC_RECV_INIT <= stage);
3523 mhd_assert ((MHD_PROC_RECV_INIT != stage) || \
3524 (MHD_CONNECTION_INIT == c->state));
3525 mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \
3527 mhd_assert ((MHD_PROC_RECV_URI != stage) || \
3529 mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \
3531 mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \
3533 mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */
3534 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3536 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3538 mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \
3540 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3541 (! c->rq.have_chunked_upload));
3542 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3543 (c->rq.have_chunked_upload));
3544 switch (stage)
3545 {
3546 case MHD_PROC_RECV_INIT:
3548 /* Some data has been received, but it is not clear yet whether
3549 * the received data is an valid HTTP request */
3551 _ ("No space left in the read buffer when " \
3552 "receiving the initial part of " \
3553 "the request line."));
3554 return;
3555 case MHD_PROC_RECV_URI:
3557 /* Some data has been received, but the request line is incomplete */
3560 /* A quick simple check whether the incomplete line looks
3561 * like an HTTP request */
3562 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
3564 {
3568 return;
3569 }
3571 _ ("No space left in the read buffer when " \
3572 "receiving the URI in " \
3573 "the request line. " \
3574 "The request uses non-standard HTTP request " \
3575 "method token."));
3576 return;
3579 return;
3582 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3585 {
3586 /* The connection must not be in MHD_EVENT_LOOP_INFO_READ state
3587 when external polling is used and some data left unprocessed. */
3589 /* failed to grow the read buffer, and the
3590 client which is supposed to handle the
3591 received data in a *blocking* fashion
3592 (in this mode) did not handle the data as
3593 it was supposed to!
3594 => we would either have to do busy-waiting
3595 (on the client, which would likely fail),
3596 or if we do nothing, we would just timeout
3597 on the connection (if a timeout is even
3598 set!).
3599 Solution: we kill the connection with an error */
3603 }
3604 else
3605 {
3606 if (MHD_PROC_RECV_BODY_NORMAL == stage)
3607 {
3608 /* A header probably has been added to a suspended connection and
3609 it took precisely all the space in the buffer.
3610 Very low probability. */
3613 }
3614 else
3615 {
3618 { /* Receiving content of the chunk */
3619 /* A header probably has been added to a suspended connection and
3620 it took precisely all the space in the buffer.
3621 Very low probability. */
3623 }
3624 else
3625 {
3626 if (0 != c->rq.current_chunk_size)
3627 { /* Waiting for chunk-closing CRLF */
3628 /* Not really possible as some payload should be
3629 processed and the space used by payload should be available. */
3631 }
3632 else
3633 { /* Reading the line with the chunk size */
3635 c->read_buffer,
3637 }
3638 }
3639 }
3640 }
3641 return;
3644 return;
3645 /* The next cases should not be possible */
3647 default:
3648 break;
3649 }
3650 mhd_assert (0);
3651}
3652
3653
3667static bool
3669{
3673 bool rbuff_grow_desired;
3677 bool rbuff_grow_required;
3678
3681
3682 rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size);
3683 if (rbuff_grow_required)
3684 rbuff_grow_desired = true;
3685 else
3686 {
3687 rbuff_grow_desired = (c->read_buffer_offset + c->daemon->pool_increment >
3688 c->read_buffer_size);
3689
3690 if ((rbuff_grow_desired) &&
3692 {
3693 if (! c->rq.have_chunked_upload)
3694 {
3696 /* Do not grow read buffer more than necessary to process the current
3697 request. */
3698 rbuff_grow_desired =
3700 }
3701 else
3702 {
3704 if (0 == c->rq.current_chunk_size)
3705 rbuff_grow_desired = /* Reading value of the next chunk size */
3707 c->read_buffer_size);
3708 else
3709 {
3710 const uint64_t cur_chunk_left =
3712 /* Do not grow read buffer more than necessary to process the current
3713 chunk with terminating CRLF. */
3715 rbuff_grow_desired =
3716 ((cur_chunk_left + 2) > (uint64_t) (c->read_buffer_size));
3717 }
3718 }
3719 }
3720 }
3721
3722 if (! rbuff_grow_desired)
3723 return true; /* No need to increase the buffer */
3724
3725 if (try_grow_read_buffer (c, rbuff_grow_required))
3726 return true; /* Buffer increase succeed */
3727
3728 if (! rbuff_grow_required)
3729 return true; /* Can continue without buffer increase */
3730
3731 /* Failed to increase the read buffer size, but need to read the data
3732 from the network.
3733 No more space left in the buffer, no more space to increase the buffer. */
3734
3735 /* 'PROCESS_READ' event state flag must be set only if the last application
3736 callback has processed some data. If any data is processed then some
3737 space in the read buffer must be available. */
3739
3740 if ((! MHD_D_IS_USING_THREADS_ (c->daemon))
3743 {
3744 /* The application is handling processing cycles.
3745 The data could be processed later. */
3747 return true;
3748 }
3749 else
3750 {
3751 enum MHD_ProcRecvDataStage stage;
3752
3753 switch (c->state)
3754 {
3756 stage = MHD_PROC_RECV_INIT;
3757 break;
3760 stage = MHD_PROC_RECV_METHOD;
3761 else if (0 == c->rq.req_target_len)
3762 stage = MHD_PROC_RECV_URI;
3763 else
3764 stage = MHD_PROC_RECV_HTTPVER;
3765 break;
3767 stage = MHD_PROC_RECV_HEADERS;
3768 break;
3770 stage = c->rq.have_chunked_upload ?
3772 break;
3774 stage = MHD_PROC_RECV_FOOTERS;
3775 break;
3794#ifdef UPGRADE_SUPPORT
3795 case MHD_CONNECTION_UPGRADE:
3796#endif
3797 default:
3799 mhd_assert (0);
3800 }
3801
3802 handle_recv_no_space (c, stage);
3803 }
3804 return false;
3805}
3806
3807
3816static void
3818{
3819 /* Do not update states of suspended connection */
3820 if (connection->suspended)
3821 return; /* States will be updated after resume. */
3822#ifdef HTTPS_SUPPORT
3823 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
3824 { /* HTTPS connection. */
3825 switch (connection->tls_state)
3826 {
3827 case MHD_TLS_CONN_INIT:
3829 return;
3832 if (0 == gnutls_record_get_direction (connection->tls_session))
3834 else
3836 return;
3838 break; /* Do normal processing */
3842 return;
3843 case MHD_TLS_CONN_TLS_CLOSING: /* Not implemented yet */
3844 case MHD_TLS_CONN_TLS_CLOSED: /* Not implemented yet */
3846 case MHD_TLS_CONN_NO_TLS: /* Not possible */
3847 default:
3848 MHD_PANIC (_ ("Invalid TLS state value.\n"));
3849 }
3850 }
3851#endif /* HTTPS_SUPPORT */
3852 while (1)
3853 {
3854#if DEBUG_STATES
3855 MHD_DLOG (connection->daemon,
3856 _ ("In function %s handling connection at state: %s\n"),
3857 MHD_FUNC_,
3858 MHD_state_to_string (connection->state));
3859#endif
3860 switch (connection->state)
3861 {
3865 break;
3867 mhd_assert (0);
3868 break;
3871 break;
3874 mhd_assert (0);
3875 break;
3878 break;
3880 if ((connection->rq.some_payload_processed) &&
3882 {
3883 /* Some data was processed, the buffer must have some free space */
3884 mhd_assert (connection->read_buffer_offset < \
3885 connection->read_buffer_size);
3886 if (! connection->rq.have_chunked_upload)
3887 {
3888 /* Not a chunked upload. Do not read more than necessary to
3889 process the current request. */
3890 if (connection->rq.remaining_upload_size >=
3891 connection->read_buffer_offset)
3893 else
3895 }
3896 else
3897 {
3898 /* Chunked upload. The size of the current request is unknown.
3899 Continue reading as the space in the read buffer is available. */
3901 }
3902 }
3903 else
3905 break;
3907 mhd_assert (0);
3908 break;
3911 break;
3913 mhd_assert (0);
3914 break;
3917 break;
3919 mhd_assert (0);
3920 break;
3922 /* headers in buffer, keep writing */
3924 break;
3926 mhd_assert (0);
3927 break;
3930 break;
3933 break;
3936 break;
3939 break;
3941 mhd_assert (0);
3942 break;
3945 break;
3947 mhd_assert (0);
3948 break;
3951 return; /* do nothing, not even reading */
3952#ifdef UPGRADE_SUPPORT
3953 case MHD_CONNECTION_UPGRADE:
3954 mhd_assert (0);
3955 break;
3956#endif /* UPGRADE_SUPPORT */
3957 default:
3958 mhd_assert (0);
3959 }
3960
3961 if (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info))
3962 {
3963 /* Check whether the space is available to receive data */
3964 if (! check_and_grow_read_buffer_space (connection))
3965 {
3966 mhd_assert (connection->discard_request);
3967 continue;
3968 }
3969 }
3970 break; /* Everything was processed. */
3971 }
3972}
3973
3974
3987static enum MHD_Result
3989 const char *key,
3990 size_t key_size,
3991 const char *value,
3992 size_t value_size,
3993 enum MHD_ValueKind kind)
3994{
3995 struct MHD_Connection *connection = (struct MHD_Connection *) cls;
3996 if (MHD_NO ==
3997 MHD_set_connection_value_n (connection,
3998 kind,
3999 key,
4000 key_size,
4001 value,
4002 value_size))
4003 {
4004#ifdef HAVE_MESSAGES
4005 MHD_DLOG (connection->daemon,
4006 _ ("Not enough memory in pool to allocate header record!\n"));
4007#endif
4011 return MHD_NO;
4012 }
4013 return MHD_YES;
4014}
4015
4016
4017#ifdef COOKIE_SUPPORT
4018
4022enum _MHD_ParseCookie
4023{
4024 MHD_PARSE_COOKIE_OK = MHD_YES,
4025 MHD_PARSE_COOKIE_OK_LAX = 2,
4026 MHD_PARSE_COOKIE_MALFORMED = -1,
4027 MHD_PARSE_COOKIE_NO_MEMORY = MHD_NO
4028};
4029
4030
4043static enum _MHD_ParseCookie
4044parse_cookies_string (char *str,
4045 const size_t str_len,
4046 struct MHD_Connection *connection)
4047{
4048 size_t i;
4049 bool non_strict;
4050 /* Skip extra whitespaces and empty cookies */
4051 const bool allow_wsp_empty = (0 >= connection->daemon->client_discipline);
4052 /* Allow whitespaces around '=' character */
4053 const bool wsp_around_eq = (-3 >= connection->daemon->client_discipline);
4054 /* Allow whitespaces in quoted cookie value */
4055 const bool wsp_in_quoted = (-2 >= connection->daemon->client_discipline);
4056 /* Allow tab as space after semicolon between cookies */
4057 const bool tab_as_sp = (0 >= connection->daemon->client_discipline);
4058 /* Allow no space after semicolon between cookies */
4059 const bool allow_no_space = (0 >= connection->daemon->client_discipline);
4060
4061 non_strict = false;
4062 i = 0;
4063 while (i < str_len)
4064 {
4065 size_t name_start;
4066 size_t name_len;
4067 size_t value_start;
4068 size_t value_len;
4069 bool val_quoted;
4070 /* Skip any whitespaces and empty cookies */
4071 while (' ' == str[i] || '\t' == str[i] || ';' == str[i])
4072 {
4073 if (! allow_wsp_empty)
4074 return MHD_PARSE_COOKIE_MALFORMED;
4075 non_strict = true;
4076 i++;
4077 if (i == str_len)
4078 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
4079 }
4080 /* 'i' must point to the first char of cookie-name */
4081 name_start = i;
4082 /* Find the end of the cookie-name */
4083 do
4084 {
4085 const char l = str[i];
4086 if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) ||
4087 (';' == l) || (0 == l))
4088 break;
4089 } while (str_len > ++i);
4090 name_len = i - name_start;
4091 /* Skip any whitespaces */
4092 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
4093 {
4094 if (! wsp_around_eq)
4095 return MHD_PARSE_COOKIE_MALFORMED;
4096 non_strict = true;
4097 i++;
4098 }
4099 if ((str_len == i) || ('=' != str[i]) || (0 == name_len))
4100 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */
4101 /* 'i' must point to the '=' char */
4102 mhd_assert ('=' == str[i]);
4103 i++;
4104 /* Skip any whitespaces */
4105 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
4106 {
4107 if (! wsp_around_eq)
4108 return MHD_PARSE_COOKIE_MALFORMED;
4109 non_strict = true;
4110 i++;
4111 }
4112 /* 'i' must point to the first char of cookie-value */
4113 if (str_len == i)
4114 {
4115 value_start = 0;
4116 value_len = 0;
4117#ifdef _DEBUG
4118 val_quoted = false; /* This assignment used in assert */
4119#endif
4120 }
4121 else
4122 {
4123 bool valid_cookie;
4124 val_quoted = ('"' == str[i]);
4125 if (val_quoted)
4126 i++;
4127 value_start = i;
4128 /* Find the end of the cookie-value */
4129 while (str_len > i)
4130 {
4131 const char l = str[i];
4132 if ((';' == l) || ('"' == l) || (',' == l) || (';' == l) ||
4133 ('\\' == l) || (0 == l))
4134 break;
4135 if ((' ' == l) || ('\t' == l))
4136 {
4137 if (! val_quoted)
4138 break;
4139 if (! wsp_in_quoted)
4140 return MHD_PARSE_COOKIE_MALFORMED;
4141 non_strict = true;
4142 }
4143 i++;
4144 }
4145 value_len = i - value_start;
4146 if (val_quoted)
4147 {
4148 if ((str_len == i) || ('"' != str[i]))
4149 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */
4150 i++;
4151 }
4152 /* Skip any whitespaces */
4153 if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i])))
4154 {
4155 do
4156 {
4157 i++;
4158 } while (str_len > i && (' ' == str[i] || '\t' == str[i]));
4159 /* Whitespace at the end? */
4160 if (str_len > i)
4161 {
4162 if (! allow_wsp_empty)
4163 return MHD_PARSE_COOKIE_MALFORMED;
4164 non_strict = true;
4165 }
4166 }
4167 if (str_len == i)
4168 valid_cookie = true;
4169 else if (';' == str[i])
4170 valid_cookie = true;
4171 else
4172 valid_cookie = false;
4173
4174 if (! valid_cookie)
4175 return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */
4176 }
4177 mhd_assert (0 != name_len);
4178 str[name_start + name_len] = 0; /* Zero-terminate the name */
4179 if (0 != value_len)
4180 {
4181 mhd_assert (value_start + value_len <= str_len);
4182 str[value_start + value_len] = 0; /* Zero-terminate the value */
4183 if (MHD_NO ==
4186 str + name_start,
4187 name_len,
4188 str + value_start,
4189 value_len))
4190 return MHD_PARSE_COOKIE_NO_MEMORY;
4191 }
4192 else
4193 {
4194 if (MHD_NO ==
4197 str + name_start,
4198 name_len,
4199 "",
4200 0))
4201 return MHD_PARSE_COOKIE_NO_MEMORY;
4202 }
4203 if (str_len > i)
4204 {
4205 mhd_assert (0 == str[i] || ';' == str[i]);
4206 mhd_assert (! val_quoted || ';' == str[i]);
4207 mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len);
4208 i++;
4209 if (str_len == i)
4210 { /* No next cookie after semicolon */
4211 if (! allow_wsp_empty)
4212 return MHD_PARSE_COOKIE_MALFORMED;
4213 non_strict = true;
4214 }
4215 else if (' ' != str[i])
4216 {/* No space after semicolon */
4217 if (('\t' == str[i]) && tab_as_sp)
4218 i++;
4219 else if (! allow_no_space)
4220 return MHD_PARSE_COOKIE_MALFORMED;
4221 non_strict = true;
4222 }
4223 else
4224 {
4225 i++;
4226 if (str_len == i)
4227 {
4228 if (! allow_wsp_empty)
4229 return MHD_PARSE_COOKIE_MALFORMED;
4230 non_strict = true;
4231 }
4232 }
4233 }
4234 }
4235 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
4236}
4237
4238
4247static enum _MHD_ParseCookie
4248parse_cookie_header (struct MHD_Connection *connection,
4249 const char *hdr,
4250 size_t hdr_len)
4251{
4252 char *cpy;
4253 size_t i;
4254 enum _MHD_ParseCookie parse_res;
4255 struct MHD_HTTP_Req_Header *const saved_tail =
4256 connection->rq.headers_received_tail;
4257 const bool allow_partially_correct_cookie =
4258 (1 >= connection->daemon->client_discipline);
4259
4260 if (0 == hdr_len)
4261 return MHD_PARSE_COOKIE_OK;
4262
4263 cpy = MHD_connection_alloc_memory_ (connection,
4264 hdr_len + 1);
4265 if (NULL == cpy)
4266 parse_res = MHD_PARSE_COOKIE_NO_MEMORY;
4267 else
4268 {
4269 memcpy (cpy,
4270 hdr,
4271 hdr_len);
4272 cpy[hdr_len] = '\0';
4273
4274 i = 0;
4275 /* Skip all initial whitespaces */
4276 while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i]))
4277 i++;
4278
4279 parse_res = parse_cookies_string (cpy + i, hdr_len - i, connection);
4280 }
4281
4282 switch (parse_res)
4283 {
4284 case MHD_PARSE_COOKIE_OK:
4285 break;
4286 case MHD_PARSE_COOKIE_OK_LAX:
4287#ifdef HAVE_MESSAGES
4288 if (saved_tail != connection->rq.headers_received_tail)
4289 MHD_DLOG (connection->daemon,
4290 _ ("The Cookie header has been parsed, but it is not fully "
4291 "compliant with the standard.\n"));
4292#endif /* HAVE_MESSAGES */
4293 break;
4294 case MHD_PARSE_COOKIE_MALFORMED:
4295 if (saved_tail != connection->rq.headers_received_tail)
4296 {
4297 if (! allow_partially_correct_cookie)
4298 {
4299 /* Remove extracted values from partially broken cookie */
4300 /* Memory remains allocated until the end of the request processing */
4301 connection->rq.headers_received_tail = saved_tail;
4302 saved_tail->next = NULL;
4303#ifdef HAVE_MESSAGES
4304 MHD_DLOG (connection->daemon,
4305 _ ("The Cookie header has been ignored as it contains "
4306 "malformed data.\n"));
4307#endif /* HAVE_MESSAGES */
4308 }
4309#ifdef HAVE_MESSAGES
4310 else
4311 MHD_DLOG (connection->daemon,
4312 _ ("The Cookie header has been only partially parsed as it "
4313 "contains malformed data.\n"));
4314#endif /* HAVE_MESSAGES */
4315 }
4316#ifdef HAVE_MESSAGES
4317 else
4318 MHD_DLOG (connection->daemon,
4319 _ ("The Cookie header has malformed data.\n"));
4320#endif /* HAVE_MESSAGES */
4321 break;
4322 case MHD_PARSE_COOKIE_NO_MEMORY:
4323#ifdef HAVE_MESSAGES
4324 MHD_DLOG (connection->daemon,
4325 _ ("Not enough memory in the connection pool to "
4326 "parse client cookies!\n"));
4327#endif /* HAVE_MESSAGES */
4328 break;
4329 default:
4330 mhd_assert (0);
4331 break;
4332 }
4333#ifndef HAVE_MESSAGES
4334 (void) saved_tail; /* Mute compiler warning */
4335#endif /* ! HAVE_MESSAGES */
4336
4337 return parse_res;
4338}
4339
4340
4341#endif /* COOKIE_SUPPORT */
4342
4343
4347#define HTTP_VER_LEN (MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1))
4348
4358static bool
4360 const char *http_string,
4361 size_t len)
4362{
4363 const char *const h = http_string;
4364 mhd_assert (NULL != http_string);
4365
4366 /* String must start with 'HTTP/d.d', case-sensetive match.
4367 * See https://www.rfc-editor.org/rfc/rfc9112#name-http-version */
4368 if ((HTTP_VER_LEN != len) ||
4369 ('H' != h[0]) || ('T' != h[1]) || ('T' != h[2]) || ('P' != h[3]) ||
4370 ('/' != h[4])
4371 || ('.' != h[6]) ||
4372 (('0' > h[5]) || ('9' < h[5])) ||
4373 (('0' > h[7]) || ('9' < h[7])))
4374 {
4375 connection->rq.http_ver = MHD_HTTP_VER_INVALID;
4379 return false;
4380 }
4381 if (1 == h[5] - '0')
4382 {
4383 /* HTTP/1.x */
4384 if (1 == h[7] - '0')
4385 connection->rq.http_ver = MHD_HTTP_VER_1_1;
4386 else if (0 == h[7] - '0')
4387 connection->rq.http_ver = MHD_HTTP_VER_1_0;
4388 else
4389 connection->rq.http_ver = MHD_HTTP_VER_1_2__1_9;
4390
4391 return true;
4392 }
4393
4394 if (0 == h[5] - '0')
4395 {
4396 /* Too old major version */
4397 connection->rq.http_ver = MHD_HTTP_VER_TOO_OLD;
4401 return false;
4402 }
4403
4404 connection->rq.http_ver = MHD_HTTP_VER_FUTURE;
4408 return false;
4409}
4410
4411
4419static void
4421 const char *method,
4422 size_t len)
4423{
4424 const char *const m = method;
4425 mhd_assert (NULL != m);
4426 mhd_assert (0 != len);
4427
4428 if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET) == len) &&
4429 (0 == memcmp (m, MHD_HTTP_METHOD_GET, len)))
4430 connection->rq.http_mthd = MHD_HTTP_MTHD_GET;
4431 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_HEAD) == len) &&
4432 (0 == memcmp (m, MHD_HTTP_METHOD_HEAD, len)))
4433 connection->rq.http_mthd = MHD_HTTP_MTHD_HEAD;
4434 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_POST) == len) &&
4435 (0 == memcmp (m, MHD_HTTP_METHOD_POST, len)))
4436 connection->rq.http_mthd = MHD_HTTP_MTHD_POST;
4437 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_PUT) == len) &&
4438 (0 == memcmp (m, MHD_HTTP_METHOD_PUT, len)))
4439 connection->rq.http_mthd = MHD_HTTP_MTHD_PUT;
4440 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_DELETE) == len) &&
4441 (0 == memcmp (m, MHD_HTTP_METHOD_DELETE, len)))
4442 connection->rq.http_mthd = MHD_HTTP_MTHD_DELETE;
4443 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_CONNECT) == len) &&
4444 (0 == memcmp (m, MHD_HTTP_METHOD_CONNECT, len)))
4445 connection->rq.http_mthd = MHD_HTTP_MTHD_CONNECT;
4446 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_OPTIONS) == len) &&
4447 (0 == memcmp (m, MHD_HTTP_METHOD_OPTIONS, len)))
4448 connection->rq.http_mthd = MHD_HTTP_MTHD_OPTIONS;
4449 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_TRACE) == len) &&
4450 (0 == memcmp (m, MHD_HTTP_METHOD_TRACE, len)))
4451 connection->rq.http_mthd = MHD_HTTP_MTHD_TRACE;
4452 else
4453 connection->rq.http_mthd = MHD_HTTP_MTHD_OTHER;
4454}
4455
4456
4464static void
4466{
4467 struct MHD_Daemon *daemon = connection->daemon;
4468 size_t processed;
4469
4470 if (NULL != connection->rp.response)
4471 return; /* already queued a response */
4472 processed = 0;
4473 connection->rq.client_aware = true;
4474 connection->in_access_handler = true;
4475 if (MHD_NO ==
4476 daemon->default_handler (daemon->default_handler_cls,
4477 connection,
4478 connection->rq.url_for_callback,
4479 connection->rq.method,
4480 connection->rq.version,
4481 NULL,
4482 &processed,
4483 &connection->rq.client_context))
4484 {
4485 connection->in_access_handler = false;
4486 /* serious internal error, close connection */
4487 CONNECTION_CLOSE_ERROR (connection,
4488 _ ("Application reported internal error, " \
4489 "closing connection."));
4490 return;
4491 }
4492 connection->in_access_handler = false;
4493}
4494
4495
4503static void
4505{
4506 struct MHD_Daemon *daemon = connection->daemon;
4507 size_t available;
4508 bool instant_retry;
4509 char *buffer_head;
4510 const int discp_lvl = daemon->client_discipline;
4511 /* RFC does not allow LF as the line termination in chunk headers.
4512 See RFC 9112, section 7.1 and section 2.2-3 */
4513 const bool bare_lf_as_crlf = (-2 > discp_lvl);
4514 /* Allow "Bad WhiteSpace" in chunk extension.
4515 RFC 9112, Section 7.1.1, Paragraph 2 */
4516 const bool allow_bws = (2 > discp_lvl);
4517
4518 mhd_assert (NULL == connection->rp.response);
4519
4520 buffer_head = connection->read_buffer;
4521 available = connection->read_buffer_offset;
4522 do
4523 {
4524 size_t to_be_processed;
4525 size_t left_unprocessed;
4526 size_t processed_size;
4527
4528 instant_retry = false;
4529 if (connection->rq.have_chunked_upload)
4530 {
4532 if ( (connection->rq.current_chunk_offset ==
4533 connection->rq.current_chunk_size) &&
4534 (0 != connection->rq.current_chunk_size) )
4535 {
4536 /* Skip CRLF chunk termination */
4537 size_t i;
4538 mhd_assert (0 != available);
4539 /* skip new line at the *end* of a chunk */
4540 i = 0;
4541 if ( (2 <= available) &&
4542 ('\r' == buffer_head[0]) &&
4543 ('\n' == buffer_head[1]) )
4544 i += 2; /* skip CRLF */
4545 else if (bare_lf_as_crlf && ('\n' == buffer_head[0]))
4546 i++; /* skip bare LF */
4547 else if (2 > available)
4548 break; /* need more upload data */
4549 if (0 == i)
4550 {
4551 /* malformed encoding */
4555 return;
4556 }
4557 available -= i;
4558 buffer_head += i;
4559 connection->rq.current_chunk_offset = 0;
4560 connection->rq.current_chunk_size = 0;
4561 if (0 == available)
4562 break;
4563 }
4564 if (0 != connection->rq.current_chunk_size)
4565 {
4566 /* Process chunk "content" */
4567 uint64_t cur_chunk_left;
4568 mhd_assert (connection->rq.current_chunk_offset < \
4569 connection->rq.current_chunk_size);
4570 cur_chunk_left
4571 = connection->rq.current_chunk_size
4572 - connection->rq.current_chunk_offset;
4573 if (cur_chunk_left > available)
4574 to_be_processed = available;
4575 else
4576 { /* cur_chunk_left <= (size_t)available */
4577 to_be_processed = (size_t) cur_chunk_left;
4578 if (available > to_be_processed)
4579 instant_retry = true;
4580 }
4581 }
4582 else
4583 { /* Need the parse the chunk size line */
4585 size_t num_dig;
4586 uint64_t chunk_size;
4587 bool broken;
4588 bool overflow;
4589
4590 mhd_assert (0 != available);
4591
4592 overflow = false;
4593 chunk_size = 0; /* Mute possible compiler warning.
4594 The real value will be set later. */
4595
4596 num_dig = MHD_strx_to_uint64_n_ (buffer_head,
4597 available,
4598 &chunk_size);
4599 mhd_assert (num_dig <= available);
4600 if (num_dig == available)
4601 continue; /* Need line delimiter */
4602
4603 broken = (0 == num_dig);
4604 if (broken)
4605 {
4606 uint64_t dummy;
4607 /* Check whether result is invalid due to uint64_t overflow */
4608 overflow = (0 != MHD_strx_to_uint64_n_ (buffer_head,
4609 1,
4610 &dummy));
4611 }
4612 else
4613 {
4618 size_t chunk_size_line_len;
4619
4620 chunk_size_line_len = 0;
4621 if ((';' == buffer_head[num_dig]) ||
4622 (allow_bws &&
4623 ((' ' == buffer_head[num_dig]) ||
4624 ('\t' == buffer_head[num_dig]))))
4625 { /* Chunk extension or "bad whitespace" after chunk length */
4626 size_t i;
4627
4628 /* Skip bad whitespaces (if any) */
4629 for (i = num_dig; i < available; ++i)
4630 {
4631 if ((' ' != buffer_head[i]) && ('\t' != buffer_head[i]))
4632 break;
4633 }
4634 if (i == available)
4635 break; /* need more data */
4636 if (';' == buffer_head[i])
4637 {
4638 /* Chunk extension */
4639 for (++i; i < available; ++i)
4640 {
4641 if (('\r' == buffer_head[i]) ||
4642 ('\n' == buffer_head[i]))
4643 break;
4644 }
4645 if (i == available)
4646 break; /* need more data */
4647 mhd_assert (i > num_dig);
4648 mhd_assert (1 <= i);
4649 if ('\r' == buffer_head[i])
4650 {
4651 if (i + 1 == available)
4652 break; /* need more data */
4653 if ('\n' == buffer_head[i + 1])
4654 chunk_size_line_len = i; /* Valid chunk header */
4655 }
4656 else
4657 {
4658 mhd_assert ('\n' == buffer_head[i]);
4659 if (bare_lf_as_crlf)
4660 chunk_size_line_len = i; /* Valid chunk header */
4661 }
4662 /* The chunk header is broken
4663 if chunk_size_line_len is zero here. */
4664 }
4665 else
4666 { /* No ';' after "bad whitespace" */
4667 mhd_assert (allow_bws);
4668 mhd_assert (0 == chunk_size_line_len);
4669 }
4670 }
4671 else
4672 {
4673 /* No chunk extension */
4674 mhd_assert (available >= num_dig);
4675 if ((2 <= (available - num_dig)) &&
4676 ('\r' == buffer_head[num_dig]) &&
4677 ('\n' == buffer_head[num_dig + 1]))
4678 chunk_size_line_len = num_dig + 2;
4679 else if (bare_lf_as_crlf &&
4680 ('\n' == buffer_head[num_dig]))
4681 chunk_size_line_len = num_dig + 1;
4682 else if (2 > (available - num_dig))
4683 break; /* need more data */
4684 }
4685
4686 if (0 != chunk_size_line_len)
4687 { /* Valid termination of the chunk size line */
4688 mhd_assert (chunk_size_line_len <= available);
4689 /* Start reading payload data of the chunk */
4690 connection->rq.current_chunk_offset = 0;
4691 connection->rq.current_chunk_size = chunk_size;
4692
4693 available -= chunk_size_line_len;
4694 buffer_head += chunk_size_line_len;
4695
4696 if (0 == chunk_size)
4697 { /* The final (termination) chunk */
4698 connection->rq.remaining_upload_size = 0;
4699 break;
4700 }
4701 if (available > 0)
4702 instant_retry = true;
4703 continue;
4704 }
4705 /* Invalid chunk size line */
4706 }
4707
4708 if (! overflow)
4712 else
4716 return;
4717 }
4718 }
4719 else
4720 {
4721 /* no chunked encoding, give all to the client */
4723 mhd_assert (0 != connection->rq.remaining_upload_size);
4724 if (connection->rq.remaining_upload_size < available)
4725 to_be_processed = (size_t) connection->rq.remaining_upload_size;
4726 else
4727 to_be_processed = available;
4728 }
4729 left_unprocessed = to_be_processed;
4730 connection->rq.client_aware = true;
4731 connection->in_access_handler = true;
4732 if (MHD_NO ==
4733 daemon->default_handler (daemon->default_handler_cls,
4734 connection,
4735 connection->rq.url_for_callback,
4736 connection->rq.method,
4737 connection->rq.version,
4738 buffer_head,
4739 &left_unprocessed,
4740 &connection->rq.client_context))
4741 {
4742 connection->in_access_handler = false;
4743 /* serious internal error, close connection */
4744 CONNECTION_CLOSE_ERROR (connection,
4745 _ ("Application reported internal error, " \
4746 "closing connection."));
4747 return;
4748 }
4749 connection->in_access_handler = false;
4750
4751 if (left_unprocessed > to_be_processed)
4752 MHD_PANIC (_ ("libmicrohttpd API violation.\n"));
4753
4754 connection->rq.some_payload_processed =
4755 (left_unprocessed != to_be_processed);
4756
4757 if (0 != left_unprocessed)
4758 {
4759 instant_retry = false; /* client did not process everything */
4760#ifdef HAVE_MESSAGES
4761 if ((! connection->rq.some_payload_processed) &&
4762 (! connection->suspended))
4763 {
4764 /* client did not process any upload data, complain if
4765 the setup was incorrect, which may prevent us from
4766 handling the rest of the request */
4767 if (MHD_D_IS_USING_THREADS_ (daemon))
4768 MHD_DLOG (daemon,
4769 _ ("WARNING: Access Handler Callback has not processed " \
4770 "any upload data and connection is not suspended. " \
4771 "This may result in hung connection.\n"));
4772 }
4773#endif /* HAVE_MESSAGES */
4774 }
4775 processed_size = to_be_processed - left_unprocessed;
4776 /* dh left "processed" bytes in buffer for next time... */
4777 buffer_head += processed_size;
4778 available -= processed_size;
4779 if (! connection->rq.have_chunked_upload)
4780 {
4782 connection->rq.remaining_upload_size -= processed_size;
4783 }
4784 else
4785 {
4787 connection->rq.current_chunk_offset += processed_size;
4788 }
4789 } while (instant_retry);
4790 /* TODO: zero out reused memory region */
4791 if ( (available > 0) &&
4792 (buffer_head != connection->read_buffer) )
4793 memmove (connection->read_buffer,
4794 buffer_head,
4795 available);
4796 else
4797 mhd_assert ((0 == available) || \
4798 (connection->read_buffer_offset == available));
4799 connection->read_buffer_offset = available;
4800}
4801
4802
4811static enum MHD_Result
4813 enum MHD_CONNECTION_STATE next_state)
4814{
4815 if ( (connection->write_buffer_append_offset !=
4816 connection->write_buffer_send_offset)
4817 /* || data_in_tls_buffers == true */
4818 )
4819 return MHD_NO;
4820 connection->write_buffer_append_offset = 0;
4821 connection->write_buffer_send_offset = 0;
4822 connection->state = next_state;
4823 return MHD_YES;
4824}
4825
4826
4834static void
4836{
4837 struct MHD_HTTP_Req_Header *pos;
4838 bool have_hdr_host;
4839 bool have_cntn_len;
4840
4841 have_hdr_host = false;
4842 have_cntn_len = false;
4843
4844 /* The presence of the request body is indicated by "Content-Length:" or
4845 "Transfer-Encoding:" request headers.
4846 See RFC 9112 section 6.1, 6.2, 6.3; RFC 9110 Section 8.6. */
4847
4850
4851 for (pos = c->rq.headers_received; NULL != pos; pos = pos->next)
4852 {
4853 if (MHD_HEADER_KIND != pos->kind)
4854 continue;
4855
4857 pos->header,
4858 pos->header_size))
4859 {
4860 if (have_hdr_host)
4861 {
4862 if (-3 < c->daemon->client_discipline)
4863 {
4867 return;
4868 }
4869 }
4870 have_hdr_host = true;
4871 }
4872#ifdef COOKIE_SUPPORT
4874 pos->header,
4875 pos->header_size))
4876 {
4877 if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (c,
4878 pos->value,
4879 pos->value_size))
4880 {
4881 handle_req_cookie_no_space (c);
4882 return;
4883 }
4884 }
4885#endif /* COOKIE_SUPPORT */
4887 pos->header,
4888 pos->header_size))
4889 {
4890 const char *clen;
4891 size_t val_len;
4892 size_t num_digits;
4893 uint64_t decoded_val;
4894
4895 val_len = pos->value_size;
4896 clen = pos->value;
4897
4898 mhd_assert ('\0' == clen[val_len]);
4899
4900 if ((have_cntn_len)
4901 && (0 < c->daemon->client_discipline))
4902 {
4906 return;
4907 }
4908
4909 num_digits = MHD_str_to_uint64_n_ (clen,
4910 val_len,
4911 &decoded_val);
4912
4913 if ((0 == num_digits) ||
4914 (val_len != num_digits) ||
4915 (MHD_SIZE_UNKNOWN == decoded_val))
4916 { /* Bad or too large value */
4917
4918 if (have_cntn_len)
4919 {
4923 return;
4924 }
4925
4926 if ((val_len != num_digits)
4927 || ('0' > clen[0]) || ('9' < clen[0]))
4928 {
4929#ifdef HAVE_MESSAGES
4930 MHD_DLOG (c->daemon,
4931 _ ("Malformed 'Content-Length' header. " \
4932 "Closing connection.\n"));
4933#endif
4937 return;
4938 }
4939
4940#ifdef HAVE_MESSAGES
4941 MHD_DLOG (c->daemon,
4942 _ ("Too large value of 'Content-Length' header. " \
4943 "Closing connection.\n"));
4944#endif
4948 return;
4949 }
4950
4951 if ((have_cntn_len) &&
4952 (c->rq.remaining_upload_size != decoded_val))
4953 {
4954 if (-3 < c->daemon->client_discipline)
4955 {
4959 return;
4960 }
4961 /* The HTTP framing is broken.
4962 Use smallest (safest) length value and force-close
4963 after processing of this request. */
4964 if (c->rq.remaining_upload_size > decoded_val)
4965 c->rq.remaining_upload_size = decoded_val;
4967 }
4968 else
4969 c->rq.remaining_upload_size = decoded_val;
4970
4971 have_cntn_len = true;
4972 }
4975 pos->header,
4976 pos->header_size))
4977 {
4978
4979 if (MHD_HTTP_VER_1_1 > c->rq.http_ver)
4980 {
4981 /* RFC 9112, 6.1, last paragraph */
4982 if (0 < c->daemon->client_discipline)
4983 {
4987 return;
4988 }
4989 /* HTTP framing potentially broken */
4991 }
4992
4993 if (c->rq.have_chunked_upload
4994 || ! MHD_str_equal_caseless_s_bin_n_ ("chunked",
4995 pos->value,
4996 pos->value_size))
4997 {
5003 return;
5004 }
5005 c->rq.have_chunked_upload = true;
5007 }
5008 }
5009
5010 if (c->rq.have_chunked_upload && have_cntn_len)
5011 {
5012 if (0 < c->daemon->client_discipline)
5013 {
5017 return;
5018 }
5019 else
5020 {
5021#ifdef HAVE_MESSAGES
5022 MHD_DLOG (c->daemon,
5023 _ ("The 'Content-Length' request header is ignored "
5024 "as chunked Transfer-Encoding is set in the "
5025 "same request.\n"));
5026#endif /* HAVE_MESSAGES */
5028 /* Must close connection after reply to prevent potential attack */
5030 }
5031 }
5032
5035 mhd_assert ((0 == c->rq.remaining_upload_size) ||
5036 have_cntn_len || c->rq.have_chunked_upload);
5037
5038 if (! have_hdr_host
5040 && (-3 < c->daemon->client_discipline))
5041 {
5042#ifdef HAVE_MESSAGES
5043 MHD_DLOG (c->daemon,
5044 _ ("Received HTTP/1.1 request without `Host' header.\n"));
5045#endif
5049 return;
5050 }
5051}
5052
5053
5061_MHD_static_inline void
5063{
5064 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
5065}
5066
5067
5072_MHD_static_inline void
5074{
5076 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
5078}
5079
5080
5081#ifndef MHD_MAX_EMPTY_LINES_SKIP
5086#define MHD_MAX_EMPTY_LINES_SKIP 1024
5087#endif /* ! MHD_MAX_EMPTY_LINES_SKIP */
5088
5096static bool
5098{
5099 size_t p;
5100 const int discp_lvl = c->daemon->client_discipline;
5101 /* Allow to skip one or more empty lines before the request line.
5102 RFC 9112, section 2.2 */
5103 const bool skip_empty_lines = (1 >= discp_lvl);
5104 /* Allow to skip more then one empty line before the request line.
5105 RFC 9112, section 2.2 */
5106 const bool skip_several_empty_lines = (skip_empty_lines && (0 >= discp_lvl));
5107 /* Allow to skip number of unlimited empty lines before the request line.
5108 RFC 9112, section 2.2 */
5109 const bool skip_unlimited_empty_lines =
5110 (skip_empty_lines && (-3 >= discp_lvl));
5111 /* Treat bare LF as the end of the line.
5112 RFC 9112, section 2.2 */
5113 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
5114 /* Treat tab as whitespace delimiter.
5115 RFC 9112, section 3 */
5116 const bool tab_as_wsp = (0 >= discp_lvl);
5117 /* Treat VT (vertical tab) and FF (form feed) as whitespace delimiters.
5118 RFC 9112, section 3 */
5119 const bool other_wsp_as_wsp = (-1 >= discp_lvl);
5120 /* Treat continuous whitespace block as a single space.
5121 RFC 9112, section 3 */
5122 const bool wsp_blocks = (-1 >= discp_lvl);
5123 /* Parse whitespace in URI, special parsing of the request line.
5124 RFC 9112, section 3.2 */
5125 const bool wsp_in_uri = (0 >= discp_lvl);
5126 /* Keep whitespace in URI, give app URI with whitespace instead of
5127 automatic redirect to fixed URI.
5128 Violates RFC 9112, section 3.2 */
5129 const bool wsp_in_uri_keep = (-2 >= discp_lvl);
5130 /* Keep bare CR character as is.
5131 Violates RFC 9112, section 2.2 */
5132 const bool bare_cr_keep = (wsp_in_uri_keep && (-3 >= discp_lvl));
5133 /* Treat bare CR as space; replace it with space before processing.
5134 RFC 9112, section 2.2 */
5135 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
5136
5139 mhd_assert (NULL == c->rq.method || \
5144 0 != c->rq.hdrs.rq_line.proc_pos);
5145
5146 if (0 == c->read_buffer_offset)
5147 {
5149 return false; /* No data to process */
5150 }
5151 p = c->rq.hdrs.rq_line.proc_pos;
5152 mhd_assert (p <= c->read_buffer_offset);
5153
5154 /* Skip empty lines, if any (and if allowed) */
5155 /* See RFC 9112, section 2.2 */
5156 if ((0 == p)
5157 && (skip_empty_lines))
5158 {
5159 /* Skip empty lines before the request line.
5160 See RFC 9112, section 2.2 */
5161 bool is_empty_line;
5163 mhd_assert (NULL == c->rq.method);
5164 mhd_assert (NULL == c->rq.url);
5165 mhd_assert (0 == c->rq.url_len);
5167 mhd_assert (0 == c->rq.req_target_len);
5168 mhd_assert (NULL == c->rq.version);
5169 do
5170 {
5171 is_empty_line = false;
5172 if ('\r' == c->read_buffer[0])
5173 {
5174 if (1 == c->read_buffer_offset)
5175 return false; /* Not enough data yet */
5176 if ('\n' == c->read_buffer[1])
5177 {
5178 is_empty_line = true;
5179 c->read_buffer += 2;
5180 c->read_buffer_size -= 2;
5181 c->read_buffer_offset -= 2;
5183 }
5184 }
5185 else if (('\n' == c->read_buffer[0]) &&
5186 (bare_lf_as_crlf))
5187 {
5188 is_empty_line = true;
5189 c->read_buffer += 1;
5190 c->read_buffer_size -= 1;
5191 c->read_buffer_offset -= 1;
5193 }
5194 if (is_empty_line)
5195 {
5196 if ((! skip_unlimited_empty_lines) &&
5197 (((unsigned int) ((skip_several_empty_lines) ?
5200 {
5202 _ ("Too many meaningless extra empty lines " \
5203 "received before the request"));
5204 return true; /* Process connection closure */
5205 }
5206 if (0 == c->read_buffer_offset)
5207 return false; /* No more data to process */
5208 }
5209 } while (is_empty_line);
5210 }
5211 /* All empty lines are skipped */
5212
5214 /* Read and parse the request line */
5216
5217 while (p < c->read_buffer_offset)
5218 {
5219 const char chr = c->read_buffer[p];
5220 bool end_of_line;
5221 /*
5222 The processing logic is different depending on the configured strictness:
5223
5224 When whitespace BLOCKS are NOT ALLOWED, the end of the whitespace is
5225 processed BEFORE processing of the current character.
5226 When whitespace BLOCKS are ALLOWED, the end of the whitespace is
5227 processed AFTER processing of the current character.
5228
5229 When space char in the URI is ALLOWED, the delimiter between the URI and
5230 the HTTP version string is processed only at the END of the line.
5231 When space in the URI is NOT ALLOWED, the delimiter between the URI and
5232 the HTTP version string is processed as soon as the FIRST whitespace is
5233 found after URI start.
5234 */
5235
5236 end_of_line = false;
5237
5238 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5239 (c->rq.hdrs.rq_line.last_ws_end > \
5241 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_start) || \
5242 (0 != c->rq.hdrs.rq_line.last_ws_end));
5243
5244 /* Check for the end of the line */
5245 if ('\r' == chr)
5246 {
5247 if (p + 1 == c->read_buffer_offset)
5248 {
5249 c->rq.hdrs.rq_line.proc_pos = p;
5250 return false; /* Not enough data yet */
5251 }
5252 else if ('\n' == c->read_buffer[p + 1])
5253 end_of_line = true;
5254 else
5255 {
5256 /* Bare CR alone */
5257 /* Must be rejected or replaced with space char.
5258 See RFC 9112, section 2.2 */
5259 if (bare_cr_as_sp)
5260 {
5261 c->read_buffer[p] = ' ';
5263 continue; /* Re-start processing of the current character */
5264 }
5265 else if (! bare_cr_keep)
5266 {
5267 /* A quick simple check whether this line looks like an HTTP request */
5268 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5270 {
5274 }
5275 else
5277 _ ("Bare CR characters are not allowed " \
5278 "in the request line.\n"));
5279 return true; /* Error in the request */
5280 }
5281 }
5282 }
5283 else if ('\n' == chr)
5284 {
5285 /* Bare LF may be recognised as a line delimiter.
5286 See RFC 9112, section 2.2 */
5287 if (bare_lf_as_crlf)
5288 end_of_line = true;
5289 else
5290 {
5291 /* While RFC does not enforce error for bare LF character,
5292 if this char is not treated as a line delimiter, it should be
5293 rejected to avoid any security weakness due to request smuggling. */
5294 /* A quick simple check whether this line looks like an HTTP request */
5295 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5297 {
5301 }
5302 else
5304 _ ("Bare LF characters are not allowed " \
5305 "in the request line.\n"));
5306 return true; /* Error in the request */
5307 }
5308 }
5309
5310 if (end_of_line)
5311 {
5312 /* Handle the end of the request line */
5313
5314 if (NULL != c->rq.method)
5315 {
5316 if (wsp_in_uri)
5317 {
5318 /* The end of the URI and the start of the HTTP version string
5319 should be determined now. */
5320 mhd_assert (NULL == c->rq.version);
5321 mhd_assert (0 == c->rq.req_target_len);
5322 if (0 != c->rq.hdrs.rq_line.last_ws_end)
5323 {
5324 /* Determine the end and the length of the URI */
5325 if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5326 {
5327 c->read_buffer [c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5328 c->rq.req_target_len =
5330 - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5331 }
5332 else if ((c->rq.hdrs.rq_line.last_ws_start + 1 <
5333 c->rq.hdrs.rq_line.last_ws_end) &&
5334 (HTTP_VER_LEN == (p - c->rq.hdrs.rq_line.last_ws_end)))
5335 {
5336 /* Found only HTTP method and HTTP version and more than one
5337 whitespace between them. Assume zero-length URI. */
5338 mhd_assert (wsp_blocks);
5340 c->read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5341 c->rq.hdrs.rq_line.rq_tgt =
5343 c->rq.req_target_len = 0;
5346 }
5347 /* Determine the start of the HTTP version string */
5348 if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5349 {
5351 }
5352 }
5353 }
5354 else
5355 {
5356 /* The end of the URI and the start of the HTTP version string
5357 should be already known. */
5358 if ((NULL == c->rq.version)
5359 && (NULL != c->rq.hdrs.rq_line.rq_tgt)
5360 && (HTTP_VER_LEN == p - (size_t) (c->rq.hdrs.rq_line.rq_tgt
5361 - c->read_buffer))
5362 && (0 != c->read_buffer[(size_t)
5363 (c->rq.hdrs.rq_line.rq_tgt
5364 - c->read_buffer) - 1]))
5365 {
5366 /* Found only HTTP method and HTTP version and more than one
5367 whitespace between them. Assume zero-length URI. */
5368 size_t uri_pos;
5369 mhd_assert (wsp_blocks);
5370 mhd_assert (0 == c->rq.req_target_len);
5371 uri_pos = (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer) - 1;
5372 mhd_assert (uri_pos < p);
5373 c->rq.version = c->rq.hdrs.rq_line.rq_tgt;
5374 c->read_buffer[uri_pos] = 0; /* Zero terminate the URI */
5375 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + uri_pos;
5376 c->rq.req_target_len = 0;
5379 }
5380 }
5381
5382 if (NULL != c->rq.version)
5383 {
5385 if (! parse_http_version (c, c->rq.version,
5386 p
5387 - (size_t) (c->rq.version
5388 - c->read_buffer)))
5389 {
5391 return true; /* Unsupported / broken HTTP version */
5392 }
5393 c->read_buffer[p] = 0; /* Zero terminate the HTTP version strings */
5394 if ('\r' == chr)
5395 {
5396 p++; /* Consume CR */
5397 mhd_assert (p < c->read_buffer_offset); /* The next character has been already checked */
5398 }
5399 p++; /* Consume LF */
5400 c->read_buffer += p;
5401 c->read_buffer_size -= p;
5402 c->read_buffer_offset -= p;
5404 c->rq.req_target_len);
5406 (0 != c->rq.req_target_len));
5408 ((size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5409 - c->rq.hdrs.rq_line.rq_tgt) < \
5410 c->rq.req_target_len));
5412 (c->rq.hdrs.rq_line.rq_tgt_qmark >= \
5413 c->rq.hdrs.rq_line.rq_tgt));
5414 return true; /* The request line is successfully parsed */
5415 }
5416 }
5417 /* Error in the request line */
5418
5419 /* A quick simple check whether this line looks like an HTTP request */
5420 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5422 {
5426 }
5427 else
5429 _ ("The request line is malformed.\n"));
5430
5431 return true;
5432 }
5433
5434 /* Process possible end of the previously found whitespace delimiter */
5435 if ((! wsp_blocks) &&
5436 (p == c->rq.hdrs.rq_line.last_ws_end) &&
5437 (0 != c->rq.hdrs.rq_line.last_ws_end))
5438 {
5439 /* Previous character was a whitespace char and whitespace blocks
5440 are not allowed. */
5441 /* The current position is the next character after
5442 a whitespace delimiter */
5443 if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5444 {
5445 /* The current position is the start of the URI */
5446 mhd_assert (0 == c->rq.req_target_len);
5447 mhd_assert (NULL == c->rq.version);
5448 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5449 /* Reset the whitespace marker */
5451 c->rq.hdrs.rq_line.last_ws_end = 0;
5452 }
5453 else
5454 {
5455 /* It was a whitespace after the start of the URI */
5456 if (! wsp_in_uri)
5457 {
5458 mhd_assert ((0 != c->rq.req_target_len) || \
5459 (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5460 mhd_assert (NULL == c->rq.version); /* Too many whitespaces? This error is handled at whitespace start */
5461 c->rq.version = c->read_buffer + p;
5462 /* Reset the whitespace marker */
5464 c->rq.hdrs.rq_line.last_ws_end = 0;
5465 }
5466 }
5467 }
5468
5469 /* Process the current character.
5470 Is it not the end of the line. */
5471 if ((' ' == chr)
5472 || (('\t' == chr) && (tab_as_wsp))
5473 || ((other_wsp_as_wsp) && ((0xb == chr) || (0xc == chr))))
5474 {
5475 /* A whitespace character */
5476 if ((0 == c->rq.hdrs.rq_line.last_ws_end) ||
5477 (p != c->rq.hdrs.rq_line.last_ws_end) ||
5478 (! wsp_blocks))
5479 {
5480 /* Found first whitespace char of the new whitespace block */
5481 if (NULL == c->rq.method)
5482 {
5483 /* Found the end of the HTTP method string */
5487 mhd_assert (0 == c->rq.req_target_len);
5488 mhd_assert (NULL == c->rq.version);
5489 if (0 == p)
5490 {
5492 _ ("The request line starts with "
5493 "a whitespace.\n"));
5494 return true; /* Error in the request */
5495 }
5496 c->read_buffer[p] = 0; /* Zero-terminate the request method string */
5497 c->rq.method = c->read_buffer;
5498 parse_http_std_method (c, c->rq.method, p);
5499 }
5500 else
5501 {
5502 /* A whitespace after the start of the URI */
5503 if (! wsp_in_uri)
5504 {
5505 /* Whitespace in URI is not allowed to be parsed */
5506 if (NULL == c->rq.version)
5507 {
5509 /* This is a delimiter between URI and HTTP version string */
5510 c->read_buffer[p] = 0; /* Zero-terminate request URI string */
5511 mhd_assert (((size_t) (c->rq.hdrs.rq_line.rq_tgt \
5512 - c->read_buffer)) <= p);
5513 c->rq.req_target_len =
5514 p - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5515 }
5516 else
5517 {
5518 /* This is a delimiter AFTER version string */
5519
5520 /* A quick simple check whether this line looks like an HTTP request */
5521 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5523 {
5527 }
5528 else
5530 _ ("The request line has more than "
5531 "two whitespaces.\n"));
5532 return true; /* Error in the request */
5533 }
5534 }
5535 else
5536 {
5537 /* Whitespace in URI is allowed to be parsed */
5538 if (0 != c->rq.hdrs.rq_line.last_ws_end)
5539 {
5540 /* The whitespace after the start of the URI has been found already */
5544 }
5545 }
5546 }
5548 c->rq.hdrs.rq_line.last_ws_end = p + 1; /* Will be updated on the next char parsing */
5549 }
5550 else
5551 {
5552 /* Continuation of the whitespace block */
5554 mhd_assert (0 != p);
5555 c->rq.hdrs.rq_line.last_ws_end = p + 1;
5556 }
5557 }
5558 else
5559 {
5560 /* Non-whitespace char, not the end of the line */
5561 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5562 (c->rq.hdrs.rq_line.last_ws_end == p) || \
5563 wsp_in_uri);
5564
5565 if ((p == c->rq.hdrs.rq_line.last_ws_end) &&
5566 (0 != c->rq.hdrs.rq_line.last_ws_end) &&
5567 (wsp_blocks))
5568 {
5569 /* The end of the whitespace block */
5570 if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5571 {
5572 /* This is the first character of the URI */
5573 mhd_assert (0 == c->rq.req_target_len);
5574 mhd_assert (NULL == c->rq.version);
5575 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5576 /* Reset the whitespace marker */
5578 c->rq.hdrs.rq_line.last_ws_end = 0;
5579 }
5580 else
5581 {
5582 if (! wsp_in_uri)
5583 {
5584 /* This is the first character of the HTTP version */
5586 mhd_assert ((0 != c->rq.req_target_len) || \
5587 (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5588 mhd_assert (NULL == c->rq.version); /* Handled at whitespace start */
5589 c->rq.version = c->read_buffer + p;
5590 /* Reset the whitespace marker */
5592 c->rq.hdrs.rq_line.last_ws_end = 0;
5593 }
5594 }
5595 }
5596
5597 /* Handle other special characters */
5598 if ('?' == chr)
5599 {
5600 if ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) &&
5601 (NULL != c->rq.hdrs.rq_line.rq_tgt))
5602 {
5604 }
5605 }
5606 else if ((0xb == chr) || (0xc == chr))
5607 {
5608 /* VT or LF characters */
5609 mhd_assert (! other_wsp_as_wsp);
5610 if ((NULL != c->rq.hdrs.rq_line.rq_tgt) &&
5611 (NULL == c->rq.version) &&
5612 (wsp_in_uri))
5613 {
5615 }
5616 else
5617 {
5619 _ ("Invalid character is in the "
5620 "request line.\n"));
5621 return true; /* Error in the request */
5622 }
5623 }
5624 else if (0 == chr)
5625 {
5626 /* NUL character */
5628 _ ("The NUL character is in the "
5629 "request line.\n"));
5630 return true; /* Error in the request */
5631 }
5632 }
5633
5634 p++;
5635 }
5636
5637 c->rq.hdrs.rq_line.proc_pos = p;
5638 return false; /* Not enough data yet */
5639}
5640
5641
5642#ifndef MHD_MAX_FIXED_URI_LEN
5646#define MHD_MAX_FIXED_URI_LEN (64 * 1024)
5647#endif /* ! MHD_MAX_FIXED_URI_LEN */
5648
5656static void
5658{
5659 char *b;
5660 size_t fixed_uri_len;
5661 size_t i;
5662 size_t o;
5663 char *hdr_name;
5664 size_t hdr_name_len;
5665
5669 c->rq.req_target_len);
5670 fixed_uri_len = c->rq.req_target_len
5671 + 2 * c->rq.hdrs.rq_line.num_ws_in_uri;
5672 if ( (fixed_uri_len + 200 > c->daemon->pool_size) ||
5673 (fixed_uri_len > MHD_MAX_FIXED_URI_LEN) ||
5674 (NULL == (b = malloc (fixed_uri_len + 1))) )
5675 {
5677 _ ("The request has whitespace character is " \
5678 "in the URI and the URI is too large to " \
5679 "send automatic redirect to fixed URI.\n"));
5680 return;
5681 }
5682 i = 0;
5683 o = 0;
5684
5685 do
5686 {
5687 const char chr = c->rq.hdrs.rq_line.rq_tgt[i++];
5688
5689 mhd_assert ('\r' != chr); /* Replaced during request line parsing */
5690 mhd_assert ('\n' != chr); /* Rejected during request line parsing */
5691 mhd_assert (0 != chr); /* Rejected during request line parsing */
5692 switch (chr)
5693 {
5694 case ' ':
5695 b[o++] = '%';
5696 b[o++] = '2';
5697 b[o++] = '0';
5698 break;
5699 case '\t':
5700 b[o++] = '%';
5701 b[o++] = '0';
5702 b[o++] = '9';
5703 break;
5704 case 0x0B: /* VT (vertical tab) */
5705 b[o++] = '%';
5706 b[o++] = '0';
5707 b[o++] = 'B';
5708 break;
5709 case 0x0C: /* FF (form feed) */
5710 b[o++] = '%';
5711 b[o++] = '0';
5712 b[o++] = 'C';
5713 break;
5714 default:
5715 b[o++] = chr;
5716 break;
5717 }
5718 } while (i < c->rq.req_target_len);
5719 mhd_assert (fixed_uri_len == o);
5720 b[o] = 0; /* Zero-terminate the result */
5721
5723 hdr_name = malloc (hdr_name_len + 1);
5724 if (NULL != hdr_name)
5725 {
5726 memcpy (hdr_name,
5728 hdr_name_len + 1);
5729 /* hdr_name and b are free()d within this call */
5733 hdr_name,
5734 hdr_name_len,
5735 b,
5736 o);
5737 return;
5738 }
5739 free (b);
5741 _ ("The request has whitespace character is in the " \
5742 "URI.\n"));
5743 return;
5744}
5745
5746
5753static bool
5755{
5756#ifdef _DEBUG
5757 size_t params_len;
5758#endif /* _DEBUG */
5760 mhd_assert (NULL == c->rq.url);
5761 mhd_assert (0 == c->rq.url_len);
5767 (c->rq.req_target_len > \
5768 (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5769 - c->rq.hdrs.rq_line.rq_tgt)));
5770
5771 /* Log callback before the request-target is modified/decoded */
5772 if (NULL != c->daemon->uri_log_callback)
5773 {
5774 c->rq.client_aware = true;
5775 c->rq.client_context =
5777 c->rq.hdrs.rq_line.rq_tgt,
5778 c);
5779 }
5780
5781 if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark)
5782 {
5783#ifdef _DEBUG
5784 params_len =
5786 - (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark - c->rq.hdrs.rq_line.rq_tgt);
5787#endif /* _DEBUG */
5788 c->rq.hdrs.rq_line.rq_tgt_qmark[0] = 0; /* Replace '?' with zero termination */
5789 if (MHD_NO == MHD_parse_arguments_ (c,
5791 c->rq.hdrs.rq_line.rq_tgt_qmark + 1,
5793 c))
5794 {
5796 return false;
5797 }
5798 }
5799#ifdef _DEBUG
5800 else
5801 params_len = 0;
5802#endif /* _DEBUG */
5803
5805 mhd_assert (strlen (c->rq.hdrs.rq_line.rq_tgt) == \
5806 c->rq.req_target_len - params_len);
5807
5808 /* Finally unescape URI itself */
5809 c->rq.url_len =
5811 c,
5812 c->rq.hdrs.rq_line.rq_tgt);
5813 c->rq.url = c->rq.hdrs.rq_line.rq_tgt;
5814
5815 if (2 == c->daemon->allow_bzero_in_url)
5816 c->rq.url_for_callback = c->rq.url;
5817 else if (strlen (c->rq.url) == c->rq.url_len)
5818 c->rq.url_for_callback = c->rq.url;
5819 else if (0 == c->daemon->allow_bzero_in_url)
5820 {
5824 return false;
5825 }
5826
5827 return true;
5828}
5829
5830
5838static bool
5840{
5841 const int discp_lvl = c->daemon->client_discipline;
5842 /* Parse whitespace in URI, special parsing of the request line */
5843 const bool wsp_in_uri = (0 >= discp_lvl);
5844 /* Keep whitespace in URI, give app URI with whitespace instead of
5845 automatic redirect to fixed URI */
5846 const bool wsp_in_uri_keep = (-2 >= discp_lvl);
5847
5848 if (! get_request_line_inner (c))
5849 {
5850 /* End of the request line has not been found yet */
5851 mhd_assert ((! wsp_in_uri) || NULL == c->rq.version);
5852 if ((NULL != c->rq.version) &&
5853 (HTTP_VER_LEN <
5855 - (size_t) (c->rq.version - c->read_buffer))))
5856 {
5861 return true; /* Error in the request */
5862 }
5863 return false;
5864 }
5866 return true; /* Error in the request */
5867
5869 mhd_assert (NULL == c->rq.url);
5870 mhd_assert (0 == c->rq.url_len);
5873 if (0 != c->rq.hdrs.rq_line.num_ws_in_uri)
5874 {
5875 if (! wsp_in_uri)
5876 {
5880 return true; /* Error in the request */
5881 }
5882 if (! wsp_in_uri_keep)
5883 {
5885 return true; /* Error in the request */
5886 }
5887 }
5888 if (! process_request_target (c))
5889 return true; /* Error in processing */
5890
5892 return true;
5893}
5894
5895
5918
5919
5927static bool
5929{
5930 switch (chr)
5931 {
5932 case '!':
5933 case '#':
5934 case '$':
5935 case '%':
5936 case '&':
5937 case '\'':
5938 case '*':
5939 case '+':
5940 case '-':
5941 case '.':
5942 case '^':
5943 case '_':
5944 case '`':
5945 case '|':
5946 case '~':
5947 case 'a':
5948 case 'b':
5949 case 'c':
5950 case 'd':
5951 case 'e':
5952 case 'f':
5953 case 'g':
5954 case 'h':
5955 case 'i':
5956 case 'j':
5957 case 'k':
5958 case 'l':
5959 case 'm':
5960 case 'n':
5961 case 'o':
5962 case 'p':
5963 case 'q':
5964 case 'r':
5965 case 's':
5966 case 't':
5967 case 'u':
5968 case 'v':
5969 case 'w':
5970 case 'x':
5971 case 'y':
5972 case 'z':
5973 case 'A':
5974 case 'B':
5975 case 'C':
5976 case 'D':
5977 case 'E':
5978 case 'F':
5979 case 'G':
5980 case 'H':
5981 case 'I':
5982 case 'J':
5983 case 'K':
5984 case 'L':
5985 case 'M':
5986 case 'N':
5987 case 'O':
5988 case 'P':
5989 case 'Q':
5990 case 'R':
5991 case 'S':
5992 case 'T':
5993 case 'U':
5994 case 'V':
5995 case 'W':
5996 case 'X':
5997 case 'Y':
5998 case 'Z':
5999 case '0':
6000 case '1':
6001 case '2':
6002 case '3':
6003 case '4':
6004 case '5':
6005 case '6':
6006 case '7':
6007 case '8':
6008 case '9':
6009 return true;
6010 default:
6011 return false;
6012 }
6013}
6014
6015
6027static enum MHD_HdrLineReadRes_
6029 bool process_footers,
6030 struct _MHD_str_w_len *hdr_name,
6031 struct _MHD_str_w_len *hdr_value)
6032{
6033 const int discp_lvl = c->daemon->client_discipline;
6034 /* Treat bare LF as the end of the line.
6035 RFC 9112, section 2.2-3
6036 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
6037 Bare LF is processed as end of the line or rejected as broken request. */
6038 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
6039 /* Keep bare CR character as is.
6040 Violates RFC 9112, section 2.2-4 */
6041 const bool bare_cr_keep = (-3 >= discp_lvl);
6042 /* Treat bare CR as space; replace it with space before processing.
6043 RFC 9112, section 2.2-4 */
6044 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
6045 /* Treat NUL as space; replace it with space before processing.
6046 RFC 9110, section 5.5-5 */
6047 const bool nul_as_sp = (-1 >= discp_lvl);
6048 /* Allow folded header lines.
6049 RFC 9112, section 5.2-4 */
6050 const bool allow_folded = (0 >= discp_lvl);
6051 /* Do not reject headers with the whitespace at the start of the first line.
6052 When allowed, the first line with whitespace character at the first
6053 position is ignored (as well as all possible line foldings of the first
6054 line).
6055 RFC 9112, section 2.2-8 */
6056 const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl);
6057 /* Allow whitespace in header (field) name.
6058 Violates RFC 9110, section 5.1-2 */
6059 const bool allow_wsp_in_name = (-2 >= discp_lvl);
6060 /* Allow zero-length header (field) name.
6061 Violates RFC 9110, section 5.1-2 */
6062 const bool allow_empty_name = (-2 >= discp_lvl);
6063 /* Allow non-tchar characters in header (field) name.
6064 Violates RFC 9110, section 5.1 */
6065 const bool allow_extended_charset = (-2 >= discp_lvl);
6066 /* Allow whitespace before colon.
6067 Violates RFC 9112, section 5.1-2 */
6068 const bool allow_wsp_before_colon = (-3 >= discp_lvl);
6069 /* Do not abort the request when header line has no colon, just skip such
6070 bad lines.
6071 RFC 9112, section 5-1 */
6072 const bool allow_line_without_colon = (-2 >= discp_lvl);
6073
6074 size_t p;
6075
6076#if ! defined (HAVE_MESSAGES) && ! defined(_DEBUG)
6077 (void) process_footers; /* Unused parameter */
6078#endif /* !HAVE_MESSAGES && !_DEBUG */
6079
6080 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6082 c->state);
6083
6084 p = c->rq.hdrs.hdr.proc_pos;
6085
6086 mhd_assert (p <= c->read_buffer_offset);
6087 while (p < c->read_buffer_offset)
6088 {
6089 const char chr = c->read_buffer[p];
6090 bool end_of_line;
6091
6092 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
6093 (c->rq.hdrs.hdr.name_len < p));
6094 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p));
6095 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
6096 (c->rq.hdrs.hdr.name_end_found));
6097 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
6098 (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start));
6099 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
6100 (0 != c->rq.hdrs.hdr.name_len));
6101 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
6102 (0 == c->rq.hdrs.hdr.name_len) || \
6103 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len));
6104 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
6105 (0 == c->rq.hdrs.hdr.value_start) || \
6106 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start));
6107
6108 /* Check for the end of the line */
6109 if ('\r' == chr)
6110 {
6111 if (0 != p)
6112 {
6113 /* Line is not empty, need to check for possible line folding */
6114 if (p + 2 >= c->read_buffer_offset)
6115 break; /* Not enough data yet to check for folded line */
6116 }
6117 else
6118 {
6119 /* Line is empty, no need to check for possible line folding */
6120 if (p + 2 > c->read_buffer_offset)
6121 break; /* Not enough data yet to check for the end of the line */
6122 }
6123 if ('\n' == c->read_buffer[p + 1])
6124 end_of_line = true;
6125 else
6126 {
6127 /* Bare CR alone */
6128 /* Must be rejected or replaced with space char.
6129 See RFC 9112, section 2.2-4 */
6130 if (bare_cr_as_sp)
6131 {
6132 c->read_buffer[p] = ' ';
6134 continue; /* Re-start processing of the current character */
6135 }
6136 else if (! bare_cr_keep)
6137 {
6138 if (! process_footers)
6142 else
6146 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6147 }
6148 end_of_line = false;
6149 }
6150 }
6151 else if ('\n' == chr)
6152 {
6153 /* Bare LF may be recognised as a line delimiter.
6154 See RFC 9112, section 2.2-3 */
6155 if (bare_lf_as_crlf)
6156 {
6157 if (0 != p)
6158 {
6159 /* Line is not empty, need to check for possible line folding */
6160 if (p + 1 >= c->read_buffer_offset)
6161 break; /* Not enough data yet to check for folded line */
6162 }
6163 end_of_line = true;
6164 }
6165 else
6166 {
6167 if (! process_footers)
6171 else
6175 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6176 }
6177 }
6178 else
6179 end_of_line = false;
6180
6181 if (end_of_line)
6182 {
6183 /* Handle the end of the line */
6187 const size_t line_len = p + (('\r' == chr) ? 2 : 1);
6188 char next_line_char;
6189 mhd_assert (line_len <= c->read_buffer_offset);
6190
6191 if (0 == p)
6192 {
6193 /* Zero-length header line. This is the end of the request header
6194 section.
6195 RFC 9112, Section 2.1-1 */
6198 mhd_assert (0 == c->rq.hdrs.hdr.name_len);
6199 mhd_assert (0 == c->rq.hdrs.hdr.ws_start);
6200 mhd_assert (0 == c->rq.hdrs.hdr.value_start);
6201 /* Consume the line with CRLF (or bare LF) */
6202 c->read_buffer += line_len;
6203 c->read_buffer_offset -= line_len;
6204 c->read_buffer_size -= line_len;
6206 }
6207
6208 mhd_assert (line_len < c->read_buffer_offset);
6209 mhd_assert (0 != line_len);
6210 mhd_assert ('\n' == c->read_buffer[line_len - 1]);
6211 next_line_char = c->read_buffer[line_len];
6212 if ((' ' == next_line_char) ||
6213 ('\t' == next_line_char))
6214 {
6215 /* Folded line */
6216 if (! allow_folded)
6217 {
6218 if (! process_footers)
6222 else
6226
6227 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6228 }
6229 /* Replace CRLF (or bare LF) character(s) with space characters.
6230 See RFC 9112, Section 5.2-4 */
6231 c->read_buffer[p] = ' ';
6232 if ('\r' == chr)
6233 c->read_buffer[p + 1] = ' ';
6234 continue; /* Re-start processing of the current character */
6235 }
6236 else
6237 {
6238 /* It is not a folded line, it's the real end of the non-empty line */
6239 bool skip_line = false;
6240 mhd_assert (0 != p);
6241 if (c->rq.hdrs.hdr.starts_with_ws)
6242 {
6243 /* This is the first line and it starts with whitespace. This line
6244 must be discarded completely.
6245 See RFC 9112, Section 2.2-8 */
6246 mhd_assert (allow_wsp_at_start);
6247#ifdef HAVE_MESSAGES
6248 MHD_DLOG (c->daemon,
6249 _ ("Whitespace-prefixed first header line " \
6250 "has been skipped.\n"));
6251#endif /* HAVE_MESSAGES */
6252 skip_line = true;
6253 }
6254 else if (! c->rq.hdrs.hdr.name_end_found)
6255 {
6256 if (! allow_line_without_colon)
6257 {
6258 if (! process_footers)
6262 else
6266
6267 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6268 }
6269 /* Skip broken line completely */
6271 skip_line = true;
6272 }
6273 if (skip_line)
6274 {
6275 /* Skip the entire line */
6276 c->read_buffer += line_len;
6277 c->read_buffer_offset -= line_len;
6278 c->read_buffer_size -= line_len;
6279 p = 0;
6280 /* Reset processing state */
6281 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
6282 /* Start processing of the next line */
6283 continue;
6284 }
6285 else
6286 {
6287 /* This line should be valid header line */
6288 size_t value_len;
6289 mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name);
6290
6291 hdr_name->str = c->read_buffer + 0; /* The name always starts at the first character */
6292 hdr_name->len = c->rq.hdrs.hdr.name_len;
6293 mhd_assert (0 == hdr_name->str[hdr_name->len]);
6294
6295 if (0 == c->rq.hdrs.hdr.value_start)
6296 {
6297 c->rq.hdrs.hdr.value_start = p;
6298 c->read_buffer[p] = 0;
6299 value_len = 0;
6300 }
6301 else if (0 != c->rq.hdrs.hdr.ws_start)
6302 {
6303 mhd_assert (p > c->rq.hdrs.hdr.ws_start);
6305 c->read_buffer[c->rq.hdrs.hdr.ws_start] = 0;
6306 value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start;
6307 }
6308 else
6309 {
6310 mhd_assert (p > c->rq.hdrs.hdr.ws_start);
6311 c->read_buffer[p] = 0;
6312 value_len = p - c->rq.hdrs.hdr.value_start;
6313 }
6314 hdr_value->str = c->read_buffer + c->rq.hdrs.hdr.value_start;
6315 hdr_value->len = value_len;
6316 mhd_assert (0 == hdr_value->str[hdr_value->len]);
6317 /* Consume the entire line */
6318 c->read_buffer += line_len;
6319 c->read_buffer_offset -= line_len;
6320 c->read_buffer_size -= line_len;
6322 }
6323 }
6324 }
6325 else if ((' ' == chr) || ('\t' == chr))
6326 {
6327 if (0 == p)
6328 {
6329 if (! allow_wsp_at_start)
6330 {
6331 if (! process_footers)
6335 else
6339 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6340 }
6341 c->rq.hdrs.hdr.starts_with_ws = true;
6342 }
6343 else if ((! c->rq.hdrs.hdr.name_end_found) &&
6344 (! c->rq.hdrs.hdr.starts_with_ws))
6345 {
6346 /* Whitespace in header name / between header name and colon */
6347 if (allow_wsp_in_name || allow_wsp_before_colon)
6348 {
6349 if (0 == c->rq.hdrs.hdr.ws_start)
6350 c->rq.hdrs.hdr.ws_start = p;
6351 }
6352 else
6353 {
6354 if (! process_footers)
6358 else
6362
6363 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6364 }
6365 }
6366 else
6367 {
6368 /* Whitespace before/inside/after header (field) value */
6369 if (0 == c->rq.hdrs.hdr.ws_start)
6370 c->rq.hdrs.hdr.ws_start = p;
6371 }
6372 }
6373 else if (0 == chr)
6374 {
6375 if (! nul_as_sp)
6376 {
6377 if (! process_footers)
6381 else
6385
6386 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6387 }
6388 c->read_buffer[p] = ' ';
6389 continue; /* Re-start processing of the current character */
6390 }
6391 else
6392 {
6393 /* Not a whitespace, not the end of the header line */
6394 mhd_assert ('\r' != chr);
6395 mhd_assert ('\n' != chr);
6396 mhd_assert ('\0' != chr);
6397 if ( (! c->rq.hdrs.hdr.name_end_found) &&
6398 (! c->rq.hdrs.hdr.starts_with_ws) )
6399 {
6400 /* Processing the header (field) name */
6401 if ( (! allow_extended_charset) &&
6402 (':' != chr) &&
6403 (! char_legal_in_field_name (chr)) )
6404 {
6409 }
6410
6411 if (':' == chr)
6412 {
6413 if (0 == c->rq.hdrs.hdr.ws_start)
6414 c->rq.hdrs.hdr.name_len = p;
6415 else
6416 {
6417 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6418 if (! allow_wsp_before_colon)
6419 {
6420 if (! process_footers)
6424 else
6428 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6429 }
6431#ifndef MHD_FAVOR_SMALL_CODE
6432 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6433#endif /* ! MHD_FAVOR_SMALL_CODE */
6434 }
6435 if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name)
6436 {
6437 if (! process_footers)
6441 else
6445 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6446 }
6447 c->rq.hdrs.hdr.name_end_found = true;
6448 c->read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */
6449 }
6450 else
6451 {
6452 if (0 != c->rq.hdrs.hdr.ws_start)
6453 {
6454 /* End of the whitespace in header (field) name */
6455 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6456 if (! allow_wsp_in_name)
6457 {
6458 if (! process_footers)
6462 else
6466
6467 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6468 }
6469#ifndef MHD_FAVOR_SMALL_CODE
6470 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6471#endif /* ! MHD_FAVOR_SMALL_CODE */
6472 }
6473 }
6474 }
6475 else
6476 {
6477 /* Processing the header (field) value */
6478 if (0 == c->rq.hdrs.hdr.value_start)
6479 c->rq.hdrs.hdr.value_start = p;
6480#ifndef MHD_FAVOR_SMALL_CODE
6481 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6482#endif /* ! MHD_FAVOR_SMALL_CODE */
6483 }
6484#ifdef MHD_FAVOR_SMALL_CODE
6485 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6486#endif /* MHD_FAVOR_SMALL_CODE */
6487 }
6488 p++;
6489 }
6490 c->rq.hdrs.hdr.proc_pos = p;
6491 return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */
6492}
6493
6494
6505static bool
6506get_req_headers (struct MHD_Connection *c, bool process_footers)
6507{
6508 do
6509 {
6510 struct _MHD_str_w_len hdr_name;
6511 struct _MHD_str_w_len hdr_value;
6512 enum MHD_HdrLineReadRes_ res;
6513
6514 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6516 c->state);
6517
6518 #ifdef _DEBUG
6519 hdr_name.str = NULL;
6520 hdr_value.str = NULL;
6521#endif /* _DEBUG */
6522 res = get_req_header (c, process_footers, &hdr_name, &hdr_value);
6524 {
6525 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6527 c->state);
6528 mhd_assert (NULL != hdr_name.str);
6529 mhd_assert (NULL != hdr_value.str);
6530 /* Values must be zero-terminated and must not have binary zeros */
6531 mhd_assert (strlen (hdr_name.str) == hdr_name.len);
6532 mhd_assert (strlen (hdr_value.str) == hdr_value.len);
6533 /* Values must not have whitespaces at the start or at the end */
6534 mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != ' '));
6535 mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != '\t'));
6536 mhd_assert ((hdr_name.len == 0) || \
6537 (hdr_name.str[hdr_name.len - 1] != ' '));
6538 mhd_assert ((hdr_name.len == 0) || \
6539 (hdr_name.str[hdr_name.len - 1] != '\t'));
6540 mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != ' '));
6541 mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != '\t'));
6542 mhd_assert ((hdr_value.len == 0) || \
6543 (hdr_value.str[hdr_value.len - 1] != ' '));
6544 mhd_assert ((hdr_value.len == 0) || \
6545 (hdr_value.str[hdr_value.len - 1] != '\t'));
6546
6547 if (MHD_NO ==
6549 (! process_footers) ?
6552 hdr_name.str, hdr_name.len,
6553 hdr_value.str, hdr_value.len))
6554 {
6555 size_t add_element_size;
6556
6557 mhd_assert (hdr_name.str < hdr_value.str);
6558
6559#ifdef HAVE_MESSAGES
6560 MHD_DLOG (c->daemon,
6561 _ ("Failed to allocate memory in the connection memory " \
6562 "pool to store %s.\n"),
6563 (! process_footers) ? _ ("header") : _ ("footer"));
6564#endif /* HAVE_MESSAGES */
6565
6566 add_element_size = hdr_value.len
6567 + (size_t) (hdr_value.str - hdr_name.str);
6568
6569 if (! process_footers)
6570 handle_req_headers_no_space (c, hdr_name.str, add_element_size);
6571 else
6572 handle_req_footers_no_space (c, hdr_name.str, add_element_size);
6573
6575 return true;
6576 }
6577 /* Reset processing state */
6579 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6581 c->state);
6582 /* Read the next header (field) line */
6583 continue;
6584 }
6586 {
6587 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6589 c->state);
6590 return false;
6591 }
6592 else if (MHD_HDR_LINE_READING_DATA_ERROR == res)
6593 {
6594 mhd_assert ((process_footers ? \
6599 return true;
6600 }
6602 break;
6603 } while (1);
6604
6605#ifdef HAVE_MESSAGES
6606 if (1 == c->rq.num_cr_sp_replaced)
6607 {
6608 MHD_DLOG (c->daemon,
6609 _ ("One bare CR character has been replaced with space " \
6610 "in %s.\n"),
6611 (! process_footers) ?
6612 _ ("the request line or in the request headers") :
6613 _ ("the request footers"));
6614 }
6615 else if (0 != c->rq.num_cr_sp_replaced)
6616 {
6617 MHD_DLOG (c->daemon,
6618 _ ("%" PRIu64 " bare CR characters have been replaced with " \
6619 "spaces in the request line and/or in the request %s.\n"),
6620 (uint64_t) c->rq.num_cr_sp_replaced,
6621 (! process_footers) ? _ ("headers") : _ ("footers"));
6622 }
6623 if (1 == c->rq.skipped_broken_lines)
6624 {
6625 MHD_DLOG (c->daemon,
6626 _ ("One %s line without colon has been skipped.\n"),
6627 (! process_footers) ? _ ("header") : _ ("footer"));
6628 }
6629 else if (0 != c->rq.skipped_broken_lines)
6630 {
6631 MHD_DLOG (c->daemon,
6632 _ ("%" PRIu64 " %s lines without colons has been skipped.\n"),
6633 (uint64_t) c->rq.skipped_broken_lines,
6634 (! process_footers) ? _ ("header") : _ ("footer"));
6635 }
6636#endif /* HAVE_MESSAGES */
6637
6638 mhd_assert (c->rq.method < c->read_buffer);
6639 if (! process_footers)
6640 {
6641 c->rq.header_size = (size_t) (c->read_buffer - c->rq.method);
6643 c->rq.field_lines.size =
6644 (size_t) ((c->read_buffer - c->rq.field_lines.start) - 1);
6645 if ('\r' == *(c->read_buffer - 2))
6646 c->rq.field_lines.size--;
6648
6650 {
6651 /* Try to re-use some of the last bytes of the request header */
6652 /* Do this only if space in the read buffer is limited AND
6653 amount of read ahead data is small. */
6658 const char *last_elmnt_end;
6659 size_t shift_back_size;
6660 if (NULL != c->rq.headers_received_tail)
6661 last_elmnt_end =
6664 else
6665 last_elmnt_end = c->rq.version + HTTP_VER_LEN;
6666 mhd_assert ((last_elmnt_end + 1) < c->read_buffer);
6667 shift_back_size = (size_t) (c->read_buffer - (last_elmnt_end + 1));
6668 if (0 != c->read_buffer_offset)
6669 memmove (c->read_buffer - shift_back_size,
6670 c->read_buffer,
6672 c->read_buffer -= shift_back_size;
6673 c->read_buffer_size += shift_back_size;
6674 }
6675 }
6676 else
6678
6679 return true;
6680}
6681
6682
6690void
6692{
6693 struct MHD_Daemon *daemon = connection->daemon;
6694#if defined(MHD_USE_THREADS)
6695 mhd_assert (NULL == daemon->worker_pool);
6696#endif /* MHD_USE_THREADS */
6697
6698 if (0 == connection->connection_timeout_ms)
6699 return; /* Skip update of activity for connections
6700 without timeout timer. */
6701 if (connection->suspended)
6702 return; /* no activity on suspended connections */
6703
6706 return; /* each connection has personal timeout */
6707
6708 if (connection->connection_timeout_ms != daemon->connection_timeout_ms)
6709 return; /* custom timeout, no need to move it in "normal" DLL */
6710#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6711 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
6712#endif
6713 /* move connection to head of timeout list (by remove + add operation) */
6715 daemon->normal_timeout_tail,
6716 connection);
6718 daemon->normal_timeout_tail,
6719 connection);
6720#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6721 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
6722#endif
6723}
6724
6725
6735void
6737 bool socket_error)
6738{
6739 ssize_t bytes_read;
6740
6741 if ( (MHD_CONNECTION_CLOSED == connection->state) ||
6742 (connection->suspended) )
6743 return;
6744#ifdef HTTPS_SUPPORT
6745 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6746 { /* HTTPS connection. */
6747 if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6748 {
6749 if (! MHD_run_tls_handshake_ (connection))
6750 return;
6751 }
6752 }
6753#endif /* HTTPS_SUPPORT */
6754
6755 mhd_assert (NULL != connection->read_buffer);
6756 if (connection->read_buffer_size == connection->read_buffer_offset)
6757 return; /* No space for receiving data. */
6758
6759 bytes_read = connection->recv_cls (connection,
6760 &connection->read_buffer
6761 [connection->read_buffer_offset],
6762 connection->read_buffer_size
6763 - connection->read_buffer_offset);
6764 if ((bytes_read < 0) || socket_error)
6765 {
6766 if ((MHD_ERR_AGAIN_ == bytes_read) && ! socket_error)
6767 return; /* No new data to process. */
6768 if ((bytes_read > 0) && connection->sk_nonblck)
6769 { /* Try to detect the socket error */
6770 int dummy;
6771 bytes_read = connection->recv_cls (connection, &dummy, sizeof (dummy));
6772 }
6773 if (MHD_ERR_CONNRESET_ == bytes_read)
6774 {
6776 (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6777 {
6778#ifdef HAVE_MESSAGES
6779 MHD_DLOG (connection->daemon,
6780 _ ("Socket has been disconnected when reading request.\n"));
6781#endif
6782 connection->discard_request = true;
6783 }
6784 MHD_connection_close_ (connection,
6786 return;
6787 }
6788
6789#ifdef HAVE_MESSAGES
6790 if (MHD_CONNECTION_INIT != connection->state)
6791 MHD_DLOG (connection->daemon,
6792 _ ("Connection socket is closed when reading " \
6793 "request due to the error: %s\n"),
6794 (bytes_read < 0) ? str_conn_error_ (bytes_read) :
6795 "detected connection closure");
6796#endif
6797 CONNECTION_CLOSE_ERROR (connection,
6798 NULL);
6799 return;
6800 }
6801
6802 if (0 == bytes_read)
6803 { /* Remote side closed connection. */
6804 connection->read_closed = true;
6806 (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6807 {
6808#ifdef HAVE_MESSAGES
6809 MHD_DLOG (connection->daemon,
6810 _ ("Connection was closed by remote side with incomplete "
6811 "request.\n"));
6812#endif
6813 connection->discard_request = true;
6814 MHD_connection_close_ (connection,
6816 }
6817 else if (MHD_CONNECTION_INIT == connection->state)
6818 /* This termination code cannot be reported to the application
6819 * because application has not been informed yet about this request */
6820 MHD_connection_close_ (connection,
6822 else
6823 MHD_connection_close_ (connection,
6825 return;
6826 }
6827 connection->read_buffer_offset += (size_t) bytes_read;
6828 MHD_update_last_activity_ (connection);
6829#if DEBUG_STATES
6830 MHD_DLOG (connection->daemon,
6831 _ ("In function %s handling connection at state: %s\n"),
6832 MHD_FUNC_,
6833 MHD_state_to_string (connection->state));
6834#endif
6835 /* TODO: check whether the next 'switch()' really needed */
6836 switch (connection->state)
6837 {
6844 /* nothing to do but default action */
6845 if (connection->read_closed)
6846 {
6847 /* TODO: check whether this really needed */
6848 MHD_connection_close_ (connection,
6850 }
6851 return;
6853 return;
6854#ifdef UPGRADE_SUPPORT
6855 case MHD_CONNECTION_UPGRADE:
6856 mhd_assert (0);
6857 return;
6858#endif /* UPGRADE_SUPPORT */
6860 /* shrink read buffer to how much is actually used */
6861 /* TODO: remove shrink as it handled in special function */
6862 if ((0 != connection->read_buffer_size) &&
6863 (connection->read_buffer_size != connection->read_buffer_offset))
6864 {
6865 mhd_assert (NULL != connection->read_buffer);
6866 connection->read_buffer =
6867 MHD_pool_reallocate (connection->pool,
6868 connection->read_buffer,
6869 connection->read_buffer_size,
6870 connection->read_buffer_offset);
6871 connection->read_buffer_size = connection->read_buffer_offset;
6872 }
6873 break;
6879 /* Milestone state, no data should be read */
6880 mhd_assert (0); /* Should not be possible */
6881 break;
6892 default:
6893 mhd_assert (0); /* Should not be possible */
6894 break;
6895 }
6896 return;
6897}
6898
6899
6908void
6910{
6911 struct MHD_Response *response;
6912 ssize_t ret;
6913 if (connection->suspended)
6914 return;
6915
6916#ifdef HTTPS_SUPPORT
6917 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6918 { /* HTTPS connection. */
6919 if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6920 {
6921 if (! MHD_run_tls_handshake_ (connection))
6922 return;
6923 }
6924 }
6925#endif /* HTTPS_SUPPORT */
6926
6927#if DEBUG_STATES
6928 MHD_DLOG (connection->daemon,
6929 _ ("In function %s handling connection at state: %s\n"),
6930 MHD_FUNC_,
6931 MHD_state_to_string (connection->state));
6932#endif
6933 switch (connection->state)
6934 {
6941 mhd_assert (0);
6942 return;
6944 ret = MHD_send_data_ (connection,
6946 [connection->continue_message_write_offset],
6948 - connection->continue_message_write_offset,
6949 true);
6950 if (ret < 0)
6951 {
6952 if (MHD_ERR_AGAIN_ == ret)
6953 return;
6954#ifdef HAVE_MESSAGES
6955 MHD_DLOG (connection->daemon,
6956 _ ("Failed to send data in request for %s.\n"),
6957 connection->rq.url);
6958#endif
6959 CONNECTION_CLOSE_ERROR (connection,
6960 NULL);
6961 return;
6962 }
6963#if _MHD_DEBUG_SEND_DATA
6964 fprintf (stderr,
6965 _ ("Sent 100 continue response: `%.*s'\n"),
6966 (int) ret,
6968#endif
6969 connection->continue_message_write_offset += (size_t) ret;
6970 MHD_update_last_activity_ (connection);
6971 return;
6977 mhd_assert (0);
6978 return;
6980 mhd_assert (0);
6981 return;
6983 {
6984 struct MHD_Response *const resp = connection->rp.response;
6985 const size_t wb_ready = connection->write_buffer_append_offset
6986 - connection->write_buffer_send_offset;
6987 mhd_assert (connection->write_buffer_append_offset >= \
6988 connection->write_buffer_send_offset);
6989 mhd_assert (NULL != resp);
6990 mhd_assert ( (0 == resp->data_size) || \
6991 (0 == resp->data_start) || \
6992 (NULL != resp->crc) );
6993 mhd_assert ( (0 == connection->rp.rsp_write_position) || \
6994 (resp->total_size ==
6995 connection->rp.rsp_write_position) );
6996 mhd_assert ((MHD_CONN_MUST_UPGRADE != connection->keepalive) || \
6997 (! connection->rp.props.send_reply_body));
6998
6999 if ( (connection->rp.props.send_reply_body) &&
7000 (NULL == resp->crc) &&
7001 (NULL == resp->data_iov) &&
7002 /* TODO: remove the next check as 'send_reply_body' is used */
7003 (0 == connection->rp.rsp_write_position) &&
7004 (! connection->rp.props.chunked) )
7005 {
7006 mhd_assert (resp->total_size >= resp->data_size);
7007 mhd_assert (0 == resp->data_start);
7008 /* Send response headers alongside the response body, if the body
7009 * data is available. */
7010 ret = MHD_send_hdr_and_body_ (connection,
7011 &connection->write_buffer
7012 [connection->write_buffer_send_offset],
7013 wb_ready,
7014 false,
7015 resp->data,
7016 resp->data_size,
7017 (resp->total_size == resp->data_size));
7018 }
7019 else
7020 {
7021 /* This is response for HEAD request or reply body is not allowed
7022 * for any other reason or reply body is dynamically generated. */
7023 /* Do not send the body data even if it's available. */
7024 ret = MHD_send_hdr_and_body_ (connection,
7025 &connection->write_buffer
7026 [connection->write_buffer_send_offset],
7027 wb_ready,
7028 false,
7029 NULL,
7030 0,
7031 ((0 == resp->total_size) ||
7032 (! connection->rp.props.send_reply_body)
7033 ));
7034 }
7035
7036 if (ret < 0)
7037 {
7038 if (MHD_ERR_AGAIN_ == ret)
7039 return;
7040#ifdef HAVE_MESSAGES
7041 MHD_DLOG (connection->daemon,
7042 _ ("Failed to send the response headers for the " \
7043 "request for `%s'. Error: %s\n"),
7044 connection->rq.url,
7045 str_conn_error_ (ret));
7046#endif
7047 CONNECTION_CLOSE_ERROR (connection,
7048 NULL);
7049 return;
7050 }
7051 /* 'ret' is not negative, it's safe to cast it to 'size_t'. */
7052 if (((size_t) ret) > wb_ready)
7053 {
7054 /* The complete header and some response data have been sent,
7055 * update both offsets. */
7056 mhd_assert (0 == connection->rp.rsp_write_position);
7057 mhd_assert (! connection->rp.props.chunked);
7058 mhd_assert (connection->rp.props.send_reply_body);
7059 connection->write_buffer_send_offset += wb_ready;
7060 connection->rp.rsp_write_position = ((size_t) ret) - wb_ready;
7061 }
7062 else
7063 connection->write_buffer_send_offset += (size_t) ret;
7064 MHD_update_last_activity_ (connection);
7065 if (MHD_CONNECTION_HEADERS_SENDING != connection->state)
7066 return;
7067 check_write_done (connection,
7069 return;
7070 }
7072 return;
7074 response = connection->rp.response;
7075 if (connection->rp.rsp_write_position <
7076 connection->rp.response->total_size)
7077 {
7078 uint64_t data_write_offset;
7079
7080#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7081 if (NULL != response->crc)
7082 MHD_mutex_lock_chk_ (&response->mutex);
7083#endif
7084 if (MHD_NO == try_ready_normal_body (connection))
7085 {
7086 /* mutex was already unlocked by try_ready_normal_body */
7087 return;
7088 }
7089#if defined(_MHD_HAVE_SENDFILE)
7090 if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
7091 {
7092 mhd_assert (NULL == response->data_iov);
7093 ret = MHD_send_sendfile_ (connection);
7094 }
7095 else /* combined with the next 'if' */
7096#endif /* _MHD_HAVE_SENDFILE */
7097 if (NULL != response->data_iov)
7098 {
7099 ret = MHD_send_iovec_ (connection,
7100 &connection->rp.resp_iov,
7101 true);
7102 }
7103 else
7104 {
7105 data_write_offset = connection->rp.rsp_write_position
7106 - response->data_start;
7107 if (data_write_offset > (uint64_t) SIZE_MAX)
7108 MHD_PANIC (_ ("Data offset exceeds limit.\n"));
7109 ret = MHD_send_data_ (connection,
7110 &response->data
7111 [(size_t) data_write_offset],
7112 response->data_size
7113 - (size_t) data_write_offset,
7114 true);
7115#if _MHD_DEBUG_SEND_DATA
7116 if (ret > 0)
7117 fprintf (stderr,
7118 _ ("Sent %d-byte DATA response: `%.*s'\n"),
7119 (int) ret,
7120 (int) ret,
7121 &rp.response->data[connection->rp.rsp_write_position
7122 - rp.response->data_start]);
7123#endif
7124 }
7125#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7126 if (NULL != response->crc)
7127 MHD_mutex_unlock_chk_ (&response->mutex);
7128#endif
7129 if (ret < 0)
7130 {
7131 if (MHD_ERR_AGAIN_ == ret)
7132 return;
7133#ifdef HAVE_MESSAGES
7134 MHD_DLOG (connection->daemon,
7135 _ ("Failed to send the response body for the " \
7136 "request for `%s'. Error: %s\n"),
7137 connection->rq.url,
7138 str_conn_error_ (ret));
7139#endif
7140 CONNECTION_CLOSE_ERROR (connection,
7141 NULL);
7142 return;
7143 }
7144 connection->rp.rsp_write_position += (size_t) ret;
7145 MHD_update_last_activity_ (connection);
7146 }
7147 if (connection->rp.rsp_write_position ==
7148 connection->rp.response->total_size)
7150 return;
7152 mhd_assert (0);
7153 return;
7155 ret = MHD_send_data_ (connection,
7156 &connection->write_buffer
7157 [connection->write_buffer_send_offset],
7158 connection->write_buffer_append_offset
7159 - connection->write_buffer_send_offset,
7160 true);
7161 if (ret < 0)
7162 {
7163 if (MHD_ERR_AGAIN_ == ret)
7164 return;
7165#ifdef HAVE_MESSAGES
7166 MHD_DLOG (connection->daemon,
7167 _ ("Failed to send the chunked response body for the " \
7168 "request for `%s'. Error: %s\n"),
7169 connection->rq.url,
7170 str_conn_error_ (ret));
7171#endif
7172 CONNECTION_CLOSE_ERROR (connection,
7173 NULL);
7174 return;
7175 }
7176 connection->write_buffer_send_offset += (size_t) ret;
7177 MHD_update_last_activity_ (connection);
7178 if (MHD_CONNECTION_CHUNKED_BODY_READY != connection->state)
7179 return;
7180 check_write_done (connection,
7181 (connection->rp.response->total_size ==
7182 connection->rp.rsp_write_position) ?
7185 return;
7188 mhd_assert (0);
7189 return;
7191 ret = MHD_send_data_ (connection,
7192 &connection->write_buffer
7193 [connection->write_buffer_send_offset],
7194 connection->write_buffer_append_offset
7195 - connection->write_buffer_send_offset,
7196 true);
7197 if (ret < 0)
7198 {
7199 if (MHD_ERR_AGAIN_ == ret)
7200 return;
7201#ifdef HAVE_MESSAGES
7202 MHD_DLOG (connection->daemon,
7203 _ ("Failed to send the footers for the " \
7204 "request for `%s'. Error: %s\n"),
7205 connection->rq.url,
7206 str_conn_error_ (ret));
7207#endif
7208 CONNECTION_CLOSE_ERROR (connection,
7209 NULL);
7210 return;
7211 }
7212 connection->write_buffer_send_offset += (size_t) ret;
7213 MHD_update_last_activity_ (connection);
7214 if (MHD_CONNECTION_FOOTERS_SENDING != connection->state)
7215 return;
7216 check_write_done (connection,
7218 return;
7220 mhd_assert (0);
7221 return;
7223 return;
7224#ifdef UPGRADE_SUPPORT
7225 case MHD_CONNECTION_UPGRADE:
7226 mhd_assert (0);
7227 return;
7228#endif /* UPGRADE_SUPPORT */
7229 default:
7230 mhd_assert (0);
7231 CONNECTION_CLOSE_ERROR (connection,
7232 _ ("Internal error.\n"));
7233 break;
7234 }
7235 return;
7236}
7237
7238
7245static bool
7247{
7248 const uint64_t timeout = c->connection_timeout_ms;
7249 uint64_t now;
7250 uint64_t since_actv;
7251
7252 if (c->suspended)
7253 return false;
7254 if (0 == timeout)
7255 return false;
7257 since_actv = now - c->last_activity;
7258 /* Keep the next lines in sync with #connection_get_wait() to avoid
7259 * undesired side-effects like busy-waiting. */
7260 if (timeout < since_actv)
7261 {
7262 if (UINT64_MAX / 2 < since_actv)
7263 {
7264 const uint64_t jump_back = c->last_activity - now;
7265 /* Very unlikely that it is more than quarter-million years pause.
7266 * More likely that system clock jumps back. */
7267 if (5000 >= jump_back)
7268 {
7269#ifdef HAVE_MESSAGES
7270 MHD_DLOG (c->daemon,
7271 _ ("Detected system clock %u milliseconds jump back.\n"),
7272 (unsigned int) jump_back);
7273#endif
7274 return false;
7275 }
7276#ifdef HAVE_MESSAGES
7277 MHD_DLOG (c->daemon,
7278 _ ("Detected too large system clock %" PRIu64 " milliseconds "
7279 "jump back.\n"),
7280 jump_back);
7281#endif
7282 }
7283 return true;
7284 }
7285 return false;
7286}
7287
7288
7297static void
7299{
7300 struct MHD_Daemon *daemon = connection->daemon;
7301#ifdef MHD_USE_THREADS
7302 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
7303 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
7304 mhd_assert (NULL == daemon->worker_pool);
7305#endif /* MHD_USE_THREADS */
7306
7307 if (connection->in_cleanup)
7308 return; /* Prevent double cleanup. */
7309 connection->in_cleanup = true;
7310 if (NULL != connection->rp.response)
7311 {
7312 MHD_destroy_response (connection->rp.response);
7313 connection->rp.response = NULL;
7314 }
7315#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7316 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
7317#endif
7318 if (connection->suspended)
7319 {
7322 connection);
7323 connection->suspended = false;
7324 }
7325 else
7326 {
7327 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
7328 {
7329 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7331 daemon->normal_timeout_tail,
7332 connection);
7333 else
7335 daemon->manual_timeout_tail,
7336 connection);
7337 }
7339 daemon->connections_tail,
7340 connection);
7341 }
7342 DLL_insert (daemon->cleanup_head,
7343 daemon->cleanup_tail,
7344 connection);
7345 connection->resuming = false;
7346 connection->in_idle = false;
7347#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7348 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
7349#endif
7351 {
7352 /* if we were at the connection limit before and are in
7353 thread-per-connection mode, signal the main thread
7354 to resume accepting connections */
7355 if ( (MHD_ITC_IS_VALID_ (daemon->itc)) &&
7356 (! MHD_itc_activate_ (daemon->itc, "c")) )
7357 {
7358#ifdef HAVE_MESSAGES
7359 MHD_DLOG (daemon,
7360 _ ("Failed to signal end of connection via inter-thread " \
7361 "communication channel.\n"));
7362#endif
7363 }
7364 }
7365}
7366
7367
7373void
7375{
7376 size_t read_buf_size;
7377
7378#ifdef HTTPS_SUPPORT
7379 mhd_assert ( (0 == (c->daemon->options & MHD_USE_TLS)) || \
7380 (MHD_TLS_CONN_INIT == c->tls_state) );
7381 mhd_assert ( (0 != (c->daemon->options & MHD_USE_TLS)) || \
7382 (MHD_TLS_CONN_NO_TLS == c->tls_state) );
7383#endif /* HTTPS_SUPPORT */
7385
7388
7389 memset (&c->rq, 0, sizeof(c->rq));
7390 memset (&c->rp, 0, sizeof(c->rp));
7391
7392 c->write_buffer = NULL;
7393 c->write_buffer_size = 0;
7396
7398
7399 c->read_buffer_offset = 0;
7400 read_buf_size = c->daemon->pool_size / 2;
7401 c->read_buffer
7402 = MHD_pool_allocate (c->pool,
7403 read_buf_size,
7404 false);
7405 c->read_buffer_size = read_buf_size;
7406}
7407
7408
7415static void
7417 bool reuse)
7418{
7419 struct MHD_Connection *const c = connection;
7420 struct MHD_Daemon *const d = connection->daemon;
7421
7422 if (! reuse)
7423 {
7424 /* Next function will destroy response, notify client,
7425 * destroy memory pool, and set connection state to "CLOSED" */
7427 c->stop_with_error ?
7430 c->read_buffer = NULL;
7431 c->read_buffer_size = 0;
7432 c->read_buffer_offset = 0;
7433 c->write_buffer = NULL;
7434 c->write_buffer_size = 0;
7437 }
7438 else
7439 {
7440 /* Reset connection to process the next request */
7441 size_t new_read_buf_size;
7444
7445 if ( (NULL != d->notify_completed) &&
7446 (c->rq.client_aware) )
7448 c,
7449 &c->rq.client_context,
7451 c->rq.client_aware = false;
7452
7453 if (NULL != c->rp.response)
7455 c->rp.response = NULL;
7456
7459 c->event_loop_info =
7460 (0 == c->read_buffer_offset) ?
7462
7463 memset (&c->rq, 0, sizeof(c->rq));
7464
7465 /* iov (if any) will be deallocated by MHD_pool_reset */
7466 memset (&c->rp, 0, sizeof(c->rp));
7467
7468 c->write_buffer = NULL;
7469 c->write_buffer_size = 0;
7473
7474 /* Reset the read buffer to the starting size,
7475 preserving the bytes we have already read. */
7476 new_read_buf_size = c->daemon->pool_size / 2;
7477 if (c->read_buffer_offset > new_read_buf_size)
7478 new_read_buf_size = c->read_buffer_offset;
7479
7480 c->read_buffer
7481 = MHD_pool_reset (c->pool,
7482 c->read_buffer,
7484 new_read_buf_size);
7485 c->read_buffer_size = new_read_buf_size;
7486 }
7487 c->rq.client_context = NULL;
7488}
7489
7490
7503enum MHD_Result
7505{
7506 struct MHD_Daemon *daemon = connection->daemon;
7507 enum MHD_Result ret;
7508#ifdef MHD_USE_THREADS
7509 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
7510 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
7511#endif /* MHD_USE_THREADS */
7512 /* 'daemon' is not used if epoll is not available and asserts are disabled */
7513 (void) daemon; /* Mute compiler warning */
7514
7515 connection->in_idle = true;
7516 while (! connection->suspended)
7517 {
7518#ifdef HTTPS_SUPPORT
7519 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
7520 { /* HTTPS connection. */
7521 if ((MHD_TLS_CONN_INIT <= connection->tls_state) &&
7522 (MHD_TLS_CONN_CONNECTED > connection->tls_state))
7523 break;
7524 }
7525#endif /* HTTPS_SUPPORT */
7526#if DEBUG_STATES
7527 MHD_DLOG (daemon,
7528 _ ("In function %s handling connection at state: %s\n"),
7529 MHD_FUNC_,
7530 MHD_state_to_string (connection->state));
7531#endif
7532 switch (connection->state)
7533 {
7536 if (get_request_line (connection))
7537 {
7540 || (connection->discard_request));
7541 continue;
7542 }
7544 break;
7548 continue;
7550 if (get_req_headers (connection, false))
7551 {
7553 mhd_assert ((MHD_CONNECTION_HEADERS_RECEIVED == connection->state) || \
7554 (connection->discard_request));
7555 continue;
7556 }
7558 break;
7560 parse_connection_headers (connection);
7561 if (MHD_CONNECTION_HEADERS_RECEIVED != connection->state)
7562 continue;
7564 if (connection->suspended)
7565 break;
7566 continue;
7568 call_connection_handler (connection); /* first call */
7569 if (MHD_CONNECTION_HEADERS_PROCESSED != connection->state)
7570 continue;
7571 if (connection->suspended)
7572 continue;
7573
7574 if ( (NULL == connection->rp.response) &&
7575 (need_100_continue (connection)) &&
7576 /* If the client is already sending the payload (body)
7577 there is no need to send "100 Continue" */
7578 (0 == connection->read_buffer_offset) )
7579 {
7581 break;
7582 }
7583 if ( (NULL != connection->rp.response) &&
7584 (0 != connection->rq.remaining_upload_size) )
7585 {
7586 /* we refused (no upload allowed!) */
7587 connection->rq.remaining_upload_size = 0;
7588 /* force close, in case client still tries to upload... */
7589 connection->discard_request = true;
7590 }
7591 connection->state = (0 == connection->rq.remaining_upload_size)
7594 if (connection->suspended)
7595 break;
7596 continue;
7598 if (connection->continue_message_write_offset ==
7600 {
7602 continue;
7603 }
7604 break;
7606 mhd_assert (0 != connection->rq.remaining_upload_size);
7607 mhd_assert (! connection->discard_request);
7608 mhd_assert (NULL == connection->rp.response);
7609 if (0 != connection->read_buffer_offset)
7610 {
7611 process_request_body (connection); /* loop call */
7612 if (MHD_CONNECTION_BODY_RECEIVING != connection->state)
7613 continue;
7614 }
7615 /* Modify here when queueing of the response during data processing
7616 will be supported */
7617 mhd_assert (! connection->discard_request);
7618 mhd_assert (NULL == connection->rp.response);
7619 if (0 == connection->rq.remaining_upload_size)
7620 {
7621 connection->state = MHD_CONNECTION_BODY_RECEIVED;
7622 continue;
7623 }
7624 break;
7626 mhd_assert (! connection->discard_request);
7627 mhd_assert (NULL == connection->rp.response);
7628 if (0 == connection->rq.remaining_upload_size)
7629 {
7630 if (connection->rq.have_chunked_upload)
7631 {
7632 /* Reset counter variables reused for footers */
7633 connection->rq.num_cr_sp_replaced = 0;
7634 connection->rq.skipped_broken_lines = 0;
7637 }
7638 else
7640 continue;
7641 }
7642 break;
7644 if (get_req_headers (connection, true))
7645 {
7647 mhd_assert ((MHD_CONNECTION_FOOTERS_RECEIVED == connection->state) || \
7648 (connection->discard_request));
7649 continue;
7650 }
7652 break;
7654 /* The header, the body, and the footers of the request has been received,
7655 * switch to the final processing of the request. */
7657 continue;
7659 call_connection_handler (connection); /* "final" call */
7660 if (connection->state != MHD_CONNECTION_FULL_REQ_RECEIVED)
7661 continue;
7662 if (NULL == connection->rp.response)
7663 break; /* try again next time */
7664 /* Response is ready, start reply */
7665 connection->state = MHD_CONNECTION_START_REPLY;
7666 continue;
7668 mhd_assert (NULL != connection->rp.response);
7670 if (MHD_NO == build_header_response (connection))
7671 {
7672 /* oops - close! */
7673 CONNECTION_CLOSE_ERROR (connection,
7674 _ ("Closing connection (failed to create "
7675 "response header).\n"));
7676 continue;
7677 }
7679 break;
7680
7682 /* no default action */
7683 break;
7685#ifdef UPGRADE_SUPPORT
7686 if (NULL != connection->rp.response->upgrade_handler)
7687 {
7688 connection->state = MHD_CONNECTION_UPGRADE;
7689 /* This connection is "upgraded". Pass socket to application. */
7690 if (MHD_NO ==
7692 connection))
7693 {
7694 /* upgrade failed, fail hard */
7695 CONNECTION_CLOSE_ERROR (connection,
7696 NULL);
7697 continue;
7698 }
7699 /* Response is not required anymore for this connection. */
7700 if (1)
7701 {
7702 struct MHD_Response *const resp = connection->rp.response;
7703
7704 connection->rp.response = NULL;
7705 MHD_destroy_response (resp);
7706 }
7707 continue;
7708 }
7709#endif /* UPGRADE_SUPPORT */
7710
7711 if (connection->rp.props.send_reply_body)
7712 {
7713 if (connection->rp.props.chunked)
7715 else
7717 }
7718 else
7720 continue;
7722 mhd_assert (connection->rp.props.send_reply_body);
7723 mhd_assert (! connection->rp.props.chunked);
7724 /* nothing to do here */
7725 break;
7727 mhd_assert (connection->rp.props.send_reply_body);
7728 mhd_assert (! connection->rp.props.chunked);
7729#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7730 if (NULL != connection->rp.response->crc)
7731 MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7732#endif
7733 if (0 == connection->rp.response->total_size)
7734 {
7735#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7736 if (NULL != connection->rp.response->crc)
7737 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7738#endif
7739 if (connection->rp.props.chunked)
7741 else
7743 continue;
7744 }
7745 if (MHD_NO != try_ready_normal_body (connection))
7746 {
7747#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7748 if (NULL != connection->rp.response->crc)
7749 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7750#endif
7752 /* Buffering for flushable socket was already enabled*/
7753
7754 break;
7755 }
7756 /* mutex was already unlocked by "try_ready_normal_body */
7757 /* not ready, no socket action */
7758 break;
7760 mhd_assert (connection->rp.props.send_reply_body);
7761 mhd_assert (connection->rp.props.chunked);
7762 /* nothing to do here */
7763 break;
7765 mhd_assert (connection->rp.props.send_reply_body);
7766 mhd_assert (connection->rp.props.chunked);
7767#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7768 if (NULL != connection->rp.response->crc)
7769 MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7770#endif
7771 if ( (0 == connection->rp.response->total_size) ||
7772 (connection->rp.rsp_write_position ==
7773 connection->rp.response->total_size) )
7774 {
7775#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7776 if (NULL != connection->rp.response->crc)
7777 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7778#endif
7780 continue;
7781 }
7782 if (1)
7783 { /* pseudo-branch for local variables scope */
7784 bool finished;
7785 if (MHD_NO != try_ready_chunked_body (connection, &finished))
7786 {
7787#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7788 if (NULL != connection->rp.response->crc)
7789 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7790#endif
7791 connection->state = finished ? MHD_CONNECTION_CHUNKED_BODY_SENT :
7793 continue;
7794 }
7795 /* mutex was already unlocked by try_ready_chunked_body */
7796 }
7797 break;
7799 mhd_assert (connection->rp.props.send_reply_body);
7800 mhd_assert (connection->rp.props.chunked);
7801 mhd_assert (connection->write_buffer_send_offset <= \
7802 connection->write_buffer_append_offset);
7803
7805 {
7806 /* oops - close! */
7807 CONNECTION_CLOSE_ERROR (connection,
7808 _ ("Closing connection (failed to create " \
7809 "response footer)."));
7810 continue;
7811 }
7812 mhd_assert (connection->write_buffer_send_offset < \
7813 connection->write_buffer_append_offset);
7815 continue;
7817 mhd_assert (connection->rp.props.send_reply_body);
7818 mhd_assert (connection->rp.props.chunked);
7819 /* no default action */
7820 break;
7822 if (MHD_HTTP_PROCESSING == connection->rp.responseCode)
7823 {
7824 /* After this type of response, we allow sending another! */
7826 MHD_destroy_response (connection->rp.response);
7827 connection->rp.response = NULL;
7828 /* FIXME: maybe partially reset memory pool? */
7829 continue;
7830 }
7831 /* Reset connection after complete reply */
7832 connection_reset (connection,
7833 MHD_CONN_USE_KEEPALIVE == connection->keepalive &&
7834 ! connection->read_closed &&
7835 ! connection->discard_request);
7836 continue;
7838 cleanup_connection (connection);
7839 connection->in_idle = false;
7840 return MHD_NO;
7841#ifdef UPGRADE_SUPPORT
7842 case MHD_CONNECTION_UPGRADE:
7843 connection->in_idle = false;
7844 return MHD_YES; /* keep open */
7845#endif /* UPGRADE_SUPPORT */
7846 default:
7847 mhd_assert (0);
7848 break;
7849 }
7850 break;
7851 }
7852 if (connection_check_timedout (connection))
7853 {
7854 MHD_connection_close_ (connection,
7856 connection->in_idle = false;
7857 return MHD_YES;
7858 }
7860 ret = MHD_YES;
7861#ifdef EPOLL_SUPPORT
7862 if ( (! connection->suspended) &&
7863 MHD_D_IS_USING_EPOLL_ (daemon) )
7864 {
7865 ret = MHD_connection_epoll_update_ (connection);
7866 }
7867#endif /* EPOLL_SUPPORT */
7868 connection->in_idle = false;
7869 return ret;
7870}
7871
7872
7873#ifdef EPOLL_SUPPORT
7882enum MHD_Result
7883MHD_connection_epoll_update_ (struct MHD_Connection *connection)
7884{
7885 struct MHD_Daemon *const daemon = connection->daemon;
7886
7888
7889 if ((0 != (MHD_EVENT_LOOP_INFO_PROCESS & connection->event_loop_info)) &&
7890 (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)))
7891 {
7892 /* Make sure that connection waiting for processing will be processed */
7893 EDLL_insert (daemon->eready_head,
7894 daemon->eready_tail,
7895 connection);
7896 connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
7897 }
7898
7899 if ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) &&
7900 (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
7901 ( ( (MHD_EVENT_LOOP_INFO_WRITE == connection->event_loop_info) &&
7902 (0 == (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY))) ||
7903 ( (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info)) &&
7904 (0 == (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) ) ) )
7905 {
7906 /* add to epoll set */
7907 struct epoll_event event;
7908
7909 event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
7910 event.data.ptr = connection;
7911 if (0 != epoll_ctl (daemon->epoll_fd,
7912 EPOLL_CTL_ADD,
7913 connection->socket_fd,
7914 &event))
7915 {
7916#ifdef HAVE_MESSAGES
7917 if (0 != (daemon->options & MHD_USE_ERROR_LOG))
7918 MHD_DLOG (daemon,
7919 _ ("Call to epoll_ctl failed: %s\n"),
7921#endif
7922 connection->state = MHD_CONNECTION_CLOSED;
7923 cleanup_connection (connection);
7924 return MHD_NO;
7925 }
7926 connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
7927 }
7928 return MHD_YES;
7929}
7930
7931
7932#endif
7933
7934
7940void
7942{
7943 connection->recv_cls = &recv_param_adapter;
7944}
7945
7946
7959_MHD_EXTERN const union MHD_ConnectionInfo *
7961 enum MHD_ConnectionInfoType info_type,
7962 ...)
7963{
7964 switch (info_type)
7965 {
7966#ifdef HTTPS_SUPPORT
7968 if (NULL == connection->tls_session)
7969 return NULL;
7970 if (1)
7971 { /* Workaround to mute compiler warning */
7972 gnutls_cipher_algorithm_t res;
7973 res = gnutls_cipher_get (connection->tls_session);
7974 connection->connection_info_dummy.cipher_algorithm = (int) res;
7975 }
7976 return &connection->connection_info_dummy;
7978 if (NULL == connection->tls_session)
7979 return NULL;
7980 if (1)
7981 { /* Workaround to mute compiler warning */
7982 gnutls_protocol_t res;
7983 res = gnutls_protocol_get_version (connection->tls_session);
7984 connection->connection_info_dummy.protocol = (int) res;
7985 }
7986 return &connection->connection_info_dummy;
7988 if (NULL == connection->tls_session)
7989 return NULL;
7990 connection->connection_info_dummy.tls_session = connection->tls_session;
7991 return &connection->connection_info_dummy;
7992#else /* ! HTTPS_SUPPORT */
7996#endif /* ! HTTPS_SUPPORT */
7998 return NULL; /* Not implemented */
8000 if (0 < connection->addr_len)
8001 {
8002 mhd_assert (sizeof (connection->addr) == \
8003 sizeof (connection->connection_info_dummy.client_addr));
8004 memcpy (&connection->connection_info_dummy.client_addr,
8005 &connection->addr,
8006 sizeof(connection->addr));
8007 return &connection->connection_info_dummy;
8008 }
8009 return NULL;
8011 connection->connection_info_dummy.daemon =
8012 MHD_get_master (connection->daemon);
8013 return &connection->connection_info_dummy;
8015 connection->connection_info_dummy.connect_fd = connection->socket_fd;
8016 return &connection->connection_info_dummy;
8019 connection->socket_context;
8020 return &connection->connection_info_dummy;
8022 connection->connection_info_dummy.suspended =
8023 connection->suspended ? MHD_YES : MHD_NO;
8024 return &connection->connection_info_dummy;
8026#if SIZEOF_UNSIGNED_INT <= (SIZEOF_UINT64_T - 2)
8027 if (UINT_MAX < connection->connection_timeout_ms / 1000)
8029 else
8030#endif /* SIZEOF_UNSIGNED_INT <=(SIZEOF_UINT64_T - 2) */
8032 (unsigned int) (connection->connection_timeout_ms / 1000);
8033 return &connection->connection_info_dummy;
8035 if ( (MHD_CONNECTION_HEADERS_RECEIVED > connection->state) ||
8036 (MHD_CONNECTION_CLOSED == connection->state) )
8037 return NULL; /* invalid, too early! */
8038 connection->connection_info_dummy.header_size = connection->rq.header_size;
8039 return &connection->connection_info_dummy;
8041 if (NULL == connection->rp.response)
8042 return NULL;
8043 connection->connection_info_dummy.http_status = connection->rp.responseCode;
8044 return &connection->connection_info_dummy;
8045 default:
8046 return NULL;
8047 }
8048}
8049
8050
8062 enum MHD_CONNECTION_OPTION option,
8063 ...)
8064{
8065 va_list ap;
8066 struct MHD_Daemon *daemon;
8067 unsigned int ui_val;
8068
8069 daemon = connection->daemon;
8070 switch (option)
8071 {
8073 if (0 == connection->connection_timeout_ms)
8075 va_start (ap, option);
8076 ui_val = va_arg (ap, unsigned int);
8077 va_end (ap);
8078#if (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT
8079 if ((UINT64_MAX / 4000 - 1) < ui_val)
8080 {
8081#ifdef HAVE_MESSAGES
8082 MHD_DLOG (connection->daemon,
8083 _ ("The specified connection timeout (%u) is too " \
8084 "large. Maximum allowed value (%" PRIu64 ") will be used " \
8085 "instead.\n"),
8086 ui_val,
8087 (UINT64_MAX / 4000 - 1));
8088#endif
8089 ui_val = UINT64_MAX / 4000 - 1;
8090 }
8091#endif /* (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT */
8092 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
8093 {
8094#if defined(MHD_USE_THREADS)
8095 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
8096#endif
8097 if (! connection->suspended)
8098 {
8099 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
8101 daemon->normal_timeout_tail,
8102 connection);
8103 else
8105 daemon->manual_timeout_tail,
8106 connection);
8107 connection->connection_timeout_ms = ((uint64_t) ui_val) * 1000;
8108 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
8110 daemon->normal_timeout_tail,
8111 connection);
8112 else
8114 daemon->manual_timeout_tail,
8115 connection);
8116 }
8117#if defined(MHD_USE_THREADS)
8118 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
8119#endif
8120 }
8121 return MHD_YES;
8122 default:
8123 return MHD_NO;
8124 }
8125}
8126
8127
8175 unsigned int status_code,
8176 struct MHD_Response *response)
8177{
8178 struct MHD_Daemon *daemon;
8179 bool reply_icy;
8180
8181 if ((NULL == connection) || (NULL == response))
8182 return MHD_NO;
8183
8184 daemon = connection->daemon;
8185 if ((! connection->in_access_handler) && (! connection->suspended) &&
8186 MHD_D_IS_USING_THREADS_ (daemon))
8187 return MHD_NO;
8188
8189 reply_icy = (0 != (status_code & MHD_ICY_FLAG));
8190 status_code &= ~MHD_ICY_FLAG;
8191
8192#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
8193 if ( (! connection->suspended) &&
8194 MHD_D_IS_USING_THREADS_ (daemon) &&
8195 (! MHD_thread_handle_ID_is_current_thread_ (connection->tid)) )
8196 {
8197#ifdef HAVE_MESSAGES
8198 MHD_DLOG (daemon,
8199 _ ("Attempted to queue response on wrong thread!\n"));
8200#endif
8201 return MHD_NO;
8202 }
8203#endif
8204
8205 if (NULL != connection->rp.response)
8206 return MHD_NO; /* The response was already set */
8207
8208 if ( (MHD_CONNECTION_HEADERS_PROCESSED != connection->state) &&
8209 (MHD_CONNECTION_FULL_REQ_RECEIVED != connection->state) )
8210 return MHD_NO; /* Wrong connection state */
8211
8212 if (daemon->shutdown)
8213 return MHD_NO;
8214
8215#ifdef UPGRADE_SUPPORT
8216 if (NULL != response->upgrade_handler)
8217 {
8218 struct MHD_HTTP_Res_Header *conn_header;
8219 if (0 == (daemon->options & MHD_ALLOW_UPGRADE))
8220 {
8221#ifdef HAVE_MESSAGES
8222 MHD_DLOG (daemon,
8223 _ ("Attempted 'upgrade' connection on daemon without" \
8224 " MHD_ALLOW_UPGRADE option!\n"));
8225#endif
8226 return MHD_NO;
8227 }
8228 if (MHD_HTTP_SWITCHING_PROTOCOLS != status_code)
8229 {
8230#ifdef HAVE_MESSAGES
8231 MHD_DLOG (daemon,
8232 _ ("Application used invalid status code for" \
8233 " 'upgrade' response!\n"));
8234#endif
8235 return MHD_NO;
8236 }
8237 if (0 == (response->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
8238 {
8239#ifdef HAVE_MESSAGES
8240 MHD_DLOG (daemon,
8241 _ ("Application used invalid response" \
8242 " without \"Connection\" header!\n"));
8243#endif
8244 return MHD_NO;
8245 }
8246 conn_header = response->first_header;
8247 mhd_assert (NULL != conn_header);
8250 if (! MHD_str_has_s_token_caseless_ (conn_header->value,
8251 "upgrade"))
8252 {
8253#ifdef HAVE_MESSAGES
8254 MHD_DLOG (daemon,
8255 _ ("Application used invalid response" \
8256 " without \"upgrade\" token in" \
8257 " \"Connection\" header!\n"));
8258#endif
8259 return MHD_NO;
8260 }
8261 if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
8262 {
8263#ifdef HAVE_MESSAGES
8264 MHD_DLOG (daemon,
8265 _ ("Connection \"Upgrade\" can be used only " \
8266 "with HTTP/1.1 connections!\n"));
8267#endif
8268 return MHD_NO;
8269 }
8270 }
8271#endif /* UPGRADE_SUPPORT */
8272 if (MHD_HTTP_SWITCHING_PROTOCOLS == status_code)
8273 {
8274#ifdef UPGRADE_SUPPORT
8275 if (NULL == response->upgrade_handler)
8276 {
8277#ifdef HAVE_MESSAGES
8278 MHD_DLOG (daemon,
8279 _ ("Application used status code 101 \"Switching Protocols\" " \
8280 "with non-'upgrade' response!\n"));
8281#endif /* HAVE_MESSAGES */
8282 return MHD_NO;
8283 }
8284#else /* ! UPGRADE_SUPPORT */
8285#ifdef HAVE_MESSAGES
8286 MHD_DLOG (daemon,
8287 _ ("Application used status code 101 \"Switching Protocols\", " \
8288 "but this MHD was built without \"Upgrade\" support!\n"));
8289#endif /* HAVE_MESSAGES */
8290 return MHD_NO;
8291#endif /* ! UPGRADE_SUPPORT */
8292 }
8293 if ( (100 > status_code) ||
8294 (999 < status_code) )
8295 {
8296#ifdef HAVE_MESSAGES
8297 MHD_DLOG (daemon,
8298 _ ("Refused wrong status code (%u). " \
8299 "HTTP requires three digits status code!\n"),
8300 status_code);
8301#endif
8302 return MHD_NO;
8303 }
8304 if (200 > status_code)
8305 {
8306 if (MHD_HTTP_VER_1_0 == connection->rq.http_ver)
8307 {
8308#ifdef HAVE_MESSAGES
8309 MHD_DLOG (daemon,
8310 _ ("Wrong status code (%u) refused. " \
8311 "HTTP/1.0 clients do not support 1xx status codes!\n"),
8312 (status_code));
8313#endif
8314 return MHD_NO;
8315 }
8316 if (0 != (response->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
8318 {
8319#ifdef HAVE_MESSAGES
8320 MHD_DLOG (daemon,
8321 _ ("Wrong status code (%u) refused. " \
8322 "HTTP/1.0 reply mode does not support 1xx status codes!\n"),
8323 (status_code));
8324#endif
8325 return MHD_NO;
8326 }
8327 }
8328 if ( (MHD_HTTP_MTHD_CONNECT == connection->rq.http_mthd) &&
8329 (2 == status_code / 100) )
8330 {
8331#ifdef HAVE_MESSAGES
8332 MHD_DLOG (daemon,
8333 _ ("Successful (%u) response code cannot be used to answer " \
8334 "\"CONNECT\" request!\n"),
8335 (status_code));
8336#endif
8337 return MHD_NO;
8338 }
8339
8340 if ( (0 != (MHD_RF_HEAD_ONLY_RESPONSE & response->flags)) &&
8341 (RP_BODY_HEADERS_ONLY < is_reply_body_needed (connection, status_code)) )
8342 {
8343#ifdef HAVE_MESSAGES
8344 MHD_DLOG (daemon,
8345 _ ("HEAD-only response cannot be used when the request requires "
8346 "reply body to be sent!\n"));
8347#endif
8348 return MHD_NO;
8349 }
8350
8351#ifdef HAVE_MESSAGES
8352 if ( (0 != (MHD_RF_INSANITY_HEADER_CONTENT_LENGTH & response->flags)) &&
8353 (0 != (MHD_RAF_HAS_CONTENT_LENGTH & response->flags_auto)) )
8354 {
8355 MHD_DLOG (daemon,
8356 _ ("The response has application-defined \"Content-Length\" " \
8357 "header. The reply to the request will be not " \
8358 "HTTP-compliant and may result in hung connection or " \
8359 "other problems!\n"));
8360 }
8361#endif
8362
8363 MHD_increment_response_rc (response);
8364 connection->rp.response = response;
8365 connection->rp.responseCode = status_code;
8366 connection->rp.responseIcy = reply_icy;
8367#if defined(_MHD_HAVE_SENDFILE)
8368 if ( (response->fd == -1) ||
8369 (response->is_pipe) ||
8370 (0 != (connection->daemon->options & MHD_USE_TLS))
8371#if defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED) && \
8372 defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE)
8373 || (! daemon->sigpipe_blocked && ! connection->sk_spipe_suppress)
8374#endif /* MHD_SEND_SPIPE_SUPPRESS_NEEDED &&
8375 MHD_SEND_SPIPE_SUPPRESS_POSSIBLE */
8376 )
8377 connection->rp.resp_sender = MHD_resp_sender_std;
8378 else
8379 connection->rp.resp_sender = MHD_resp_sender_sendfile;
8380#endif /* _MHD_HAVE_SENDFILE */
8381 /* FIXME: if 'is_pipe' is set, TLS is off, and we have *splice*, we could use splice()
8382 to avoid two user-space copies... */
8383
8384 if ( (MHD_HTTP_MTHD_HEAD == connection->rq.http_mthd) ||
8385 (MHD_HTTP_OK > status_code) ||
8386 (MHD_HTTP_NO_CONTENT == status_code) ||
8387 (MHD_HTTP_NOT_MODIFIED == status_code) )
8388 {
8389 /* if this is a "HEAD" request, or a status code for
8390 which a body is not allowed, pretend that we
8391 have already sent the full message body. */
8392 /* TODO: remove the next assignment, use 'rp_props.send_reply_body' in
8393 * checks */
8394 connection->rp.rsp_write_position = response->total_size;
8395 }
8396 if (MHD_CONNECTION_HEADERS_PROCESSED == connection->state)
8397 {
8398 /* response was queued "early", refuse to read body / footers or
8399 further requests! */
8400 connection->discard_request = true;
8401 connection->state = MHD_CONNECTION_START_REPLY;
8402 connection->rq.remaining_upload_size = 0;
8403 }
8404 if (! connection->in_idle)
8405 (void) MHD_connection_handle_idle (connection);
8406 MHD_update_last_activity_ (connection);
8407 return MHD_YES;
8408}
8409
8410
8411/* end of connection.c */
#define ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG
Definition connection.c:167
#define REQUEST_CONTENTLENGTH_TOOLARGE
Definition connection.c:587
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:779
#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:575
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:705
#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:563
#define REQ_HTTP_VER_IS_NOT_SUPPORTED
Definition connection.c:639
#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:544
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:628
#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
#define REQUEST_HTTP1_0_TR_ENCODING
Definition connection.c:527
static bool get_req_headers(struct MHD_Connection *c, bool process_footers)
#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP
Definition connection.c:616
#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:506
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)
static void parse_connection_headers(struct MHD_Connection *c)
#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:521
#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:493
static void connection_switch_from_recv_to_send(struct MHD_Connection *connection)
#define ERR_RSP_WSP_BEFORE_FOOTER
Definition connection.c:328
#define ERR_RSP_INVALID_CHR_IN_HEADER
Definition connection.c:386
#define REQUEST_MULTIPLE_HOST_HDR
Definition connection.c:475
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:550
#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:600
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:912
_MHD_EXTERN int MHD_get_connection_values(struct MHD_Connection *connection, enum MHD_ValueKind kind, MHD_KeyValueIterator iterator, void *iterator_cls)
Definition connection.c:873
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:965
_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:835
_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:2294
_MHD_EXTERN struct MHD_Response * MHD_create_response_from_buffer_static(size_t size, const void *buffer)
Definition response.c:1512
_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_str_equal_caseless_s_bin_n_(a, s, l)
Definition mhd_str.h:122
#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:2340
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
struct MHD_HTTP_Req_Header * prev
Definition internal.h:371
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