GNU libmicrohttpd 1.0.5
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_Res_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 /* append 'pos' to the linked list of headers */
985 if (NULL == connection->rq.headers_received_tail)
986 {
987 mhd_assert (NULL == connection->rq.headers_received);
988 connection->rq.headers_received = pos;
989 connection->rq.headers_received_tail = pos;
990 }
991 else
992 {
993 mhd_assert (NULL != connection->rq.headers_received);
994 mhd_assert (NULL == connection->rq.headers_received_tail->next);
995 mhd_assert (pos != connection->rq.headers_received_tail);
996 mhd_assert (pos != connection->rq.headers_received);
997 connection->rq.headers_received_tail->next = pos;
998 connection->rq.headers_received_tail = pos;
999 }
1000 return MHD_YES;
1001}
1002
1003
1031 enum MHD_ValueKind kind,
1032 const char *key,
1033 size_t key_size,
1034 const char *value,
1035 size_t value_size)
1036{
1037 if ( (MHD_GET_ARGUMENT_KIND != kind) &&
1038 ( ((key ? strlen (key) : 0) != key_size) ||
1039 ((value ? strlen (value) : 0) != value_size) ) )
1040 return MHD_NO; /* binary zero is allowed only in GET arguments */
1041
1042 return MHD_set_connection_value_n_nocheck_ (connection,
1043 kind,
1044 key,
1045 key_size,
1046 value,
1047 value_size);
1048}
1049
1050
1078 enum MHD_ValueKind kind,
1079 const char *key,
1080 const char *value)
1081{
1082 return MHD_set_connection_value_n_nocheck_ (connection,
1083 kind,
1084 key,
1085 NULL != key
1086 ? strlen (key)
1087 : 0,
1088 value,
1089 NULL != value
1090 ? strlen (value)
1091 : 0);
1092}
1093
1094
1105_MHD_EXTERN const char *
1107 enum MHD_ValueKind kind,
1108 const char *key)
1109{
1110 const char *value;
1111
1112 value = NULL;
1113 (void) MHD_lookup_connection_value_n (connection,
1114 kind,
1115 key,
1116 (NULL == key) ? 0 : strlen (key),
1117 &value,
1118 NULL);
1119 return value;
1120}
1121
1122
1144 enum MHD_ValueKind kind,
1145 const char *key,
1146 size_t key_size,
1147 const char **value_ptr,
1148 size_t *value_size_ptr)
1149{
1150 struct MHD_HTTP_Req_Header *pos;
1151
1152 if (NULL == connection)
1153 return MHD_NO;
1154
1155 if (NULL == key)
1156 {
1157 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1158 {
1159 if ( (0 != (kind & pos->kind)) &&
1160 (NULL == pos->header) )
1161 break;
1162 }
1163 }
1164 else
1165 {
1166 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1167 {
1168 if ( (0 != (kind & pos->kind)) &&
1169 (key_size == pos->header_size) &&
1170 ( (key == pos->header) ||
1172 pos->header,
1173 key_size) ) ) )
1174 break;
1175 }
1176 }
1177
1178 if (NULL == pos)
1179 return MHD_NO;
1180
1181 if (NULL != value_ptr)
1182 *value_ptr = pos->value;
1183
1184 if (NULL != value_size_ptr)
1185 *value_size_ptr = pos->value_size;
1186
1187 return MHD_YES;
1188}
1189
1190
1206static bool
1208 const char *header,
1209 size_t header_len,
1210 const char *token,
1211 size_t token_len)
1212{
1213 struct MHD_HTTP_Req_Header *pos;
1214
1215 if ((NULL == connection) || (NULL == header) || (0 == header[0]) ||
1216 (NULL == token) || (0 == token[0]))
1217 return false;
1218
1219 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1220 {
1221 if ((0 != (pos->kind & MHD_HEADER_KIND)) &&
1222 (header_len == pos->header_size) &&
1223 ( (header == pos->header) ||
1225 pos->header,
1226 header_len)) ) &&
1227 (MHD_str_has_token_caseless_ (pos->value, token, token_len)))
1228 return true;
1229 }
1230 return false;
1231}
1232
1233
1245#define MHD_lookup_header_s_token_ci(c,h,tkn) \
1246 MHD_lookup_header_token_ci ((c),(h),MHD_STATICSTR_LEN_ (h), \
1247 (tkn),MHD_STATICSTR_LEN_ (tkn))
1248
1249
1257static bool
1259{
1260 const char *expect;
1261
1262 if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
1263 return false;
1264
1265 if (0 == connection->rq.remaining_upload_size)
1266 return false;
1267
1268 if (MHD_NO ==
1274 &expect,
1275 NULL))
1276 return false;
1277
1278 if (MHD_str_equal_caseless_ (expect,
1279 "100-continue"))
1280 return true;
1281
1282 return false;
1283}
1284
1285
1292void
1294{
1295 const struct MHD_Daemon *daemon = connection->daemon;
1296
1297 if (0 == (daemon->options & MHD_USE_TURBO))
1298 {
1299#ifdef HTTPS_SUPPORT
1300 /* For TLS connection use shutdown of TLS layer
1301 * and do not shutdown TCP socket. This give more
1302 * chances to send TLS closure data to remote side.
1303 * Closure of TLS layer will be interpreted by
1304 * remote side as end of transmission. */
1305 if (0 != (daemon->options & MHD_USE_TLS))
1306 {
1307 if (! MHD_tls_connection_shutdown (connection))
1308 shutdown (connection->socket_fd,
1309 SHUT_WR);
1310 }
1311 else /* Combined with next 'shutdown()'. */
1312#endif /* HTTPS_SUPPORT */
1313 shutdown (connection->socket_fd,
1314 SHUT_WR);
1315 }
1316 connection->state = MHD_CONNECTION_CLOSED;
1318}
1319
1320
1330void
1332 enum MHD_RequestTerminationCode termination_code)
1333{
1334 struct MHD_Daemon *daemon = connection->daemon;
1335 struct MHD_Response *resp = connection->rp.response;
1336
1337 mhd_assert (! connection->suspended);
1338#ifdef MHD_USE_THREADS
1339 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
1340 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
1341#endif /* MHD_USE_THREADS */
1342 if ( (NULL != daemon->notify_completed) &&
1343 (connection->rq.client_aware) )
1344 daemon->notify_completed (daemon->notify_completed_cls,
1345 connection,
1346 &connection->rq.client_context,
1347 termination_code);
1348 connection->rq.client_aware = false;
1349 if (NULL != resp)
1350 {
1351 connection->rp.response = NULL;
1352 MHD_destroy_response (resp);
1353 }
1354 if (NULL != connection->pool)
1355 {
1356 MHD_pool_destroy (connection->pool);
1357 connection->pool = NULL;
1358 }
1359
1360 MHD_connection_mark_closed_ (connection);
1361}
1362
1363
1364#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
1375void
1377{
1378 struct MHD_Daemon *daemon = connection->daemon;
1379 struct MHD_UpgradeResponseHandle *urh = connection->urh;
1380
1381#ifdef MHD_USE_THREADS
1382 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
1385#endif /* MHD_USE_THREADS */
1386
1387 if (0 == (daemon->options & MHD_USE_TLS))
1388 return; /* Nothing to do with non-TLS connection. */
1389
1390 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
1391 DLL_remove (daemon->urh_head,
1392 daemon->urh_tail,
1393 urh);
1394#ifdef EPOLL_SUPPORT
1395 if (MHD_D_IS_USING_EPOLL_ (daemon) &&
1396 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
1397 EPOLL_CTL_DEL,
1398 connection->socket_fd,
1399 NULL)) )
1400 {
1401 MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
1402 }
1403 if (urh->in_eready_list)
1404 {
1405 EDLL_remove (daemon->eready_urh_head,
1406 daemon->eready_urh_tail,
1407 urh);
1408 urh->in_eready_list = false;
1409 }
1410#endif /* EPOLL_SUPPORT */
1411 if (MHD_INVALID_SOCKET != urh->mhd.socket)
1412 {
1413#ifdef EPOLL_SUPPORT
1414 if (MHD_D_IS_USING_EPOLL_ (daemon) &&
1415 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
1416 EPOLL_CTL_DEL,
1417 urh->mhd.socket,
1418 NULL)) )
1419 {
1420 MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
1421 }
1422#endif /* EPOLL_SUPPORT */
1423 /* Reflect remote disconnect to application by breaking
1424 * socketpair connection. */
1425 shutdown (urh->mhd.socket, SHUT_RDWR);
1426 }
1427 /* Socketpair sockets will remain open as they will be
1428 * used with MHD_UPGRADE_ACTION_CLOSE. They will be
1429 * closed by cleanup_upgraded_connection() during
1430 * connection's final cleanup.
1431 */
1432}
1433
1434
1435#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT*/
1436
1437
1445static void
1447 const char *emsg)
1448{
1449 connection->stop_with_error = true;
1450 connection->discard_request = true;
1451#ifdef HAVE_MESSAGES
1452 if (NULL != emsg)
1453 MHD_DLOG (connection->daemon,
1454 "%s\n",
1455 emsg);
1456#else /* ! HAVE_MESSAGES */
1457 (void) emsg; /* Mute compiler warning. */
1458#endif /* ! HAVE_MESSAGES */
1459 MHD_connection_close_ (connection,
1461}
1462
1463
1468#ifdef HAVE_MESSAGES
1469#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, emsg)
1470#else
1471#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, NULL)
1472#endif
1473
1474
1487static enum MHD_Result
1489{
1490 ssize_t ret;
1491 struct MHD_Response *response;
1492
1493 response = connection->rp.response;
1494 mhd_assert (connection->rp.props.send_reply_body);
1495
1496 if ( (0 == response->total_size) ||
1497 /* TODO: replace the next check with assert */
1498 (connection->rp.rsp_write_position == response->total_size) )
1499 return MHD_YES; /* 0-byte response is always ready */
1500 if (NULL != response->data_iov)
1501 {
1502 size_t copy_size;
1503
1504 if (NULL != connection->rp.resp_iov.iov)
1505 return MHD_YES;
1506 copy_size = response->data_iovcnt * sizeof(MHD_iovec_);
1507 connection->rp.resp_iov.iov = MHD_connection_alloc_memory_ (connection,
1508 copy_size);
1509 if (NULL == connection->rp.resp_iov.iov)
1510 {
1511 MHD_mutex_unlock_chk_ (&response->mutex);
1512 /* not enough memory */
1513 CONNECTION_CLOSE_ERROR (connection,
1514 _ ("Closing connection (out of memory)."));
1515 return MHD_NO;
1516 }
1517 memcpy (connection->rp.resp_iov.iov,
1518 response->data_iov,
1519 copy_size);
1520 connection->rp.resp_iov.cnt = response->data_iovcnt;
1521 connection->rp.resp_iov.sent = 0;
1522 return MHD_YES;
1523 }
1524 if (NULL == response->crc)
1525 return MHD_YES;
1526 if ( (response->data_start <=
1527 connection->rp.rsp_write_position) &&
1528 (response->data_size + response->data_start >
1529 connection->rp.rsp_write_position) )
1530 return MHD_YES; /* response already ready */
1531#if defined(_MHD_HAVE_SENDFILE)
1532 if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
1533 {
1534 /* will use sendfile, no need to bother response crc */
1535 return MHD_YES;
1536 }
1537#endif /* _MHD_HAVE_SENDFILE */
1538
1539 ret = response->crc (response->crc_cls,
1540 connection->rp.rsp_write_position,
1541 (char *) response->data,
1542 (size_t) MHD_MIN ((uint64_t) response->data_buffer_size,
1543 response->total_size
1544 - connection->rp.rsp_write_position));
1545 if (0 > ret)
1546 {
1547 /* either error or http 1.0 transfer, close socket! */
1548 /* TODO: do not update total size, check whether response
1549 * was really with unknown size */
1550 response->total_size = connection->rp.rsp_write_position;
1551#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1552 MHD_mutex_unlock_chk_ (&response->mutex);
1553#endif
1555 MHD_connection_close_ (connection,
1557 else
1558 CONNECTION_CLOSE_ERROR (connection,
1559 _ ("Closing connection (application reported " \
1560 "error generating data)."));
1561 return MHD_NO;
1562 }
1563 response->data_start = connection->rp.rsp_write_position;
1564 response->data_size = (size_t) ret;
1565 if (0 == ret)
1566 {
1568#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1569 MHD_mutex_unlock_chk_ (&response->mutex);
1570#endif
1571 return MHD_NO;
1572 }
1573 return MHD_YES;
1574}
1575
1576
1589static enum MHD_Result
1591 bool *p_finished)
1592{
1593 ssize_t ret;
1594 struct MHD_Response *response;
1595 static const size_t max_chunk = 0xFFFFFF;
1596 char chunk_hdr[6]; /* 6: max strlen of "FFFFFF" */
1597 /* "FFFFFF" + "\r\n" */
1598 static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2;
1599 /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */
1600 static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2;
1601 size_t chunk_hdr_len;
1602 uint64_t left_to_send;
1603 size_t size_to_fill;
1604
1605 response = connection->rp.response;
1606 mhd_assert (NULL != response->crc || NULL != response->data);
1607
1608 mhd_assert (0 == connection->write_buffer_append_offset);
1609
1610 /* The buffer must be reasonably large enough */
1611 if (128 > connection->write_buffer_size)
1612 {
1613 size_t size;
1614
1615 size = connection->write_buffer_size + MHD_pool_get_free (connection->pool);
1616 if (128 > size)
1617 {
1618#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1619 MHD_mutex_unlock_chk_ (&response->mutex);
1620#endif
1621 /* not enough memory */
1622 CONNECTION_CLOSE_ERROR (connection,
1623 _ ("Closing connection (out of memory)."));
1624 return MHD_NO;
1625 }
1626 /* Limit the buffer size to the largest usable size for chunks */
1627 if ( (max_chunk + max_chunk_overhead) < size)
1628 size = max_chunk + max_chunk_overhead;
1629 mhd_assert ((NULL == connection->write_buffer) || \
1630 MHD_pool_is_resizable_inplace (connection->pool, \
1631 connection->write_buffer, \
1632 connection->write_buffer_size));
1633 connection->write_buffer =
1634 MHD_pool_reallocate (connection->pool,
1635 connection->write_buffer,
1636 connection->write_buffer_size,
1637 size);
1638 mhd_assert (NULL != connection->write_buffer);
1639 connection->write_buffer_size = size;
1640 }
1641 mhd_assert (max_chunk_overhead < connection->write_buffer_size);
1642
1643 if (MHD_SIZE_UNKNOWN == response->total_size)
1644 left_to_send = MHD_SIZE_UNKNOWN;
1645 else
1646 left_to_send = response->total_size
1647 - connection->rp.rsp_write_position;
1648
1649 size_to_fill = connection->write_buffer_size - max_chunk_overhead;
1650 /* Limit size for the callback to the max usable size */
1651 if (max_chunk < size_to_fill)
1652 size_to_fill = max_chunk;
1653 if (left_to_send < size_to_fill)
1654 size_to_fill = (size_t) left_to_send;
1655
1656 if (0 == left_to_send)
1657 /* nothing to send, don't bother calling crc */
1659 else if ( (response->data_start <=
1660 connection->rp.rsp_write_position) &&
1661 (response->data_start + response->data_size >
1662 connection->rp.rsp_write_position) )
1663 {
1664 /* difference between rsp_write_position and data_start is less
1665 than data_size which is size_t type, no need to check for overflow */
1666 const size_t data_write_offset
1667 = (size_t) (connection->rp.rsp_write_position
1668 - response->data_start);
1669 /* buffer already ready, use what is there for the chunk */
1670 mhd_assert (SSIZE_MAX >= (response->data_size - data_write_offset));
1671 mhd_assert (response->data_size >= data_write_offset);
1672 ret = (ssize_t) (response->data_size - data_write_offset);
1673 if ( ((size_t) ret) > size_to_fill)
1674 ret = (ssize_t) size_to_fill;
1675 memcpy (&connection->write_buffer[max_chunk_hdr_len],
1676 &response->data[data_write_offset],
1677 (size_t) ret);
1678 }
1679 else
1680 {
1681 if (NULL == response->crc)
1682 { /* There is no way to reach this code */
1683#if defined(MHD_USE_THREADS)
1684 MHD_mutex_unlock_chk_ (&response->mutex);
1685#endif
1686 CONNECTION_CLOSE_ERROR (connection,
1687 _ ("No callback for the chunked data."));
1688 return MHD_NO;
1689 }
1690 ret = response->crc (response->crc_cls,
1691 connection->rp.rsp_write_position,
1692 &connection->write_buffer[max_chunk_hdr_len],
1693 size_to_fill);
1694 }
1696 {
1697 /* error, close socket! */
1698 /* TODO: remove update of the response size */
1699 response->total_size = connection->rp.rsp_write_position;
1700#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1701 MHD_mutex_unlock_chk_ (&response->mutex);
1702#endif
1703 CONNECTION_CLOSE_ERROR (connection,
1704 _ ("Closing connection (application error " \
1705 "generating response)."));
1706 return MHD_NO;
1707 }
1709 {
1710 *p_finished = true;
1711 /* TODO: remove update of the response size */
1712 response->total_size = connection->rp.rsp_write_position;
1713 return MHD_YES;
1714 }
1715 if (0 == ret)
1716 {
1718#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1719 MHD_mutex_unlock_chk_ (&response->mutex);
1720#endif
1721 return MHD_NO;
1722 }
1723 if (size_to_fill < (size_t) ret)
1724 {
1725#if defined(MHD_USE_THREADS)
1726 MHD_mutex_unlock_chk_ (&response->mutex);
1727#endif
1728 CONNECTION_CLOSE_ERROR (connection,
1729 _ ("Closing connection (application returned " \
1730 "more data than requested)."));
1731 return MHD_NO;
1732 }
1733 chunk_hdr_len = MHD_uint32_to_strx ((uint32_t) ret, chunk_hdr,
1734 sizeof(chunk_hdr));
1735 mhd_assert (chunk_hdr_len != 0);
1736 mhd_assert (chunk_hdr_len < sizeof(chunk_hdr));
1737 *p_finished = false;
1738 connection->write_buffer_send_offset =
1739 (max_chunk_hdr_len - (chunk_hdr_len + 2));
1740 memcpy (connection->write_buffer + connection->write_buffer_send_offset,
1741 chunk_hdr,
1742 chunk_hdr_len);
1743 connection->write_buffer[max_chunk_hdr_len - 2] = '\r';
1744 connection->write_buffer[max_chunk_hdr_len - 1] = '\n';
1745 connection->write_buffer[max_chunk_hdr_len + (size_t) ret] = '\r';
1746 connection->write_buffer[max_chunk_hdr_len + (size_t) ret + 1] = '\n';
1747 connection->rp.rsp_write_position += (size_t) ret;
1748 connection->write_buffer_append_offset = max_chunk_hdr_len + (size_t) ret + 2;
1749 return MHD_YES;
1750}
1751
1752
1775static enum MHD_ConnKeepAlive
1777{
1778 struct MHD_Connection *const c = connection;
1779 struct MHD_Response *const r = c->rp.response;
1780
1781 mhd_assert (NULL != r);
1783 return MHD_CONN_MUST_CLOSE;
1784
1785#ifdef UPGRADE_SUPPORT
1786 /* TODO: Move below the next check when MHD stops closing connections
1787 * when response is queued in first callback */
1788 if (NULL != r->upgrade_handler)
1789 {
1790 /* No "close" token is enforced by 'add_response_header_connection()' */
1792 /* Valid HTTP version is enforced by 'MHD_queue_response()' */
1795 return MHD_CONN_MUST_UPGRADE;
1796 }
1797#endif /* UPGRADE_SUPPORT */
1798
1799 mhd_assert ( (! c->stop_with_error) || (c->discard_request));
1800 if ((c->read_closed) || (c->discard_request))
1801 return MHD_CONN_MUST_CLOSE;
1802
1804 return MHD_CONN_MUST_CLOSE;
1806 return MHD_CONN_MUST_CLOSE;
1807
1809 return MHD_CONN_MUST_CLOSE;
1810
1813 "close"))
1814 return MHD_CONN_MUST_CLOSE;
1815
1816 if ((MHD_HTTP_VER_1_0 == connection->rq.http_ver) ||
1817 (0 != (connection->rp.response->flags & MHD_RF_HTTP_1_0_SERVER)))
1818 {
1819 if (MHD_lookup_header_s_token_ci (connection,
1821 "Keep-Alive"))
1823
1824 return MHD_CONN_MUST_CLOSE;
1825 }
1826
1829
1830 return MHD_CONN_MUST_CLOSE;
1831}
1832
1833
1843static bool
1844get_date_str (char *date)
1845{
1846 static const char *const days[] = {
1847 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1848 };
1849 static const char *const mons[] = {
1850 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1851 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1852 };
1853 static const size_t buf_len = 29;
1854 struct tm now;
1855 time_t t;
1856 const char *src;
1857#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
1858 ! defined(HAVE_GMTIME_R)
1859 struct tm *pNow;
1860#endif
1861
1862 if ((time_t) -1 == time (&t))
1863 return false;
1864#if defined(HAVE_C11_GMTIME_S)
1865 if (NULL == gmtime_s (&t,
1866 &now))
1867 return false;
1868#elif defined(HAVE_W32_GMTIME_S)
1869 if (0 != gmtime_s (&now,
1870 &t))
1871 return false;
1872#elif defined(HAVE_GMTIME_R)
1873 if (NULL == gmtime_r (&t,
1874 &now))
1875 return false;
1876#else
1877 pNow = gmtime (&t);
1878 if (NULL == pNow)
1879 return false;
1880 now = *pNow;
1881#endif
1882
1883 /* Day of the week */
1884 src = days[now.tm_wday % 7];
1885 date[0] = src[0];
1886 date[1] = src[1];
1887 date[2] = src[2];
1888 date[3] = ',';
1889 date[4] = ' ';
1890 /* Day of the month */
1891 if (2 != MHD_uint8_to_str_pad ((uint8_t) now.tm_mday, 2,
1892 date + 5, buf_len - 5))
1893 return false;
1894 date[7] = ' ';
1895 /* Month */
1896 src = mons[now.tm_mon % 12];
1897 date[8] = src[0];
1898 date[9] = src[1];
1899 date[10] = src[2];
1900 date[11] = ' ';
1901 /* Year */
1902 if (4 != MHD_uint16_to_str ((uint16_t) (1900 + now.tm_year), date + 12,
1903 buf_len - 12))
1904 return false;
1905 date[16] = ' ';
1906 /* Time */
1907 MHD_uint8_to_str_pad ((uint8_t) now.tm_hour, 2, date + 17, buf_len - 17);
1908 date[19] = ':';
1909 MHD_uint8_to_str_pad ((uint8_t) now.tm_min, 2, date + 20, buf_len - 20);
1910 date[22] = ':';
1911 MHD_uint8_to_str_pad ((uint8_t) now.tm_sec, 2, date + 23, buf_len - 23);
1912 date[25] = ' ';
1913 date[26] = 'G';
1914 date[27] = 'M';
1915 date[28] = 'T';
1916
1917 return true;
1918}
1919
1920
1928static bool
1929get_date_header (char *header)
1930{
1931 if (! get_date_str (header + 6))
1932 {
1933 header[0] = 0;
1934 return false;
1935 }
1936 header[0] = 'D';
1937 header[1] = 'a';
1938 header[2] = 't';
1939 header[3] = 'e';
1940 header[4] = ':';
1941 header[5] = ' ';
1942 header[35] = '\r';
1943 header[36] = '\n';
1944 header[37] = 0;
1945 return true;
1946}
1947
1948
1961static bool
1963 bool required)
1964{
1965 size_t new_size;
1966 size_t avail_size;
1967 const size_t def_grow_size = connection->daemon->pool_increment;
1968 void *rb;
1969
1970 avail_size = MHD_pool_get_free (connection->pool);
1971 if (0 == avail_size)
1972 return false; /* No more space available */
1973 if (0 == connection->read_buffer_size)
1974 new_size = avail_size / 2; /* Use half of available buffer for reading */
1975 else
1976 {
1977 size_t grow_size;
1978
1979 grow_size = avail_size / 8;
1980 if (def_grow_size > grow_size)
1981 { /* Shortage of space */
1982 const size_t left_free =
1983 connection->read_buffer_size - connection->read_buffer_offset;
1984 mhd_assert (connection->read_buffer_size >= \
1985 connection->read_buffer_offset);
1986 if ((def_grow_size <= grow_size + left_free)
1987 && (left_free < def_grow_size))
1988 grow_size = def_grow_size - left_free; /* Use precise 'def_grow_size' for new free space */
1989 else if (! required)
1990 return false; /* Grow is not mandatory, leave some space in pool */
1991 else
1992 {
1993 /* Shortage of space, but grow is mandatory */
1994 const size_t small_inc =
1995 ((MHD_BUF_INC_SIZE > def_grow_size) ?
1996 def_grow_size : MHD_BUF_INC_SIZE) / 8;
1997 if (small_inc < avail_size)
1998 grow_size = small_inc;
1999 else
2000 grow_size = avail_size;
2001 }
2002 }
2003 new_size = connection->read_buffer_size + grow_size;
2004 }
2005 /* Make sure that read buffer will not be moved */
2006 if ((NULL != connection->read_buffer) &&
2007 ! MHD_pool_is_resizable_inplace (connection->pool,
2008 connection->read_buffer,
2009 connection->read_buffer_size))
2010 {
2011 mhd_assert (0);
2012 return false;
2013 }
2014 /* we can actually grow the buffer, do it! */
2015 rb = MHD_pool_reallocate (connection->pool,
2016 connection->read_buffer,
2017 connection->read_buffer_size,
2018 new_size);
2019 if (NULL == rb)
2020 {
2021 /* This should NOT be possible: we just computed 'new_size' so that
2022 it should fit. If it happens, somehow our read buffer is not in
2023 the right position in the pool, say because someone called
2024 MHD_pool_allocate() without 'from_end' set to 'true'? Anyway,
2025 should be investigated! (Ideally provide all data from
2026 *pool and connection->read_buffer and new_size for debugging). */
2027 mhd_assert (0);
2028 return false;
2029 }
2030 mhd_assert (connection->read_buffer == rb);
2031 connection->read_buffer = rb;
2032 mhd_assert (NULL != connection->read_buffer);
2033 connection->read_buffer_size = new_size;
2034 return true;
2035}
2036
2037
2042static void
2044{
2045 struct MHD_Connection *const c = connection;
2046 void *new_buf;
2047
2048 if ((NULL == c->read_buffer) || (0 == c->read_buffer_size))
2049 {
2050 mhd_assert (0 == c->read_buffer_size);
2052 return;
2053 }
2054
2056 if (0 == c->read_buffer_offset)
2057 {
2059 c->read_buffer = NULL;
2060 c->read_buffer_size = 0;
2061 }
2062 else
2063 {
2065 c->read_buffer_size));
2068 mhd_assert (c->read_buffer == new_buf);
2069 c->read_buffer = new_buf;
2071 }
2072}
2073
2074
2081static size_t
2083{
2084 struct MHD_Connection *const c = connection;
2085 struct MemoryPool *const pool = connection->pool;
2086 void *new_buf;
2087 size_t new_size;
2088 size_t free_size;
2089
2090 mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
2093
2094 free_size = MHD_pool_get_free (pool);
2095 if (0 != free_size)
2096 {
2097 new_size = c->write_buffer_size + free_size;
2098 /* This function must not move the buffer position.
2099 * MHD_pool_reallocate () may return the new position only if buffer was
2100 * allocated 'from_end' or is not the last allocation,
2101 * which should not happen. */
2102 mhd_assert ((NULL == c->write_buffer) || \
2104 c->write_buffer_size));
2105 new_buf = MHD_pool_reallocate (pool,
2106 c->write_buffer,
2108 new_size);
2109 mhd_assert ((c->write_buffer == new_buf) || (NULL == c->write_buffer));
2110 c->write_buffer = new_buf;
2111 c->write_buffer_size = new_size;
2113 {
2114 /* All data have been sent, reset offsets to zero. */
2117 }
2118 }
2119
2121}
2122
2123
2124#if 0 /* disable unused function */
2133static void
2134connection_shrink_write_buffer (struct MHD_Connection *connection)
2135{
2136 struct MHD_Connection *const c = connection;
2137 struct MemoryPool *const pool = connection->pool;
2138 void *new_buf;
2139
2140 mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
2143
2144 if ( (NULL == c->write_buffer) || (0 == c->write_buffer_size))
2145 {
2148 c->write_buffer = NULL;
2149 return;
2150 }
2152 return;
2153
2154 new_buf = MHD_pool_reallocate (pool, c->write_buffer, c->write_buffer_size,
2156 mhd_assert ((c->write_buffer == new_buf) || \
2157 (0 == c->write_buffer_append_offset));
2159 if (0 == c->write_buffer_size)
2160 c->write_buffer = NULL;
2161 else
2162 c->write_buffer = new_buf;
2163}
2164
2165
2166#endif /* unused function */
2167
2168
2176static void
2178{
2179 /* Read buffer is not needed for this request, shrink it.*/
2180 connection_shrink_read_buffer (connection);
2181}
2182
2183
2211
2212
2224static enum replyBodyUse
2226 unsigned int rcode)
2227{
2228 struct MHD_Connection *const c = connection;
2229
2230 mhd_assert (100 <= rcode);
2231 mhd_assert (999 >= rcode);
2232
2233 if (199 >= rcode)
2234 return RP_BODY_NONE;
2235
2236 if (MHD_HTTP_NO_CONTENT == rcode)
2237 return RP_BODY_NONE;
2238
2239#if 0
2240 /* This check is not needed as upgrade handler is used only with code 101 */
2241#ifdef UPGRADE_SUPPORT
2242 if (NULL != rp.response->upgrade_handler)
2243 return RP_BODY_NONE;
2244#endif /* UPGRADE_SUPPORT */
2245#endif
2246
2247#if 0
2248 /* CONNECT is not supported by MHD */
2249 /* Successful responses for connect requests are filtered by
2250 * MHD_queue_response() */
2251 if ( (MHD_HTTP_MTHD_CONNECT == c->rq.http_mthd) &&
2252 (2 == rcode / 100) )
2253 return false; /* Actually pass-through CONNECT is not supported by MHD */
2254#endif
2255
2256 /* Reply body headers could be used.
2257 * Check whether reply body itself must be used. */
2258
2260 return RP_BODY_HEADERS_ONLY;
2261
2262 if (MHD_HTTP_NOT_MODIFIED == rcode)
2263 return RP_BODY_HEADERS_ONLY;
2264
2265 /* Reply body must be sent. The body may have zero length, but body size
2266 * must be indicated by headers ('Content-Length:' or
2267 * 'Transfer-Encoding: chunked'). */
2268 return RP_BODY_SEND;
2269}
2270
2271
2280static void
2282{
2283 struct MHD_Connection *const c = connection;
2284 struct MHD_Response *const r = c->rp.response;
2285 enum replyBodyUse use_rp_body;
2286 bool use_chunked;
2287
2288 mhd_assert (NULL != r);
2289
2290 /* ** Adjust reply properties ** */
2291
2293 use_rp_body = is_reply_body_needed (c, c->rp.responseCode);
2294 c->rp.props.send_reply_body = (use_rp_body > RP_BODY_HEADERS_ONLY);
2296
2297#ifdef UPGRADE_SUPPORT
2298 mhd_assert ( (NULL == r->upgrade_handler) ||
2299 (RP_BODY_NONE == use_rp_body) );
2300#endif /* UPGRADE_SUPPORT */
2301
2303 {
2304 if ((MHD_SIZE_UNKNOWN == r->total_size) ||
2306 { /* Use chunked reply encoding if possible */
2307
2308 /* Check whether chunked encoding is supported by the client */
2310 use_chunked = false;
2311 /* Check whether chunked encoding is allowed for the reply */
2312 else if (0 != (r->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
2314 use_chunked = false;
2315 else
2316 /* If chunked encoding is supported and allowed, and response size
2317 * is unknown, use chunked even for non-Keep-Alive connections.
2318 * See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3
2319 * Also use chunked if it is enforced by application and supported by
2320 * the client. */
2321 use_chunked = true;
2322 }
2323 else
2324 use_chunked = false;
2325
2326 if ( (MHD_SIZE_UNKNOWN == r->total_size) &&
2327 (! use_chunked) )
2328 {
2329 /* End of the stream is indicated by closure */
2331 }
2332 }
2333 else
2334 use_chunked = false; /* chunked encoding cannot be used without body */
2335
2336 c->rp.props.chunked = use_chunked;
2337#ifdef _DEBUG
2338 c->rp.props.set = true;
2339#endif /* _DEBUG */
2340}
2341
2342
2347static void
2349{
2350 struct MHD_Connection *const c = connection;
2351 struct MHD_Response *const r = c->rp.response;
2352
2353 mhd_assert (c->rp.props.set);
2354#ifdef HAVE_MESSAGES
2355 if ( (! c->rp.props.use_reply_body_headers) &&
2356 (0 != r->total_size) )
2357 {
2358 MHD_DLOG (c->daemon,
2359 _ ("This reply with response code %u cannot use reply body. "
2360 "Non-empty response body is ignored and not used.\n"),
2361 (unsigned) (c->rp.responseCode));
2362 }
2363 if ( (! c->rp.props.use_reply_body_headers) &&
2365 {
2366 MHD_DLOG (c->daemon,
2367 _ ("This reply with response code %u cannot use reply body. "
2368 "Application defined \"Content-Length\" header violates"
2369 "HTTP specification.\n"),
2370 (unsigned) (c->rp.responseCode));
2371 }
2372#else
2373 (void) c; /* Mute compiler warning */
2374 (void) r; /* Mute compiler warning */
2375#endif
2376}
2377
2378
2390static bool
2391buffer_append (char *buf,
2392 size_t *ppos,
2393 size_t buf_size,
2394 const char *append,
2395 size_t append_size)
2396{
2397 mhd_assert (NULL != buf); /* Mute static analyzer */
2398 if (buf_size < *ppos + append_size)
2399 return false;
2400 memcpy (buf + *ppos, append, append_size);
2401 *ppos += append_size;
2402 return true;
2403}
2404
2405
2416#define buffer_append_s(buf,ppos,buf_size,str) \
2417 buffer_append (buf,ppos,buf_size,str, MHD_STATICSTR_LEN_ (str))
2418
2419
2439static bool
2441 size_t *ppos,
2442 size_t buf_size,
2443 struct MHD_Response *response,
2444 bool filter_transf_enc,
2445 bool filter_content_len,
2446 bool add_close,
2447 bool add_keep_alive)
2448{
2449 struct MHD_Response *const r = response;
2450 struct MHD_HTTP_Res_Header *hdr;
2451 size_t el_size;
2452
2453 mhd_assert (! add_close || ! add_keep_alive);
2454
2456 filter_transf_enc = false; /* No such header */
2457 if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2458 filter_content_len = false; /* No such header */
2459 if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2460 {
2461 add_close = false; /* No such header */
2462 add_keep_alive = false; /* No such header */
2463 }
2464 else if (0 != (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE))
2465 add_close = false; /* "close" token was already set */
2466
2467 for (hdr = r->first_header; NULL != hdr; hdr = hdr->next)
2468 {
2469 size_t initial_pos = *ppos;
2470 if (MHD_HEADER_KIND != hdr->kind)
2471 continue;
2472 if (filter_transf_enc)
2473 { /* Need to filter-out "Transfer-Encoding" */
2475 hdr->header_size) &&
2477 hdr->header, hdr->header_size)) )
2478 {
2479 filter_transf_enc = false; /* There is the only one such header */
2480 continue; /* Skip "Transfer-Encoding" header */
2481 }
2482 }
2483 if (filter_content_len)
2484 { /* Need to filter-out "Content-Length" */
2486 hdr->header_size) &&
2488 hdr->header, hdr->header_size)) )
2489 {
2490 /* Reset filter flag if only one header is allowed */
2491 filter_transf_enc =
2493 continue; /* Skip "Content-Length" header */
2494 }
2495 }
2496
2497 /* Add user header */
2498 el_size = hdr->header_size + 2 + hdr->value_size + 2;
2499 if (buf_size < *ppos + el_size)
2500 return false;
2501 memcpy (buf + *ppos, hdr->header, hdr->header_size);
2502 (*ppos) += hdr->header_size;
2503 buf[(*ppos)++] = ':';
2504 buf[(*ppos)++] = ' ';
2505 if (add_close || add_keep_alive)
2506 {
2507 /* "Connection:" header must be always the first one */
2510 hdr->header_size));
2511
2512 if (add_close)
2513 {
2514 el_size += MHD_STATICSTR_LEN_ ("close, ");
2515 if (buf_size < initial_pos + el_size)
2516 return false;
2517 memcpy (buf + *ppos, "close, ",
2518 MHD_STATICSTR_LEN_ ("close, "));
2519 *ppos += MHD_STATICSTR_LEN_ ("close, ");
2520 }
2521 else
2522 {
2523 el_size += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2524 if (buf_size < initial_pos + el_size)
2525 return false;
2526 memcpy (buf + *ppos, "Keep-Alive, ",
2527 MHD_STATICSTR_LEN_ ("Keep-Alive, "));
2528 *ppos += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2529 }
2530 add_close = false;
2531 add_keep_alive = false;
2532 }
2533 if (0 != hdr->value_size)
2534 memcpy (buf + *ppos, hdr->value, hdr->value_size);
2535 *ppos += hdr->value_size;
2536 buf[(*ppos)++] = '\r';
2537 buf[(*ppos)++] = '\n';
2538 mhd_assert (initial_pos + el_size == (*ppos));
2539 }
2540 return true;
2541}
2542
2543
2552static enum MHD_Result
2554{
2555 struct MHD_Connection *const c = connection;
2556 struct MHD_Response *const r = c->rp.response;
2557 char *buf;
2558 size_t pos;
2559 size_t buf_size;
2560 size_t el_size;
2561 unsigned rcode;
2562 bool use_conn_close;
2563 bool use_conn_k_alive;
2564
2565 mhd_assert (NULL != r);
2566
2567 /* ** Adjust response properties ** */
2569
2570 mhd_assert (c->rp.props.set);
2574#ifdef UPGRADE_SUPPORT
2575 mhd_assert ((NULL == r->upgrade_handler) || \
2577#else /* ! UPGRADE_SUPPORT */
2579#endif /* ! UPGRADE_SUPPORT */
2581 mhd_assert ((! c->rp.props.send_reply_body) || \
2583#ifdef UPGRADE_SUPPORT
2584 mhd_assert (NULL == r->upgrade_handler || \
2586#endif /* UPGRADE_SUPPORT */
2587
2589
2590 rcode = (unsigned) c->rp.responseCode;
2592 {
2593 /* The closure of connection must be always indicated by header
2594 * to avoid hung connections */
2595 use_conn_close = true;
2596 use_conn_k_alive = false;
2597 }
2598 else if (MHD_CONN_USE_KEEPALIVE == c->keepalive)
2599 {
2600 use_conn_close = false;
2601 /* Add "Connection: keep-alive" if request is HTTP/1.0 or
2602 * if reply is HTTP/1.0
2603 * For HTTP/1.1 add header only if explicitly requested by app
2604 * (by response flag), as "Keep-Alive" is default for HTTP/1.1. */
2605 if ((0 != (r->flags & MHD_RF_SEND_KEEP_ALIVE_HEADER)) ||
2606 (MHD_HTTP_VER_1_0 == c->rq.http_ver) ||
2607 (0 != (r->flags & MHD_RF_HTTP_1_0_SERVER)))
2608 use_conn_k_alive = true;
2609 else
2610 use_conn_k_alive = false;
2611 }
2612 else
2613 {
2614 use_conn_close = false;
2615 use_conn_k_alive = false;
2616 }
2617
2618 /* ** Actually build the response header ** */
2619
2620 /* Get all space available */
2622 buf = c->write_buffer;
2624 buf_size = c->write_buffer_size;
2625 if (0 == buf_size)
2626 return MHD_NO;
2627 mhd_assert (NULL != buf);
2628
2629 /* * The status line * */
2630
2631 /* The HTTP version */
2632 if (! c->rp.responseIcy)
2633 { /* HTTP reply */
2634 if (0 == (r->flags & MHD_RF_HTTP_1_0_SERVER))
2635 { /* HTTP/1.1 reply */
2636 /* Use HTTP/1.1 responses for HTTP/1.0 clients.
2637 * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
2638 if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_1))
2639 return MHD_NO;
2640 }
2641 else
2642 { /* HTTP/1.0 reply */
2643 if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_0))
2644 return MHD_NO;
2645 }
2646 }
2647 else
2648 { /* ICY reply */
2649 if (! buffer_append_s (buf, &pos, buf_size, "ICY"))
2650 return MHD_NO;
2651 }
2652
2653 /* The response code */
2654 if (buf_size < pos + 5) /* space + code + space */
2655 return MHD_NO;
2656 buf[pos++] = ' ';
2657 pos += MHD_uint16_to_str ((uint16_t) rcode, buf + pos,
2658 buf_size - pos);
2659 buf[pos++] = ' ';
2660
2661 /* The reason phrase */
2662 el_size = MHD_get_reason_phrase_len_for (rcode);
2663 if (0 == el_size)
2664 {
2665 if (! buffer_append_s (buf, &pos, buf_size, "Non-Standard Status"))
2666 return MHD_NO;
2667 }
2668 else if (! buffer_append (buf, &pos, buf_size,
2670 el_size))
2671 return MHD_NO;
2672
2673 /* The linefeed */
2674 if (buf_size < pos + 2)
2675 return MHD_NO;
2676 buf[pos++] = '\r';
2677 buf[pos++] = '\n';
2678
2679 /* * The headers * */
2680
2681 /* Main automatic headers */
2682
2683 /* The "Date:" header */
2684 if ( (0 == (r->flags_auto & MHD_RAF_HAS_DATE_HDR)) &&
2686 {
2687 /* Additional byte for unused zero-termination */
2688 if (buf_size < pos + 38)
2689 return MHD_NO;
2690 if (get_date_header (buf + pos))
2691 pos += 37;
2692 }
2693 /* The "Connection:" header */
2694 mhd_assert (! use_conn_close || ! use_conn_k_alive);
2695 mhd_assert (! use_conn_k_alive || ! use_conn_close);
2696 if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2697 {
2698 if (use_conn_close)
2699 {
2700 if (! buffer_append_s (buf, &pos, buf_size,
2701 MHD_HTTP_HEADER_CONNECTION ": close\r\n"))
2702 return MHD_NO;
2703 }
2704 else if (use_conn_k_alive)
2705 {
2706 if (! buffer_append_s (buf, &pos, buf_size,
2707 MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n"))
2708 return MHD_NO;
2709 }
2710 }
2711
2712 /* User-defined headers */
2713
2714 if (! add_user_headers (buf, &pos, buf_size, r,
2715 ! c->rp.props.chunked,
2717 (0 ==
2719 use_conn_close,
2720 use_conn_k_alive))
2721 return MHD_NO;
2722
2723 /* Other automatic headers */
2724
2725 if ( (c->rp.props.use_reply_body_headers) &&
2726 (0 == (r->flags & MHD_RF_HEAD_ONLY_RESPONSE)) )
2727 {
2728 /* Body-specific headers */
2729
2730 if (c->rp.props.chunked)
2731 { /* Chunked encoding is used */
2733 { /* No chunked encoding header set by user */
2734 if (! buffer_append_s (buf, &pos, buf_size,
2736 "chunked\r\n"))
2737 return MHD_NO;
2738 }
2739 }
2740 else /* Chunked encoding is not used */
2741 {
2742 if (MHD_SIZE_UNKNOWN != r->total_size)
2743 { /* The size is known */
2744 if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2745 { /* The response does not have "Content-Length" header */
2746 if (! buffer_append_s (buf, &pos, buf_size,
2748 return MHD_NO;
2749 el_size = MHD_uint64_to_str (r->total_size, buf + pos,
2750 buf_size - pos);
2751 if (0 == el_size)
2752 return MHD_NO;
2753 pos += el_size;
2754
2755 if (buf_size < pos + 2)
2756 return MHD_NO;
2757 buf[pos++] = '\r';
2758 buf[pos++] = '\n';
2759 }
2760 }
2761 }
2762 }
2763
2764 /* * Header termination * */
2765 if (buf_size < pos + 2)
2766 return MHD_NO;
2767 buf[pos++] = '\r';
2768 buf[pos++] = '\n';
2769
2771 return MHD_YES;
2772}
2773
2774
2784static enum MHD_Result
2786{
2787 char *buf;
2788 size_t buf_size;
2789 size_t used_size;
2790 struct MHD_Connection *const c = connection;
2791 struct MHD_HTTP_Res_Header *pos;
2792
2793 mhd_assert (connection->rp.props.chunked);
2794 /* TODO: allow combining of the final footer with the last chunk,
2795 * modify the next assert. */
2797 mhd_assert (NULL != c->rp.response);
2798
2799 buf_size = connection_maximize_write_buffer (c);
2800 /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */
2801 if (buf_size < 5)
2802 return MHD_NO;
2805 mhd_assert (NULL != buf);
2806 used_size = 0;
2807 buf[used_size++] = '0';
2808 buf[used_size++] = '\r';
2809 buf[used_size++] = '\n';
2810
2811 for (pos = c->rp.response->first_header; NULL != pos; pos = pos->next)
2812 {
2813 if (MHD_FOOTER_KIND == pos->kind)
2814 {
2815 size_t new_used_size; /* resulting size with this header */
2816 /* '4' is colon, space, linefeeds */
2817 new_used_size = used_size + pos->header_size + pos->value_size + 4;
2818 if (new_used_size > buf_size)
2819 return MHD_NO;
2820 memcpy (buf + used_size, pos->header, pos->header_size);
2821 used_size += pos->header_size;
2822 buf[used_size++] = ':';
2823 buf[used_size++] = ' ';
2824 memcpy (buf + used_size, pos->value, pos->value_size);
2825 used_size += pos->value_size;
2826 buf[used_size++] = '\r';
2827 buf[used_size++] = '\n';
2828 mhd_assert (used_size == new_used_size);
2829 }
2830 }
2831 if (used_size + 2 > buf_size)
2832 return MHD_NO;
2833 buf[used_size++] = '\r';
2834 buf[used_size++] = '\n';
2835
2836 c->write_buffer_append_offset += used_size;
2838
2839 return MHD_YES;
2840}
2841
2842
2859static void
2861 unsigned int status_code,
2862 const char *message,
2863 size_t message_len,
2864 char *header_name,
2865 size_t header_name_len,
2866 char *header_value,
2867 size_t header_value_len)
2868{
2869 struct MHD_Response *response;
2870 enum MHD_Result iret;
2871
2872 mhd_assert (! connection->stop_with_error); /* Do not send error twice */
2873 if (connection->stop_with_error)
2874 { /* Should not happen */
2875 if (MHD_CONNECTION_CLOSED > connection->state)
2876 connection->state = MHD_CONNECTION_CLOSED;
2877 free (header_name);
2878 free (header_value);
2879 return;
2880 }
2881 connection->stop_with_error = true;
2882 connection->discard_request = true;
2883#ifdef HAVE_MESSAGES
2884 MHD_DLOG (connection->daemon,
2885 _ ("Error processing request (HTTP response code is %u ('%s')). " \
2886 "Closing connection.\n"),
2887 status_code,
2888 message);
2889#endif
2891 {
2892#ifdef HAVE_MESSAGES
2893 MHD_DLOG (connection->daemon,
2894 _ ("Too late to send an error response, " \
2895 "response is being sent already.\n"),
2896 status_code,
2897 message);
2898#endif
2899 CONNECTION_CLOSE_ERROR (connection,
2900 _ ("Too late for error response."));
2901 free (header_name);
2902 free (header_value);
2903 return;
2904 }
2905 /* TODO: remove when special error queue function is implemented */
2907 if (0 != connection->read_buffer_size)
2908 {
2909 /* Read buffer is not needed anymore, discard it
2910 * to free some space for error response. */
2911 MHD_pool_deallocate (connection->pool,
2912 connection->read_buffer,
2913 connection->read_buffer_size);
2914 connection->read_buffer = NULL;
2915 connection->read_buffer_size = 0;
2916 connection->read_buffer_offset = 0;
2917 }
2918 if (NULL != connection->rp.response)
2919 {
2920 MHD_destroy_response (connection->rp.response);
2921 connection->rp.response = NULL;
2922 }
2923 response = MHD_create_response_from_buffer_static (message_len,
2924 message);
2925 if (NULL == response)
2926 {
2927#ifdef HAVE_MESSAGES
2928 MHD_DLOG (connection->daemon,
2929 _ ("Failed to create error response.\n"),
2930 status_code,
2931 message);
2932#endif
2933 /* can't even send a reply, at least close the connection */
2934 connection->state = MHD_CONNECTION_CLOSED;
2935 free (header_name);
2936 free (header_value);
2937 return;
2938 }
2939 mhd_assert ((0 == header_name_len) || (NULL != header_name));
2940 mhd_assert ((NULL == header_name) || (0 != header_name_len));
2941 mhd_assert ((0 == header_value_len) || (NULL != header_value));
2942 mhd_assert ((NULL == header_value) || (0 != header_value_len));
2943 mhd_assert ((NULL == header_name) || (NULL != header_value));
2944 mhd_assert ((NULL != header_value) || (NULL == header_name));
2945 if (NULL != header_name)
2946 {
2947 iret = MHD_add_response_entry_no_alloc_ (response,
2949 header_name, header_name_len,
2950 header_value, header_value_len);
2951 if (MHD_NO == iret)
2952 {
2953 free (header_name);
2954 free (header_value);
2955 }
2956 }
2957 else
2958 iret = MHD_YES;
2959
2960 if (MHD_NO != iret)
2961 {
2962 bool before = connection->in_access_handler;
2963
2964 /* Fake the flag for the internal call */
2965 connection->in_access_handler = true;
2966 iret = MHD_queue_response (connection,
2967 status_code,
2968 response);
2969 connection->in_access_handler = before;
2970 }
2971 MHD_destroy_response (response);
2972 if (MHD_NO == iret)
2973 {
2974 /* can't even send a reply, at least close the connection */
2975 CONNECTION_CLOSE_ERROR (connection,
2976 _ ("Closing connection " \
2977 "(failed to queue error response)."));
2978 return;
2979 }
2980 mhd_assert (NULL != connection->rp.response);
2981 /* Do not reuse this connection. */
2982 connection->keepalive = MHD_CONN_MUST_CLOSE;
2983 if (MHD_NO == build_header_response (connection))
2984 {
2985 /* No memory. Release everything. */
2986 connection->rq.version = NULL;
2987 connection->rq.method = NULL;
2988 connection->rq.url = NULL;
2989 connection->rq.url_len = 0;
2990 connection->rq.url_for_callback = NULL;
2991 connection->rq.headers_received = NULL;
2992 connection->rq.headers_received_tail = NULL;
2993 connection->write_buffer = NULL;
2994 connection->write_buffer_size = 0;
2995 connection->write_buffer_send_offset = 0;
2996 connection->write_buffer_append_offset = 0;
2997 connection->read_buffer
2998 = MHD_pool_reset (connection->pool,
2999 NULL,
3000 0,
3001 0);
3002 connection->read_buffer_size = 0;
3003
3004 /* Retry with empty buffer */
3005 if (MHD_NO == build_header_response (connection))
3006 {
3007 CONNECTION_CLOSE_ERROR (connection,
3008 _ ("Closing connection " \
3009 "(failed to create error response header)."));
3010 return;
3011 }
3012 }
3014}
3015
3016
3020#ifdef HAVE_MESSAGES
3021# define transmit_error_response_static(c, code, msg) \
3022 transmit_error_response_len (c, code, \
3023 msg, MHD_STATICSTR_LEN_ (msg), \
3024 NULL, 0, NULL, 0)
3025#else /* ! HAVE_MESSAGES */
3026# define transmit_error_response_static(c, code, msg) \
3027 transmit_error_response_len (c, code, \
3028 "", 0, \
3029 NULL, 0, NULL, 0)
3030#endif /* ! HAVE_MESSAGES */
3031
3035#ifdef HAVE_MESSAGES
3036# define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l) \
3037 transmit_error_response_len (c, code, \
3038 m, MHD_STATICSTR_LEN_ (m), \
3039 hd_n, hd_n_l, \
3040 hd_v, hd_v_l)
3041#else /* ! HAVE_MESSAGES */
3042# define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l) \
3043 transmit_error_response_len (c, code, \
3044 "", 0, \
3045 hd_n, hd_n_l, \
3046 hd_v, hd_v_l)
3047#endif /* ! HAVE_MESSAGES */
3048
3049
3060static bool
3062{
3064 if (! c->rq.have_chunked_upload)
3065 return 0 != c->read_buffer_offset;
3066
3067 /* Chunked upload */
3068 mhd_assert (0 != c->rq.remaining_upload_size); /* Must not be possible in MHD_CONNECTION_BODY_RECEIVING state */
3070 {
3071 /* 0 == c->rq.current_chunk_size: Waiting the chunk size (chunk header).
3072 0 != c->rq.current_chunk_size: Waiting for chunk-closing CRLF. */
3073 return false;
3074 }
3075 return 0 != c->read_buffer_offset; /* Chunk payload data in the read buffer */
3076}
3077
3078
3095
3096
3097#ifndef MHD_MAX_REASONABLE_HEADERS_SIZE_
3107# define MHD_MAX_REASONABLE_HEADERS_SIZE_ (6 * 1024)
3108#endif /* ! MHD_MAX_REASONABLE_HEADERS_SIZE_ */
3109
3110#ifndef MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
3121# define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 8000
3122#endif /* ! MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ */
3123
3124#ifndef MHD_MIN_REASONABLE_HEADERS_SIZE_
3132# define MHD_MIN_REASONABLE_HEADERS_SIZE_ 26
3133#endif /* ! MHD_MIN_REASONABLE_HEADERS_SIZE_ */
3134
3135#ifndef MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
3143# define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 40
3144#endif /* ! MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ */
3145
3146#ifndef MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
3154# define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 16
3155#endif /* ! MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ */
3156
3157#ifndef MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
3163# define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 4
3164#endif /* ! MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ */
3165
3166
3178static unsigned int
3180 enum MHD_ProcRecvDataStage stage,
3181 const char *add_element,
3182 size_t add_element_size)
3183{
3184 size_t method_size;
3185 size_t uri_size;
3186 size_t opt_headers_size;
3187 size_t host_field_line_size;
3188
3191 mhd_assert ((0 == add_element_size) || (NULL != add_element));
3192
3194 {
3196 opt_headers_size =
3197 (size_t) ((c->read_buffer + c->read_buffer_offset)
3198 - c->rq.field_lines.start);
3199 }
3200 else
3201 opt_headers_size = c->rq.field_lines.size;
3202
3203 /* The read buffer is fully used by the request line, the field lines
3204 (headers) and internal information.
3205 The return status code works as a suggestion for the client to reduce
3206 one of the request elements. */
3207
3208 if ((MHD_PROC_RECV_BODY_CHUNKED == stage) &&
3209 (MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ < add_element_size))
3210 {
3211 /* Request could be re-tried easily with smaller chunk sizes */
3213 }
3214
3215 host_field_line_size = 0;
3216 /* The "Host:" field line is mandatory.
3217 The total size of the field lines (headers) cannot be smaller than
3218 the size of the "Host:" field line. */
3219 if ((MHD_PROC_RECV_HEADERS == stage)
3220 && (0 != add_element_size))
3221 {
3222 static const size_t header_host_key_len =
3224 const bool is_host_header =
3225 (header_host_key_len + 1 <= add_element_size)
3226 && ( (0 == add_element[header_host_key_len])
3227 || (':' == add_element[header_host_key_len]) )
3229 add_element,
3230 header_host_key_len);
3231 if (is_host_header)
3232 {
3233 const bool is_parsed = ! (
3235 (add_element_size == c->read_buffer_offset) &&
3236 (c->read_buffer == add_element) );
3237 size_t actual_element_size;
3238
3239 mhd_assert (! is_parsed || (0 == add_element[header_host_key_len]));
3240 /* The actual size should be larger due to CRLF or LF chars,
3241 however the exact termination sequence is not known here and
3242 as perfect precision is not required, to simplify the code
3243 assume the minimal length. */
3244 if (is_parsed)
3245 actual_element_size = add_element_size + 1; /* "1" for LF */
3246 else
3247 actual_element_size = add_element_size;
3248
3249 host_field_line_size = actual_element_size;
3250 mhd_assert (opt_headers_size >= actual_element_size);
3251 opt_headers_size -= actual_element_size;
3252 }
3253 }
3254 if (0 == host_field_line_size)
3255 {
3256 static const size_t host_field_name_len =
3258 size_t host_field_name_value_len;
3262 host_field_name_len,
3263 NULL,
3264 &host_field_name_value_len))
3265 {
3266 /* Calculate the minimal size of the field line: no space between
3267 colon and the field value, line terminated by LR */
3268 host_field_line_size =
3269 host_field_name_len + host_field_name_value_len + 2; /* "2" for ':' and LF */
3270
3271 /* The "Host:" field could be added by application */
3272 if (opt_headers_size >= host_field_line_size)
3273 {
3274 opt_headers_size -= host_field_line_size;
3275 /* Take into account typical space after colon and CR at the end of the line */
3276 if (opt_headers_size >= 2)
3277 opt_headers_size -= 2;
3278 }
3279 else
3280 host_field_line_size = 0; /* No "Host:" field line set by the client */
3281 }
3282 }
3283
3284 uri_size = c->rq.req_target_len;
3286 method_size = 0; /* Do not recommend shorter request method */
3287 else
3288 {
3289 mhd_assert (NULL != c->rq.method);
3290 method_size = strlen (c->rq.method);
3291 }
3292
3293 if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3294 {
3295 /* Typically the easiest way to reduce request header size is
3296 a removal of some optional headers. */
3297 if (opt_headers_size > (uri_size / 8))
3298 {
3299 if ((opt_headers_size / 2) > method_size)
3301 else
3302 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3303 }
3304 else
3305 { /* Request target is MUCH larger than headers */
3306 if ((uri_size / 16) > method_size)
3307 return MHD_HTTP_URI_TOO_LONG;
3308 else
3309 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3310 }
3311 }
3312 if ((size_t) MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3313 {
3314 /* If request target size if larger than maximum reasonable size
3315 recommend client to reduce the request target size (length). */
3316 if ((uri_size / 16) > method_size)
3317 return MHD_HTTP_URI_TOO_LONG; /* Request target is MUCH larger than headers */
3318 else
3319 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3320 }
3321
3322 /* The read buffer is too small to handle reasonably large requests */
3323
3324 if ((size_t) MHD_MIN_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3325 {
3326 /* Recommend application to retry with minimal headers */
3327 if ((opt_headers_size * 4) > uri_size)
3328 {
3329 if (opt_headers_size > method_size)
3331 else
3332 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3333 }
3334 else
3335 { /* Request target is significantly larger than headers */
3336 if (uri_size > method_size * 4)
3337 return MHD_HTTP_URI_TOO_LONG;
3338 else
3339 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3340 }
3341 }
3342 if ((size_t) MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3343 {
3344 /* Recommend application to retry with a shorter request target */
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_METHOD_SIZE_ < method_size)
3352 {
3353 /* The request target (URI) and headers are (reasonably) very small.
3354 Some non-standard long request method is used. */
3355 /* The last resort response as it means "the method is not supported
3356 by the server for any URI". */
3358 }
3359
3360 /* The almost impossible situation: all elements are small, but cannot
3361 fit the buffer. The application set the buffer size to
3362 critically low value? */
3363
3364 if ((1 < opt_headers_size) || (1 < uri_size))
3365 {
3366 if (opt_headers_size >= uri_size)
3368 else
3369 return MHD_HTTP_URI_TOO_LONG;
3370 }
3371
3372 /* Nothing to reduce in the request.
3373 Reply with some status. */
3374 if (0 != host_field_line_size)
3376
3377 return MHD_HTTP_URI_TOO_LONG;
3378}
3379
3380
3391static void
3393 const char *add_header,
3394 size_t add_header_size)
3395{
3396 unsigned int err_code;
3397
3398 err_code = get_no_space_err_status_code (c,
3400 add_header,
3401 add_header_size);
3403 err_code,
3405}
3406
3407
3408#ifdef COOKIE_SUPPORT
3414static void
3415handle_req_cookie_no_space (struct MHD_Connection *c)
3416{
3417 unsigned int err_code;
3418
3419 err_code = get_no_space_err_status_code (c,
3421 NULL,
3422 0);
3424 err_code,
3426}
3427
3428
3429#endif /* COOKIE_SUPPORT */
3430
3431
3442static void
3444 const char *chunk_size_line,
3445 size_t chunk_size_line_size)
3446{
3447 unsigned int err_code;
3448
3449 if (NULL != chunk_size_line)
3450 {
3451 const char *semicol;
3452 /* Check for chunk extension */
3453 semicol = memchr (chunk_size_line, ';', chunk_size_line_size);
3454 if (NULL != semicol)
3455 { /* Chunk extension present. It could be removed without any loss of the
3456 details of the request. */
3460 }
3461 }
3462 err_code = get_no_space_err_status_code (c,
3464 chunk_size_line,
3465 chunk_size_line_size);
3467 err_code,
3469}
3470
3471
3482static void
3484 const char *add_footer,
3485 size_t add_footer_size)
3486{
3487 (void) add_footer; (void) add_footer_size; /* Unused */
3489
3490 /* Footers should be optional */
3494}
3495
3496
3507static void
3509 enum MHD_ProcRecvDataStage stage)
3510{
3511 mhd_assert (MHD_PROC_RECV_INIT <= stage);
3514 mhd_assert ((MHD_PROC_RECV_INIT != stage) || \
3515 (MHD_CONNECTION_INIT == c->state));
3516 mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \
3518 mhd_assert ((MHD_PROC_RECV_URI != stage) || \
3520 mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \
3522 mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \
3524 mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */
3525 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3527 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3529 mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \
3531 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3532 (! c->rq.have_chunked_upload));
3533 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3534 (c->rq.have_chunked_upload));
3535 switch (stage)
3536 {
3537 case MHD_PROC_RECV_INIT:
3539 /* Some data has been received, but it is not clear yet whether
3540 * the received data is an valid HTTP request */
3542 _ ("No space left in the read buffer when " \
3543 "receiving the initial part of " \
3544 "the request line."));
3545 return;
3546 case MHD_PROC_RECV_URI:
3548 /* Some data has been received, but the request line is incomplete */
3551 /* A quick simple check whether the incomplete line looks
3552 * like an HTTP request */
3553 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
3555 {
3559 return;
3560 }
3562 _ ("No space left in the read buffer when " \
3563 "receiving the URI in " \
3564 "the request line. " \
3565 "The request uses non-standard HTTP request " \
3566 "method token."));
3567 return;
3570 return;
3573 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3576 {
3577 /* The connection must not be in MHD_EVENT_LOOP_INFO_READ state
3578 when external polling is used and some data left unprocessed. */
3580 /* failed to grow the read buffer, and the
3581 client which is supposed to handle the
3582 received data in a *blocking* fashion
3583 (in this mode) did not handle the data as
3584 it was supposed to!
3585 => we would either have to do busy-waiting
3586 (on the client, which would likely fail),
3587 or if we do nothing, we would just timeout
3588 on the connection (if a timeout is even
3589 set!).
3590 Solution: we kill the connection with an error */
3594 }
3595 else
3596 {
3597 if (MHD_PROC_RECV_BODY_NORMAL == stage)
3598 {
3599 /* A header probably has been added to a suspended connection and
3600 it took precisely all the space in the buffer.
3601 Very low probability. */
3604 }
3605 else
3606 {
3609 { /* Receiving content of the chunk */
3610 /* A header probably has been added to a suspended connection and
3611 it took precisely all the space in the buffer.
3612 Very low probability. */
3614 }
3615 else
3616 {
3617 if (0 != c->rq.current_chunk_size)
3618 { /* Waiting for chunk-closing CRLF */
3619 /* Not really possible as some payload should be
3620 processed and the space used by payload should be available. */
3622 }
3623 else
3624 { /* Reading the line with the chunk size */
3626 c->read_buffer,
3628 }
3629 }
3630 }
3631 }
3632 return;
3635 return;
3636 /* The next cases should not be possible */
3638 default:
3639 break;
3640 }
3641 mhd_assert (0);
3642}
3643
3644
3658static bool
3660{
3664 bool rbuff_grow_desired;
3668 bool rbuff_grow_required;
3669
3672
3673 rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size);
3674 if (rbuff_grow_required)
3675 rbuff_grow_desired = true;
3676 else
3677 {
3678 rbuff_grow_desired = (c->read_buffer_offset + c->daemon->pool_increment >
3679 c->read_buffer_size);
3680
3681 if ((rbuff_grow_desired) &&
3683 {
3684 if (! c->rq.have_chunked_upload)
3685 {
3687 /* Do not grow read buffer more than necessary to process the current
3688 request. */
3689 rbuff_grow_desired =
3691 }
3692 else
3693 {
3695 if (0 == c->rq.current_chunk_size)
3696 rbuff_grow_desired = /* Reading value of the next chunk size */
3698 c->read_buffer_size);
3699 else
3700 {
3701 const uint64_t cur_chunk_left =
3703 /* Do not grow read buffer more than necessary to process the current
3704 chunk with terminating CRLF. */
3706 rbuff_grow_desired =
3707 ((cur_chunk_left + 2) > (uint64_t) (c->read_buffer_size));
3708 }
3709 }
3710 }
3711 }
3712
3713 if (! rbuff_grow_desired)
3714 return true; /* No need to increase the buffer */
3715
3716 if (try_grow_read_buffer (c, rbuff_grow_required))
3717 return true; /* Buffer increase succeed */
3718
3719 if (! rbuff_grow_required)
3720 return true; /* Can continue without buffer increase */
3721
3722 /* Failed to increase the read buffer size, but need to read the data
3723 from the network.
3724 No more space left in the buffer, no more space to increase the buffer. */
3725
3726 /* 'PROCESS_READ' event state flag must be set only if the last application
3727 callback has processed some data. If any data is processed then some
3728 space in the read buffer must be available. */
3730
3731 if ((! MHD_D_IS_USING_THREADS_ (c->daemon))
3734 {
3735 /* The application is handling processing cycles.
3736 The data could be processed later. */
3738 return true;
3739 }
3740 else
3741 {
3742 enum MHD_ProcRecvDataStage stage;
3743
3744 switch (c->state)
3745 {
3747 stage = MHD_PROC_RECV_INIT;
3748 break;
3751 stage = MHD_PROC_RECV_METHOD;
3752 else if (0 == c->rq.req_target_len)
3753 stage = MHD_PROC_RECV_URI;
3754 else
3755 stage = MHD_PROC_RECV_HTTPVER;
3756 break;
3758 stage = MHD_PROC_RECV_HEADERS;
3759 break;
3761 stage = c->rq.have_chunked_upload ?
3763 break;
3765 stage = MHD_PROC_RECV_FOOTERS;
3766 break;
3785#ifdef UPGRADE_SUPPORT
3786 case MHD_CONNECTION_UPGRADE:
3787#endif
3788 default:
3790 mhd_assert (0);
3791 }
3792
3793 handle_recv_no_space (c, stage);
3794 }
3795 return false;
3796}
3797
3798
3807static void
3809{
3810 /* Do not update states of suspended connection */
3811 if (connection->suspended)
3812 return; /* States will be updated after resume. */
3813#ifdef HTTPS_SUPPORT
3814 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
3815 { /* HTTPS connection. */
3816 switch (connection->tls_state)
3817 {
3818 case MHD_TLS_CONN_INIT:
3820 return;
3823 if (0 == gnutls_record_get_direction (connection->tls_session))
3825 else
3827 return;
3829 break; /* Do normal processing */
3833 return;
3834 case MHD_TLS_CONN_TLS_CLOSING: /* Not implemented yet */
3835 case MHD_TLS_CONN_TLS_CLOSED: /* Not implemented yet */
3837 case MHD_TLS_CONN_NO_TLS: /* Not possible */
3838 default:
3839 MHD_PANIC (_ ("Invalid TLS state value.\n"));
3840 }
3841 }
3842#endif /* HTTPS_SUPPORT */
3843 while (1)
3844 {
3845#if DEBUG_STATES
3846 MHD_DLOG (connection->daemon,
3847 _ ("In function %s handling connection at state: %s\n"),
3848 MHD_FUNC_,
3849 MHD_state_to_string (connection->state));
3850#endif
3851 switch (connection->state)
3852 {
3856 break;
3858 mhd_assert (0);
3859 break;
3862 break;
3865 mhd_assert (0);
3866 break;
3869 break;
3871 if ((connection->rq.some_payload_processed) &&
3873 {
3874 /* Some data was processed, the buffer must have some free space */
3875 mhd_assert (connection->read_buffer_offset < \
3876 connection->read_buffer_size);
3877 if (! connection->rq.have_chunked_upload)
3878 {
3879 /* Not a chunked upload. Do not read more than necessary to
3880 process the current request. */
3881 if (connection->rq.remaining_upload_size >=
3882 connection->read_buffer_offset)
3884 else
3886 }
3887 else
3888 {
3889 /* Chunked upload. The size of the current request is unknown.
3890 Continue reading as the space in the read buffer is available. */
3892 }
3893 }
3894 else
3896 break;
3898 mhd_assert (0);
3899 break;
3902 break;
3904 mhd_assert (0);
3905 break;
3908 break;
3910 mhd_assert (0);
3911 break;
3913 /* headers in buffer, keep writing */
3915 break;
3917 mhd_assert (0);
3918 break;
3921 break;
3924 break;
3927 break;
3930 break;
3932 mhd_assert (0);
3933 break;
3936 break;
3938 mhd_assert (0);
3939 break;
3942 return; /* do nothing, not even reading */
3943#ifdef UPGRADE_SUPPORT
3944 case MHD_CONNECTION_UPGRADE:
3945 mhd_assert (0);
3946 break;
3947#endif /* UPGRADE_SUPPORT */
3948 default:
3949 mhd_assert (0);
3950 }
3951
3952 if (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info))
3953 {
3954 /* Check whether the space is available to receive data */
3955 if (! check_and_grow_read_buffer_space (connection))
3956 {
3957 mhd_assert (connection->discard_request);
3958 continue;
3959 }
3960 }
3961 break; /* Everything was processed. */
3962 }
3963}
3964
3965
3978static enum MHD_Result
3980 const char *key,
3981 size_t key_size,
3982 const char *value,
3983 size_t value_size,
3984 enum MHD_ValueKind kind)
3985{
3986 struct MHD_Connection *connection = (struct MHD_Connection *) cls;
3987 if (MHD_NO ==
3988 MHD_set_connection_value_n (connection,
3989 kind,
3990 key,
3991 key_size,
3992 value,
3993 value_size))
3994 {
3995#ifdef HAVE_MESSAGES
3996 MHD_DLOG (connection->daemon,
3997 _ ("Not enough memory in pool to allocate header record!\n"));
3998#endif
4002 return MHD_NO;
4003 }
4004 return MHD_YES;
4005}
4006
4007
4008#ifdef COOKIE_SUPPORT
4009
4013enum _MHD_ParseCookie
4014{
4015 MHD_PARSE_COOKIE_OK = MHD_YES,
4016 MHD_PARSE_COOKIE_OK_LAX = 2,
4017 MHD_PARSE_COOKIE_MALFORMED = -1,
4018 MHD_PARSE_COOKIE_NO_MEMORY = MHD_NO
4019};
4020
4021
4034static enum _MHD_ParseCookie
4035parse_cookies_string (char *str,
4036 const size_t str_len,
4037 struct MHD_Connection *connection)
4038{
4039 size_t i;
4040 bool non_strict;
4041 /* Skip extra whitespaces and empty cookies */
4042 const bool allow_wsp_empty = (0 >= connection->daemon->client_discipline);
4043 /* Allow whitespaces around '=' character */
4044 const bool wsp_around_eq = (-3 >= connection->daemon->client_discipline);
4045 /* Allow whitespaces in quoted cookie value */
4046 const bool wsp_in_quoted = (-2 >= connection->daemon->client_discipline);
4047 /* Allow tab as space after semicolon between cookies */
4048 const bool tab_as_sp = (0 >= connection->daemon->client_discipline);
4049 /* Allow no space after semicolon between cookies */
4050 const bool allow_no_space = (0 >= connection->daemon->client_discipline);
4051
4052 non_strict = false;
4053 i = 0;
4054 while (i < str_len)
4055 {
4056 size_t name_start;
4057 size_t name_len;
4058 size_t value_start;
4059 size_t value_len;
4060 bool val_quoted;
4061 /* Skip any whitespaces and empty cookies */
4062 while (' ' == str[i] || '\t' == str[i] || ';' == str[i])
4063 {
4064 if (! allow_wsp_empty)
4065 return MHD_PARSE_COOKIE_MALFORMED;
4066 non_strict = true;
4067 i++;
4068 if (i == str_len)
4069 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
4070 }
4071 /* 'i' must point to the first char of cookie-name */
4072 name_start = i;
4073 /* Find the end of the cookie-name */
4074 do
4075 {
4076 const char l = str[i];
4077 if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) ||
4078 (';' == l) || (0 == l))
4079 break;
4080 } while (str_len > ++i);
4081 name_len = i - name_start;
4082 /* Skip any whitespaces */
4083 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
4084 {
4085 if (! wsp_around_eq)
4086 return MHD_PARSE_COOKIE_MALFORMED;
4087 non_strict = true;
4088 i++;
4089 }
4090 if ((str_len == i) || ('=' != str[i]) || (0 == name_len))
4091 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */
4092 /* 'i' must point to the '=' char */
4093 mhd_assert ('=' == str[i]);
4094 i++;
4095 /* Skip any whitespaces */
4096 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
4097 {
4098 if (! wsp_around_eq)
4099 return MHD_PARSE_COOKIE_MALFORMED;
4100 non_strict = true;
4101 i++;
4102 }
4103 /* 'i' must point to the first char of cookie-value */
4104 if (str_len == i)
4105 {
4106 value_start = 0;
4107 value_len = 0;
4108#ifdef _DEBUG
4109 val_quoted = false; /* This assignment used in assert */
4110#endif
4111 }
4112 else
4113 {
4114 bool valid_cookie;
4115 val_quoted = ('"' == str[i]);
4116 if (val_quoted)
4117 i++;
4118 value_start = i;
4119 /* Find the end of the cookie-value */
4120 while (str_len > i)
4121 {
4122 const char l = str[i];
4123 if ((';' == l) || ('"' == l) || (',' == l) || (';' == l) ||
4124 ('\\' == l) || (0 == l))
4125 break;
4126 if ((' ' == l) || ('\t' == l))
4127 {
4128 if (! val_quoted)
4129 break;
4130 if (! wsp_in_quoted)
4131 return MHD_PARSE_COOKIE_MALFORMED;
4132 non_strict = true;
4133 }
4134 i++;
4135 }
4136 value_len = i - value_start;
4137 if (val_quoted)
4138 {
4139 if ((str_len == i) || ('"' != str[i]))
4140 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */
4141 i++;
4142 }
4143 /* Skip any whitespaces */
4144 if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i])))
4145 {
4146 do
4147 {
4148 i++;
4149 } while (str_len > i && (' ' == str[i] || '\t' == str[i]));
4150 /* Whitespace at the end? */
4151 if (str_len > i)
4152 {
4153 if (! allow_wsp_empty)
4154 return MHD_PARSE_COOKIE_MALFORMED;
4155 non_strict = true;
4156 }
4157 }
4158 if (str_len == i)
4159 valid_cookie = true;
4160 else if (';' == str[i])
4161 valid_cookie = true;
4162 else
4163 valid_cookie = false;
4164
4165 if (! valid_cookie)
4166 return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */
4167 }
4168 mhd_assert (0 != name_len);
4169 str[name_start + name_len] = 0; /* Zero-terminate the name */
4170 if (0 != value_len)
4171 {
4172 mhd_assert (value_start + value_len <= str_len);
4173 str[value_start + value_len] = 0; /* Zero-terminate the value */
4174 if (MHD_NO ==
4177 str + name_start,
4178 name_len,
4179 str + value_start,
4180 value_len))
4181 return MHD_PARSE_COOKIE_NO_MEMORY;
4182 }
4183 else
4184 {
4185 if (MHD_NO ==
4188 str + name_start,
4189 name_len,
4190 "",
4191 0))
4192 return MHD_PARSE_COOKIE_NO_MEMORY;
4193 }
4194 if (str_len > i)
4195 {
4196 mhd_assert (0 == str[i] || ';' == str[i]);
4197 mhd_assert (! val_quoted || ';' == str[i]);
4198 mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len);
4199 i++;
4200 if (str_len == i)
4201 { /* No next cookie after semicolon */
4202 if (! allow_wsp_empty)
4203 return MHD_PARSE_COOKIE_MALFORMED;
4204 non_strict = true;
4205 }
4206 else if (' ' != str[i])
4207 {/* No space after semicolon */
4208 if (('\t' == str[i]) && tab_as_sp)
4209 i++;
4210 else if (! allow_no_space)
4211 return MHD_PARSE_COOKIE_MALFORMED;
4212 non_strict = true;
4213 }
4214 else
4215 {
4216 i++;
4217 if (str_len == i)
4218 {
4219 if (! allow_wsp_empty)
4220 return MHD_PARSE_COOKIE_MALFORMED;
4221 non_strict = true;
4222 }
4223 }
4224 }
4225 }
4226 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
4227}
4228
4229
4238static enum _MHD_ParseCookie
4239parse_cookie_header (struct MHD_Connection *connection,
4240 const char *hdr,
4241 size_t hdr_len)
4242{
4243 char *cpy;
4244 size_t i;
4245 enum _MHD_ParseCookie parse_res;
4246 struct MHD_HTTP_Req_Header *const saved_tail =
4247 connection->rq.headers_received_tail;
4248 const bool allow_partially_correct_cookie =
4249 (1 >= connection->daemon->client_discipline);
4250
4251 if (0 == hdr_len)
4252 return MHD_PARSE_COOKIE_OK;
4253
4254 cpy = MHD_connection_alloc_memory_ (connection,
4255 hdr_len + 1);
4256 if (NULL == cpy)
4257 parse_res = MHD_PARSE_COOKIE_NO_MEMORY;
4258 else
4259 {
4260 memcpy (cpy,
4261 hdr,
4262 hdr_len);
4263 cpy[hdr_len] = '\0';
4264
4265 i = 0;
4266 /* Skip all initial whitespaces */
4267 while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i]))
4268 i++;
4269
4270 parse_res = parse_cookies_string (cpy + i, hdr_len - i, connection);
4271 }
4272
4273 switch (parse_res)
4274 {
4275 case MHD_PARSE_COOKIE_OK:
4276 break;
4277 case MHD_PARSE_COOKIE_OK_LAX:
4278#ifdef HAVE_MESSAGES
4279 if (saved_tail != connection->rq.headers_received_tail)
4280 MHD_DLOG (connection->daemon,
4281 _ ("The Cookie header has been parsed, but it is not fully "
4282 "compliant with the standard.\n"));
4283#endif /* HAVE_MESSAGES */
4284 break;
4285 case MHD_PARSE_COOKIE_MALFORMED:
4286 if (saved_tail != connection->rq.headers_received_tail)
4287 {
4288 if (! allow_partially_correct_cookie)
4289 {
4290 /* Remove extracted values from partially broken cookie */
4291 /* Memory remains allocated until the end of the request processing */
4292 connection->rq.headers_received_tail = saved_tail;
4293 saved_tail->next = NULL;
4294#ifdef HAVE_MESSAGES
4295 MHD_DLOG (connection->daemon,
4296 _ ("The Cookie header has been ignored as it contains "
4297 "malformed data.\n"));
4298#endif /* HAVE_MESSAGES */
4299 }
4300#ifdef HAVE_MESSAGES
4301 else
4302 MHD_DLOG (connection->daemon,
4303 _ ("The Cookie header has been only partially parsed as it "
4304 "contains malformed data.\n"));
4305#endif /* HAVE_MESSAGES */
4306 }
4307#ifdef HAVE_MESSAGES
4308 else
4309 MHD_DLOG (connection->daemon,
4310 _ ("The Cookie header has malformed data.\n"));
4311#endif /* HAVE_MESSAGES */
4312 break;
4313 case MHD_PARSE_COOKIE_NO_MEMORY:
4314#ifdef HAVE_MESSAGES
4315 MHD_DLOG (connection->daemon,
4316 _ ("Not enough memory in the connection pool to "
4317 "parse client cookies!\n"));
4318#endif /* HAVE_MESSAGES */
4319 break;
4320 default:
4321 mhd_assert (0);
4322 break;
4323 }
4324#ifndef HAVE_MESSAGES
4325 (void) saved_tail; /* Mute compiler warning */
4326#endif /* ! HAVE_MESSAGES */
4327
4328 return parse_res;
4329}
4330
4331
4332#endif /* COOKIE_SUPPORT */
4333
4334
4338#define HTTP_VER_LEN (MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1))
4339
4349static bool
4351 const char *http_string,
4352 size_t len)
4353{
4354 const char *const h = http_string;
4355 mhd_assert (NULL != http_string);
4356
4357 /* String must start with 'HTTP/d.d', case-sensetive match.
4358 * See https://www.rfc-editor.org/rfc/rfc9112#name-http-version */
4359 if ((HTTP_VER_LEN != len) ||
4360 ('H' != h[0]) || ('T' != h[1]) || ('T' != h[2]) || ('P' != h[3]) ||
4361 ('/' != h[4])
4362 || ('.' != h[6]) ||
4363 (('0' > h[5]) || ('9' < h[5])) ||
4364 (('0' > h[7]) || ('9' < h[7])))
4365 {
4366 connection->rq.http_ver = MHD_HTTP_VER_INVALID;
4370 return false;
4371 }
4372 if (1 == h[5] - '0')
4373 {
4374 /* HTTP/1.x */
4375 if (1 == h[7] - '0')
4376 connection->rq.http_ver = MHD_HTTP_VER_1_1;
4377 else if (0 == h[7] - '0')
4378 connection->rq.http_ver = MHD_HTTP_VER_1_0;
4379 else
4380 connection->rq.http_ver = MHD_HTTP_VER_1_2__1_9;
4381
4382 return true;
4383 }
4384
4385 if (0 == h[5] - '0')
4386 {
4387 /* Too old major version */
4388 connection->rq.http_ver = MHD_HTTP_VER_TOO_OLD;
4392 return false;
4393 }
4394
4395 connection->rq.http_ver = MHD_HTTP_VER_FUTURE;
4399 return false;
4400}
4401
4402
4410static void
4412 const char *method,
4413 size_t len)
4414{
4415 const char *const m = method;
4416 mhd_assert (NULL != m);
4417 mhd_assert (0 != len);
4418
4419 if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET) == len) &&
4420 (0 == memcmp (m, MHD_HTTP_METHOD_GET, len)))
4421 connection->rq.http_mthd = MHD_HTTP_MTHD_GET;
4422 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_HEAD) == len) &&
4423 (0 == memcmp (m, MHD_HTTP_METHOD_HEAD, len)))
4424 connection->rq.http_mthd = MHD_HTTP_MTHD_HEAD;
4425 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_POST) == len) &&
4426 (0 == memcmp (m, MHD_HTTP_METHOD_POST, len)))
4427 connection->rq.http_mthd = MHD_HTTP_MTHD_POST;
4428 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_PUT) == len) &&
4429 (0 == memcmp (m, MHD_HTTP_METHOD_PUT, len)))
4430 connection->rq.http_mthd = MHD_HTTP_MTHD_PUT;
4431 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_DELETE) == len) &&
4432 (0 == memcmp (m, MHD_HTTP_METHOD_DELETE, len)))
4433 connection->rq.http_mthd = MHD_HTTP_MTHD_DELETE;
4434 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_CONNECT) == len) &&
4435 (0 == memcmp (m, MHD_HTTP_METHOD_CONNECT, len)))
4436 connection->rq.http_mthd = MHD_HTTP_MTHD_CONNECT;
4437 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_OPTIONS) == len) &&
4438 (0 == memcmp (m, MHD_HTTP_METHOD_OPTIONS, len)))
4439 connection->rq.http_mthd = MHD_HTTP_MTHD_OPTIONS;
4440 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_TRACE) == len) &&
4441 (0 == memcmp (m, MHD_HTTP_METHOD_TRACE, len)))
4442 connection->rq.http_mthd = MHD_HTTP_MTHD_TRACE;
4443 else
4444 connection->rq.http_mthd = MHD_HTTP_MTHD_OTHER;
4445}
4446
4447
4455static void
4457{
4458 struct MHD_Daemon *daemon = connection->daemon;
4459 size_t processed;
4460
4461 if (NULL != connection->rp.response)
4462 return; /* already queued a response */
4463 processed = 0;
4464 connection->rq.client_aware = true;
4465 connection->in_access_handler = true;
4466 if (MHD_NO ==
4467 daemon->default_handler (daemon->default_handler_cls,
4468 connection,
4469 connection->rq.url_for_callback,
4470 connection->rq.method,
4471 connection->rq.version,
4472 NULL,
4473 &processed,
4474 &connection->rq.client_context))
4475 {
4476 connection->in_access_handler = false;
4477 /* serious internal error, close connection */
4478 CONNECTION_CLOSE_ERROR (connection,
4479 _ ("Application reported internal error, " \
4480 "closing connection."));
4481 return;
4482 }
4483 connection->in_access_handler = false;
4484}
4485
4486
4494static void
4496{
4497 struct MHD_Daemon *daemon = connection->daemon;
4498 size_t available;
4499 bool instant_retry;
4500 char *buffer_head;
4501 const int discp_lvl = daemon->client_discipline;
4502 /* RFC does not allow LF as the line termination in chunk headers.
4503 See RFC 9112, section 7.1 and section 2.2-3 */
4504 const bool bare_lf_as_crlf = (-2 > discp_lvl);
4505 /* Allow "Bad WhiteSpace" in chunk extension.
4506 RFC 9112, Section 7.1.1, Paragraph 2 */
4507 const bool allow_bws = (2 > discp_lvl);
4508
4509 mhd_assert (NULL == connection->rp.response);
4510
4511 buffer_head = connection->read_buffer;
4512 available = connection->read_buffer_offset;
4513 do
4514 {
4515 size_t to_be_processed;
4516 size_t left_unprocessed;
4517 size_t processed_size;
4518
4519 instant_retry = false;
4520 if (connection->rq.have_chunked_upload)
4521 {
4523 if ( (connection->rq.current_chunk_offset ==
4524 connection->rq.current_chunk_size) &&
4525 (0 != connection->rq.current_chunk_size) )
4526 {
4527 /* Skip CRLF chunk termination */
4528 size_t i;
4529 mhd_assert (0 != available);
4530 /* skip new line at the *end* of a chunk */
4531 i = 0;
4532 if ( (2 <= available) &&
4533 ('\r' == buffer_head[0]) &&
4534 ('\n' == buffer_head[1]) )
4535 i += 2; /* skip CRLF */
4536 else if (bare_lf_as_crlf && ('\n' == buffer_head[0]))
4537 i++; /* skip bare LF */
4538 else if (2 > available)
4539 break; /* need more upload data */
4540 if (0 == i)
4541 {
4542 /* malformed encoding */
4546 return;
4547 }
4548 available -= i;
4549 buffer_head += i;
4550 connection->rq.current_chunk_offset = 0;
4551 connection->rq.current_chunk_size = 0;
4552 if (0 == available)
4553 break;
4554 }
4555 if (0 != connection->rq.current_chunk_size)
4556 {
4557 /* Process chunk "content" */
4558 uint64_t cur_chunk_left;
4559 mhd_assert (connection->rq.current_chunk_offset < \
4560 connection->rq.current_chunk_size);
4561 cur_chunk_left
4562 = connection->rq.current_chunk_size
4563 - connection->rq.current_chunk_offset;
4564 if (cur_chunk_left > available)
4565 to_be_processed = available;
4566 else
4567 { /* cur_chunk_left <= (size_t)available */
4568 to_be_processed = (size_t) cur_chunk_left;
4569 if (available > to_be_processed)
4570 instant_retry = true;
4571 }
4572 }
4573 else
4574 { /* Need the parse the chunk size line */
4576 size_t num_dig;
4577 uint64_t chunk_size;
4578 bool broken;
4579 bool overflow;
4580
4581 mhd_assert (0 != available);
4582
4583 overflow = false;
4584 chunk_size = 0; /* Mute possible compiler warning.
4585 The real value will be set later. */
4586
4587 num_dig = MHD_strx_to_uint64_n_ (buffer_head,
4588 available,
4589 &chunk_size);
4590 mhd_assert (num_dig <= available);
4591 if (num_dig == available)
4592 continue; /* Need line delimiter */
4593
4594 broken = (0 == num_dig);
4595 if (broken)
4596 {
4597 uint64_t dummy;
4598 /* Check whether result is invalid due to uint64_t overflow */
4599 overflow = (0 != MHD_strx_to_uint64_n_ (buffer_head,
4600 1,
4601 &dummy));
4602 }
4603 else
4604 {
4609 size_t chunk_size_line_len;
4610
4611 chunk_size_line_len = 0;
4612 if ((';' == buffer_head[num_dig]) ||
4613 (allow_bws &&
4614 ((' ' == buffer_head[num_dig]) ||
4615 ('\t' == buffer_head[num_dig]))))
4616 { /* Chunk extension or "bad whitespace" after chunk length */
4617 size_t i;
4618
4619 /* Skip bad whitespaces (if any) */
4620 for (i = num_dig; i < available; ++i)
4621 {
4622 if ((' ' != buffer_head[i]) && ('\t' != buffer_head[i]))
4623 break;
4624 }
4625 if (i == available)
4626 break; /* need more data */
4627 if (';' == buffer_head[i])
4628 {
4629 /* Chunk extension */
4630 for (++i; i < available; ++i)
4631 {
4632 if (('\r' == buffer_head[i]) ||
4633 ('\n' == buffer_head[i]))
4634 break;
4635 }
4636 if (i == available)
4637 break; /* need more data */
4638 mhd_assert (i > num_dig);
4639 mhd_assert (1 <= i);
4640 if ('\r' == buffer_head[i])
4641 {
4642 if (i + 1 == available)
4643 break; /* need more data */
4644 if ('\n' == buffer_head[i + 1])
4645 chunk_size_line_len = i; /* Valid chunk header */
4646 }
4647 else
4648 {
4649 mhd_assert ('\n' == buffer_head[i]);
4650 if (bare_lf_as_crlf)
4651 chunk_size_line_len = i; /* Valid chunk header */
4652 }
4653 /* The chunk header is broken
4654 if chunk_size_line_len is zero here. */
4655 }
4656 else
4657 { /* No ';' after "bad whitespace" */
4658 mhd_assert (allow_bws);
4659 mhd_assert (0 == chunk_size_line_len);
4660 }
4661 }
4662 else
4663 {
4664 /* No chunk extension */
4665 mhd_assert (available >= num_dig);
4666 if ((2 <= (available - num_dig)) &&
4667 ('\r' == buffer_head[num_dig]) &&
4668 ('\n' == buffer_head[num_dig + 1]))
4669 chunk_size_line_len = num_dig + 2;
4670 else if (bare_lf_as_crlf &&
4671 ('\n' == buffer_head[num_dig]))
4672 chunk_size_line_len = num_dig + 1;
4673 else if (2 > (available - num_dig))
4674 break; /* need more data */
4675 }
4676
4677 if (0 != chunk_size_line_len)
4678 { /* Valid termination of the chunk size line */
4679 mhd_assert (chunk_size_line_len <= available);
4680 /* Start reading payload data of the chunk */
4681 connection->rq.current_chunk_offset = 0;
4682 connection->rq.current_chunk_size = chunk_size;
4683
4684 available -= chunk_size_line_len;
4685 buffer_head += chunk_size_line_len;
4686
4687 if (0 == chunk_size)
4688 { /* The final (termination) chunk */
4689 connection->rq.remaining_upload_size = 0;
4690 break;
4691 }
4692 if (available > 0)
4693 instant_retry = true;
4694 continue;
4695 }
4696 /* Invalid chunk size line */
4697 }
4698
4699 if (! overflow)
4703 else
4707 return;
4708 }
4709 }
4710 else
4711 {
4712 /* no chunked encoding, give all to the client */
4714 mhd_assert (0 != connection->rq.remaining_upload_size);
4715 if (connection->rq.remaining_upload_size < available)
4716 to_be_processed = (size_t) connection->rq.remaining_upload_size;
4717 else
4718 to_be_processed = available;
4719 }
4720 left_unprocessed = to_be_processed;
4721 connection->rq.client_aware = true;
4722 connection->in_access_handler = true;
4723 if (MHD_NO ==
4724 daemon->default_handler (daemon->default_handler_cls,
4725 connection,
4726 connection->rq.url_for_callback,
4727 connection->rq.method,
4728 connection->rq.version,
4729 buffer_head,
4730 &left_unprocessed,
4731 &connection->rq.client_context))
4732 {
4733 connection->in_access_handler = false;
4734 /* serious internal error, close connection */
4735 CONNECTION_CLOSE_ERROR (connection,
4736 _ ("Application reported internal error, " \
4737 "closing connection."));
4738 return;
4739 }
4740 connection->in_access_handler = false;
4741
4742 if (left_unprocessed > to_be_processed)
4743 MHD_PANIC (_ ("libmicrohttpd API violation.\n"));
4744
4745 connection->rq.some_payload_processed =
4746 (left_unprocessed != to_be_processed);
4747
4748 if (0 != left_unprocessed)
4749 {
4750 instant_retry = false; /* client did not process everything */
4751#ifdef HAVE_MESSAGES
4752 if ((! connection->rq.some_payload_processed) &&
4753 (! connection->suspended))
4754 {
4755 /* client did not process any upload data, complain if
4756 the setup was incorrect, which may prevent us from
4757 handling the rest of the request */
4758 if (MHD_D_IS_USING_THREADS_ (daemon))
4759 MHD_DLOG (daemon,
4760 _ ("WARNING: Access Handler Callback has not processed " \
4761 "any upload data and connection is not suspended. " \
4762 "This may result in hung connection.\n"));
4763 }
4764#endif /* HAVE_MESSAGES */
4765 }
4766 processed_size = to_be_processed - left_unprocessed;
4767 /* dh left "processed" bytes in buffer for next time... */
4768 buffer_head += processed_size;
4769 available -= processed_size;
4770 if (! connection->rq.have_chunked_upload)
4771 {
4773 connection->rq.remaining_upload_size -= processed_size;
4774 }
4775 else
4776 {
4778 connection->rq.current_chunk_offset += processed_size;
4779 }
4780 } while (instant_retry);
4781 /* TODO: zero out reused memory region */
4782 if ( (available > 0) &&
4783 (buffer_head != connection->read_buffer) )
4784 memmove (connection->read_buffer,
4785 buffer_head,
4786 available);
4787 else
4788 mhd_assert ((0 == available) || \
4789 (connection->read_buffer_offset == available));
4790 connection->read_buffer_offset = available;
4791}
4792
4793
4802static enum MHD_Result
4804 enum MHD_CONNECTION_STATE next_state)
4805{
4806 if ( (connection->write_buffer_append_offset !=
4807 connection->write_buffer_send_offset)
4808 /* || data_in_tls_buffers == true */
4809 )
4810 return MHD_NO;
4811 connection->write_buffer_append_offset = 0;
4812 connection->write_buffer_send_offset = 0;
4813 connection->state = next_state;
4814 return MHD_YES;
4815}
4816
4817
4825static void
4827{
4828 struct MHD_HTTP_Req_Header *pos;
4829 bool have_hdr_host;
4830 bool have_cntn_len;
4831
4832 have_hdr_host = false;
4833 have_cntn_len = false;
4834
4835 /* The presence of the request body is indicated by "Content-Length:" or
4836 "Transfer-Encoding:" request headers.
4837 See RFC 9112 section 6.1, 6.2, 6.3; RFC 9110 Section 8.6. */
4838
4841
4842 for (pos = c->rq.headers_received; NULL != pos; pos = pos->next)
4843 {
4844 if (MHD_HEADER_KIND != pos->kind)
4845 continue;
4846
4848 pos->header,
4849 pos->header_size))
4850 {
4851 if (have_hdr_host)
4852 {
4853 if (-3 < c->daemon->client_discipline)
4854 {
4858 return;
4859 }
4860 }
4861 have_hdr_host = true;
4862 }
4863#ifdef COOKIE_SUPPORT
4865 pos->header,
4866 pos->header_size))
4867 {
4868 if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (c,
4869 pos->value,
4870 pos->value_size))
4871 {
4872 handle_req_cookie_no_space (c);
4873 return;
4874 }
4875 }
4876#endif /* COOKIE_SUPPORT */
4878 pos->header,
4879 pos->header_size))
4880 {
4881 const char *clen;
4882 size_t val_len;
4883 size_t num_digits;
4884 uint64_t decoded_val;
4885
4886 val_len = pos->value_size;
4887 clen = pos->value;
4888
4889 mhd_assert ('\0' == clen[val_len]);
4890
4891 if ((have_cntn_len)
4892 && (0 < c->daemon->client_discipline))
4893 {
4897 return;
4898 }
4899
4900 num_digits = MHD_str_to_uint64_n_ (clen,
4901 val_len,
4902 &decoded_val);
4903
4904 if ((0 == num_digits) ||
4905 (val_len != num_digits) ||
4906 (MHD_SIZE_UNKNOWN == decoded_val))
4907 { /* Bad or too large value */
4908
4909 if (have_cntn_len)
4910 {
4914 return;
4915 }
4916
4917 if ((val_len != num_digits)
4918 || ('0' > clen[0]) || ('9' < clen[0]))
4919 {
4920#ifdef HAVE_MESSAGES
4921 MHD_DLOG (c->daemon,
4922 _ ("Malformed 'Content-Length' header. " \
4923 "Closing connection.\n"));
4924#endif
4928 return;
4929 }
4930
4931#ifdef HAVE_MESSAGES
4932 MHD_DLOG (c->daemon,
4933 _ ("Too large value of 'Content-Length' header. " \
4934 "Closing connection.\n"));
4935#endif
4939 return;
4940 }
4941
4942 if ((have_cntn_len) &&
4943 (c->rq.remaining_upload_size != decoded_val))
4944 {
4945 if (-3 < c->daemon->client_discipline)
4946 {
4950 return;
4951 }
4952 /* The HTTP framing is broken.
4953 Use smallest (safest) length value and force-close
4954 after processing of this request. */
4955 if (c->rq.remaining_upload_size > decoded_val)
4956 c->rq.remaining_upload_size = decoded_val;
4958 }
4959 else
4960 c->rq.remaining_upload_size = decoded_val;
4961
4962 have_cntn_len = true;
4963 }
4966 pos->header,
4967 pos->header_size))
4968 {
4969
4970 if (MHD_HTTP_VER_1_1 > c->rq.http_ver)
4971 {
4972 /* RFC 9112, 6.1, last paragraph */
4973 if (0 < c->daemon->client_discipline)
4974 {
4978 return;
4979 }
4980 /* HTTP framing potentially broken */
4982 }
4983
4984 if (c->rq.have_chunked_upload
4985 || ! MHD_str_equal_caseless_s_bin_n_ ("chunked",
4986 pos->value,
4987 pos->value_size))
4988 {
4994 return;
4995 }
4996 c->rq.have_chunked_upload = true;
4998 }
4999 }
5000
5001 if (c->rq.have_chunked_upload && have_cntn_len)
5002 {
5003 if (0 < c->daemon->client_discipline)
5004 {
5008 return;
5009 }
5010 else
5011 {
5012#ifdef HAVE_MESSAGES
5013 MHD_DLOG (c->daemon,
5014 _ ("The 'Content-Length' request header is ignored "
5015 "as chunked Transfer-Encoding is set in the "
5016 "same request.\n"));
5017#endif /* HAVE_MESSAGES */
5019 /* Must close connection after reply to prevent potential attack */
5021 }
5022 }
5023
5026 mhd_assert ((0 == c->rq.remaining_upload_size) ||
5027 have_cntn_len || c->rq.have_chunked_upload);
5028
5029 if (! have_hdr_host
5031 && (-3 < c->daemon->client_discipline))
5032 {
5033#ifdef HAVE_MESSAGES
5034 MHD_DLOG (c->daemon,
5035 _ ("Received HTTP/1.1 request without `Host' header.\n"));
5036#endif
5040 return;
5041 }
5042}
5043
5044
5052_MHD_static_inline void
5054{
5055 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
5056}
5057
5058
5063_MHD_static_inline void
5065{
5067 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
5069}
5070
5071
5072#ifndef MHD_MAX_EMPTY_LINES_SKIP
5077#define MHD_MAX_EMPTY_LINES_SKIP 1024
5078#endif /* ! MHD_MAX_EMPTY_LINES_SKIP */
5079
5087static bool
5089{
5090 size_t p;
5091 const int discp_lvl = c->daemon->client_discipline;
5092 /* Allow to skip one or more empty lines before the request line.
5093 RFC 9112, section 2.2 */
5094 const bool skip_empty_lines = (1 >= discp_lvl);
5095 /* Allow to skip more then one empty line before the request line.
5096 RFC 9112, section 2.2 */
5097 const bool skip_several_empty_lines = (skip_empty_lines && (0 >= discp_lvl));
5098 /* Allow to skip number of unlimited empty lines before the request line.
5099 RFC 9112, section 2.2 */
5100 const bool skip_unlimited_empty_lines =
5101 (skip_empty_lines && (-3 >= discp_lvl));
5102 /* Treat bare LF as the end of the line.
5103 RFC 9112, section 2.2 */
5104 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
5105 /* Treat tab as whitespace delimiter.
5106 RFC 9112, section 3 */
5107 const bool tab_as_wsp = (0 >= discp_lvl);
5108 /* Treat VT (vertical tab) and FF (form feed) as whitespace delimiters.
5109 RFC 9112, section 3 */
5110 const bool other_wsp_as_wsp = (-1 >= discp_lvl);
5111 /* Treat continuous whitespace block as a single space.
5112 RFC 9112, section 3 */
5113 const bool wsp_blocks = (-1 >= discp_lvl);
5114 /* Parse whitespace in URI, special parsing of the request line.
5115 RFC 9112, section 3.2 */
5116 const bool wsp_in_uri = (0 >= discp_lvl);
5117 /* Keep whitespace in URI, give app URI with whitespace instead of
5118 automatic redirect to fixed URI.
5119 Violates RFC 9112, section 3.2 */
5120 const bool wsp_in_uri_keep = (-2 >= discp_lvl);
5121 /* Keep bare CR character as is.
5122 Violates RFC 9112, section 2.2 */
5123 const bool bare_cr_keep = (wsp_in_uri_keep && (-3 >= discp_lvl));
5124 /* Treat bare CR as space; replace it with space before processing.
5125 RFC 9112, section 2.2 */
5126 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
5127
5130 mhd_assert (NULL == c->rq.method || \
5135 0 != c->rq.hdrs.rq_line.proc_pos);
5136
5137 if (0 == c->read_buffer_offset)
5138 {
5140 return false; /* No data to process */
5141 }
5142 p = c->rq.hdrs.rq_line.proc_pos;
5143 mhd_assert (p <= c->read_buffer_offset);
5144
5145 /* Skip empty lines, if any (and if allowed) */
5146 /* See RFC 9112, section 2.2 */
5147 if ((0 == p)
5148 && (skip_empty_lines))
5149 {
5150 /* Skip empty lines before the request line.
5151 See RFC 9112, section 2.2 */
5152 bool is_empty_line;
5154 mhd_assert (NULL == c->rq.method);
5155 mhd_assert (NULL == c->rq.url);
5156 mhd_assert (0 == c->rq.url_len);
5158 mhd_assert (0 == c->rq.req_target_len);
5159 mhd_assert (NULL == c->rq.version);
5160 do
5161 {
5162 is_empty_line = false;
5163 if ('\r' == c->read_buffer[0])
5164 {
5165 if (1 == c->read_buffer_offset)
5166 return false; /* Not enough data yet */
5167 if ('\n' == c->read_buffer[1])
5168 {
5169 is_empty_line = true;
5170 c->read_buffer += 2;
5171 c->read_buffer_size -= 2;
5172 c->read_buffer_offset -= 2;
5174 }
5175 }
5176 else if (('\n' == c->read_buffer[0]) &&
5177 (bare_lf_as_crlf))
5178 {
5179 is_empty_line = true;
5180 c->read_buffer += 1;
5181 c->read_buffer_size -= 1;
5182 c->read_buffer_offset -= 1;
5184 }
5185 if (is_empty_line)
5186 {
5187 if ((! skip_unlimited_empty_lines) &&
5188 (((unsigned int) ((skip_several_empty_lines) ?
5191 {
5193 _ ("Too many meaningless extra empty lines " \
5194 "received before the request"));
5195 return true; /* Process connection closure */
5196 }
5197 if (0 == c->read_buffer_offset)
5198 return false; /* No more data to process */
5199 }
5200 } while (is_empty_line);
5201 }
5202 /* All empty lines are skipped */
5203
5205 /* Read and parse the request line */
5207
5208 while (p < c->read_buffer_offset)
5209 {
5210 const char chr = c->read_buffer[p];
5211 bool end_of_line;
5212 /*
5213 The processing logic is different depending on the configured strictness:
5214
5215 When whitespace BLOCKS are NOT ALLOWED, the end of the whitespace is
5216 processed BEFORE processing of the current character.
5217 When whitespace BLOCKS are ALLOWED, the end of the whitespace is
5218 processed AFTER processing of the current character.
5219
5220 When space char in the URI is ALLOWED, the delimiter between the URI and
5221 the HTTP version string is processed only at the END of the line.
5222 When space in the URI is NOT ALLOWED, the delimiter between the URI and
5223 the HTTP version string is processed as soon as the FIRST whitespace is
5224 found after URI start.
5225 */
5226
5227 end_of_line = false;
5228
5229 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5230 (c->rq.hdrs.rq_line.last_ws_end > \
5232 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_start) || \
5233 (0 != c->rq.hdrs.rq_line.last_ws_end));
5234
5235 /* Check for the end of the line */
5236 if ('\r' == chr)
5237 {
5238 if (p + 1 == c->read_buffer_offset)
5239 {
5240 c->rq.hdrs.rq_line.proc_pos = p;
5241 return false; /* Not enough data yet */
5242 }
5243 else if ('\n' == c->read_buffer[p + 1])
5244 end_of_line = true;
5245 else
5246 {
5247 /* Bare CR alone */
5248 /* Must be rejected or replaced with space char.
5249 See RFC 9112, section 2.2 */
5250 if (bare_cr_as_sp)
5251 {
5252 c->read_buffer[p] = ' ';
5254 continue; /* Re-start processing of the current character */
5255 }
5256 else if (! bare_cr_keep)
5257 {
5258 /* A quick simple check whether this line looks like an HTTP request */
5259 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5261 {
5265 }
5266 else
5268 _ ("Bare CR characters are not allowed " \
5269 "in the request line.\n"));
5270 return true; /* Error in the request */
5271 }
5272 }
5273 }
5274 else if ('\n' == chr)
5275 {
5276 /* Bare LF may be recognised as a line delimiter.
5277 See RFC 9112, section 2.2 */
5278 if (bare_lf_as_crlf)
5279 end_of_line = true;
5280 else
5281 {
5282 /* While RFC does not enforce error for bare LF character,
5283 if this char is not treated as a line delimiter, it should be
5284 rejected to avoid any security weakness due to request smuggling. */
5285 /* A quick simple check whether this line looks like an HTTP request */
5286 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5288 {
5292 }
5293 else
5295 _ ("Bare LF characters are not allowed " \
5296 "in the request line.\n"));
5297 return true; /* Error in the request */
5298 }
5299 }
5300
5301 if (end_of_line)
5302 {
5303 /* Handle the end of the request line */
5304
5305 if (NULL != c->rq.method)
5306 {
5307 if (wsp_in_uri)
5308 {
5309 /* The end of the URI and the start of the HTTP version string
5310 should be determined now. */
5311 mhd_assert (NULL == c->rq.version);
5312 mhd_assert (0 == c->rq.req_target_len);
5313 if (0 != c->rq.hdrs.rq_line.last_ws_end)
5314 {
5315 /* Determine the end and the length of the URI */
5316 if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5317 {
5318 c->read_buffer [c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5319 c->rq.req_target_len =
5321 - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5322 }
5323 else if ((c->rq.hdrs.rq_line.last_ws_start + 1 <
5324 c->rq.hdrs.rq_line.last_ws_end) &&
5325 (HTTP_VER_LEN == (p - c->rq.hdrs.rq_line.last_ws_end)))
5326 {
5327 /* Found only HTTP method and HTTP version and more than one
5328 whitespace between them. Assume zero-length URI. */
5329 mhd_assert (wsp_blocks);
5331 c->read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5332 c->rq.hdrs.rq_line.rq_tgt =
5334 c->rq.req_target_len = 0;
5337 }
5338 /* Determine the start of the HTTP version string */
5339 if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5340 {
5342 }
5343 }
5344 }
5345 else
5346 {
5347 /* The end of the URI and the start of the HTTP version string
5348 should be already known. */
5349 if ((NULL == c->rq.version)
5350 && (NULL != c->rq.hdrs.rq_line.rq_tgt)
5351 && (HTTP_VER_LEN == p - (size_t) (c->rq.hdrs.rq_line.rq_tgt
5352 - c->read_buffer))
5353 && (0 != c->read_buffer[(size_t)
5354 (c->rq.hdrs.rq_line.rq_tgt
5355 - c->read_buffer) - 1]))
5356 {
5357 /* Found only HTTP method and HTTP version and more than one
5358 whitespace between them. Assume zero-length URI. */
5359 size_t uri_pos;
5360 mhd_assert (wsp_blocks);
5361 mhd_assert (0 == c->rq.req_target_len);
5362 uri_pos = (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer) - 1;
5363 mhd_assert (uri_pos < p);
5364 c->rq.version = c->rq.hdrs.rq_line.rq_tgt;
5365 c->read_buffer[uri_pos] = 0; /* Zero terminate the URI */
5366 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + uri_pos;
5367 c->rq.req_target_len = 0;
5370 }
5371 }
5372
5373 if (NULL != c->rq.version)
5374 {
5376 if (! parse_http_version (c, c->rq.version,
5377 p
5378 - (size_t) (c->rq.version
5379 - c->read_buffer)))
5380 {
5382 return true; /* Unsupported / broken HTTP version */
5383 }
5384 c->read_buffer[p] = 0; /* Zero terminate the HTTP version strings */
5385 if ('\r' == chr)
5386 {
5387 p++; /* Consume CR */
5388 mhd_assert (p < c->read_buffer_offset); /* The next character has been already checked */
5389 }
5390 p++; /* Consume LF */
5391 c->read_buffer += p;
5392 c->read_buffer_size -= p;
5393 c->read_buffer_offset -= p;
5395 c->rq.req_target_len);
5397 (0 != c->rq.req_target_len));
5399 ((size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5400 - c->rq.hdrs.rq_line.rq_tgt) < \
5401 c->rq.req_target_len));
5403 (c->rq.hdrs.rq_line.rq_tgt_qmark >= \
5404 c->rq.hdrs.rq_line.rq_tgt));
5405 return true; /* The request line is successfully parsed */
5406 }
5407 }
5408 /* Error in the request line */
5409
5410 /* A quick simple check whether this line looks like an HTTP request */
5411 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5413 {
5417 }
5418 else
5420 _ ("The request line is malformed.\n"));
5421
5422 return true;
5423 }
5424
5425 /* Process possible end of the previously found whitespace delimiter */
5426 if ((! wsp_blocks) &&
5427 (p == c->rq.hdrs.rq_line.last_ws_end) &&
5428 (0 != c->rq.hdrs.rq_line.last_ws_end))
5429 {
5430 /* Previous character was a whitespace char and whitespace blocks
5431 are not allowed. */
5432 /* The current position is the next character after
5433 a whitespace delimiter */
5434 if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5435 {
5436 /* The current position is the start of the URI */
5437 mhd_assert (0 == c->rq.req_target_len);
5438 mhd_assert (NULL == c->rq.version);
5439 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5440 /* Reset the whitespace marker */
5442 c->rq.hdrs.rq_line.last_ws_end = 0;
5443 }
5444 else
5445 {
5446 /* It was a whitespace after the start of the URI */
5447 if (! wsp_in_uri)
5448 {
5449 mhd_assert ((0 != c->rq.req_target_len) || \
5450 (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5451 mhd_assert (NULL == c->rq.version); /* Too many whitespaces? This error is handled at whitespace start */
5452 c->rq.version = c->read_buffer + p;
5453 /* Reset the whitespace marker */
5455 c->rq.hdrs.rq_line.last_ws_end = 0;
5456 }
5457 }
5458 }
5459
5460 /* Process the current character.
5461 Is it not the end of the line. */
5462 if ((' ' == chr)
5463 || (('\t' == chr) && (tab_as_wsp))
5464 || ((other_wsp_as_wsp) && ((0xb == chr) || (0xc == chr))))
5465 {
5466 /* A whitespace character */
5467 if ((0 == c->rq.hdrs.rq_line.last_ws_end) ||
5468 (p != c->rq.hdrs.rq_line.last_ws_end) ||
5469 (! wsp_blocks))
5470 {
5471 /* Found first whitespace char of the new whitespace block */
5472 if (NULL == c->rq.method)
5473 {
5474 /* Found the end of the HTTP method string */
5478 mhd_assert (0 == c->rq.req_target_len);
5479 mhd_assert (NULL == c->rq.version);
5480 if (0 == p)
5481 {
5483 _ ("The request line starts with "
5484 "a whitespace.\n"));
5485 return true; /* Error in the request */
5486 }
5487 c->read_buffer[p] = 0; /* Zero-terminate the request method string */
5488 c->rq.method = c->read_buffer;
5489 parse_http_std_method (c, c->rq.method, p);
5490 }
5491 else
5492 {
5493 /* A whitespace after the start of the URI */
5494 if (! wsp_in_uri)
5495 {
5496 /* Whitespace in URI is not allowed to be parsed */
5497 if (NULL == c->rq.version)
5498 {
5500 /* This is a delimiter between URI and HTTP version string */
5501 c->read_buffer[p] = 0; /* Zero-terminate request URI string */
5502 mhd_assert (((size_t) (c->rq.hdrs.rq_line.rq_tgt \
5503 - c->read_buffer)) <= p);
5504 c->rq.req_target_len =
5505 p - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5506 }
5507 else
5508 {
5509 /* This is a delimiter AFTER version string */
5510
5511 /* A quick simple check whether this line looks like an HTTP request */
5512 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5514 {
5518 }
5519 else
5521 _ ("The request line has more than "
5522 "two whitespaces.\n"));
5523 return true; /* Error in the request */
5524 }
5525 }
5526 else
5527 {
5528 /* Whitespace in URI is allowed to be parsed */
5529 if (0 != c->rq.hdrs.rq_line.last_ws_end)
5530 {
5531 /* The whitespace after the start of the URI has been found already */
5535 }
5536 }
5537 }
5539 c->rq.hdrs.rq_line.last_ws_end = p + 1; /* Will be updated on the next char parsing */
5540 }
5541 else
5542 {
5543 /* Continuation of the whitespace block */
5545 mhd_assert (0 != p);
5546 c->rq.hdrs.rq_line.last_ws_end = p + 1;
5547 }
5548 }
5549 else
5550 {
5551 /* Non-whitespace char, not the end of the line */
5552 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5553 (c->rq.hdrs.rq_line.last_ws_end == p) || \
5554 wsp_in_uri);
5555
5556 if ((p == c->rq.hdrs.rq_line.last_ws_end) &&
5557 (0 != c->rq.hdrs.rq_line.last_ws_end) &&
5558 (wsp_blocks))
5559 {
5560 /* The end of the whitespace block */
5561 if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5562 {
5563 /* This is the first character of the URI */
5564 mhd_assert (0 == c->rq.req_target_len);
5565 mhd_assert (NULL == c->rq.version);
5566 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5567 /* Reset the whitespace marker */
5569 c->rq.hdrs.rq_line.last_ws_end = 0;
5570 }
5571 else
5572 {
5573 if (! wsp_in_uri)
5574 {
5575 /* This is the first character of the HTTP version */
5577 mhd_assert ((0 != c->rq.req_target_len) || \
5578 (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5579 mhd_assert (NULL == c->rq.version); /* Handled at whitespace start */
5580 c->rq.version = c->read_buffer + p;
5581 /* Reset the whitespace marker */
5583 c->rq.hdrs.rq_line.last_ws_end = 0;
5584 }
5585 }
5586 }
5587
5588 /* Handle other special characters */
5589 if ('?' == chr)
5590 {
5591 if ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) &&
5592 (NULL != c->rq.hdrs.rq_line.rq_tgt))
5593 {
5595 }
5596 }
5597 else if ((0xb == chr) || (0xc == chr))
5598 {
5599 /* VT or LF characters */
5600 mhd_assert (! other_wsp_as_wsp);
5601 if ((NULL != c->rq.hdrs.rq_line.rq_tgt) &&
5602 (NULL == c->rq.version) &&
5603 (wsp_in_uri))
5604 {
5606 }
5607 else
5608 {
5610 _ ("Invalid character is in the "
5611 "request line.\n"));
5612 return true; /* Error in the request */
5613 }
5614 }
5615 else if (0 == chr)
5616 {
5617 /* NUL character */
5619 _ ("The NUL character is in the "
5620 "request line.\n"));
5621 return true; /* Error in the request */
5622 }
5623 }
5624
5625 p++;
5626 }
5627
5628 c->rq.hdrs.rq_line.proc_pos = p;
5629 return false; /* Not enough data yet */
5630}
5631
5632
5633#ifndef MHD_MAX_FIXED_URI_LEN
5637#define MHD_MAX_FIXED_URI_LEN (64 * 1024)
5638#endif /* ! MHD_MAX_FIXED_URI_LEN */
5639
5647static void
5649{
5650 char *b;
5651 size_t fixed_uri_len;
5652 size_t i;
5653 size_t o;
5654 char *hdr_name;
5655 size_t hdr_name_len;
5656
5660 c->rq.req_target_len);
5661 fixed_uri_len = c->rq.req_target_len
5662 + 2 * c->rq.hdrs.rq_line.num_ws_in_uri;
5663 if ( (fixed_uri_len + 200 > c->daemon->pool_size) ||
5664 (fixed_uri_len > MHD_MAX_FIXED_URI_LEN) ||
5665 (NULL == (b = malloc (fixed_uri_len + 1))) )
5666 {
5668 _ ("The request has whitespace character is " \
5669 "in the URI and the URI is too large to " \
5670 "send automatic redirect to fixed URI.\n"));
5671 return;
5672 }
5673 i = 0;
5674 o = 0;
5675
5676 do
5677 {
5678 const char chr = c->rq.hdrs.rq_line.rq_tgt[i++];
5679
5680 mhd_assert ('\r' != chr); /* Replaced during request line parsing */
5681 mhd_assert ('\n' != chr); /* Rejected during request line parsing */
5682 mhd_assert (0 != chr); /* Rejected during request line parsing */
5683 switch (chr)
5684 {
5685 case ' ':
5686 b[o++] = '%';
5687 b[o++] = '2';
5688 b[o++] = '0';
5689 break;
5690 case '\t':
5691 b[o++] = '%';
5692 b[o++] = '0';
5693 b[o++] = '9';
5694 break;
5695 case 0x0B: /* VT (vertical tab) */
5696 b[o++] = '%';
5697 b[o++] = '0';
5698 b[o++] = 'B';
5699 break;
5700 case 0x0C: /* FF (form feed) */
5701 b[o++] = '%';
5702 b[o++] = '0';
5703 b[o++] = 'C';
5704 break;
5705 default:
5706 b[o++] = chr;
5707 break;
5708 }
5709 } while (i < c->rq.req_target_len);
5710 mhd_assert (fixed_uri_len == o);
5711 b[o] = 0; /* Zero-terminate the result */
5712
5714 hdr_name = malloc (hdr_name_len + 1);
5715 if (NULL != hdr_name)
5716 {
5717 memcpy (hdr_name,
5719 hdr_name_len + 1);
5720 /* hdr_name and b are free()d within this call */
5724 hdr_name,
5725 hdr_name_len,
5726 b,
5727 o);
5728 return;
5729 }
5730 free (b);
5732 _ ("The request has whitespace character is in the " \
5733 "URI.\n"));
5734 return;
5735}
5736
5737
5744static bool
5746{
5747#ifdef _DEBUG
5748 size_t params_len;
5749#endif /* _DEBUG */
5751 mhd_assert (NULL == c->rq.url);
5752 mhd_assert (0 == c->rq.url_len);
5758 (c->rq.req_target_len > \
5759 (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5760 - c->rq.hdrs.rq_line.rq_tgt)));
5761
5762 /* Log callback before the request-target is modified/decoded */
5763 if (NULL != c->daemon->uri_log_callback)
5764 {
5765 c->rq.client_aware = true;
5766 c->rq.client_context =
5768 c->rq.hdrs.rq_line.rq_tgt,
5769 c);
5770 }
5771
5772 if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark)
5773 {
5774#ifdef _DEBUG
5775 params_len =
5777 - (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark - c->rq.hdrs.rq_line.rq_tgt);
5778#endif /* _DEBUG */
5779 c->rq.hdrs.rq_line.rq_tgt_qmark[0] = 0; /* Replace '?' with zero termination */
5780 if (MHD_NO == MHD_parse_arguments_ (c,
5782 c->rq.hdrs.rq_line.rq_tgt_qmark + 1,
5784 c))
5785 {
5787 return false;
5788 }
5789 }
5790#ifdef _DEBUG
5791 else
5792 params_len = 0;
5793#endif /* _DEBUG */
5794
5796 mhd_assert (strlen (c->rq.hdrs.rq_line.rq_tgt) == \
5797 c->rq.req_target_len - params_len);
5798
5799 /* Finally unescape URI itself */
5800 c->rq.url_len =
5802 c,
5803 c->rq.hdrs.rq_line.rq_tgt);
5804 c->rq.url = c->rq.hdrs.rq_line.rq_tgt;
5805
5806 if (2 == c->daemon->allow_bzero_in_url)
5807 c->rq.url_for_callback = c->rq.url;
5808 else if (strlen (c->rq.url) == c->rq.url_len)
5809 c->rq.url_for_callback = c->rq.url;
5810 else if (0 == c->daemon->allow_bzero_in_url)
5811 {
5815 return false;
5816 }
5817
5818 return true;
5819}
5820
5821
5829static bool
5831{
5832 const int discp_lvl = c->daemon->client_discipline;
5833 /* Parse whitespace in URI, special parsing of the request line */
5834 const bool wsp_in_uri = (0 >= discp_lvl);
5835 /* Keep whitespace in URI, give app URI with whitespace instead of
5836 automatic redirect to fixed URI */
5837 const bool wsp_in_uri_keep = (-2 >= discp_lvl);
5838
5839 if (! get_request_line_inner (c))
5840 {
5841 /* End of the request line has not been found yet */
5842 mhd_assert ((! wsp_in_uri) || NULL == c->rq.version);
5843 if ((NULL != c->rq.version) &&
5844 (HTTP_VER_LEN <
5846 - (size_t) (c->rq.version - c->read_buffer))))
5847 {
5852 return true; /* Error in the request */
5853 }
5854 return false;
5855 }
5857 return true; /* Error in the request */
5858
5860 mhd_assert (NULL == c->rq.url);
5861 mhd_assert (0 == c->rq.url_len);
5864 if (0 != c->rq.hdrs.rq_line.num_ws_in_uri)
5865 {
5866 if (! wsp_in_uri)
5867 {
5871 return true; /* Error in the request */
5872 }
5873 if (! wsp_in_uri_keep)
5874 {
5876 return true; /* Error in the request */
5877 }
5878 }
5879 if (! process_request_target (c))
5880 return true; /* Error in processing */
5881
5883 return true;
5884}
5885
5886
5909
5910
5918static bool
5920{
5921 switch (chr)
5922 {
5923 case '!':
5924 case '#':
5925 case '$':
5926 case '%':
5927 case '&':
5928 case '\'':
5929 case '*':
5930 case '+':
5931 case '-':
5932 case '.':
5933 case '^':
5934 case '_':
5935 case '`':
5936 case '|':
5937 case '~':
5938 case 'a':
5939 case 'b':
5940 case 'c':
5941 case 'd':
5942 case 'e':
5943 case 'f':
5944 case 'g':
5945 case 'h':
5946 case 'i':
5947 case 'j':
5948 case 'k':
5949 case 'l':
5950 case 'm':
5951 case 'n':
5952 case 'o':
5953 case 'p':
5954 case 'q':
5955 case 'r':
5956 case 's':
5957 case 't':
5958 case 'u':
5959 case 'v':
5960 case 'w':
5961 case 'x':
5962 case 'y':
5963 case 'z':
5964 case 'A':
5965 case 'B':
5966 case 'C':
5967 case 'D':
5968 case 'E':
5969 case 'F':
5970 case 'G':
5971 case 'H':
5972 case 'I':
5973 case 'J':
5974 case 'K':
5975 case 'L':
5976 case 'M':
5977 case 'N':
5978 case 'O':
5979 case 'P':
5980 case 'Q':
5981 case 'R':
5982 case 'S':
5983 case 'T':
5984 case 'U':
5985 case 'V':
5986 case 'W':
5987 case 'X':
5988 case 'Y':
5989 case 'Z':
5990 case '0':
5991 case '1':
5992 case '2':
5993 case '3':
5994 case '4':
5995 case '5':
5996 case '6':
5997 case '7':
5998 case '8':
5999 case '9':
6000 return true;
6001 default:
6002 return false;
6003 }
6004}
6005
6006
6018static enum MHD_HdrLineReadRes_
6020 bool process_footers,
6021 struct _MHD_str_w_len *hdr_name,
6022 struct _MHD_str_w_len *hdr_value)
6023{
6024 const int discp_lvl = c->daemon->client_discipline;
6025 /* Treat bare LF as the end of the line.
6026 RFC 9112, section 2.2-3
6027 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
6028 Bare LF is processed as end of the line or rejected as broken request. */
6029 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
6030 /* Keep bare CR character as is.
6031 Violates RFC 9112, section 2.2-4 */
6032 const bool bare_cr_keep = (-3 >= discp_lvl);
6033 /* Treat bare CR as space; replace it with space before processing.
6034 RFC 9112, section 2.2-4 */
6035 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
6036 /* Treat NUL as space; replace it with space before processing.
6037 RFC 9110, section 5.5-5 */
6038 const bool nul_as_sp = (-1 >= discp_lvl);
6039 /* Allow folded header lines.
6040 RFC 9112, section 5.2-4 */
6041 const bool allow_folded = (0 >= discp_lvl);
6042 /* Do not reject headers with the whitespace at the start of the first line.
6043 When allowed, the first line with whitespace character at the first
6044 position is ignored (as well as all possible line foldings of the first
6045 line).
6046 RFC 9112, section 2.2-8 */
6047 const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl);
6048 /* Allow whitespace in header (field) name.
6049 Violates RFC 9110, section 5.1-2 */
6050 const bool allow_wsp_in_name = (-2 >= discp_lvl);
6051 /* Allow zero-length header (field) name.
6052 Violates RFC 9110, section 5.1-2 */
6053 const bool allow_empty_name = (-2 >= discp_lvl);
6054 /* Allow non-tchar characters in header (field) name.
6055 Violates RFC 9110, section 5.1 */
6056 const bool allow_extended_charset = (-2 >= discp_lvl);
6057 /* Allow whitespace before colon.
6058 Violates RFC 9112, section 5.1-2 */
6059 const bool allow_wsp_before_colon = (-3 >= discp_lvl);
6060 /* Do not abort the request when header line has no colon, just skip such
6061 bad lines.
6062 RFC 9112, section 5-1 */
6063 const bool allow_line_without_colon = (-2 >= discp_lvl);
6064
6065 size_t p;
6066
6067#if ! defined (HAVE_MESSAGES) && ! defined(_DEBUG)
6068 (void) process_footers; /* Unused parameter */
6069#endif /* !HAVE_MESSAGES && !_DEBUG */
6070
6071 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6073 c->state);
6074
6075 p = c->rq.hdrs.hdr.proc_pos;
6076
6077 mhd_assert (p <= c->read_buffer_offset);
6078 while (p < c->read_buffer_offset)
6079 {
6080 const char chr = c->read_buffer[p];
6081 bool end_of_line;
6082
6083 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
6084 (c->rq.hdrs.hdr.name_len < p));
6085 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p));
6086 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
6087 (c->rq.hdrs.hdr.name_end_found));
6088 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
6089 (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start));
6090 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
6091 (0 != c->rq.hdrs.hdr.name_len));
6092 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
6093 (0 == c->rq.hdrs.hdr.name_len) || \
6094 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len));
6095 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
6096 (0 == c->rq.hdrs.hdr.value_start) || \
6097 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start));
6098
6099 /* Check for the end of the line */
6100 if ('\r' == chr)
6101 {
6102 if (0 != p)
6103 {
6104 /* Line is not empty, need to check for possible line folding */
6105 if (p + 2 >= c->read_buffer_offset)
6106 break; /* Not enough data yet to check for folded line */
6107 }
6108 else
6109 {
6110 /* Line is empty, no need to check for possible line folding */
6111 if (p + 2 > c->read_buffer_offset)
6112 break; /* Not enough data yet to check for the end of the line */
6113 }
6114 if ('\n' == c->read_buffer[p + 1])
6115 end_of_line = true;
6116 else
6117 {
6118 /* Bare CR alone */
6119 /* Must be rejected or replaced with space char.
6120 See RFC 9112, section 2.2-4 */
6121 if (bare_cr_as_sp)
6122 {
6123 c->read_buffer[p] = ' ';
6125 continue; /* Re-start processing of the current character */
6126 }
6127 else if (! bare_cr_keep)
6128 {
6129 if (! process_footers)
6133 else
6137 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6138 }
6139 end_of_line = false;
6140 }
6141 }
6142 else if ('\n' == chr)
6143 {
6144 /* Bare LF may be recognised as a line delimiter.
6145 See RFC 9112, section 2.2-3 */
6146 if (bare_lf_as_crlf)
6147 {
6148 if (0 != p)
6149 {
6150 /* Line is not empty, need to check for possible line folding */
6151 if (p + 1 >= c->read_buffer_offset)
6152 break; /* Not enough data yet to check for folded line */
6153 }
6154 end_of_line = true;
6155 }
6156 else
6157 {
6158 if (! process_footers)
6162 else
6166 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6167 }
6168 }
6169 else
6170 end_of_line = false;
6171
6172 if (end_of_line)
6173 {
6174 /* Handle the end of the line */
6178 const size_t line_len = p + (('\r' == chr) ? 2 : 1);
6179 char next_line_char;
6180 mhd_assert (line_len <= c->read_buffer_offset);
6181
6182 if (0 == p)
6183 {
6184 /* Zero-length header line. This is the end of the request header
6185 section.
6186 RFC 9112, Section 2.1-1 */
6189 mhd_assert (0 == c->rq.hdrs.hdr.name_len);
6190 mhd_assert (0 == c->rq.hdrs.hdr.ws_start);
6191 mhd_assert (0 == c->rq.hdrs.hdr.value_start);
6192 /* Consume the line with CRLF (or bare LF) */
6193 c->read_buffer += line_len;
6194 c->read_buffer_offset -= line_len;
6195 c->read_buffer_size -= line_len;
6197 }
6198
6199 mhd_assert (line_len < c->read_buffer_offset);
6200 mhd_assert (0 != line_len);
6201 mhd_assert ('\n' == c->read_buffer[line_len - 1]);
6202 next_line_char = c->read_buffer[line_len];
6203 if ((' ' == next_line_char) ||
6204 ('\t' == next_line_char))
6205 {
6206 /* Folded line */
6207 if (! allow_folded)
6208 {
6209 if (! process_footers)
6213 else
6217
6218 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6219 }
6220 /* Replace CRLF (or bare LF) character(s) with space characters.
6221 See RFC 9112, Section 5.2-4 */
6222 c->read_buffer[p] = ' ';
6223 if ('\r' == chr)
6224 c->read_buffer[p + 1] = ' ';
6225 continue; /* Re-start processing of the current character */
6226 }
6227 else
6228 {
6229 /* It is not a folded line, it's the real end of the non-empty line */
6230 bool skip_line = false;
6231 mhd_assert (0 != p);
6232 if (c->rq.hdrs.hdr.starts_with_ws)
6233 {
6234 /* This is the first line and it starts with whitespace. This line
6235 must be discarded completely.
6236 See RFC 9112, Section 2.2-8 */
6237 mhd_assert (allow_wsp_at_start);
6238#ifdef HAVE_MESSAGES
6239 MHD_DLOG (c->daemon,
6240 _ ("Whitespace-prefixed first header line " \
6241 "has been skipped.\n"));
6242#endif /* HAVE_MESSAGES */
6243 skip_line = true;
6244 }
6245 else if (! c->rq.hdrs.hdr.name_end_found)
6246 {
6247 if (! allow_line_without_colon)
6248 {
6249 if (! process_footers)
6253 else
6257
6258 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6259 }
6260 /* Skip broken line completely */
6262 skip_line = true;
6263 }
6264 if (skip_line)
6265 {
6266 /* Skip the entire line */
6267 c->read_buffer += line_len;
6268 c->read_buffer_offset -= line_len;
6269 c->read_buffer_size -= line_len;
6270 p = 0;
6271 /* Reset processing state */
6272 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
6273 /* Start processing of the next line */
6274 continue;
6275 }
6276 else
6277 {
6278 /* This line should be valid header line */
6279 size_t value_len;
6280 mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name);
6281
6282 hdr_name->str = c->read_buffer + 0; /* The name always starts at the first character */
6283 hdr_name->len = c->rq.hdrs.hdr.name_len;
6284 mhd_assert (0 == hdr_name->str[hdr_name->len]);
6285
6286 if (0 == c->rq.hdrs.hdr.value_start)
6287 {
6288 c->rq.hdrs.hdr.value_start = p;
6289 c->read_buffer[p] = 0;
6290 value_len = 0;
6291 }
6292 else if (0 != c->rq.hdrs.hdr.ws_start)
6293 {
6294 mhd_assert (p > c->rq.hdrs.hdr.ws_start);
6296 c->read_buffer[c->rq.hdrs.hdr.ws_start] = 0;
6297 value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start;
6298 }
6299 else
6300 {
6301 mhd_assert (p > c->rq.hdrs.hdr.ws_start);
6302 c->read_buffer[p] = 0;
6303 value_len = p - c->rq.hdrs.hdr.value_start;
6304 }
6305 hdr_value->str = c->read_buffer + c->rq.hdrs.hdr.value_start;
6306 hdr_value->len = value_len;
6307 mhd_assert (0 == hdr_value->str[hdr_value->len]);
6308 /* Consume the entire line */
6309 c->read_buffer += line_len;
6310 c->read_buffer_offset -= line_len;
6311 c->read_buffer_size -= line_len;
6313 }
6314 }
6315 }
6316 else if ((' ' == chr) || ('\t' == chr))
6317 {
6318 if (0 == p)
6319 {
6320 if (! allow_wsp_at_start)
6321 {
6322 if (! process_footers)
6326 else
6330 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6331 }
6332 c->rq.hdrs.hdr.starts_with_ws = true;
6333 }
6334 else if ((! c->rq.hdrs.hdr.name_end_found) &&
6335 (! c->rq.hdrs.hdr.starts_with_ws))
6336 {
6337 /* Whitespace in header name / between header name and colon */
6338 if (allow_wsp_in_name || allow_wsp_before_colon)
6339 {
6340 if (0 == c->rq.hdrs.hdr.ws_start)
6341 c->rq.hdrs.hdr.ws_start = p;
6342 }
6343 else
6344 {
6345 if (! process_footers)
6349 else
6353
6354 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6355 }
6356 }
6357 else
6358 {
6359 /* Whitespace before/inside/after header (field) value */
6360 if (0 == c->rq.hdrs.hdr.ws_start)
6361 c->rq.hdrs.hdr.ws_start = p;
6362 }
6363 }
6364 else if (0 == chr)
6365 {
6366 if (! nul_as_sp)
6367 {
6368 if (! process_footers)
6372 else
6376
6377 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6378 }
6379 c->read_buffer[p] = ' ';
6380 continue; /* Re-start processing of the current character */
6381 }
6382 else
6383 {
6384 /* Not a whitespace, not the end of the header line */
6385 mhd_assert ('\r' != chr);
6386 mhd_assert ('\n' != chr);
6387 mhd_assert ('\0' != chr);
6388 if ( (! c->rq.hdrs.hdr.name_end_found) &&
6389 (! c->rq.hdrs.hdr.starts_with_ws) )
6390 {
6391 /* Processing the header (field) name */
6392 if ( (! allow_extended_charset) &&
6393 (':' != chr) &&
6394 (! char_legal_in_field_name (chr)) )
6395 {
6400 }
6401
6402 if (':' == chr)
6403 {
6404 if (0 == c->rq.hdrs.hdr.ws_start)
6405 c->rq.hdrs.hdr.name_len = p;
6406 else
6407 {
6408 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6409 if (! allow_wsp_before_colon)
6410 {
6411 if (! process_footers)
6415 else
6419 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6420 }
6422#ifndef MHD_FAVOR_SMALL_CODE
6423 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6424#endif /* ! MHD_FAVOR_SMALL_CODE */
6425 }
6426 if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name)
6427 {
6428 if (! process_footers)
6432 else
6436 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6437 }
6438 c->rq.hdrs.hdr.name_end_found = true;
6439 c->read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */
6440 }
6441 else
6442 {
6443 if (0 != c->rq.hdrs.hdr.ws_start)
6444 {
6445 /* End of the whitespace in header (field) name */
6446 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6447 if (! allow_wsp_in_name)
6448 {
6449 if (! process_footers)
6453 else
6457
6458 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6459 }
6460#ifndef MHD_FAVOR_SMALL_CODE
6461 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6462#endif /* ! MHD_FAVOR_SMALL_CODE */
6463 }
6464 }
6465 }
6466 else
6467 {
6468 /* Processing the header (field) value */
6469 if (0 == c->rq.hdrs.hdr.value_start)
6470 c->rq.hdrs.hdr.value_start = p;
6471#ifndef MHD_FAVOR_SMALL_CODE
6472 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6473#endif /* ! MHD_FAVOR_SMALL_CODE */
6474 }
6475#ifdef MHD_FAVOR_SMALL_CODE
6476 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6477#endif /* MHD_FAVOR_SMALL_CODE */
6478 }
6479 p++;
6480 }
6481 c->rq.hdrs.hdr.proc_pos = p;
6482 return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */
6483}
6484
6485
6496static bool
6497get_req_headers (struct MHD_Connection *c, bool process_footers)
6498{
6499 do
6500 {
6501 struct _MHD_str_w_len hdr_name;
6502 struct _MHD_str_w_len hdr_value;
6503 enum MHD_HdrLineReadRes_ res;
6504
6505 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6507 c->state);
6508
6509 #ifdef _DEBUG
6510 hdr_name.str = NULL;
6511 hdr_value.str = NULL;
6512#endif /* _DEBUG */
6513 res = get_req_header (c, process_footers, &hdr_name, &hdr_value);
6515 {
6516 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6518 c->state);
6519 mhd_assert (NULL != hdr_name.str);
6520 mhd_assert (NULL != hdr_value.str);
6521 /* Values must be zero-terminated and must not have binary zeros */
6522 mhd_assert (strlen (hdr_name.str) == hdr_name.len);
6523 mhd_assert (strlen (hdr_value.str) == hdr_value.len);
6524 /* Values must not have whitespaces at the start or at the end */
6525 mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != ' '));
6526 mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != '\t'));
6527 mhd_assert ((hdr_name.len == 0) || \
6528 (hdr_name.str[hdr_name.len - 1] != ' '));
6529 mhd_assert ((hdr_name.len == 0) || \
6530 (hdr_name.str[hdr_name.len - 1] != '\t'));
6531 mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != ' '));
6532 mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != '\t'));
6533 mhd_assert ((hdr_value.len == 0) || \
6534 (hdr_value.str[hdr_value.len - 1] != ' '));
6535 mhd_assert ((hdr_value.len == 0) || \
6536 (hdr_value.str[hdr_value.len - 1] != '\t'));
6537
6538 if (MHD_NO ==
6540 (! process_footers) ?
6543 hdr_name.str, hdr_name.len,
6544 hdr_value.str, hdr_value.len))
6545 {
6546 size_t add_element_size;
6547
6548 mhd_assert (hdr_name.str < hdr_value.str);
6549
6550#ifdef HAVE_MESSAGES
6551 MHD_DLOG (c->daemon,
6552 _ ("Failed to allocate memory in the connection memory " \
6553 "pool to store %s.\n"),
6554 (! process_footers) ? _ ("header") : _ ("footer"));
6555#endif /* HAVE_MESSAGES */
6556
6557 add_element_size = hdr_value.len
6558 + (size_t) (hdr_value.str - hdr_name.str);
6559
6560 if (! process_footers)
6561 handle_req_headers_no_space (c, hdr_name.str, add_element_size);
6562 else
6563 handle_req_footers_no_space (c, hdr_name.str, add_element_size);
6564
6566 return true;
6567 }
6568 /* Reset processing state */
6570 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6572 c->state);
6573 /* Read the next header (field) line */
6574 continue;
6575 }
6577 {
6578 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6580 c->state);
6581 return false;
6582 }
6583 else if (MHD_HDR_LINE_READING_DATA_ERROR == res)
6584 {
6585 mhd_assert ((process_footers ? \
6590 return true;
6591 }
6593 break;
6594 } while (1);
6595
6596#ifdef HAVE_MESSAGES
6597 if (1 == c->rq.num_cr_sp_replaced)
6598 {
6599 MHD_DLOG (c->daemon,
6600 _ ("One bare CR character has been replaced with space " \
6601 "in %s.\n"),
6602 (! process_footers) ?
6603 _ ("the request line or in the request headers") :
6604 _ ("the request footers"));
6605 }
6606 else if (0 != c->rq.num_cr_sp_replaced)
6607 {
6608 MHD_DLOG (c->daemon,
6609 _ ("%" PRIu64 " bare CR characters have been replaced with " \
6610 "spaces in the request line and/or in the request %s.\n"),
6611 (uint64_t) c->rq.num_cr_sp_replaced,
6612 (! process_footers) ? _ ("headers") : _ ("footers"));
6613 }
6614 if (1 == c->rq.skipped_broken_lines)
6615 {
6616 MHD_DLOG (c->daemon,
6617 _ ("One %s line without colon has been skipped.\n"),
6618 (! process_footers) ? _ ("header") : _ ("footer"));
6619 }
6620 else if (0 != c->rq.skipped_broken_lines)
6621 {
6622 MHD_DLOG (c->daemon,
6623 _ ("%" PRIu64 " %s lines without colons has been skipped.\n"),
6624 (uint64_t) c->rq.skipped_broken_lines,
6625 (! process_footers) ? _ ("header") : _ ("footer"));
6626 }
6627#endif /* HAVE_MESSAGES */
6628
6629 mhd_assert (c->rq.method < c->read_buffer);
6630 if (! process_footers)
6631 {
6632 c->rq.header_size = (size_t) (c->read_buffer - c->rq.method);
6634 c->rq.field_lines.size =
6635 (size_t) ((c->read_buffer - c->rq.field_lines.start) - 1);
6636 if ('\r' == *(c->read_buffer - 2))
6637 c->rq.field_lines.size--;
6639
6641 {
6642 /* Try to re-use some of the last bytes of the request header */
6643 /* Do this only if space in the read buffer is limited AND
6644 amount of read ahead data is small. */
6649 const char *last_elmnt_end;
6650 size_t shift_back_size;
6651 if (NULL != c->rq.headers_received_tail)
6652 last_elmnt_end =
6655 else
6656 last_elmnt_end = c->rq.version + HTTP_VER_LEN;
6657 mhd_assert ((last_elmnt_end + 1) < c->read_buffer);
6658 shift_back_size = (size_t) (c->read_buffer - (last_elmnt_end + 1));
6659 if (0 != c->read_buffer_offset)
6660 memmove (c->read_buffer - shift_back_size,
6661 c->read_buffer,
6663 c->read_buffer -= shift_back_size;
6664 c->read_buffer_size += shift_back_size;
6665 }
6666 }
6667 else
6669
6670 return true;
6671}
6672
6673
6681void
6683{
6684 struct MHD_Daemon *daemon = connection->daemon;
6685#if defined(MHD_USE_THREADS)
6686 mhd_assert (NULL == daemon->worker_pool);
6687#endif /* MHD_USE_THREADS */
6688
6689 if (0 == connection->connection_timeout_ms)
6690 return; /* Skip update of activity for connections
6691 without timeout timer. */
6692 if (connection->suspended)
6693 return; /* no activity on suspended connections */
6694
6697 return; /* each connection has personal timeout */
6698
6699 if (connection->connection_timeout_ms != daemon->connection_timeout_ms)
6700 return; /* custom timeout, no need to move it in "normal" DLL */
6701#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6702 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
6703#endif
6704 /* move connection to head of timeout list (by remove + add operation) */
6706 daemon->normal_timeout_tail,
6707 connection);
6709 daemon->normal_timeout_tail,
6710 connection);
6711#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6712 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
6713#endif
6714}
6715
6716
6726void
6728 bool socket_error)
6729{
6730 ssize_t bytes_read;
6731
6732 if ( (MHD_CONNECTION_CLOSED == connection->state) ||
6733 (connection->suspended) )
6734 return;
6735#ifdef HTTPS_SUPPORT
6736 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6737 { /* HTTPS connection. */
6738 if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6739 {
6740 if (! MHD_run_tls_handshake_ (connection))
6741 return;
6742 }
6743 }
6744#endif /* HTTPS_SUPPORT */
6745
6746 mhd_assert (NULL != connection->read_buffer);
6747 if (connection->read_buffer_size == connection->read_buffer_offset)
6748 return; /* No space for receiving data. */
6749
6750 bytes_read = connection->recv_cls (connection,
6751 &connection->read_buffer
6752 [connection->read_buffer_offset],
6753 connection->read_buffer_size
6754 - connection->read_buffer_offset);
6755 if ((bytes_read < 0) || socket_error)
6756 {
6757 if ((MHD_ERR_AGAIN_ == bytes_read) && ! socket_error)
6758 return; /* No new data to process. */
6759 if ((bytes_read > 0) && connection->sk_nonblck)
6760 { /* Try to detect the socket error */
6761 int dummy;
6762 bytes_read = connection->recv_cls (connection, &dummy, sizeof (dummy));
6763 }
6764 if (MHD_ERR_CONNRESET_ == bytes_read)
6765 {
6767 (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6768 {
6769#ifdef HAVE_MESSAGES
6770 MHD_DLOG (connection->daemon,
6771 _ ("Socket has been disconnected when reading request.\n"));
6772#endif
6773 connection->discard_request = true;
6774 }
6775 MHD_connection_close_ (connection,
6777 return;
6778 }
6779
6780#ifdef HAVE_MESSAGES
6781 if (MHD_CONNECTION_INIT != connection->state)
6782 MHD_DLOG (connection->daemon,
6783 _ ("Connection socket is closed when reading " \
6784 "request due to the error: %s\n"),
6785 (bytes_read < 0) ? str_conn_error_ (bytes_read) :
6786 "detected connection closure");
6787#endif
6788 CONNECTION_CLOSE_ERROR (connection,
6789 NULL);
6790 return;
6791 }
6792
6793 if (0 == bytes_read)
6794 { /* Remote side closed connection. */
6795 connection->read_closed = true;
6797 (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6798 {
6799#ifdef HAVE_MESSAGES
6800 MHD_DLOG (connection->daemon,
6801 _ ("Connection was closed by remote side with incomplete "
6802 "request.\n"));
6803#endif
6804 connection->discard_request = true;
6805 MHD_connection_close_ (connection,
6807 }
6808 else if (MHD_CONNECTION_INIT == connection->state)
6809 /* This termination code cannot be reported to the application
6810 * because application has not been informed yet about this request */
6811 MHD_connection_close_ (connection,
6813 else
6814 MHD_connection_close_ (connection,
6816 return;
6817 }
6818 connection->read_buffer_offset += (size_t) bytes_read;
6819 MHD_update_last_activity_ (connection);
6820#if DEBUG_STATES
6821 MHD_DLOG (connection->daemon,
6822 _ ("In function %s handling connection at state: %s\n"),
6823 MHD_FUNC_,
6824 MHD_state_to_string (connection->state));
6825#endif
6826 /* TODO: check whether the next 'switch()' really needed */
6827 switch (connection->state)
6828 {
6835 /* nothing to do but default action */
6836 if (connection->read_closed)
6837 {
6838 /* TODO: check whether this really needed */
6839 MHD_connection_close_ (connection,
6841 }
6842 return;
6844 return;
6845#ifdef UPGRADE_SUPPORT
6846 case MHD_CONNECTION_UPGRADE:
6847 mhd_assert (0);
6848 return;
6849#endif /* UPGRADE_SUPPORT */
6851 /* shrink read buffer to how much is actually used */
6852 /* TODO: remove shrink as it handled in special function */
6853 if ((0 != connection->read_buffer_size) &&
6854 (connection->read_buffer_size != connection->read_buffer_offset))
6855 {
6856 mhd_assert (NULL != connection->read_buffer);
6857 connection->read_buffer =
6858 MHD_pool_reallocate (connection->pool,
6859 connection->read_buffer,
6860 connection->read_buffer_size,
6861 connection->read_buffer_offset);
6862 connection->read_buffer_size = connection->read_buffer_offset;
6863 }
6864 break;
6870 /* Milestone state, no data should be read */
6871 mhd_assert (0); /* Should not be possible */
6872 break;
6883 default:
6884 mhd_assert (0); /* Should not be possible */
6885 break;
6886 }
6887 return;
6888}
6889
6890
6899void
6901{
6902 struct MHD_Response *response;
6903 ssize_t ret;
6904 if (connection->suspended)
6905 return;
6906
6907#ifdef HTTPS_SUPPORT
6908 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6909 { /* HTTPS connection. */
6910 if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6911 {
6912 if (! MHD_run_tls_handshake_ (connection))
6913 return;
6914 }
6915 }
6916#endif /* HTTPS_SUPPORT */
6917
6918#if DEBUG_STATES
6919 MHD_DLOG (connection->daemon,
6920 _ ("In function %s handling connection at state: %s\n"),
6921 MHD_FUNC_,
6922 MHD_state_to_string (connection->state));
6923#endif
6924 switch (connection->state)
6925 {
6932 mhd_assert (0);
6933 return;
6935 ret = MHD_send_data_ (connection,
6937 [connection->continue_message_write_offset],
6939 - connection->continue_message_write_offset,
6940 true);
6941 if (ret < 0)
6942 {
6943 if (MHD_ERR_AGAIN_ == ret)
6944 return;
6945#ifdef HAVE_MESSAGES
6946 MHD_DLOG (connection->daemon,
6947 _ ("Failed to send data in request for %s.\n"),
6948 connection->rq.url);
6949#endif
6950 CONNECTION_CLOSE_ERROR (connection,
6951 NULL);
6952 return;
6953 }
6954#if _MHD_DEBUG_SEND_DATA
6955 fprintf (stderr,
6956 _ ("Sent 100 continue response: `%.*s'\n"),
6957 (int) ret,
6959#endif
6960 connection->continue_message_write_offset += (size_t) ret;
6961 MHD_update_last_activity_ (connection);
6962 return;
6968 mhd_assert (0);
6969 return;
6971 mhd_assert (0);
6972 return;
6974 {
6975 struct MHD_Response *const resp = connection->rp.response;
6976 const size_t wb_ready = connection->write_buffer_append_offset
6977 - connection->write_buffer_send_offset;
6978 mhd_assert (connection->write_buffer_append_offset >= \
6979 connection->write_buffer_send_offset);
6980 mhd_assert (NULL != resp);
6981 mhd_assert ( (0 == resp->data_size) || \
6982 (0 == resp->data_start) || \
6983 (NULL != resp->crc) );
6984 mhd_assert ( (0 == connection->rp.rsp_write_position) || \
6985 (resp->total_size ==
6986 connection->rp.rsp_write_position) );
6987 mhd_assert ((MHD_CONN_MUST_UPGRADE != connection->keepalive) || \
6988 (! connection->rp.props.send_reply_body));
6989
6990 if ( (connection->rp.props.send_reply_body) &&
6991 (NULL == resp->crc) &&
6992 (NULL == resp->data_iov) &&
6993 /* TODO: remove the next check as 'send_reply_body' is used */
6994 (0 == connection->rp.rsp_write_position) &&
6995 (! connection->rp.props.chunked) )
6996 {
6997 mhd_assert (resp->total_size >= resp->data_size);
6998 mhd_assert (0 == resp->data_start);
6999 /* Send response headers alongside the response body, if the body
7000 * data is available. */
7001 ret = MHD_send_hdr_and_body_ (connection,
7002 &connection->write_buffer
7003 [connection->write_buffer_send_offset],
7004 wb_ready,
7005 false,
7006 resp->data,
7007 resp->data_size,
7008 (resp->total_size == resp->data_size));
7009 }
7010 else
7011 {
7012 /* This is response for HEAD request or reply body is not allowed
7013 * for any other reason or reply body is dynamically generated. */
7014 /* Do not send the body data even if it's available. */
7015 ret = MHD_send_hdr_and_body_ (connection,
7016 &connection->write_buffer
7017 [connection->write_buffer_send_offset],
7018 wb_ready,
7019 false,
7020 NULL,
7021 0,
7022 ((0 == resp->total_size) ||
7023 (! connection->rp.props.send_reply_body)
7024 ));
7025 }
7026
7027 if (ret < 0)
7028 {
7029 if (MHD_ERR_AGAIN_ == ret)
7030 return;
7031#ifdef HAVE_MESSAGES
7032 MHD_DLOG (connection->daemon,
7033 _ ("Failed to send the response headers for the " \
7034 "request for `%s'. Error: %s\n"),
7035 connection->rq.url,
7036 str_conn_error_ (ret));
7037#endif
7038 CONNECTION_CLOSE_ERROR (connection,
7039 NULL);
7040 return;
7041 }
7042 /* 'ret' is not negative, it's safe to cast it to 'size_t'. */
7043 if (((size_t) ret) > wb_ready)
7044 {
7045 /* The complete header and some response data have been sent,
7046 * update both offsets. */
7047 mhd_assert (0 == connection->rp.rsp_write_position);
7048 mhd_assert (! connection->rp.props.chunked);
7049 mhd_assert (connection->rp.props.send_reply_body);
7050 connection->write_buffer_send_offset += wb_ready;
7051 connection->rp.rsp_write_position = ((size_t) ret) - wb_ready;
7052 }
7053 else
7054 connection->write_buffer_send_offset += (size_t) ret;
7055 MHD_update_last_activity_ (connection);
7056 if (MHD_CONNECTION_HEADERS_SENDING != connection->state)
7057 return;
7058 check_write_done (connection,
7060 return;
7061 }
7063 return;
7065 response = connection->rp.response;
7066 if (connection->rp.rsp_write_position <
7067 connection->rp.response->total_size)
7068 {
7069 uint64_t data_write_offset;
7070
7071#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7072 if (NULL != response->crc)
7073 MHD_mutex_lock_chk_ (&response->mutex);
7074#endif
7075 if (MHD_NO == try_ready_normal_body (connection))
7076 {
7077 /* mutex was already unlocked by try_ready_normal_body */
7078 return;
7079 }
7080#if defined(_MHD_HAVE_SENDFILE)
7081 if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
7082 {
7083 mhd_assert (NULL == response->data_iov);
7084 ret = MHD_send_sendfile_ (connection);
7085 }
7086 else /* combined with the next 'if' */
7087#endif /* _MHD_HAVE_SENDFILE */
7088 if (NULL != response->data_iov)
7089 {
7090 ret = MHD_send_iovec_ (connection,
7091 &connection->rp.resp_iov,
7092 true);
7093 }
7094 else
7095 {
7096 data_write_offset = connection->rp.rsp_write_position
7097 - response->data_start;
7098 if (data_write_offset > (uint64_t) SIZE_MAX)
7099 MHD_PANIC (_ ("Data offset exceeds limit.\n"));
7100 ret = MHD_send_data_ (connection,
7101 &response->data
7102 [(size_t) data_write_offset],
7103 response->data_size
7104 - (size_t) data_write_offset,
7105 true);
7106#if _MHD_DEBUG_SEND_DATA
7107 if (ret > 0)
7108 fprintf (stderr,
7109 _ ("Sent %d-byte DATA response: `%.*s'\n"),
7110 (int) ret,
7111 (int) ret,
7112 &rp.response->data[connection->rp.rsp_write_position
7113 - rp.response->data_start]);
7114#endif
7115 }
7116#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7117 if (NULL != response->crc)
7118 MHD_mutex_unlock_chk_ (&response->mutex);
7119#endif
7120 if (ret < 0)
7121 {
7122 if (MHD_ERR_AGAIN_ == ret)
7123 return;
7124#ifdef HAVE_MESSAGES
7125 MHD_DLOG (connection->daemon,
7126 _ ("Failed to send the response body for the " \
7127 "request for `%s'. Error: %s\n"),
7128 connection->rq.url,
7129 str_conn_error_ (ret));
7130#endif
7131 CONNECTION_CLOSE_ERROR (connection,
7132 NULL);
7133 return;
7134 }
7135 connection->rp.rsp_write_position += (size_t) ret;
7136 MHD_update_last_activity_ (connection);
7137 }
7138 if (connection->rp.rsp_write_position ==
7139 connection->rp.response->total_size)
7141 return;
7143 mhd_assert (0);
7144 return;
7146 ret = MHD_send_data_ (connection,
7147 &connection->write_buffer
7148 [connection->write_buffer_send_offset],
7149 connection->write_buffer_append_offset
7150 - connection->write_buffer_send_offset,
7151 true);
7152 if (ret < 0)
7153 {
7154 if (MHD_ERR_AGAIN_ == ret)
7155 return;
7156#ifdef HAVE_MESSAGES
7157 MHD_DLOG (connection->daemon,
7158 _ ("Failed to send the chunked response body for the " \
7159 "request for `%s'. Error: %s\n"),
7160 connection->rq.url,
7161 str_conn_error_ (ret));
7162#endif
7163 CONNECTION_CLOSE_ERROR (connection,
7164 NULL);
7165 return;
7166 }
7167 connection->write_buffer_send_offset += (size_t) ret;
7168 MHD_update_last_activity_ (connection);
7169 if (MHD_CONNECTION_CHUNKED_BODY_READY != connection->state)
7170 return;
7171 check_write_done (connection,
7172 (connection->rp.response->total_size ==
7173 connection->rp.rsp_write_position) ?
7176 return;
7179 mhd_assert (0);
7180 return;
7182 ret = MHD_send_data_ (connection,
7183 &connection->write_buffer
7184 [connection->write_buffer_send_offset],
7185 connection->write_buffer_append_offset
7186 - connection->write_buffer_send_offset,
7187 true);
7188 if (ret < 0)
7189 {
7190 if (MHD_ERR_AGAIN_ == ret)
7191 return;
7192#ifdef HAVE_MESSAGES
7193 MHD_DLOG (connection->daemon,
7194 _ ("Failed to send the footers for the " \
7195 "request for `%s'. Error: %s\n"),
7196 connection->rq.url,
7197 str_conn_error_ (ret));
7198#endif
7199 CONNECTION_CLOSE_ERROR (connection,
7200 NULL);
7201 return;
7202 }
7203 connection->write_buffer_send_offset += (size_t) ret;
7204 MHD_update_last_activity_ (connection);
7205 if (MHD_CONNECTION_FOOTERS_SENDING != connection->state)
7206 return;
7207 check_write_done (connection,
7209 return;
7211 mhd_assert (0);
7212 return;
7214 return;
7215#ifdef UPGRADE_SUPPORT
7216 case MHD_CONNECTION_UPGRADE:
7217 mhd_assert (0);
7218 return;
7219#endif /* UPGRADE_SUPPORT */
7220 default:
7221 mhd_assert (0);
7222 CONNECTION_CLOSE_ERROR (connection,
7223 _ ("Internal error.\n"));
7224 break;
7225 }
7226 return;
7227}
7228
7229
7236static bool
7238{
7239 const uint64_t timeout = c->connection_timeout_ms;
7240 uint64_t now;
7241 uint64_t since_actv;
7242
7243 if (c->suspended)
7244 return false;
7245 if (0 == timeout)
7246 return false;
7248 since_actv = now - c->last_activity;
7249 /* Keep the next lines in sync with #connection_get_wait() to avoid
7250 * undesired side-effects like busy-waiting. */
7251 if (timeout < since_actv)
7252 {
7253 if (UINT64_MAX / 2 < since_actv)
7254 {
7255 const uint64_t jump_back = c->last_activity - now;
7256 /* Very unlikely that it is more than quarter-million years pause.
7257 * More likely that system clock jumps back. */
7258 if (5000 >= jump_back)
7259 {
7260#ifdef HAVE_MESSAGES
7261 MHD_DLOG (c->daemon,
7262 _ ("Detected system clock %u milliseconds jump back.\n"),
7263 (unsigned int) jump_back);
7264#endif
7265 return false;
7266 }
7267#ifdef HAVE_MESSAGES
7268 MHD_DLOG (c->daemon,
7269 _ ("Detected too large system clock %" PRIu64 " milliseconds "
7270 "jump back.\n"),
7271 jump_back);
7272#endif
7273 }
7274 return true;
7275 }
7276 return false;
7277}
7278
7279
7288static void
7290{
7291 struct MHD_Daemon *daemon = connection->daemon;
7292#ifdef MHD_USE_THREADS
7293 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
7294 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
7295 mhd_assert (NULL == daemon->worker_pool);
7296#endif /* MHD_USE_THREADS */
7297
7298 if (connection->in_cleanup)
7299 return; /* Prevent double cleanup. */
7300 connection->in_cleanup = true;
7301 if (NULL != connection->rp.response)
7302 {
7303 MHD_destroy_response (connection->rp.response);
7304 connection->rp.response = NULL;
7305 }
7306#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7307 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
7308#endif
7309 if (connection->suspended)
7310 {
7313 connection);
7314 connection->suspended = false;
7315 }
7316 else
7317 {
7318 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
7319 {
7320 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7322 daemon->normal_timeout_tail,
7323 connection);
7324 else
7326 daemon->manual_timeout_tail,
7327 connection);
7328 }
7330 daemon->connections_tail,
7331 connection);
7332 }
7333 DLL_insert (daemon->cleanup_head,
7334 daemon->cleanup_tail,
7335 connection);
7336 connection->resuming = false;
7337 connection->in_idle = false;
7338#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7339 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
7340#endif
7342 {
7343 /* if we were at the connection limit before and are in
7344 thread-per-connection mode, signal the main thread
7345 to resume accepting connections */
7346 if ( (MHD_ITC_IS_VALID_ (daemon->itc)) &&
7347 (! MHD_itc_activate_ (daemon->itc, "c")) )
7348 {
7349#ifdef HAVE_MESSAGES
7350 MHD_DLOG (daemon,
7351 _ ("Failed to signal end of connection via inter-thread " \
7352 "communication channel.\n"));
7353#endif
7354 }
7355 }
7356}
7357
7358
7364void
7366{
7367 size_t read_buf_size;
7368
7369#ifdef HTTPS_SUPPORT
7370 mhd_assert ( (0 == (c->daemon->options & MHD_USE_TLS)) || \
7371 (MHD_TLS_CONN_INIT == c->tls_state) );
7372 mhd_assert ( (0 != (c->daemon->options & MHD_USE_TLS)) || \
7373 (MHD_TLS_CONN_NO_TLS == c->tls_state) );
7374#endif /* HTTPS_SUPPORT */
7376
7379
7380 memset (&c->rq, 0, sizeof(c->rq));
7381 memset (&c->rp, 0, sizeof(c->rp));
7382
7383 c->write_buffer = NULL;
7384 c->write_buffer_size = 0;
7387
7389
7390 c->read_buffer_offset = 0;
7391 read_buf_size = c->daemon->pool_size / 2;
7392 c->read_buffer
7393 = MHD_pool_allocate (c->pool,
7394 read_buf_size,
7395 false);
7396 c->read_buffer_size = read_buf_size;
7397}
7398
7399
7406static void
7408 bool reuse)
7409{
7410 struct MHD_Connection *const c = connection;
7411 struct MHD_Daemon *const d = connection->daemon;
7412
7413 if (! reuse)
7414 {
7415 /* Next function will destroy response, notify client,
7416 * destroy memory pool, and set connection state to "CLOSED" */
7418 c->stop_with_error ?
7421 c->read_buffer = NULL;
7422 c->read_buffer_size = 0;
7423 c->read_buffer_offset = 0;
7424 c->write_buffer = NULL;
7425 c->write_buffer_size = 0;
7428 }
7429 else
7430 {
7431 /* Reset connection to process the next request */
7432 size_t new_read_buf_size;
7435
7436 if ( (NULL != d->notify_completed) &&
7437 (c->rq.client_aware) )
7439 c,
7440 &c->rq.client_context,
7442 c->rq.client_aware = false;
7443
7444 if (NULL != c->rp.response)
7446 c->rp.response = NULL;
7447
7450 c->event_loop_info =
7451 (0 == c->read_buffer_offset) ?
7453
7454 memset (&c->rq, 0, sizeof(c->rq));
7455
7456 /* iov (if any) will be deallocated by MHD_pool_reset */
7457 memset (&c->rp, 0, sizeof(c->rp));
7458
7459 c->write_buffer = NULL;
7460 c->write_buffer_size = 0;
7464
7465 /* Reset the read buffer to the starting size,
7466 preserving the bytes we have already read. */
7467 new_read_buf_size = c->daemon->pool_size / 2;
7468 if (c->read_buffer_offset > new_read_buf_size)
7469 new_read_buf_size = c->read_buffer_offset;
7470
7471 c->read_buffer
7472 = MHD_pool_reset (c->pool,
7473 c->read_buffer,
7475 new_read_buf_size);
7476 c->read_buffer_size = new_read_buf_size;
7477 }
7478 c->rq.client_context = NULL;
7479}
7480
7481
7494enum MHD_Result
7496{
7497 struct MHD_Daemon *daemon = connection->daemon;
7498 enum MHD_Result ret;
7499#ifdef MHD_USE_THREADS
7500 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
7501 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
7502#endif /* MHD_USE_THREADS */
7503 /* 'daemon' is not used if epoll is not available and asserts are disabled */
7504 (void) daemon; /* Mute compiler warning */
7505
7506 connection->in_idle = true;
7507 while (! connection->suspended)
7508 {
7509#ifdef HTTPS_SUPPORT
7510 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
7511 { /* HTTPS connection. */
7512 if ((MHD_TLS_CONN_INIT <= connection->tls_state) &&
7513 (MHD_TLS_CONN_CONNECTED > connection->tls_state))
7514 break;
7515 }
7516#endif /* HTTPS_SUPPORT */
7517#if DEBUG_STATES
7518 MHD_DLOG (daemon,
7519 _ ("In function %s handling connection at state: %s\n"),
7520 MHD_FUNC_,
7521 MHD_state_to_string (connection->state));
7522#endif
7523 switch (connection->state)
7524 {
7527 if (get_request_line (connection))
7528 {
7531 || (connection->discard_request));
7532 continue;
7533 }
7535 break;
7539 continue;
7541 if (get_req_headers (connection, false))
7542 {
7544 mhd_assert ((MHD_CONNECTION_HEADERS_RECEIVED == connection->state) || \
7545 (connection->discard_request));
7546 continue;
7547 }
7549 break;
7551 parse_connection_headers (connection);
7552 if (MHD_CONNECTION_HEADERS_RECEIVED != connection->state)
7553 continue;
7555 if (connection->suspended)
7556 break;
7557 continue;
7559 call_connection_handler (connection); /* first call */
7560 if (MHD_CONNECTION_HEADERS_PROCESSED != connection->state)
7561 continue;
7562 if (connection->suspended)
7563 continue;
7564
7565 if ( (NULL == connection->rp.response) &&
7566 (need_100_continue (connection)) &&
7567 /* If the client is already sending the payload (body)
7568 there is no need to send "100 Continue" */
7569 (0 == connection->read_buffer_offset) )
7570 {
7572 break;
7573 }
7574 if ( (NULL != connection->rp.response) &&
7575 (0 != connection->rq.remaining_upload_size) )
7576 {
7577 /* we refused (no upload allowed!) */
7578 connection->rq.remaining_upload_size = 0;
7579 /* force close, in case client still tries to upload... */
7580 connection->discard_request = true;
7581 }
7582 connection->state = (0 == connection->rq.remaining_upload_size)
7585 if (connection->suspended)
7586 break;
7587 continue;
7589 if (connection->continue_message_write_offset ==
7591 {
7593 continue;
7594 }
7595 break;
7597 mhd_assert (0 != connection->rq.remaining_upload_size);
7598 mhd_assert (! connection->discard_request);
7599 mhd_assert (NULL == connection->rp.response);
7600 if (0 != connection->read_buffer_offset)
7601 {
7602 process_request_body (connection); /* loop call */
7603 if (MHD_CONNECTION_BODY_RECEIVING != connection->state)
7604 continue;
7605 }
7606 /* Modify here when queueing of the response during data processing
7607 will be supported */
7608 mhd_assert (! connection->discard_request);
7609 mhd_assert (NULL == connection->rp.response);
7610 if (0 == connection->rq.remaining_upload_size)
7611 {
7612 connection->state = MHD_CONNECTION_BODY_RECEIVED;
7613 continue;
7614 }
7615 break;
7617 mhd_assert (! connection->discard_request);
7618 mhd_assert (NULL == connection->rp.response);
7619 if (0 == connection->rq.remaining_upload_size)
7620 {
7621 if (connection->rq.have_chunked_upload)
7622 {
7623 /* Reset counter variables reused for footers */
7624 connection->rq.num_cr_sp_replaced = 0;
7625 connection->rq.skipped_broken_lines = 0;
7628 }
7629 else
7631 continue;
7632 }
7633 break;
7635 if (get_req_headers (connection, true))
7636 {
7638 mhd_assert ((MHD_CONNECTION_FOOTERS_RECEIVED == connection->state) || \
7639 (connection->discard_request));
7640 continue;
7641 }
7643 break;
7645 /* The header, the body, and the footers of the request has been received,
7646 * switch to the final processing of the request. */
7648 continue;
7650 call_connection_handler (connection); /* "final" call */
7651 if (connection->state != MHD_CONNECTION_FULL_REQ_RECEIVED)
7652 continue;
7653 if (NULL == connection->rp.response)
7654 break; /* try again next time */
7655 /* Response is ready, start reply */
7656 connection->state = MHD_CONNECTION_START_REPLY;
7657 continue;
7659 mhd_assert (NULL != connection->rp.response);
7661 if (MHD_NO == build_header_response (connection))
7662 {
7663 /* oops - close! */
7664 CONNECTION_CLOSE_ERROR (connection,
7665 _ ("Closing connection (failed to create "
7666 "response header).\n"));
7667 continue;
7668 }
7670 break;
7671
7673 /* no default action */
7674 break;
7676#ifdef UPGRADE_SUPPORT
7677 if (NULL != connection->rp.response->upgrade_handler)
7678 {
7679 connection->state = MHD_CONNECTION_UPGRADE;
7680 /* This connection is "upgraded". Pass socket to application. */
7681 if (MHD_NO ==
7683 connection))
7684 {
7685 /* upgrade failed, fail hard */
7686 CONNECTION_CLOSE_ERROR (connection,
7687 NULL);
7688 continue;
7689 }
7690 /* Response is not required anymore for this connection. */
7691 if (1)
7692 {
7693 struct MHD_Response *const resp = connection->rp.response;
7694
7695 connection->rp.response = NULL;
7696 MHD_destroy_response (resp);
7697 }
7698 continue;
7699 }
7700#endif /* UPGRADE_SUPPORT */
7701
7702 if (connection->rp.props.send_reply_body)
7703 {
7704 if (connection->rp.props.chunked)
7706 else
7708 }
7709 else
7711 continue;
7713 mhd_assert (connection->rp.props.send_reply_body);
7714 mhd_assert (! connection->rp.props.chunked);
7715 /* nothing to do here */
7716 break;
7718 mhd_assert (connection->rp.props.send_reply_body);
7719 mhd_assert (! connection->rp.props.chunked);
7720#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7721 if (NULL != connection->rp.response->crc)
7722 MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7723#endif
7724 if (0 == connection->rp.response->total_size)
7725 {
7726#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7727 if (NULL != connection->rp.response->crc)
7728 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7729#endif
7730 if (connection->rp.props.chunked)
7732 else
7734 continue;
7735 }
7736 if (MHD_NO != try_ready_normal_body (connection))
7737 {
7738#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7739 if (NULL != connection->rp.response->crc)
7740 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7741#endif
7743 /* Buffering for flushable socket was already enabled*/
7744
7745 break;
7746 }
7747 /* mutex was already unlocked by "try_ready_normal_body */
7748 /* not ready, no socket action */
7749 break;
7751 mhd_assert (connection->rp.props.send_reply_body);
7752 mhd_assert (connection->rp.props.chunked);
7753 /* nothing to do here */
7754 break;
7756 mhd_assert (connection->rp.props.send_reply_body);
7757 mhd_assert (connection->rp.props.chunked);
7758#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7759 if (NULL != connection->rp.response->crc)
7760 MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7761#endif
7762 if ( (0 == connection->rp.response->total_size) ||
7763 (connection->rp.rsp_write_position ==
7764 connection->rp.response->total_size) )
7765 {
7766#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7767 if (NULL != connection->rp.response->crc)
7768 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7769#endif
7771 continue;
7772 }
7773 if (1)
7774 { /* pseudo-branch for local variables scope */
7775 bool finished;
7776 if (MHD_NO != try_ready_chunked_body (connection, &finished))
7777 {
7778#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7779 if (NULL != connection->rp.response->crc)
7780 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7781#endif
7782 connection->state = finished ? MHD_CONNECTION_CHUNKED_BODY_SENT :
7784 continue;
7785 }
7786 /* mutex was already unlocked by try_ready_chunked_body */
7787 }
7788 break;
7790 mhd_assert (connection->rp.props.send_reply_body);
7791 mhd_assert (connection->rp.props.chunked);
7792 mhd_assert (connection->write_buffer_send_offset <= \
7793 connection->write_buffer_append_offset);
7794
7796 {
7797 /* oops - close! */
7798 CONNECTION_CLOSE_ERROR (connection,
7799 _ ("Closing connection (failed to create " \
7800 "response footer)."));
7801 continue;
7802 }
7803 mhd_assert (connection->write_buffer_send_offset < \
7804 connection->write_buffer_append_offset);
7806 continue;
7808 mhd_assert (connection->rp.props.send_reply_body);
7809 mhd_assert (connection->rp.props.chunked);
7810 /* no default action */
7811 break;
7813 if (MHD_HTTP_PROCESSING == connection->rp.responseCode)
7814 {
7815 /* After this type of response, we allow sending another! */
7817 MHD_destroy_response (connection->rp.response);
7818 connection->rp.response = NULL;
7819 /* FIXME: maybe partially reset memory pool? */
7820 continue;
7821 }
7822 /* Reset connection after complete reply */
7823 connection_reset (connection,
7824 MHD_CONN_USE_KEEPALIVE == connection->keepalive &&
7825 ! connection->read_closed &&
7826 ! connection->discard_request);
7827 continue;
7829 cleanup_connection (connection);
7830 connection->in_idle = false;
7831 return MHD_NO;
7832#ifdef UPGRADE_SUPPORT
7833 case MHD_CONNECTION_UPGRADE:
7834 connection->in_idle = false;
7835 return MHD_YES; /* keep open */
7836#endif /* UPGRADE_SUPPORT */
7837 default:
7838 mhd_assert (0);
7839 break;
7840 }
7841 break;
7842 }
7843 if (connection_check_timedout (connection))
7844 {
7845 MHD_connection_close_ (connection,
7847 connection->in_idle = false;
7848 return MHD_YES;
7849 }
7851 ret = MHD_YES;
7852#ifdef EPOLL_SUPPORT
7853 if ( (! connection->suspended) &&
7854 MHD_D_IS_USING_EPOLL_ (daemon) )
7855 {
7856 ret = MHD_connection_epoll_update_ (connection);
7857 }
7858#endif /* EPOLL_SUPPORT */
7859 connection->in_idle = false;
7860 return ret;
7861}
7862
7863
7864#ifdef EPOLL_SUPPORT
7873enum MHD_Result
7874MHD_connection_epoll_update_ (struct MHD_Connection *connection)
7875{
7876 struct MHD_Daemon *const daemon = connection->daemon;
7877
7879
7880 if ((0 != (MHD_EVENT_LOOP_INFO_PROCESS & connection->event_loop_info)) &&
7881 (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)))
7882 {
7883 /* Make sure that connection waiting for processing will be processed */
7884 EDLL_insert (daemon->eready_head,
7885 daemon->eready_tail,
7886 connection);
7887 connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
7888 }
7889
7890 if ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) &&
7891 (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
7892 ( ( (MHD_EVENT_LOOP_INFO_WRITE == connection->event_loop_info) &&
7893 (0 == (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY))) ||
7894 ( (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info)) &&
7895 (0 == (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) ) ) )
7896 {
7897 /* add to epoll set */
7898 struct epoll_event event;
7899
7900 event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
7901 event.data.ptr = connection;
7902 if (0 != epoll_ctl (daemon->epoll_fd,
7903 EPOLL_CTL_ADD,
7904 connection->socket_fd,
7905 &event))
7906 {
7907#ifdef HAVE_MESSAGES
7908 if (0 != (daemon->options & MHD_USE_ERROR_LOG))
7909 MHD_DLOG (daemon,
7910 _ ("Call to epoll_ctl failed: %s\n"),
7912#endif
7913 connection->state = MHD_CONNECTION_CLOSED;
7914 cleanup_connection (connection);
7915 return MHD_NO;
7916 }
7917 connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
7918 }
7919 return MHD_YES;
7920}
7921
7922
7923#endif
7924
7925
7931void
7933{
7934 connection->recv_cls = &recv_param_adapter;
7935}
7936
7937
7950_MHD_EXTERN const union MHD_ConnectionInfo *
7952 enum MHD_ConnectionInfoType info_type,
7953 ...)
7954{
7955 switch (info_type)
7956 {
7957#ifdef HTTPS_SUPPORT
7959 if (NULL == connection->tls_session)
7960 return NULL;
7961 if (1)
7962 { /* Workaround to mute compiler warning */
7963 gnutls_cipher_algorithm_t res;
7964 res = gnutls_cipher_get (connection->tls_session);
7965 connection->connection_info_dummy.cipher_algorithm = (int) res;
7966 }
7967 return &connection->connection_info_dummy;
7969 if (NULL == connection->tls_session)
7970 return NULL;
7971 if (1)
7972 { /* Workaround to mute compiler warning */
7973 gnutls_protocol_t res;
7974 res = gnutls_protocol_get_version (connection->tls_session);
7975 connection->connection_info_dummy.protocol = (int) res;
7976 }
7977 return &connection->connection_info_dummy;
7979 if (NULL == connection->tls_session)
7980 return NULL;
7981 connection->connection_info_dummy.tls_session = connection->tls_session;
7982 return &connection->connection_info_dummy;
7983#else /* ! HTTPS_SUPPORT */
7987#endif /* ! HTTPS_SUPPORT */
7989 return NULL; /* Not implemented */
7991 if (0 < connection->addr_len)
7992 {
7993 mhd_assert (sizeof (connection->addr) == \
7994 sizeof (connection->connection_info_dummy.client_addr));
7995 memcpy (&connection->connection_info_dummy.client_addr,
7996 &connection->addr,
7997 sizeof(connection->addr));
7998 return &connection->connection_info_dummy;
7999 }
8000 return NULL;
8002 connection->connection_info_dummy.daemon =
8003 MHD_get_master (connection->daemon);
8004 return &connection->connection_info_dummy;
8006 connection->connection_info_dummy.connect_fd = connection->socket_fd;
8007 return &connection->connection_info_dummy;
8010 connection->socket_context;
8011 return &connection->connection_info_dummy;
8013 connection->connection_info_dummy.suspended =
8014 connection->suspended ? MHD_YES : MHD_NO;
8015 return &connection->connection_info_dummy;
8017#if SIZEOF_UNSIGNED_INT <= (SIZEOF_UINT64_T - 2)
8018 if (UINT_MAX < connection->connection_timeout_ms / 1000)
8020 else
8021#endif /* SIZEOF_UNSIGNED_INT <=(SIZEOF_UINT64_T - 2) */
8023 (unsigned int) (connection->connection_timeout_ms / 1000);
8024 return &connection->connection_info_dummy;
8026 if ( (MHD_CONNECTION_HEADERS_RECEIVED > connection->state) ||
8027 (MHD_CONNECTION_CLOSED == connection->state) )
8028 return NULL; /* invalid, too early! */
8029 connection->connection_info_dummy.header_size = connection->rq.header_size;
8030 return &connection->connection_info_dummy;
8032 if (NULL == connection->rp.response)
8033 return NULL;
8034 connection->connection_info_dummy.http_status = connection->rp.responseCode;
8035 return &connection->connection_info_dummy;
8036 default:
8037 return NULL;
8038 }
8039}
8040
8041
8053 enum MHD_CONNECTION_OPTION option,
8054 ...)
8055{
8056 va_list ap;
8057 struct MHD_Daemon *daemon;
8058 unsigned int ui_val;
8059
8060 daemon = connection->daemon;
8061 switch (option)
8062 {
8064 if (0 == connection->connection_timeout_ms)
8066 va_start (ap, option);
8067 ui_val = va_arg (ap, unsigned int);
8068 va_end (ap);
8069#if (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT
8070 if ((UINT64_MAX / 4000 - 1) < ui_val)
8071 {
8072#ifdef HAVE_MESSAGES
8073 MHD_DLOG (connection->daemon,
8074 _ ("The specified connection timeout (%u) is too " \
8075 "large. Maximum allowed value (%" PRIu64 ") will be used " \
8076 "instead.\n"),
8077 ui_val,
8078 (UINT64_MAX / 4000 - 1));
8079#endif
8080 ui_val = UINT64_MAX / 4000 - 1;
8081 }
8082#endif /* (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT */
8083 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
8084 {
8085#if defined(MHD_USE_THREADS)
8086 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
8087#endif
8088 if (! connection->suspended)
8089 {
8090 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
8092 daemon->normal_timeout_tail,
8093 connection);
8094 else
8096 daemon->manual_timeout_tail,
8097 connection);
8098 connection->connection_timeout_ms = ((uint64_t) ui_val) * 1000;
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 }
8108#if defined(MHD_USE_THREADS)
8109 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
8110#endif
8111 }
8112 return MHD_YES;
8113 default:
8114 return MHD_NO;
8115 }
8116}
8117
8118
8166 unsigned int status_code,
8167 struct MHD_Response *response)
8168{
8169 struct MHD_Daemon *daemon;
8170 bool reply_icy;
8171
8172 if ((NULL == connection) || (NULL == response))
8173 return MHD_NO;
8174
8175 daemon = connection->daemon;
8176 if ((! connection->in_access_handler) && (! connection->suspended) &&
8177 MHD_D_IS_USING_THREADS_ (daemon))
8178 return MHD_NO;
8179
8180 reply_icy = (0 != (status_code & MHD_ICY_FLAG));
8181 status_code &= ~MHD_ICY_FLAG;
8182
8183#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
8184 if ( (! connection->suspended) &&
8185 MHD_D_IS_USING_THREADS_ (daemon) &&
8186 (! MHD_thread_handle_ID_is_current_thread_ (connection->tid)) )
8187 {
8188#ifdef HAVE_MESSAGES
8189 MHD_DLOG (daemon,
8190 _ ("Attempted to queue response on wrong thread!\n"));
8191#endif
8192 return MHD_NO;
8193 }
8194#endif
8195
8196 if (NULL != connection->rp.response)
8197 return MHD_NO; /* The response was already set */
8198
8199 if ( (MHD_CONNECTION_HEADERS_PROCESSED != connection->state) &&
8200 (MHD_CONNECTION_FULL_REQ_RECEIVED != connection->state) )
8201 return MHD_NO; /* Wrong connection state */
8202
8203 if (daemon->shutdown)
8204 return MHD_NO;
8205
8206#ifdef UPGRADE_SUPPORT
8207 if (NULL != response->upgrade_handler)
8208 {
8209 struct MHD_HTTP_Res_Header *conn_header;
8210 if (0 == (daemon->options & MHD_ALLOW_UPGRADE))
8211 {
8212#ifdef HAVE_MESSAGES
8213 MHD_DLOG (daemon,
8214 _ ("Attempted 'upgrade' connection on daemon without" \
8215 " MHD_ALLOW_UPGRADE option!\n"));
8216#endif
8217 return MHD_NO;
8218 }
8219 if (MHD_HTTP_SWITCHING_PROTOCOLS != status_code)
8220 {
8221#ifdef HAVE_MESSAGES
8222 MHD_DLOG (daemon,
8223 _ ("Application used invalid status code for" \
8224 " 'upgrade' response!\n"));
8225#endif
8226 return MHD_NO;
8227 }
8228 if (0 == (response->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
8229 {
8230#ifdef HAVE_MESSAGES
8231 MHD_DLOG (daemon,
8232 _ ("Application used invalid response" \
8233 " without \"Connection\" header!\n"));
8234#endif
8235 return MHD_NO;
8236 }
8237 conn_header = response->first_header;
8238 mhd_assert (NULL != conn_header);
8241 if (! MHD_str_has_s_token_caseless_ (conn_header->value,
8242 "upgrade"))
8243 {
8244#ifdef HAVE_MESSAGES
8245 MHD_DLOG (daemon,
8246 _ ("Application used invalid response" \
8247 " without \"upgrade\" token in" \
8248 " \"Connection\" header!\n"));
8249#endif
8250 return MHD_NO;
8251 }
8252 if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
8253 {
8254#ifdef HAVE_MESSAGES
8255 MHD_DLOG (daemon,
8256 _ ("Connection \"Upgrade\" can be used only " \
8257 "with HTTP/1.1 connections!\n"));
8258#endif
8259 return MHD_NO;
8260 }
8261 }
8262#endif /* UPGRADE_SUPPORT */
8263 if (MHD_HTTP_SWITCHING_PROTOCOLS == status_code)
8264 {
8265#ifdef UPGRADE_SUPPORT
8266 if (NULL == response->upgrade_handler)
8267 {
8268#ifdef HAVE_MESSAGES
8269 MHD_DLOG (daemon,
8270 _ ("Application used status code 101 \"Switching Protocols\" " \
8271 "with non-'upgrade' response!\n"));
8272#endif /* HAVE_MESSAGES */
8273 return MHD_NO;
8274 }
8275#else /* ! UPGRADE_SUPPORT */
8276#ifdef HAVE_MESSAGES
8277 MHD_DLOG (daemon,
8278 _ ("Application used status code 101 \"Switching Protocols\", " \
8279 "but this MHD was built without \"Upgrade\" support!\n"));
8280#endif /* HAVE_MESSAGES */
8281 return MHD_NO;
8282#endif /* ! UPGRADE_SUPPORT */
8283 }
8284 if ( (100 > status_code) ||
8285 (999 < status_code) )
8286 {
8287#ifdef HAVE_MESSAGES
8288 MHD_DLOG (daemon,
8289 _ ("Refused wrong status code (%u). " \
8290 "HTTP requires three digits status code!\n"),
8291 status_code);
8292#endif
8293 return MHD_NO;
8294 }
8295 if (200 > status_code)
8296 {
8297 if (MHD_HTTP_VER_1_0 == connection->rq.http_ver)
8298 {
8299#ifdef HAVE_MESSAGES
8300 MHD_DLOG (daemon,
8301 _ ("Wrong status code (%u) refused. " \
8302 "HTTP/1.0 clients do not support 1xx status codes!\n"),
8303 (status_code));
8304#endif
8305 return MHD_NO;
8306 }
8307 if (0 != (response->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
8309 {
8310#ifdef HAVE_MESSAGES
8311 MHD_DLOG (daemon,
8312 _ ("Wrong status code (%u) refused. " \
8313 "HTTP/1.0 reply mode does not support 1xx status codes!\n"),
8314 (status_code));
8315#endif
8316 return MHD_NO;
8317 }
8318 }
8319 if ( (MHD_HTTP_MTHD_CONNECT == connection->rq.http_mthd) &&
8320 (2 == status_code / 100) )
8321 {
8322#ifdef HAVE_MESSAGES
8323 MHD_DLOG (daemon,
8324 _ ("Successful (%u) response code cannot be used to answer " \
8325 "\"CONNECT\" request!\n"),
8326 (status_code));
8327#endif
8328 return MHD_NO;
8329 }
8330
8331 if ( (0 != (MHD_RF_HEAD_ONLY_RESPONSE & response->flags)) &&
8332 (RP_BODY_HEADERS_ONLY < is_reply_body_needed (connection, status_code)) )
8333 {
8334#ifdef HAVE_MESSAGES
8335 MHD_DLOG (daemon,
8336 _ ("HEAD-only response cannot be used when the request requires "
8337 "reply body to be sent!\n"));
8338#endif
8339 return MHD_NO;
8340 }
8341
8342#ifdef HAVE_MESSAGES
8343 if ( (0 != (MHD_RF_INSANITY_HEADER_CONTENT_LENGTH & response->flags)) &&
8344 (0 != (MHD_RAF_HAS_CONTENT_LENGTH & response->flags_auto)) )
8345 {
8346 MHD_DLOG (daemon,
8347 _ ("The response has application-defined \"Content-Length\" " \
8348 "header. The reply to the request will be not " \
8349 "HTTP-compliant and may result in hung connection or " \
8350 "other problems!\n"));
8351 }
8352#endif
8353
8354 MHD_increment_response_rc (response);
8355 connection->rp.response = response;
8356 connection->rp.responseCode = status_code;
8357 connection->rp.responseIcy = reply_icy;
8358#if defined(_MHD_HAVE_SENDFILE)
8359 if ( (response->fd == -1) ||
8360 (response->is_pipe) ||
8361 (0 != (connection->daemon->options & MHD_USE_TLS))
8362#if defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED) && \
8363 defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE)
8364 || (! daemon->sigpipe_blocked && ! connection->sk_spipe_suppress)
8365#endif /* MHD_SEND_SPIPE_SUPPRESS_NEEDED &&
8366 MHD_SEND_SPIPE_SUPPRESS_POSSIBLE */
8367 )
8368 connection->rp.resp_sender = MHD_resp_sender_std;
8369 else
8370 connection->rp.resp_sender = MHD_resp_sender_sendfile;
8371#endif /* _MHD_HAVE_SENDFILE */
8372 /* FIXME: if 'is_pipe' is set, TLS is off, and we have *splice*, we could use splice()
8373 to avoid two user-space copies... */
8374
8375 if ( (MHD_HTTP_MTHD_HEAD == connection->rq.http_mthd) ||
8376 (MHD_HTTP_OK > status_code) ||
8377 (MHD_HTTP_NO_CONTENT == status_code) ||
8378 (MHD_HTTP_NOT_MODIFIED == status_code) )
8379 {
8380 /* if this is a "HEAD" request, or a status code for
8381 which a body is not allowed, pretend that we
8382 have already sent the full message body. */
8383 /* TODO: remove the next assignment, use 'rp_props.send_reply_body' in
8384 * checks */
8385 connection->rp.rsp_write_position = response->total_size;
8386 }
8387 if (MHD_CONNECTION_HEADERS_PROCESSED == connection->state)
8388 {
8389 /* response was queued "early", refuse to read body / footers or
8390 further requests! */
8391 connection->discard_request = true;
8392 connection->state = MHD_CONNECTION_START_REPLY;
8393 connection->rq.remaining_upload_size = 0;
8394 }
8395 if (! connection->in_idle)
8396 (void) MHD_connection_handle_idle (connection);
8397 MHD_update_last_activity_ (connection);
8398 return MHD_YES;
8399}
8400
8401
8402/* 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:2289
_MHD_EXTERN struct MHD_Response * MHD_create_response_from_buffer_static(size_t size, const void *buffer)
Definition response.c:1507
_MHD_EXTERN enum MHD_Result MHD_set_connection_option(struct MHD_Connection *connection, enum MHD_CONNECTION_OPTION option,...)
#define MHD_ICY_FLAG
Definition microhttpd.h:556
_MHD_EXTERN const union MHD_ConnectionInfo * MHD_get_connection_info(struct MHD_Connection *connection, enum MHD_ConnectionInfoType info_type,...)
#define MHD_HTTP_VERSION_1_0
#define MHD_HTTP_VERSION_1_1
enum MHD_Result MHD_parse_arguments_(struct MHD_Connection *connection, enum MHD_ValueKind kind, char *args, MHD_ArgumentIterator_ cb, void *cls)
Definition internal.c:169
MHD internal shared structures.
MHD_CONNECTION_STATE
Definition internal.h:613
@ MHD_CONNECTION_BODY_RECEIVED
Definition internal.h:663
@ MHD_CONNECTION_CHUNKED_BODY_SENT
Definition internal.h:725
@ MHD_CONNECTION_REQ_HEADERS_RECEIVING
Definition internal.h:636
@ MHD_CONNECTION_BODY_RECEIVING
Definition internal.h:656
@ MHD_CONNECTION_HEADERS_SENDING
Definition internal.h:694
@ MHD_CONNECTION_FOOTERS_SENDING
Definition internal.h:730
@ MHD_CONNECTION_FOOTERS_RECEIVED
Definition internal.h:675
@ MHD_CONNECTION_FULL_REPLY_SENT
Definition internal.h:736
@ MHD_CONNECTION_HEADERS_SENT
Definition internal.h:699
@ MHD_CONNECTION_HEADERS_PROCESSED
Definition internal.h:646
@ MHD_CONNECTION_INIT
Definition internal.h:618
@ MHD_CONNECTION_CLOSED
Definition internal.h:741
@ MHD_CONNECTION_REQ_LINE_RECEIVED
Definition internal.h:631
@ MHD_CONNECTION_NORMAL_BODY_UNREADY
Definition internal.h:705
@ MHD_CONNECTION_HEADERS_RECEIVED
Definition internal.h:641
@ MHD_CONNECTION_NORMAL_BODY_READY
Definition internal.h:710
@ MHD_CONNECTION_START_REPLY
Definition internal.h:688
@ MHD_CONNECTION_FOOTERS_RECEIVING
Definition internal.h:668
@ MHD_CONNECTION_CHUNKED_BODY_READY
Definition internal.h:720
@ MHD_CONNECTION_FULL_REQ_RECEIVED
Definition internal.h:681
@ MHD_CONNECTION_CHUNKED_BODY_UNREADY
Definition internal.h:715
@ MHD_CONNECTION_CONTINUE_SENDING
Definition internal.h:651
@ MHD_CONNECTION_REQ_LINE_RECEIVING
Definition internal.h:624
#define XDLL_insert(head, tail, element)
Definition internal.h:2718
MHD_EpollState
Definition internal.h:169
@ MHD_EPOLL_STATE_SUSPENDED
Definition internal.h:202
@ MHD_EPOLL_STATE_IN_EREADY_EDLL
Definition internal.h:192
@ MHD_EPOLL_STATE_READ_READY
Definition internal.h:181
@ MHD_EPOLL_STATE_IN_EPOLL_SET
Definition internal.h:197
@ MHD_EPOLL_STATE_WRITE_READY
Definition internal.h:187
@ MHD_TLS_CONN_TLS_CLOSING
Definition internal.h:766
@ MHD_TLS_CONN_WR_CLOSING
Definition internal.h:764
@ MHD_TLS_CONN_INVALID_STATE
Definition internal.h:769
@ MHD_TLS_CONN_WR_CLOSED
Definition internal.h:765
@ MHD_TLS_CONN_NO_TLS
Definition internal.h:760
@ MHD_TLS_CONN_INIT
Definition internal.h:761
@ MHD_TLS_CONN_TLS_CLOSED
Definition internal.h:767
@ MHD_TLS_CONN_TLS_FAILED
Definition internal.h:768
@ MHD_TLS_CONN_CONNECTED
Definition internal.h:763
@ MHD_TLS_CONN_HANDSHAKING
Definition internal.h:762
#define DLL_insert(head, tail, element)
Definition internal.h:2671
@ MHD_EVENT_LOOP_INFO_PROCESS_READ
Definition internal.h:235
@ MHD_EVENT_LOOP_INFO_PROCESS
Definition internal.h:229
@ MHD_EVENT_LOOP_INFO_READ
Definition internal.h:219
@ MHD_EVENT_LOOP_INFO_WRITE
Definition internal.h:224
@ MHD_EVENT_LOOP_INFO_CLEANUP
Definition internal.h:241
#define EDLL_insert(head, tail, element)
Definition internal.h:2765
struct MHD_IoVec MHD_iovec_
Definition internal.h:440
#define MHD_MIN(a, b)
Definition internal.h:130
#define MHD_IS_HTTP_VER_SUPPORTED(ver)
Definition internal.h:881
_MHD_static_inline struct MHD_Daemon * MHD_get_master(struct MHD_Daemon *const daemon)
Definition internal.h:2913
@ MHD_RAF_HAS_DATE_HDR
Definition internal.h:411
@ MHD_RAF_HAS_CONTENT_LENGTH
Definition internal.h:410
@ MHD_RAF_HAS_CONNECTION_CLOSE
Definition internal.h:408
@ MHD_RAF_HAS_TRANS_ENC_CHUNKED
Definition internal.h:409
@ MHD_RAF_HAS_CONNECTION_HDR
Definition internal.h:407
@ MHD_HTTP_VER_1_0
Definition internal.h:860
@ MHD_HTTP_VER_1_1
Definition internal.h:865
@ MHD_HTTP_VER_TOO_OLD
Definition internal.h:855
@ MHD_HTTP_VER_INVALID
Definition internal.h:845
@ MHD_HTTP_VER_UNKNOWN
Definition internal.h:850
@ MHD_HTTP_VER_1_2__1_9
Definition internal.h:870
@ MHD_HTTP_VER_FUTURE
Definition internal.h:875
MHD_ConnKeepAlive
Definition internal.h:818
@ MHD_CONN_USE_KEEPALIVE
Definition internal.h:832
@ MHD_CONN_MUST_UPGRADE
Definition internal.h:837
@ MHD_CONN_MUST_CLOSE
Definition internal.h:822
@ MHD_CONN_KEEPALIVE_UNKOWN
Definition internal.h:827
#define MHD_IS_HTTP_VER_1_1_COMPAT(ver)
Definition internal.h:890
#define MHD_BUF_INC_SIZE
Definition internal.h:142
@ MHD_HTTP_MTHD_GET
Definition internal.h:907
@ MHD_HTTP_MTHD_CONNECT
Definition internal.h:927
@ MHD_HTTP_MTHD_DELETE
Definition internal.h:923
@ MHD_HTTP_MTHD_OPTIONS
Definition internal.h:931
@ MHD_HTTP_MTHD_TRACE
Definition internal.h:935
@ MHD_HTTP_MTHD_HEAD
Definition internal.h:911
@ MHD_HTTP_MTHD_POST
Definition internal.h:915
@ MHD_HTTP_MTHD_OTHER
Definition internal.h:939
@ MHD_HTTP_MTHD_NO_METHOD
Definition internal.h:903
@ MHD_HTTP_MTHD_PUT
Definition internal.h:919
#define EDLL_remove(head, tail, element)
Definition internal.h:2785
#define PRIu64
Definition internal.h:53
#define XDLL_remove(head, tail, element)
Definition internal.h:2740
#define MHD_D_IS_USING_THREAD_PER_CONN_(d)
Definition internal.h:2591
#define DLL_remove(head, tail, element)
Definition internal.h:2693
#define MHD_D_IS_USING_THREADS_(d)
Definition internal.h:2587
#define MHD_D_IS_USING_EPOLL_(d)
Definition internal.h:2563
void * MHD_pool_reallocate(struct MemoryPool *pool, void *old, size_t old_size, size_t new_size)
Definition memorypool.c:533
void MHD_pool_destroy(struct MemoryPool *pool)
Definition memorypool.c:341
bool MHD_pool_is_resizable_inplace(struct MemoryPool *pool, void *block, size_t block_size)
Definition memorypool.c:440
void MHD_pool_deallocate(struct MemoryPool *pool, void *block, size_t block_size)
Definition memorypool.c:625
void * MHD_pool_try_alloc(struct MemoryPool *pool, size_t size, size_t *required_bytes)
Definition memorypool.c:481
size_t MHD_pool_get_free(struct MemoryPool *pool)
Definition memorypool.c:374
void * MHD_pool_reset(struct MemoryPool *pool, void *keep, size_t copy_bytes, size_t new_size)
Definition memorypool.c:729
void * MHD_pool_allocate(struct MemoryPool *pool, size_t size, bool from_end)
Definition memorypool.c:399
memory pool; mostly used for efficient (de)allocation for each connection and bounding memory use for...
macros for mhd_assert()
#define mhd_assert(ignore)
Definition mhd_assert.h:45
Header for platform missing functions.
Header for platform-independent inter-thread communication.
#define MHD_PANIC(msg)
Definition mhd_itc.h:45
limits values definitions
#define SSIZE_MAX
Definition mhd_limits.h:121
#define UINT64_MAX
Definition mhd_limits.h:93
#define SIZE_MAX
Definition mhd_limits.h:111
#define UINT_MAX
Definition mhd_limits.h:53
Header for platform-independent locks abstraction.
#define MHD_mutex_unlock_chk_(ignore)
Definition mhd_locks.h:198
#define MHD_mutex_lock_chk_(ignore)
Definition mhd_locks.h:196
#define NULL
uint64_t MHD_monotonic_msec_counter(void)
internal monotonic clock functions implementations
#define _(String)
Definition mhd_options.h:42
#define _MHD_EXTERN
Definition mhd_options.h:53
#define MHD_FUNC_
ssize_t MHD_send_hdr_and_body_(struct MHD_Connection *connection, const char *header, size_t header_size, bool never_push_hdr, const char *body, size_t body_size, bool complete_response)
Definition mhd_send.c:905
ssize_t MHD_send_iovec_(struct MHD_Connection *connection, struct MHD_iovec_track_ *const r_iov, bool push_data)
Definition mhd_send.c:1622
ssize_t MHD_send_data_(struct MHD_Connection *connection, const char *buffer, size_t buffer_size, bool push_data)
Definition mhd_send.c:753
Declarations of send() wrappers.
#define MHD_SCKT_ERR_IS_(err, code)
#define MHD_SCKT_ERR_IS_EAGAIN_(err)
#define MHD_SCKT_ERR_IS_LOW_RESOURCES_(err)
#define MHD_socket_last_strerr_()
#define MHD_SCKT_EOPNOTSUPP_
#define MHD_SCKT_EBADF_
#define MHD_socket_get_error_()
#define MHD_SCKT_ERR_IS_REMOTE_DISCNN_(err)
#define MHD_SCKT_EINVAL_
#define MHD_SCKT_ERR_IS_EINTR_(err)
#define MHD_SCKT_SEND_MAX_SIZE_
#define MHD_SCKT_ENOTCONN_
#define MHD_recv_(s, b, l)
#define MHD_SEND_SPIPE_SUPPRESS_NEEDED
int MHD_str_equal_caseless_(const char *str1, const char *str2)
Definition mhd_str.c:683
size_t MHD_uint8_to_str_pad(uint8_t val, uint8_t min_digits, char *buf, size_t buf_size)
Definition mhd_str.c:1629
size_t MHD_uint16_to_str(uint16_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1550
size_t MHD_str_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition mhd_str.c:1238
size_t MHD_uint64_to_str(uint64_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1591
size_t MHD_strx_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition mhd_str.c:1415
int MHD_str_equal_caseless_n_(const char *const str1, const char *const str2, size_t maxlen)
Definition mhd_str.c:717
bool MHD_str_has_token_caseless_(const char *str, const char *const token, size_t token_len)
Definition mhd_str.c:782
bool MHD_str_equal_caseless_bin_n_(const char *const str1, const char *const str2, size_t len)
Definition mhd_str.c:749
size_t MHD_uint32_to_strx(uint32_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1516
Header for string manipulating helpers.
#define MHD_str_has_s_token_caseless_(str, tkn)
Definition mhd_str.h:154
#define MHD_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:2335
Methods for managing response objects.
enum MHD_Result MHD_response_execute_upgrade_(struct MHD_Response *response, struct MHD_Connection *connection)
MHD_socket socket_fd
Definition internal.h:1489
size_t write_buffer_size
Definition internal.h:1447
size_t write_buffer_send_offset
Definition internal.h:1452
socklen_t addr_len
Definition internal.h:1469
enum MHD_ConnectionEventLoopInfo event_loop_info
Definition internal.h:1576
size_t write_buffer_append_offset
Definition internal.h:1458
char * write_buffer
Definition internal.h:1415
bool stop_with_error
Definition internal.h:1532
bool discard_request
Definition internal.h:1541
ReceiveCallback recv_cls
Definition internal.h:1581
volatile bool resuming
Definition internal.h:1627
void * socket_context
Definition internal.h:1395
uint64_t last_activity
Definition internal.h:1475
enum MHD_ConnKeepAlive keepalive
Definition internal.h:1402
struct MHD_Request rq
Definition internal.h:1371
union MHD_ConnectionInfo connection_info_dummy
Definition internal.h:1632
size_t continue_message_write_offset
Definition internal.h:1464
size_t read_buffer_offset
Definition internal.h:1442
struct MHD_Reply rp
Definition internal.h:1376
struct MemoryPool * pool
Definition internal.h:1386
enum MHD_CONNECTION_STATE state
Definition internal.h:1571
char * read_buffer
Definition internal.h:1409
struct MHD_Daemon * daemon
Definition internal.h:1366
bool in_access_handler
Definition internal.h:1622
bool sk_spipe_suppress
Definition internal.h:1505
uint64_t connection_timeout_ms
Definition internal.h:1482
struct sockaddr_storage * addr
Definition internal.h:1421
size_t read_buffer_size
Definition internal.h:1436
size_t pool_size
Definition internal.h:2142
MHD_AccessHandlerCallback default_handler
Definition internal.h:1873
LogCallback uri_log_callback
Definition internal.h:2063
struct MHD_Connection * normal_timeout_tail
Definition internal.h:2006
int client_discipline
Definition internal.h:2271
void * unescape_callback_cls
Definition internal.h:2078
struct MHD_Connection * connections_tail
Definition internal.h:1906
struct MHD_Connection * suspended_connections_tail
Definition internal.h:1916
struct MHD_Connection * manual_timeout_head
Definition internal.h:2014
struct MHD_Connection * normal_timeout_head
Definition internal.h:1999
MHD_RequestCompletedCallback notify_completed
Definition internal.h:2038
struct MHD_Connection * cleanup_head
Definition internal.h:1921
struct MHD_itc_ itc
Definition internal.h:2204
uint64_t connection_timeout_ms
Definition internal.h:2259
struct MHD_Connection * manual_timeout_tail
Definition internal.h:2021
volatile bool shutdown
Definition internal.h:2209
enum MHD_FLAG options
Definition internal.h:1886
struct MHD_Connection * cleanup_tail
Definition internal.h:1926
bool sigpipe_blocked
Definition internal.h:2299
UnescapeCallback unescape_callback
Definition internal.h:2073
void * notify_completed_cls
Definition internal.h:2043
struct MHD_Connection * suspended_connections_head
Definition internal.h:1911
int allow_bzero_in_url
Definition internal.h:2278
void * default_handler_cls
Definition internal.h:1878
struct MHD_Connection * connections_head
Definition internal.h:1901
size_t pool_increment
Definition internal.h:2147
void * uri_log_callback_cls
Definition internal.h:2068
enum MHD_ValueKind kind
Definition internal.h:396
const char * value
Definition internal.h:386
struct MHD_HTTP_Req_Header * next
Definition internal.h:366
const char * header
Definition internal.h:376
struct MHD_HTTP_Res_Header * next
Definition internal.h:323
enum MHD_ValueKind kind
Definition internal.h:353
uint64_t rsp_write_position
Definition internal.h:1301
bool responseIcy
Definition internal.h:1294
struct MHD_iovec_track_ resp_iov
Definition internal.h:1309
struct MHD_Response * response
Definition internal.h:1282
unsigned int responseCode
Definition internal.h:1288
struct MHD_Reply_Properties props
Definition internal.h:1318
unsigned int skipped_empty_lines
Definition internal.h:955
struct MHD_HTTP_Req_Header * headers_received
Definition internal.h:1117
union MHD_HeadersProcessing hdrs
Definition internal.h:1247
uint64_t current_chunk_size
Definition internal.h:1167
uint64_t current_chunk_offset
Definition internal.h:1173
bool some_payload_processed
Definition internal.h:1190
size_t skipped_broken_lines
Definition internal.h:1242
void * client_context
Definition internal.h:1198
const char * url
Definition internal.h:1096
size_t url_len
Definition internal.h:1101
const char * version
Definition internal.h:1075
size_t num_cr_sp_replaced
Definition internal.h:1237
enum MHD_HTTP_Version http_ver
Definition internal.h:1080
size_t req_target_len
Definition internal.h:1106
const char * method
Definition internal.h:1085
size_t header_size
Definition internal.h:1130
union MHD_StartOrSize field_lines
Definition internal.h:1140
struct MHD_HTTP_Req_Header * headers_received_tail
Definition internal.h:1122
enum MHD_HTTP_Method http_mthd
Definition internal.h:1090
bool client_aware
Definition internal.h:1205
bool have_chunked_upload
Definition internal.h:1158
const char * url_for_callback
Definition internal.h:1112
uint64_t remaining_upload_size
Definition internal.h:1146
size_t data_buffer_size
Definition internal.h:557
MHD_iovec_ * data_iov
Definition internal.h:588
const char * data
Definition internal.h:489
uint64_t data_start
Definition internal.h:541
MHD_ContentReaderCallback crc
Definition internal.h:501
enum MHD_ResponseAutoFlags flags_auto
Definition internal.h:578
unsigned int data_iovcnt
Definition internal.h:593
size_t data_size
Definition internal.h:552
enum MHD_ResponseFlags flags
Definition internal.h:573
void * crc_cls
Definition internal.h:495
struct MHD_HTTP_Res_Header * first_header
Definition internal.h:478
uint64_t total_size
Definition internal.h:535
MHD_iovec_ * iov
Definition internal.h:453
const char * str
unsigned int connection_timeout
struct MHD_Daemon * daemon
unsigned int http_status
struct sockaddr * client_addr
MHD_socket connect_fd
struct MHD_RequestLineProcessing rq_line
Definition internal.h:1036
struct MHD_HeaderProcessing hdr
Definition internal.h:1041
const char * start
Definition internal.h:1055