GNU libmicrohttpd 1.0.9
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#include "mhd_check.h"
59
66#define MHD_ALLOW_BARE_LF_AS_CRLF_(discp_lvl) (0 >= discp_lvl)
67
76#define MHD_CHUNK_HEADER_REASONABLE_LEN 24
77
81#define HTTP_100_CONTINUE "HTTP/1.1 100 Continue\r\n\r\n"
82
87#ifdef HAVE_MESSAGES
88#define ERR_MSG_REQUEST_TOO_BIG \
89 "<html>" \
90 "<head><title>Request too big</title></head>" \
91 "<body>Request HTTP header is too big for the memory constraints " \
92 "of this webserver.</body>" \
93 "</html>"
94#else
95#define ERR_MSG_REQUEST_TOO_BIG ""
96#endif
97
101#ifdef HAVE_MESSAGES
102#define ERR_MSG_REQUEST_HEADER_TOO_BIG \
103 "<html>" \
104 "<head><title>Request too big</title></head>" \
105 "<body><p>The total size of the request headers, which includes the " \
106 "request target and the request field lines, exceeds the memory " \
107 "constraints of this web server.</p>" \
108 "<p>The request could be re-tried with shorter field lines, a shorter " \
109 "request target or a shorter request method token.</p></body>" \
110 "</html>"
111#else
112#define ERR_MSG_REQUEST_HEADER_TOO_BIG ""
113#endif
114
118#ifdef HAVE_MESSAGES
119#define ERR_MSG_REQUEST_HEADER_WITH_COOKIES_TOO_BIG \
120 "<html>" \
121 "<head><title>Request too big</title></head>" \
122 "<body><p>The total size of the request headers, which includes the " \
123 "request target and the request field lines, exceeds the memory " \
124 "constraints of this web server.</p> " \
125 "<p>The request could be re-tried with smaller " \
126 "<b>&quot;Cookie:&quot;</b> field value, shorter other field lines, " \
127 "a shorter request target or a shorter request method token.</p></body> " \
128 "</html>"
129#else
130#define ERR_MSG_REQUEST_HEADER_WITH_COOKIES_TOO_BIG ""
131#endif
132
137#ifdef HAVE_MESSAGES
138#define ERR_MSG_REQUEST_CHUNK_LINE_EXT_TOO_BIG \
139 "<html>" \
140 "<head><title>Request too big</title></head>" \
141 "<body><p>The total size of the request target, the request field lines " \
142 "and the chunk size line exceeds the memory constraints of this web " \
143 "server.</p>" \
144 "<p>The request could be re-tried without chunk extensions, with a smaller " \
145 "chunk size, shorter field lines, a shorter request target or a shorter " \
146 "request method token.</p></body>" \
147 "</html>"
148#else
149#define ERR_MSG_REQUEST_CHUNK_LINE_EXT_TOO_BIG ""
150#endif
151
156#ifdef HAVE_MESSAGES
157#define ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG \
158 "<html>" \
159 "<head><title>Request too big</title></head>" \
160 "<body><p>The total size of the request target, the request field lines " \
161 "and the chunk size line exceeds the memory constraints of this web " \
162 "server.</p>" \
163 "<p>The request could be re-tried with a smaller " \
164 "chunk size, shorter field lines, a shorter request target or a shorter " \
165 "request method token.</p></body>" \
166 "</html>"
167#else
168#define ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG ""
169#endif
170
174#ifdef HAVE_MESSAGES
175#define ERR_MSG_REQUEST_FOOTER_TOO_BIG \
176 "<html>" \
177 "<head><title>Request too big</title></head>" \
178 "<body><p>The total size of the request headers, which includes the " \
179 "request target, the request field lines and the chunked trailer " \
180 "section exceeds the memory constraints of this web server.</p>" \
181 "<p>The request could be re-tried with a shorter chunked trailer " \
182 "section, shorter field lines, a shorter request target or " \
183 "a shorter request method token.</p></body>" \
184 "</html>"
185#else
186#define ERR_MSG_REQUEST_FOOTER_TOO_BIG ""
187#endif
188
192#ifdef HAVE_MESSAGES
193#define RQ_LINE_TOO_MANY_WSP \
194 "<html>" \
195 "<head><title>Request broken</title></head>" \
196 "<body>The request line has more then two whitespaces.</body>" \
197 "</html>"
198#else
199#define RQ_LINE_TOO_MANY_WSP ""
200#endif
201
206#ifdef HAVE_MESSAGES
207#define BARE_CR_IN_HEADER \
208 "<html>" \
209 "<head><title>Request broken</title></head>" \
210 "<body>Request HTTP header has bare CR character without " \
211 "following LF character.</body>" \
212 "</html>"
213#else
214#define BARE_CR_IN_HEADER ""
215#endif
216
221#ifdef HAVE_MESSAGES
222#define BARE_CR_IN_FOOTER \
223 "<html>" \
224 "<head><title>Request broken</title></head>" \
225 "<body>Request HTTP footer has bare CR character without " \
226 "following LF character.</body>" \
227 "</html>"
228#else
229#define BARE_CR_IN_FOOTER ""
230#endif
231
236#ifdef HAVE_MESSAGES
237#define BARE_LF_IN_HEADER \
238 "<html>" \
239 "<head><title>Request broken</title></head>" \
240 "<body>Request HTTP header has bare LF character without " \
241 "preceding CR character.</body>" \
242 "</html>"
243#else
244#define BARE_LF_IN_HEADER ""
245#endif
246
251#ifdef HAVE_MESSAGES
252#define BARE_LF_IN_FOOTER \
253 "<html>" \
254 "<head><title>Request broken</title></head>" \
255 "<body>Request HTTP footer has bare LF character without " \
256 "preceding CR character.</body>" \
257 "</html>"
258#else
259#define BARE_LF_IN_FOOTER ""
260#endif
261
265#ifdef HAVE_MESSAGES
266#define RQ_TARGET_INVALID_CHAR \
267 "<html>" \
268 "<head><title>Request broken</title></head>" \
269 "<body>HTTP request has invalid characters in " \
270 "the request-target.</body>" \
271 "</html>"
272#else
273#define RQ_TARGET_INVALID_CHAR ""
274#endif
275
279#ifdef HAVE_MESSAGES
280#define ERR_RSP_OBS_FOLD \
281 "<html>" \
282 "<head><title>Request broken</title></head>" \
283 "<body>Obsolete line folding is used in HTTP request header.</body>" \
284 "</html>"
285#else
286#define ERR_RSP_OBS_FOLD ""
287#endif
288
292#ifdef HAVE_MESSAGES
293#define ERR_RSP_OBS_FOLD_FOOTER \
294 "<html>" \
295 "<head><title>Request broken</title></head>" \
296 "<body>Obsolete line folding is used in HTTP request footer.</body>" \
297 "</html>"
298#else
299#define ERR_RSP_OBS_FOLD_FOOTER ""
300#endif
301
306#ifdef HAVE_MESSAGES
307#define ERR_RSP_WSP_BEFORE_HEADER \
308 "<html>" \
309 "<head><title>Request broken</title></head>" \
310 "<body>HTTP request has whitespace between the request line and " \
311 "the first header.</body>" \
312 "</html>"
313#else
314#define ERR_RSP_WSP_BEFORE_HEADER ""
315#endif
316
321#ifdef HAVE_MESSAGES
322#define ERR_RSP_WSP_BEFORE_FOOTER \
323 "<html>" \
324 "<head><title>Request broken</title></head>" \
325 "<body>First HTTP footer line has whitespace at the first " \
326 "position.</body>" \
327 "</html>"
328#else
329#define ERR_RSP_WSP_BEFORE_FOOTER ""
330#endif
331
336#ifdef HAVE_MESSAGES
337#define ERR_RSP_WSP_IN_HEADER_NAME \
338 "<html>" \
339 "<head><title>Request broken</title></head>" \
340 "<body>HTTP request has whitespace before the first colon " \
341 "in header line.</body>" \
342 "</html>"
343#else
344#define ERR_RSP_WSP_IN_HEADER_NAME ""
345#endif
346
351#ifdef HAVE_MESSAGES
352#define ERR_RSP_WSP_IN_FOOTER_NAME \
353 "<html>" \
354 "<head><title>Request broken</title></head>" \
355 "<body>HTTP request has whitespace before the first colon " \
356 "in footer line.</body>" \
357 "</html>"
358#else
359#define ERR_RSP_WSP_IN_FOOTER_NAME ""
360#endif
361
362
367#ifdef HAVE_MESSAGES
368#define ERR_RSP_INVALID_CHAR_IN_FIELD_NAME \
369 "<html>" \
370 "<head><title>Request broken</title></head>" \
371 "<body>HTTP request has invalid character in field name.</body>" \
372 "</html>"
373#else
374#define ERR_RSP_INVALID_CHAR_IN_FIELD_NAME ""
375#endif
376
380#ifdef HAVE_MESSAGES
381#define ERR_RSP_INVALID_CHR_IN_HEADER \
382 "<html>" \
383 "<head><title>Request broken</title></head>" \
384 "<body>HTTP request has invalid character in header.</body>" \
385 "</html>"
386#else
387#define ERR_RSP_INVALID_CHR_IN_HEADER ""
388#endif
389
393#ifdef HAVE_MESSAGES
394#define ERR_RSP_INVALID_CHR_IN_FOOTER \
395 "<html>" \
396 "<head><title>Request broken</title></head>" \
397 "<body>HTTP request has invalid character in footer.</body>" \
398 "</html>"
399#else
400#define ERR_RSP_INVALID_CHR_IN_FOOTER ""
401#endif
402
406#ifdef HAVE_MESSAGES
407#define ERR_RSP_HEADER_WITHOUT_COLON \
408 "<html>" \
409 "<head><title>Request broken</title></head>" \
410 "<body>HTTP request header line has no colon character.</body>" \
411 "</html>"
412#else
413#define ERR_RSP_HEADER_WITHOUT_COLON ""
414#endif
415
419#ifdef HAVE_MESSAGES
420#define ERR_RSP_FOOTER_WITHOUT_COLON \
421 "<html>" \
422 "<head><title>Request broken</title></head>" \
423 "<body>HTTP request footer line has no colon character.</body>" \
424 "</html>"
425#else
426#define ERR_RSP_FOOTER_WITHOUT_COLON ""
427#endif
428
432#ifdef HAVE_MESSAGES
433#define ERR_RSP_EMPTY_HEADER_NAME \
434 "<html>" \
435 "<head><title>Request broken</title></head>" \
436 "<body>HTTP request header has empty header name.</body>" \
437 "</html>"
438#else
439#define ERR_RSP_EMPTY_HEADER_NAME ""
440#endif
441
445#ifdef HAVE_MESSAGES
446#define ERR_RSP_EMPTY_FOOTER_NAME \
447 "<html>" \
448 "<head><title>Request broken</title></head>" \
449 "<body>HTTP request footer has empty footer name.</body>" \
450 "</html>"
451#else
452#define ERR_RSP_EMPTY_FOOTER_NAME ""
453#endif
454
462#ifdef HAVE_MESSAGES
463#define REQUEST_LACKS_HOST \
464 "<html>" \
465 "<head><title>&quot;Host:&quot; header required</title></head>" \
466 "<body>HTTP/1.1 request without <b>&quot;Host:&quot;</b>.</body>" \
467 "</html>"
468
469#else
470#define REQUEST_LACKS_HOST ""
471#endif
472
476#define REQUEST_MULTIPLE_HOST_HDR \
477 "<html>" \
478 "<head><title>Multiple &quot;Host:&quot; headers</title></head>" \
479 "<body>Request contains several <b>&quot;Host:&quot;</b> headers." \
480 "</body></html>"
481
486#ifdef HAVE_MESSAGES
487#define REQUEST_AMBIGUOUS_CONTENT_LENGTH \
488 "<html>" \
489 "<head><title>&quot;Content-Length:&quot; header must be unique</title></head>" \
490 "<body>HTTP/1.1 request with multiple <b>&quot;Content-Length:&quot;</b> headers.</body>" \
491 "</html>"
492
493#else
494#define REQUEST_AMBIGUOUS_CONTENT_LENGTH ""
495#endif
496
500#ifdef HAVE_MESSAGES
501#define REQUEST_UNSUPPORTED_TR_ENCODING \
502 "<html>" \
503 "<head><title>Unsupported Transfer-Encoding</title></head>" \
504 "<body>The Transfer-Encoding used in request is not supported.</body>" \
505 "</html>"
506#else
507#define REQUEST_UNSUPPORTED_TR_ENCODING ""
508#endif
509
514#ifdef HAVE_MESSAGES
515#define REQUEST_LENGTH_WITH_TR_ENCODING \
516 "<html>" \
517 "<head><title>Malformed request</title></head>" \
518 "<body>Wrong combination of the request headers: both Transfer-Encoding " \
519 "and Content-Length headers are used at the same time.</body>" \
520 "</html>"
521#else
522#define REQUEST_LENGTH_WITH_TR_ENCODING ""
523#endif
524
528#define REQUEST_HTTP1_0_TR_ENCODING \
529 "<html><head><title>Malformed request</title></head>" \
530 "<body><b>&quot;Transfer-Encoding:&quot;</b> must not be used " \
531 "with HTTP/1.0.</body></html>"
532
540#ifdef HAVE_MESSAGES
541#define REQUEST_MALFORMED \
542 "<html><head><title>Request malformed</title></head>" \
543 "<body>HTTP request is syntactically incorrect.</body></html>"
544#else
545#define REQUEST_MALFORMED ""
546#endif
547
551#define REQUEST_HAS_NUL_CHAR_IN_PATH \
552 "<html><head><title>Bad Request Path</title></head>" \
553 "<body>The request path contains invalid characters.</body></html>"
554
559#ifdef HAVE_MESSAGES
560#define REQUEST_CHUNKED_MALFORMED \
561 "<html><head><title>Request malformed</title></head>" \
562 "<body>HTTP chunked encoding is syntactically incorrect.</body></html>"
563#else
564#define REQUEST_CHUNKED_MALFORMED ""
565#endif
566
570#ifdef HAVE_MESSAGES
571#define REQUEST_CHUNK_TOO_LARGE \
572 "<html><head><title>Request content too large</title></head>" \
573 "<body>The chunk size used in HTTP chunked encoded " \
574 "request is too large.</body></html>"
575#else
576#define REQUEST_CHUNK_TOO_LARGE ""
577#endif
578
582#ifdef HAVE_MESSAGES
583#define REQUEST_CONTENTLENGTH_TOOLARGE \
584 "<html><head><title>Request content too large</title></head>" \
585 "<body>HTTP request has too large value for " \
586 "<b>Content-Length</b> header.</body></html>"
587#else
588#define REQUEST_CONTENTLENGTH_TOOLARGE ""
589#endif
590
595#ifdef HAVE_MESSAGES
596#define REQUEST_CONTENTLENGTH_MALFORMED \
597 "<html><head><title>Request malformed</title></head>" \
598 "<body>HTTP request has wrong value for " \
599 "<b>Content-Length</b> header.</body></html>"
600#else
601#define REQUEST_CONTENTLENGTH_MALFORMED ""
602#endif
603
610#ifdef HAVE_MESSAGES
611#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP \
612 "<html><head><title>Internal server error</title></head>" \
613 "<body>Please ask the developer of this Web server to carefully " \
614 "read the GNU libmicrohttpd documentation about connection " \
615 "management and blocking.</body></html>"
616#else
617#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP ""
618#endif
619
623#ifdef HAVE_MESSAGES
624#define REQ_HTTP_VER_IS_TOO_OLD \
625 "<html><head><title>Requested HTTP version is not supported</title></head>" \
626 "<body>Requested HTTP version is too old and not " \
627 "supported.</body></html>"
628#else
629#define REQ_HTTP_VER_IS_TOO_OLD ""
630#endif
631
635#ifdef HAVE_MESSAGES
636#define REQ_HTTP_VER_IS_NOT_SUPPORTED \
637 "<html><head><title>Requested HTTP version is not supported</title></head>" \
638 "<body>Requested HTTP version is not supported.</body></html>"
639#else
640#define REQ_HTTP_VER_IS_NOT_SUPPORTED ""
641#endif
642
643
647#define MHD_SENFILE_CHUNK_ (0x20000)
648
652#define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000)
653
654#ifdef HAVE_MESSAGES
660static const char *
661str_conn_error_ (ssize_t mhd_err_code)
662{
663 switch (mhd_err_code)
664 {
665 case MHD_ERR_AGAIN_:
666 return _ ("The operation would block, retry later");
668 return _ ("The connection was forcibly closed by remote peer");
669 case MHD_ERR_NOTCONN_:
670 return _ ("The socket is not connected");
671 case MHD_ERR_NOMEM_:
672 return _ ("Not enough system resources to serve the request");
673 case MHD_ERR_BADF_:
674 return _ ("Bad FD value");
675 case MHD_ERR_INVAL_:
676 return _ ("Argument value is invalid");
678 return _ ("Argument value is not supported");
679 case MHD_ERR_PIPE_:
680 return _ ("The socket is no longer available for sending");
681 case MHD_ERR_TLS_:
682 return _ ("TLS encryption or decryption error");
683 default:
684 break; /* Mute compiler warning */
685 }
686 if (0 <= mhd_err_code)
687 return _ ("Not an error code");
688
689 mhd_assert (0); /* Should never be reachable */
690 return _ ("Wrong error code value");
691}
692
693
694#endif /* HAVE_MESSAGES */
695
705void *
707 size_t size)
708{
709 struct MHD_Connection *const c = connection; /* a short alias */
710 struct MemoryPool *const pool = c->pool; /* a short alias */
711 size_t need_to_be_freed = 0;
712 void *res;
713
714 res = MHD_pool_try_alloc (pool,
715 size,
716 &need_to_be_freed);
717 if (NULL != res)
718 return res;
719
721 c->write_buffer,
723 {
725 need_to_be_freed)
726 {
727 char *buf;
728 const size_t new_buf_size = c->write_buffer_size - need_to_be_freed;
729 buf = MHD_pool_reallocate (pool,
730 c->write_buffer,
732 new_buf_size);
733 mhd_assert (c->write_buffer == buf);
734 mhd_assert (c->write_buffer_append_offset <= new_buf_size);
735 mhd_assert (c->write_buffer_send_offset <= new_buf_size);
736 c->write_buffer_size = new_buf_size;
737 c->write_buffer = buf;
738 }
739 else
740 return NULL;
741 }
742 else if (MHD_pool_is_resizable_inplace (pool,
743 c->read_buffer,
745 {
746 if (c->read_buffer_size - c->read_buffer_offset >= need_to_be_freed)
747 {
748 char *buf;
749 const size_t new_buf_size = c->read_buffer_size - need_to_be_freed;
750 buf = MHD_pool_reallocate (pool,
751 c->read_buffer,
753 new_buf_size);
754 mhd_assert (c->read_buffer == buf);
755 mhd_assert (c->read_buffer_offset <= new_buf_size);
756 c->read_buffer_size = new_buf_size;
757 c->read_buffer = buf;
758 }
759 else
760 return NULL;
761 }
762 else
763 return NULL;
764 res = MHD_pool_allocate (pool, size, true);
765 mhd_assert (NULL != res); /* It has been checked that pool has enough space */
766 return res;
767}
768
769
779static ssize_t
781 void *other,
782 size_t i)
783{
784 ssize_t ret;
785
786 if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
787 (MHD_CONNECTION_CLOSED == connection->state) )
788 {
789 return MHD_ERR_NOTCONN_;
790 }
792 i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */
793
794 ret = MHD_recv_ (connection->socket_fd,
795 other,
796 i);
797 if (0 > ret)
798 {
799 const int err = MHD_socket_get_error_ ();
800 if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
801 {
802#ifdef EPOLL_SUPPORT
803 /* Got EAGAIN --- no longer read-ready */
804 connection->epoll_state &=
806#endif /* EPOLL_SUPPORT */
807 return MHD_ERR_AGAIN_;
808 }
809 if (MHD_SCKT_ERR_IS_EINTR_ (err))
810 return MHD_ERR_AGAIN_;
812 return MHD_ERR_CONNRESET_;
814 return MHD_ERR_OPNOTSUPP_;
816 return MHD_ERR_NOTCONN_;
818 return MHD_ERR_INVAL_;
820 return MHD_ERR_NOMEM_;
822 return MHD_ERR_BADF_;
823 /* Treat any other error as a hard error. */
824 return MHD_ERR_NOTCONN_;
825 }
826#ifdef EPOLL_SUPPORT
827 else if (i > (size_t) ret)
828 connection->epoll_state &=
830#endif /* EPOLL_SUPPORT */
831 return ret;
832}
833
834
837 const char **uri,
838 size_t *uri_size)
839{
840 if (NULL != uri)
841 *uri = NULL;
842 if (NULL != uri_size)
843 *uri_size = 0u;
844
845 if (connection->state < MHD_CONNECTION_REQ_LINE_RECEIVED)
846 return MHD_NO;
847 if (connection->state >= MHD_CONNECTION_START_REPLY)
848 return MHD_NO;
849 if (NULL == connection->rq.url)
850 return MHD_NO;
851
852 if (NULL != uri)
853 *uri = connection->rq.url;
854 if (NULL != uri_size)
855 *uri_size = connection->rq.url_len;
856
857 return MHD_YES;
858}
859
860
873_MHD_EXTERN int
875 enum MHD_ValueKind kind,
876 MHD_KeyValueIterator iterator,
877 void *iterator_cls)
878{
879 int ret;
880 struct MHD_HTTP_Req_Header *pos;
881
882 if (NULL == connection)
883 return -1;
884 ret = 0;
885 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
886 if (0 != (pos->kind & kind))
887 {
888 ret++;
889 if ( (NULL != iterator) &&
890 (MHD_NO == iterator (iterator_cls,
891 pos->kind,
892 pos->header,
893 pos->value)) )
894 return ret;
895 }
896 return ret;
897}
898
899
912_MHD_EXTERN int
914 enum MHD_ValueKind kind,
915 MHD_KeyValueIteratorN iterator,
916 void *iterator_cls)
917{
918 int ret;
919 struct MHD_HTTP_Req_Header *pos;
920
921 if (NULL == connection)
922 return -1;
923 ret = 0;
924
925 if (NULL == iterator)
926 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
927 {
928 if (0 != (kind & pos->kind))
929 ret++;
930 }
931 else
932 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
933 if (0 != (kind & pos->kind))
934 {
935 ret++;
936 if (MHD_NO == iterator (iterator_cls,
937 pos->kind,
938 pos->header,
939 pos->header_size,
940 pos->value,
941 pos->value_size))
942 return ret;
943 }
944 return ret;
945}
946
947
965static enum MHD_Result
967 enum MHD_ValueKind kind,
968 const char *key,
969 size_t key_size,
970 const char *value,
971 size_t value_size)
972{
973 struct MHD_HTTP_Req_Header *pos;
974
975 pos = MHD_connection_alloc_memory_ (connection,
976 sizeof (struct MHD_HTTP_Req_Header));
977 if (NULL == pos)
978 return MHD_NO;
979 pos->header = key;
980 pos->header_size = key_size;
981 pos->value = value;
982 pos->value_size = value_size;
983 pos->kind = kind;
984 pos->next = NULL;
985 pos->prev = NULL;
986 /* append 'pos' to the linked list of headers */
987 if (NULL == connection->rq.headers_received_tail)
988 {
989 mhd_assert (NULL == connection->rq.headers_received);
990 connection->rq.headers_received = pos;
991 connection->rq.headers_received_tail = pos;
992 }
993 else
994 {
995 mhd_assert (NULL != connection->rq.headers_received);
996 mhd_assert (NULL == connection->rq.headers_received_tail->next);
997 mhd_assert (pos != connection->rq.headers_received_tail);
998 mhd_assert (pos != connection->rq.headers_received);
999 connection->rq.headers_received_tail->next = pos;
1000 connection->rq.headers_received_tail = pos;
1001 }
1002 return MHD_YES;
1003}
1004
1005
1033 enum MHD_ValueKind kind,
1034 const char *key,
1035 size_t key_size,
1036 const char *value,
1037 size_t value_size)
1038{
1039 if ( (MHD_GET_ARGUMENT_KIND != kind) &&
1040 ( ((key ? strlen (key) : 0) != key_size) ||
1041 ((value ? strlen (value) : 0) != value_size) ) )
1042 return MHD_NO; /* binary zero is allowed only in GET arguments */
1043
1044 return MHD_set_connection_value_n_nocheck_ (connection,
1045 kind,
1046 key,
1047 key_size,
1048 value,
1049 value_size);
1050}
1051
1052
1080 enum MHD_ValueKind kind,
1081 const char *key,
1082 const char *value)
1083{
1084 return MHD_set_connection_value_n_nocheck_ (connection,
1085 kind,
1086 key,
1087 NULL != key
1088 ? strlen (key)
1089 : 0,
1090 value,
1091 NULL != value
1092 ? strlen (value)
1093 : 0);
1094}
1095
1096
1107_MHD_EXTERN const char *
1109 enum MHD_ValueKind kind,
1110 const char *key)
1111{
1112 const char *value;
1113
1114 value = NULL;
1115 (void) MHD_lookup_connection_value_n (connection,
1116 kind,
1117 key,
1118 (NULL == key) ? 0 : strlen (key),
1119 &value,
1120 NULL);
1121 return value;
1122}
1123
1124
1146 enum MHD_ValueKind kind,
1147 const char *key,
1148 size_t key_size,
1149 const char **value_ptr,
1150 size_t *value_size_ptr)
1151{
1152 struct MHD_HTTP_Req_Header *pos;
1153
1154 if (NULL == connection)
1155 return MHD_NO;
1156
1157 if (NULL == key)
1158 {
1159 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1160 {
1161 if ( (0 != (kind & pos->kind)) &&
1162 (NULL == pos->header) )
1163 break;
1164 }
1165 }
1166 else
1167 {
1168 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1169 {
1170 if ( (0 != (kind & pos->kind)) &&
1171 (key_size == pos->header_size) &&
1172 ( (key == pos->header) ||
1174 pos->header,
1175 key_size) ) ) )
1176 break;
1177 }
1178 }
1179
1180 if (NULL == pos)
1181 return MHD_NO;
1182
1183 if (NULL != value_ptr)
1184 *value_ptr = pos->value;
1185
1186 if (NULL != value_size_ptr)
1187 *value_size_ptr = pos->value_size;
1188
1189 return MHD_YES;
1190}
1191
1192
1208static bool
1210 const char *header,
1211 size_t header_len,
1212 const char *token,
1213 size_t token_len)
1214{
1215 struct MHD_HTTP_Req_Header *pos;
1216
1217 if ((NULL == connection) || (NULL == header) || (0 == header[0]) ||
1218 (NULL == token) || (0 == token[0]))
1219 return false;
1220
1221 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1222 {
1223 if ((0 != (pos->kind & MHD_HEADER_KIND)) &&
1224 (header_len == pos->header_size) &&
1225 ( (header == pos->header) ||
1227 pos->header,
1228 header_len)) ) &&
1229 (MHD_str_has_token_caseless_ (pos->value, token, token_len)))
1230 return true;
1231 }
1232 return false;
1233}
1234
1235
1247#define MHD_lookup_header_s_token_ci(c,h,tkn) \
1248 MHD_lookup_header_token_ci ((c),(h),MHD_STATICSTR_LEN_ (h), \
1249 (tkn),MHD_STATICSTR_LEN_ (tkn))
1250
1251
1259static bool
1261{
1262 const char *expect;
1263
1264 if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
1265 return false;
1266
1267 if (0 == connection->rq.remaining_upload_size)
1268 return false;
1269
1270 if (MHD_NO ==
1276 &expect,
1277 NULL))
1278 return false;
1279
1280 if (MHD_str_equal_caseless_ (expect,
1281 "100-continue"))
1282 return true;
1283
1284 return false;
1285}
1286
1287
1294void
1296{
1297 const struct MHD_Daemon *daemon = connection->daemon;
1298
1299 if (0 == (daemon->options & MHD_USE_TURBO))
1300 {
1301#ifdef HTTPS_SUPPORT
1302 /* For TLS connection use shutdown of TLS layer
1303 * and do not shutdown TCP socket. This give more
1304 * chances to send TLS closure data to remote side.
1305 * Closure of TLS layer will be interpreted by
1306 * remote side as end of transmission. */
1307 if (0 != (daemon->options & MHD_USE_TLS))
1308 {
1309 if (! MHD_tls_connection_shutdown (connection))
1310 shutdown (connection->socket_fd,
1311 SHUT_WR);
1312 }
1313 else /* Combined with next 'shutdown()'. */
1314#endif /* HTTPS_SUPPORT */
1315 shutdown (connection->socket_fd,
1316 SHUT_WR);
1317 }
1318 connection->state = MHD_CONNECTION_CLOSED;
1320}
1321
1322
1332void
1334 enum MHD_RequestTerminationCode termination_code)
1335{
1336 struct MHD_Daemon *daemon = connection->daemon;
1337 struct MHD_Response *resp = connection->rp.response;
1338
1339 mhd_assert (! connection->suspended);
1340#ifdef MHD_USE_THREADS
1341 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
1342 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
1343#endif /* MHD_USE_THREADS */
1344 if ( (NULL != daemon->notify_completed) &&
1345 (connection->rq.client_aware) )
1346 daemon->notify_completed (daemon->notify_completed_cls,
1347 connection,
1348 &connection->rq.client_context,
1349 termination_code);
1350 connection->rq.client_aware = false;
1351 if (NULL != resp)
1352 {
1353 connection->rp.response = NULL;
1354 MHD_destroy_response (resp);
1355 }
1356 if (NULL != connection->pool)
1357 {
1358 MHD_pool_destroy (connection->pool);
1359 connection->pool = NULL;
1360 }
1361
1362 MHD_connection_mark_closed_ (connection);
1363}
1364
1365
1366#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
1377void
1379{
1380 struct MHD_Daemon *daemon = connection->daemon;
1381 struct MHD_UpgradeResponseHandle *urh = connection->urh;
1382
1383#ifdef MHD_USE_THREADS
1384 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
1387#endif /* MHD_USE_THREADS */
1388
1389 if (0 == (daemon->options & MHD_USE_TLS))
1390 return; /* Nothing to do with non-TLS connection. */
1391
1392 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
1393 DLL_remove (daemon->urh_head,
1394 daemon->urh_tail,
1395 urh);
1396#ifdef EPOLL_SUPPORT
1397 if (MHD_D_IS_USING_EPOLL_ (daemon) &&
1398 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
1399 EPOLL_CTL_DEL,
1400 connection->socket_fd,
1401 NULL)) )
1402 {
1403 MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
1404 }
1405 if (urh->in_eready_list)
1406 {
1407 EDLL_remove (daemon->eready_urh_head,
1408 daemon->eready_urh_tail,
1409 urh);
1410 urh->in_eready_list = false;
1411 }
1412#endif /* EPOLL_SUPPORT */
1413 if (MHD_INVALID_SOCKET != urh->mhd.socket)
1414 {
1415#ifdef EPOLL_SUPPORT
1416 if (MHD_D_IS_USING_EPOLL_ (daemon) &&
1417 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
1418 EPOLL_CTL_DEL,
1419 urh->mhd.socket,
1420 NULL)) )
1421 {
1422 MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
1423 }
1424#endif /* EPOLL_SUPPORT */
1425 /* Reflect remote disconnect to application by breaking
1426 * socketpair connection. */
1427 shutdown (urh->mhd.socket, SHUT_RDWR);
1428 }
1429 /* Socketpair sockets will remain open as they will be
1430 * used with MHD_UPGRADE_ACTION_CLOSE. They will be
1431 * closed by cleanup_upgraded_connection() during
1432 * connection's final cleanup.
1433 */
1434}
1435
1436
1437#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT*/
1438
1439
1447static void
1449 const char *emsg)
1450{
1451 connection->stop_with_error = true;
1452 connection->discard_request = true;
1453#ifdef HAVE_MESSAGES
1454 if (NULL != emsg)
1455 MHD_DLOG (connection->daemon,
1456 "%s\n",
1457 emsg);
1458#else /* ! HAVE_MESSAGES */
1459 (void) emsg; /* Mute compiler warning. */
1460#endif /* ! HAVE_MESSAGES */
1461 MHD_connection_close_ (connection,
1463}
1464
1465
1470#ifdef HAVE_MESSAGES
1471#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, emsg)
1472#else
1473#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, NULL)
1474#endif
1475
1476
1489static enum MHD_Result
1491{
1492 ssize_t ret;
1493 struct MHD_Response *response;
1494
1495 response = connection->rp.response;
1496 mhd_assert (connection->rp.props.send_reply_body);
1497
1498 if ( (0 == response->total_size) ||
1499 /* TODO: replace the next check with assert */
1500 (connection->rp.rsp_write_position == response->total_size) )
1501 return MHD_YES; /* 0-byte response is always ready */
1502 if (NULL != response->data_iov)
1503 {
1504 size_t copy_size;
1505
1506 if (NULL != connection->rp.resp_iov.iov)
1507 return MHD_YES;
1508 copy_size = response->data_iovcnt * sizeof(MHD_iovec_);
1509 connection->rp.resp_iov.iov = MHD_connection_alloc_memory_ (connection,
1510 copy_size);
1511 if (NULL == connection->rp.resp_iov.iov)
1512 {
1513 MHD_mutex_unlock_chk_ (&response->mutex);
1514 /* not enough memory */
1515 CONNECTION_CLOSE_ERROR (connection,
1516 _ ("Closing connection (out of memory)."));
1517 return MHD_NO;
1518 }
1519 memcpy (connection->rp.resp_iov.iov,
1520 response->data_iov,
1521 copy_size);
1522 connection->rp.resp_iov.cnt = response->data_iovcnt;
1523 connection->rp.resp_iov.sent = 0;
1524 return MHD_YES;
1525 }
1526 if (NULL == response->crc)
1527 return MHD_YES;
1528 if ( (response->data_start <=
1529 connection->rp.rsp_write_position) &&
1530 (response->data_size + response->data_start >
1531 connection->rp.rsp_write_position) )
1532 return MHD_YES; /* response already ready */
1533#if defined(_MHD_HAVE_SENDFILE)
1534 if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
1535 {
1536 /* will use sendfile, no need to bother response crc */
1537 return MHD_YES;
1538 }
1539#endif /* _MHD_HAVE_SENDFILE */
1540
1541 ret = response->crc (response->crc_cls,
1542 connection->rp.rsp_write_position,
1543 (char *) response->data,
1544 (size_t) MHD_MIN ((uint64_t) response->data_buffer_size,
1545 response->total_size
1546 - connection->rp.rsp_write_position));
1547 if (0 > ret)
1548 {
1549 /* either error or http 1.0 transfer, close socket! */
1550 /* TODO: do not update total size, check whether response
1551 * was really with unknown size */
1552 response->total_size = connection->rp.rsp_write_position;
1553#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1554 MHD_mutex_unlock_chk_ (&response->mutex);
1555#endif
1557 MHD_connection_close_ (connection,
1559 else
1560 CONNECTION_CLOSE_ERROR (connection,
1561 _ ("Closing connection (application reported " \
1562 "error generating data)."));
1563 return MHD_NO;
1564 }
1565 response->data_start = connection->rp.rsp_write_position;
1566 response->data_size = (size_t) ret;
1567 if (0 == ret)
1568 {
1570#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1571 MHD_mutex_unlock_chk_ (&response->mutex);
1572#endif
1573 return MHD_NO;
1574 }
1575 return MHD_YES;
1576}
1577
1578
1591static enum MHD_Result
1593 bool *p_finished)
1594{
1595 ssize_t ret;
1596 struct MHD_Response *response;
1597 static const size_t max_chunk = 0xFFFFFF;
1598 char chunk_hdr[7]; /* 6: max strlen of "FFFFFF" */
1599 /* "FFFFFF" + "\r\n" */
1600 static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2;
1601 /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */
1602 static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2;
1603 size_t chunk_hdr_len;
1604 uint64_t left_to_send;
1605 size_t size_to_fill;
1606
1607 response = connection->rp.response;
1608 mhd_assert (NULL != response->crc || NULL != response->data);
1609
1610 mhd_assert (0 == connection->write_buffer_append_offset);
1611
1612 /* The buffer must be reasonably large enough */
1613 if (128 > connection->write_buffer_size)
1614 {
1615 size_t size;
1616
1617 size = connection->write_buffer_size + MHD_pool_get_free (connection->pool);
1618 if (128 > size)
1619 {
1620#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1621 MHD_mutex_unlock_chk_ (&response->mutex);
1622#endif
1623 /* not enough memory */
1624 CONNECTION_CLOSE_ERROR (connection,
1625 _ ("Closing connection (out of memory)."));
1626 return MHD_NO;
1627 }
1628 /* Limit the buffer size to the largest usable size for chunks */
1629 if ( (max_chunk + max_chunk_overhead) < size)
1630 size = max_chunk + max_chunk_overhead;
1631 mhd_assert ((NULL == connection->write_buffer) || \
1632 MHD_pool_is_resizable_inplace (connection->pool, \
1633 connection->write_buffer, \
1634 connection->write_buffer_size));
1635 connection->write_buffer =
1636 MHD_pool_reallocate (connection->pool,
1637 connection->write_buffer,
1638 connection->write_buffer_size,
1639 size);
1640 mhd_assert (NULL != connection->write_buffer);
1641 connection->write_buffer_size = size;
1642 }
1643 mhd_assert (max_chunk_overhead < connection->write_buffer_size);
1644
1645 if (MHD_SIZE_UNKNOWN == response->total_size)
1646 left_to_send = MHD_SIZE_UNKNOWN;
1647 else
1648 left_to_send = response->total_size
1649 - connection->rp.rsp_write_position;
1650
1651 size_to_fill = connection->write_buffer_size - max_chunk_overhead;
1652 /* Limit size for the callback to the max usable size */
1653 if (max_chunk < size_to_fill)
1654 size_to_fill = max_chunk;
1655 if (left_to_send < size_to_fill)
1656 size_to_fill = (size_t) left_to_send;
1657
1658 if (0 == left_to_send)
1659 /* nothing to send, don't bother calling crc */
1661 else if ( (response->data_start <=
1662 connection->rp.rsp_write_position) &&
1663 (response->data_start + response->data_size >
1664 connection->rp.rsp_write_position) )
1665 {
1666 /* difference between rsp_write_position and data_start is less
1667 than data_size which is size_t type, no need to check for overflow */
1668 const size_t data_write_offset
1669 = (size_t) (connection->rp.rsp_write_position
1670 - response->data_start);
1671 /* buffer already ready, use what is there for the chunk */
1672 mhd_assert (SSIZE_MAX >= (response->data_size - data_write_offset));
1673 mhd_assert (response->data_size >= data_write_offset);
1674 ret = (ssize_t) (response->data_size - data_write_offset);
1675 if ( ((size_t) ret) > size_to_fill)
1676 ret = (ssize_t) size_to_fill;
1677 memcpy (&connection->write_buffer[max_chunk_hdr_len],
1678 &response->data[data_write_offset],
1679 (size_t) ret);
1680 }
1681 else
1682 {
1683 if (NULL == response->crc)
1684 { /* There is no way to reach this code */
1685#if defined(MHD_USE_THREADS)
1686 MHD_mutex_unlock_chk_ (&response->mutex);
1687#endif
1688 CONNECTION_CLOSE_ERROR (connection,
1689 _ ("No callback for the chunked data."));
1690 return MHD_NO;
1691 }
1692 ret = response->crc (response->crc_cls,
1693 connection->rp.rsp_write_position,
1694 &connection->write_buffer[max_chunk_hdr_len],
1695 size_to_fill);
1696 }
1698 {
1699 /* error, close socket! */
1700 /* TODO: remove update of the response size */
1701 response->total_size = connection->rp.rsp_write_position;
1702#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1703 MHD_mutex_unlock_chk_ (&response->mutex);
1704#endif
1705 CONNECTION_CLOSE_ERROR (connection,
1706 _ ("Closing connection (application error " \
1707 "generating response)."));
1708 return MHD_NO;
1709 }
1711 {
1712 *p_finished = true;
1713 /* TODO: remove update of the response size */
1714 response->total_size = connection->rp.rsp_write_position;
1715 return MHD_YES;
1716 }
1717 if (0 == ret)
1718 {
1720#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1721 MHD_mutex_unlock_chk_ (&response->mutex);
1722#endif
1723 return MHD_NO;
1724 }
1725 if (size_to_fill < (size_t) ret)
1726 {
1727#if defined(MHD_USE_THREADS)
1728 MHD_mutex_unlock_chk_ (&response->mutex);
1729#endif
1730 CONNECTION_CLOSE_ERROR (connection,
1731 _ ("Closing connection (application returned " \
1732 "more data than requested)."));
1733 return MHD_NO;
1734 }
1735 chunk_hdr_len = MHD_uint32_to_strx ((uint32_t) ret,
1736 chunk_hdr,
1737 sizeof(chunk_hdr));
1738 mhd_assert (chunk_hdr_len != 0);
1739 mhd_assert (chunk_hdr_len <= sizeof(chunk_hdr));
1740 /* This underflow would turn 'write_buffer_send_offset' into a huge value and
1741 make the following memcpy() write outside the write buffer. */
1742 if (MHD_CHECK_FAILED_ ((chunk_hdr_len + 2) <= max_chunk_hdr_len))
1743 {
1744 MHD_CHECK_LOG_ (connection->daemon,
1745 "(chunk_hdr_len + 2) <= max_chunk_hdr_len");
1746#if defined(MHD_USE_THREADS)
1747 MHD_mutex_unlock_chk_ (&response->mutex);
1748#endif /* MHD_USE_THREADS */
1749 connection_close_error (connection,
1750 NULL);
1751 return MHD_NO;
1752 }
1753 *p_finished = false;
1754 connection->write_buffer_send_offset =
1755 (max_chunk_hdr_len - (chunk_hdr_len + 2));
1756 memcpy (connection->write_buffer + connection->write_buffer_send_offset,
1757 chunk_hdr,
1758 chunk_hdr_len);
1759 connection->write_buffer[max_chunk_hdr_len - 2] = '\r';
1760 connection->write_buffer[max_chunk_hdr_len - 1] = '\n';
1761 connection->write_buffer[max_chunk_hdr_len + (size_t) ret] = '\r';
1762 connection->write_buffer[max_chunk_hdr_len + (size_t) ret + 1] = '\n';
1763 connection->rp.rsp_write_position += (size_t) ret;
1764 connection->write_buffer_append_offset = max_chunk_hdr_len + (size_t) ret + 2;
1765 return MHD_YES;
1766}
1767
1768
1791static enum MHD_ConnKeepAlive
1793{
1794 struct MHD_Connection *const c = connection;
1795 struct MHD_Response *const r = c->rp.response;
1797 mhd_assert (NULL != r);
1799 return MHD_CONN_MUST_CLOSE;
1800
1801#ifdef UPGRADE_SUPPORT
1802 /* TODO: Move below the next check when MHD stops closing connections
1803 * when response is queued in first callback */
1804 if (NULL != r->upgrade_handler)
1805 {
1806 /* No "close" token is enforced by 'add_response_header_connection()' */
1808 /* Valid HTTP version is enforced by 'MHD_queue_response()' */
1811 return MHD_CONN_MUST_UPGRADE;
1812 }
1813#endif /* UPGRADE_SUPPORT */
1814
1815 mhd_assert ( (! c->stop_with_error) || (c->discard_request));
1816 if ((c->read_closed) || (c->discard_request))
1817 return MHD_CONN_MUST_CLOSE;
1818
1820 return MHD_CONN_MUST_CLOSE;
1822 return MHD_CONN_MUST_CLOSE;
1823
1825 return MHD_CONN_MUST_CLOSE;
1826
1829 "close"))
1830 return MHD_CONN_MUST_CLOSE;
1831
1832 if ((MHD_HTTP_VER_1_0 == connection->rq.http_ver) ||
1833 (0 != (connection->rp.response->flags & MHD_RF_HTTP_1_0_SERVER)))
1834 {
1835 if (MHD_lookup_header_s_token_ci (connection,
1837 "Keep-Alive"))
1839
1840 return MHD_CONN_MUST_CLOSE;
1841 }
1842
1845
1846 return MHD_CONN_MUST_CLOSE;
1847}
1848
1849
1859static bool
1860get_date_str (char *date)
1861{
1862 static const char *const days[] = {
1863 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1864 };
1865 static const char *const mons[] = {
1866 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1867 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1868 };
1869 static const size_t buf_len = 29;
1870 struct tm now;
1871 time_t t;
1872 const char *src;
1873#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
1874 ! defined(HAVE_GMTIME_R)
1875 struct tm *pNow;
1876#endif
1877
1878 if ((time_t) -1 == time (&t))
1879 return false;
1880#if defined(HAVE_C11_GMTIME_S)
1881 if (NULL == gmtime_s (&t,
1882 &now))
1883 return false;
1884#elif defined(HAVE_W32_GMTIME_S)
1885 if (0 != gmtime_s (&now,
1886 &t))
1887 return false;
1888#elif defined(HAVE_GMTIME_R)
1889 if (NULL == gmtime_r (&t,
1890 &now))
1891 return false;
1892#else
1893 pNow = gmtime (&t);
1894 if (NULL == pNow)
1895 return false;
1896 now = *pNow;
1897#endif
1898
1899 /* Day of the week */
1900 src = days[now.tm_wday % 7];
1901 date[0] = src[0];
1902 date[1] = src[1];
1903 date[2] = src[2];
1904 date[3] = ',';
1905 date[4] = ' ';
1906 /* Day of the month */
1907 if (2 != MHD_uint8_to_str_pad ((uint8_t) now.tm_mday, 2,
1908 date + 5, buf_len - 5))
1909 return false;
1910 date[7] = ' ';
1911 /* Month */
1912 src = mons[now.tm_mon % 12];
1913 date[8] = src[0];
1914 date[9] = src[1];
1915 date[10] = src[2];
1916 date[11] = ' ';
1917 /* Year */
1918 if (4 != MHD_uint16_to_str ((uint16_t) (1900 + now.tm_year), date + 12,
1919 buf_len - 12))
1920 return false;
1921 date[16] = ' ';
1922 /* Time */
1923 MHD_uint8_to_str_pad ((uint8_t) now.tm_hour, 2, date + 17, buf_len - 17);
1924 date[19] = ':';
1925 MHD_uint8_to_str_pad ((uint8_t) now.tm_min, 2, date + 20, buf_len - 20);
1926 date[22] = ':';
1927 MHD_uint8_to_str_pad ((uint8_t) now.tm_sec, 2, date + 23, buf_len - 23);
1928 date[25] = ' ';
1929 date[26] = 'G';
1930 date[27] = 'M';
1931 date[28] = 'T';
1932
1933 return true;
1934}
1935
1936
1944static bool
1945get_date_header (char *header)
1946{
1947 if (! get_date_str (header + 6))
1948 {
1949 header[0] = 0;
1950 return false;
1951 }
1952 header[0] = 'D';
1953 header[1] = 'a';
1954 header[2] = 't';
1955 header[3] = 'e';
1956 header[4] = ':';
1957 header[5] = ' ';
1958 header[35] = '\r';
1959 header[36] = '\n';
1960 header[37] = 0;
1961 return true;
1962}
1963
1964
1977static bool
1979 bool required)
1980{
1981 size_t new_size;
1982 size_t avail_size;
1983 const size_t def_grow_size = connection->daemon->pool_increment;
1984 void *rb;
1985
1986 avail_size = MHD_pool_get_free (connection->pool);
1987 if (0 == avail_size)
1988 return false; /* No more space available */
1989 if (0 == connection->read_buffer_size)
1990 new_size = avail_size / 2; /* Use half of available buffer for reading */
1991 else
1992 {
1993 size_t grow_size;
1994
1995 grow_size = avail_size / 8;
1996 if (def_grow_size > grow_size)
1997 { /* Shortage of space */
1998 const size_t left_free =
1999 connection->read_buffer_size - connection->read_buffer_offset;
2000 mhd_assert (connection->read_buffer_size >= \
2001 connection->read_buffer_offset);
2002 if ((def_grow_size <= grow_size + left_free)
2003 && (left_free < def_grow_size))
2004 grow_size = def_grow_size - left_free; /* Use precise 'def_grow_size' for new free space */
2005 else if (! required)
2006 return false; /* Grow is not mandatory, leave some space in pool */
2007 else
2008 {
2009 /* Shortage of space, but grow is mandatory */
2010 const size_t small_inc =
2011 ((MHD_BUF_INC_SIZE > def_grow_size) ?
2012 def_grow_size : MHD_BUF_INC_SIZE) / 8;
2013 if (small_inc < avail_size)
2014 grow_size = small_inc;
2015 else
2016 grow_size = avail_size;
2017 }
2018 }
2019 new_size = connection->read_buffer_size + grow_size;
2020 }
2021 /* Make sure that read buffer will not be moved */
2022 if ((NULL != connection->read_buffer) &&
2023 ! MHD_pool_is_resizable_inplace (connection->pool,
2024 connection->read_buffer,
2025 connection->read_buffer_size))
2026 {
2027 mhd_assert (0);
2028 return false;
2029 }
2030 /* we can actually grow the buffer, do it! */
2031 rb = MHD_pool_reallocate (connection->pool,
2032 connection->read_buffer,
2033 connection->read_buffer_size,
2034 new_size);
2035 if (NULL == rb)
2036 {
2037 /* This should NOT be possible: we just computed 'new_size' so that
2038 it should fit. If it happens, somehow our read buffer is not in
2039 the right position in the pool, say because someone called
2040 MHD_pool_allocate() without 'from_end' set to 'true'? Anyway,
2041 should be investigated! (Ideally provide all data from
2042 *pool and connection->read_buffer and new_size for debugging). */
2043 mhd_assert (0);
2044 return false;
2045 }
2046 mhd_assert (connection->read_buffer == rb);
2047 connection->read_buffer = rb;
2048 mhd_assert (NULL != connection->read_buffer);
2049 connection->read_buffer_size = new_size;
2050 return true;
2051}
2052
2053
2058static void
2060{
2061 struct MHD_Connection *const c = connection;
2062 void *new_buf;
2063
2064 if ((NULL == c->read_buffer) || (0 == c->read_buffer_size))
2065 {
2066 mhd_assert (0 == c->read_buffer_size);
2068 return;
2069 }
2070
2072 if (0 == c->read_buffer_offset)
2073 {
2075 c->read_buffer = NULL;
2076 c->read_buffer_size = 0;
2077 }
2078 else
2079 {
2081 c->read_buffer_size));
2084 mhd_assert (c->read_buffer == new_buf);
2085 c->read_buffer = new_buf;
2087 }
2088}
2089
2090
2097static size_t
2099{
2100 struct MHD_Connection *const c = connection;
2101 struct MemoryPool *const pool = connection->pool;
2102 void *new_buf;
2103 size_t new_size;
2104 size_t free_size;
2105
2106 mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
2109
2110 free_size = MHD_pool_get_free (pool);
2111 if (0 != free_size)
2112 {
2113 new_size = c->write_buffer_size + free_size;
2114 /* This function must not move the buffer position.
2115 * MHD_pool_reallocate () may return the new position only if buffer was
2116 * allocated 'from_end' or is not the last allocation,
2117 * which should not happen. */
2118 mhd_assert ((NULL == c->write_buffer) || \
2120 c->write_buffer_size));
2121 new_buf = MHD_pool_reallocate (pool,
2122 c->write_buffer,
2124 new_size);
2125 mhd_assert ((c->write_buffer == new_buf) || (NULL == c->write_buffer));
2126 c->write_buffer = new_buf;
2127 c->write_buffer_size = new_size;
2129 {
2130 /* All data have been sent, reset offsets to zero. */
2133 }
2134 }
2135
2137}
2138
2139
2140#if 0 /* disable unused function */
2149static void
2150connection_shrink_write_buffer (struct MHD_Connection *connection)
2151{
2152 struct MHD_Connection *const c = connection;
2153 struct MemoryPool *const pool = connection->pool;
2154 void *new_buf;
2155
2156 mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
2159
2160 if ( (NULL == c->write_buffer) || (0 == c->write_buffer_size))
2161 {
2164 c->write_buffer = NULL;
2165 return;
2166 }
2168 return;
2169
2170 new_buf = MHD_pool_reallocate (pool, c->write_buffer, c->write_buffer_size,
2172 mhd_assert ((c->write_buffer == new_buf) || \
2173 (0 == c->write_buffer_append_offset));
2175 if (0 == c->write_buffer_size)
2176 c->write_buffer = NULL;
2177 else
2178 c->write_buffer = new_buf;
2179}
2180
2181
2182#endif /* unused function */
2183
2184
2192static void
2194{
2195 /* Read buffer is not needed for this request, shrink it.*/
2196 connection_shrink_read_buffer (connection);
2197}
2198
2199
2227
2228
2240static enum replyBodyUse
2242 unsigned int rcode)
2243{
2244 struct MHD_Connection *const c = connection;
2246 mhd_assert (100 <= rcode);
2247 mhd_assert (999 >= rcode);
2248
2249 if (199 >= rcode)
2250 return RP_BODY_NONE;
2251
2252 if (MHD_HTTP_NO_CONTENT == rcode)
2253 return RP_BODY_NONE;
2254
2255#if 0
2256 /* This check is not needed as upgrade handler is used only with code 101 */
2257#ifdef UPGRADE_SUPPORT
2258 if (NULL != rp.response->upgrade_handler)
2259 return RP_BODY_NONE;
2260#endif /* UPGRADE_SUPPORT */
2261#endif
2262
2263#if 0
2264 /* CONNECT is not supported by MHD */
2265 /* Successful responses for connect requests are filtered by
2266 * MHD_queue_response() */
2267 if ( (MHD_HTTP_MTHD_CONNECT == c->rq.http_mthd) &&
2268 (2 == rcode / 100) )
2269 return false; /* Actually pass-through CONNECT is not supported by MHD */
2270#endif
2271
2272 /* Reply body headers could be used.
2273 * Check whether reply body itself must be used. */
2274
2276 return RP_BODY_HEADERS_ONLY;
2277
2278 if (MHD_HTTP_NOT_MODIFIED == rcode)
2279 return RP_BODY_HEADERS_ONLY;
2280
2281 /* Reply body must be sent. The body may have zero length, but body size
2282 * must be indicated by headers ('Content-Length:' or
2283 * 'Transfer-Encoding: chunked'). */
2284 return RP_BODY_SEND;
2285}
2286
2287
2296static void
2298{
2299 struct MHD_Connection *const c = connection;
2300 struct MHD_Response *const r = c->rp.response;
2301 enum replyBodyUse use_rp_body;
2302 bool use_chunked;
2303
2304 mhd_assert (NULL != r);
2305
2306 /* ** Adjust reply properties ** */
2307
2309 use_rp_body = is_reply_body_needed (c, c->rp.responseCode);
2310 c->rp.props.send_reply_body = (use_rp_body > RP_BODY_HEADERS_ONLY);
2312
2313#ifdef UPGRADE_SUPPORT
2314 mhd_assert ( (NULL == r->upgrade_handler) ||
2315 (RP_BODY_NONE == use_rp_body) );
2316#endif /* UPGRADE_SUPPORT */
2317
2319 {
2320 if ((MHD_SIZE_UNKNOWN == r->total_size) ||
2322 { /* Use chunked reply encoding if possible */
2323
2324 /* Check whether chunked encoding is supported by the client */
2326 use_chunked = false;
2327 /* Check whether chunked encoding is allowed for the reply */
2328 else if (0 != (r->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
2330 use_chunked = false;
2331 else
2332 /* If chunked encoding is supported and allowed, and response size
2333 * is unknown, use chunked even for non-Keep-Alive connections.
2334 * See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3
2335 * Also use chunked if it is enforced by application and supported by
2336 * the client. */
2337 use_chunked = true;
2338 }
2339 else
2340 use_chunked = false;
2341
2342 if ( (MHD_SIZE_UNKNOWN == r->total_size) &&
2343 (! use_chunked) )
2344 {
2345 /* End of the stream is indicated by closure */
2347 }
2348 }
2349 else
2350 use_chunked = false; /* chunked encoding cannot be used without body */
2351
2352 c->rp.props.chunked = use_chunked;
2353#ifdef _DEBUG
2354 c->rp.props.set = true;
2355#endif /* _DEBUG */
2356}
2357
2358
2363static void
2365{
2366 struct MHD_Connection *const c = connection;
2367 struct MHD_Response *const r = c->rp.response;
2369 mhd_assert (c->rp.props.set);
2370#ifdef HAVE_MESSAGES
2371 if ( (! c->rp.props.use_reply_body_headers) &&
2372 (0 != r->total_size) )
2373 {
2374 MHD_DLOG (c->daemon,
2375 _ ("This reply with response code %u cannot use reply body. "
2376 "Non-empty response body is ignored and not used.\n"),
2377 (unsigned) (c->rp.responseCode));
2378 }
2379 if ( (! c->rp.props.use_reply_body_headers) &&
2381 {
2382 MHD_DLOG (c->daemon,
2383 _ ("This reply with response code %u cannot use reply body. "
2384 "Application defined \"Content-Length\" header violates"
2385 "HTTP specification.\n"),
2386 (unsigned) (c->rp.responseCode));
2387 }
2388#else
2389 (void) c; /* Mute compiler warning */
2390 (void) r; /* Mute compiler warning */
2391#endif
2392}
2393
2394
2406static bool
2407buffer_append (char *buf,
2408 size_t *ppos,
2409 size_t buf_size,
2410 const char *append,
2411 size_t append_size)
2412{
2413 mhd_assert (NULL != buf); /* Mute static analyzer */
2414 if (buf_size < *ppos + append_size)
2415 return false;
2416 memcpy (buf + *ppos, append, append_size);
2417 *ppos += append_size;
2418 return true;
2419}
2420
2421
2432#define buffer_append_s(buf,ppos,buf_size,str) \
2433 buffer_append (buf,ppos,buf_size,str, MHD_STATICSTR_LEN_ (str))
2434
2435
2455static bool
2457 size_t *ppos,
2458 size_t buf_size,
2459 struct MHD_Response *response,
2460 bool filter_transf_enc,
2461 bool filter_content_len,
2462 bool add_close,
2463 bool add_keep_alive)
2464{
2465 struct MHD_Response *const r = response;
2466 struct MHD_HTTP_Res_Header *hdr;
2467 size_t el_size;
2469 mhd_assert (! add_close || ! add_keep_alive);
2470
2472 filter_transf_enc = false; /* No such header */
2473 if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2474 filter_content_len = false; /* No such header */
2475 if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2476 {
2477 add_close = false; /* No such header */
2478 add_keep_alive = false; /* No such header */
2479 }
2480 else if (0 != (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE))
2481 add_close = false; /* "close" token was already set */
2482
2483 for (hdr = r->first_header; NULL != hdr; hdr = hdr->next)
2484 {
2485 size_t initial_pos = *ppos;
2486 if (MHD_HEADER_KIND != hdr->kind)
2487 continue;
2488 if (filter_transf_enc)
2489 { /* Need to filter-out "Transfer-Encoding" */
2491 hdr->header_size) &&
2493 hdr->header, hdr->header_size)) )
2494 {
2495 filter_transf_enc = false; /* There is the only one such header */
2496 continue; /* Skip "Transfer-Encoding" header */
2497 }
2498 }
2499 if (filter_content_len)
2500 { /* Need to filter-out "Content-Length" */
2502 hdr->header_size) &&
2504 hdr->header, hdr->header_size)) )
2505 {
2506 /* Reset filter flag if only one header is allowed */
2507 filter_transf_enc =
2509 continue; /* Skip "Content-Length" header */
2510 }
2511 }
2512
2513 /* Add user header */
2514 /* Check available space using subtractions to avoid integer overflow.
2515 '4' is two colons/space plus the final CRLF. */
2516 if ( (buf_size - *ppos < 4) ||
2517 (buf_size - *ppos - 4 < hdr->header_size) ||
2518 (buf_size - *ppos - 4 - hdr->header_size < hdr->value_size) )
2519 return false;
2520 el_size = hdr->header_size + 2 + hdr->value_size + 2;
2521 memcpy (buf + *ppos, hdr->header, hdr->header_size);
2522 (*ppos) += hdr->header_size;
2523 buf[(*ppos)++] = ':';
2524 buf[(*ppos)++] = ' ';
2525 if (add_close || add_keep_alive)
2526 {
2527 /* "Connection:" header must be always the first one */
2530 hdr->header_size));
2531
2532 if (add_close)
2533 {
2534 el_size += MHD_STATICSTR_LEN_ ("close, ");
2535 if (buf_size < initial_pos + el_size)
2536 return false;
2537 memcpy (buf + *ppos, "close, ",
2538 MHD_STATICSTR_LEN_ ("close, "));
2539 *ppos += MHD_STATICSTR_LEN_ ("close, ");
2540 }
2541 else
2542 {
2543 el_size += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2544 if (buf_size < initial_pos + el_size)
2545 return false;
2546 memcpy (buf + *ppos, "Keep-Alive, ",
2547 MHD_STATICSTR_LEN_ ("Keep-Alive, "));
2548 *ppos += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2549 }
2550 add_close = false;
2551 add_keep_alive = false;
2552 }
2553 if (0 != hdr->value_size)
2554 memcpy (buf + *ppos, hdr->value, hdr->value_size);
2555 *ppos += hdr->value_size;
2556 buf[(*ppos)++] = '\r';
2557 buf[(*ppos)++] = '\n';
2558 mhd_assert (initial_pos + el_size == (*ppos));
2559 }
2560 return true;
2561}
2562
2563
2572static enum MHD_Result
2574{
2575 struct MHD_Connection *const c = connection;
2576 struct MHD_Response *const r = c->rp.response;
2577 char *buf;
2578 size_t pos;
2579 size_t buf_size;
2580 size_t el_size;
2581 unsigned rcode;
2582 bool use_conn_close;
2583 bool use_conn_k_alive;
2585 mhd_assert (NULL != r);
2586
2587 /* ** Adjust response properties ** */
2589
2590 mhd_assert (c->rp.props.set);
2594#ifdef UPGRADE_SUPPORT
2595 mhd_assert ((NULL == r->upgrade_handler) || \
2597#else /* ! UPGRADE_SUPPORT */
2599#endif /* ! UPGRADE_SUPPORT */
2601 mhd_assert ((! c->rp.props.send_reply_body) || \
2603#ifdef UPGRADE_SUPPORT
2604 mhd_assert (NULL == r->upgrade_handler || \
2606#endif /* UPGRADE_SUPPORT */
2607
2609
2610 rcode = (unsigned) c->rp.responseCode;
2612 {
2613 /* The closure of connection must be always indicated by header
2614 * to avoid hung connections */
2615 use_conn_close = true;
2616 use_conn_k_alive = false;
2617 }
2618 else if (MHD_CONN_USE_KEEPALIVE == c->keepalive)
2619 {
2620 use_conn_close = false;
2621 /* Add "Connection: keep-alive" if request is HTTP/1.0 or
2622 * if reply is HTTP/1.0
2623 * For HTTP/1.1 add header only if explicitly requested by app
2624 * (by response flag), as "Keep-Alive" is default for HTTP/1.1. */
2625 if ((0 != (r->flags & MHD_RF_SEND_KEEP_ALIVE_HEADER)) ||
2626 (MHD_HTTP_VER_1_0 == c->rq.http_ver) ||
2627 (0 != (r->flags & MHD_RF_HTTP_1_0_SERVER)))
2628 use_conn_k_alive = true;
2629 else
2630 use_conn_k_alive = false;
2631 }
2632 else
2633 {
2634 use_conn_close = false;
2635 use_conn_k_alive = false;
2636 }
2637
2638 /* ** Actually build the response header ** */
2639
2640 /* Get all space available */
2642 buf = c->write_buffer;
2644 buf_size = c->write_buffer_size;
2645 if (0 == buf_size)
2646 return MHD_NO;
2647 mhd_assert (NULL != buf);
2648
2649 /* * The status line * */
2650
2651 /* The HTTP version */
2652 if (! c->rp.responseIcy)
2653 { /* HTTP reply */
2654 if (0 == (r->flags & MHD_RF_HTTP_1_0_SERVER))
2655 { /* HTTP/1.1 reply */
2656 /* Use HTTP/1.1 responses for HTTP/1.0 clients.
2657 * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
2658 if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_1))
2659 return MHD_NO;
2660 }
2661 else
2662 { /* HTTP/1.0 reply */
2663 if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_0))
2664 return MHD_NO;
2665 }
2666 }
2667 else
2668 { /* ICY reply */
2669 if (! buffer_append_s (buf, &pos, buf_size, "ICY"))
2670 return MHD_NO;
2671 }
2672
2673 /* The response code */
2674 if (buf_size < pos + 5) /* space + code + space */
2675 return MHD_NO;
2676 buf[pos++] = ' ';
2677 pos += MHD_uint16_to_str ((uint16_t) rcode, buf + pos,
2678 buf_size - pos);
2679 buf[pos++] = ' ';
2680
2681 /* The reason phrase */
2682 el_size = MHD_get_reason_phrase_len_for (rcode);
2683 if (0 == el_size)
2684 {
2685 if (! buffer_append_s (buf, &pos, buf_size, "Non-Standard Status"))
2686 return MHD_NO;
2687 }
2688 else if (! buffer_append (buf, &pos, buf_size,
2690 el_size))
2691 return MHD_NO;
2692
2693 /* The linefeed */
2694 if (buf_size < pos + 2)
2695 return MHD_NO;
2696 buf[pos++] = '\r';
2697 buf[pos++] = '\n';
2698
2699 /* * The headers * */
2700
2701 /* Main automatic headers */
2702
2703 /* The "Date:" header */
2704 if ( (0 == (r->flags_auto & MHD_RAF_HAS_DATE_HDR)) &&
2706 {
2707 /* Additional byte for unused zero-termination */
2708 if (buf_size < pos + 38)
2709 return MHD_NO;
2710 if (get_date_header (buf + pos))
2711 pos += 37;
2712 }
2713 /* The "Connection:" header */
2714 mhd_assert (! use_conn_close || ! use_conn_k_alive);
2715 mhd_assert (! use_conn_k_alive || ! use_conn_close);
2716 if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2717 {
2718 if (use_conn_close)
2719 {
2720 if (! buffer_append_s (buf, &pos, buf_size,
2721 MHD_HTTP_HEADER_CONNECTION ": close\r\n"))
2722 return MHD_NO;
2723 }
2724 else if (use_conn_k_alive)
2725 {
2726 if (! buffer_append_s (buf, &pos, buf_size,
2727 MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n"))
2728 return MHD_NO;
2729 }
2730 }
2731
2732 /* User-defined headers */
2733
2734 if (! add_user_headers (buf, &pos, buf_size, r,
2735 ! c->rp.props.chunked,
2737 (0 ==
2739 use_conn_close,
2740 use_conn_k_alive))
2741 return MHD_NO;
2742
2743 /* Other automatic headers */
2744
2745 if ( (c->rp.props.use_reply_body_headers) &&
2746 (0 == (r->flags & MHD_RF_HEAD_ONLY_RESPONSE)) )
2747 {
2748 /* Body-specific headers */
2749
2750 if (c->rp.props.chunked)
2751 { /* Chunked encoding is used */
2753 { /* No chunked encoding header set by user */
2754 if (! buffer_append_s (buf, &pos, buf_size,
2756 "chunked\r\n"))
2757 return MHD_NO;
2758 }
2759 }
2760 else /* Chunked encoding is not used */
2761 {
2762 if (MHD_SIZE_UNKNOWN != r->total_size)
2763 { /* The size is known */
2764 if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2765 { /* The response does not have "Content-Length" header */
2766 if (! buffer_append_s (buf, &pos, buf_size,
2768 return MHD_NO;
2769 el_size = MHD_uint64_to_str (r->total_size, buf + pos,
2770 buf_size - pos);
2771 if (0 == el_size)
2772 return MHD_NO;
2773 pos += el_size;
2774
2775 if (buf_size < pos + 2)
2776 return MHD_NO;
2777 buf[pos++] = '\r';
2778 buf[pos++] = '\n';
2779 }
2780 }
2781 }
2782 }
2783
2784 /* * Header termination * */
2785 if (buf_size < pos + 2)
2786 return MHD_NO;
2787 buf[pos++] = '\r';
2788 buf[pos++] = '\n';
2789
2791 return MHD_YES;
2792}
2793
2794
2804static enum MHD_Result
2806{
2807 char *buf;
2808 size_t buf_size;
2809 size_t used_size;
2810 struct MHD_Connection *const c = connection;
2811 struct MHD_HTTP_Res_Header *pos;
2812
2813 mhd_assert (connection->rp.props.chunked);
2814 /* TODO: allow combining of the final footer with the last chunk,
2815 * modify the next assert. */
2817 mhd_assert (NULL != c->rp.response);
2818
2819 buf_size = connection_maximize_write_buffer (c);
2820 /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */
2821 if (buf_size < 5)
2822 return MHD_NO;
2825 mhd_assert (NULL != buf);
2826 used_size = 0;
2827 buf[used_size++] = '0';
2828 buf[used_size++] = '\r';
2829 buf[used_size++] = '\n';
2830
2831 for (pos = c->rp.response->first_header; NULL != pos; pos = pos->next)
2832 {
2833 if (MHD_FOOTER_KIND == pos->kind)
2834 {
2835 size_t new_used_size; /* resulting size with this header */
2836 /* '4' is colon, space, linefeeds */
2837 /* Check available space using subtractions to avoid integer overflow. */
2838 if ( (buf_size - used_size < 4) ||
2839 (buf_size - used_size - 4 < pos->header_size) ||
2840 (buf_size - used_size - 4 - pos->header_size < pos->value_size) )
2841 return MHD_NO;
2842 new_used_size = used_size + pos->header_size + pos->value_size + 4;
2843 memcpy (buf + used_size, pos->header, pos->header_size);
2844 used_size += pos->header_size;
2845 buf[used_size++] = ':';
2846 buf[used_size++] = ' ';
2847 memcpy (buf + used_size, pos->value, pos->value_size);
2848 used_size += pos->value_size;
2849 buf[used_size++] = '\r';
2850 buf[used_size++] = '\n';
2851 mhd_assert (used_size == new_used_size);
2852 }
2853 }
2854 if (used_size + 2 > buf_size)
2855 return MHD_NO;
2856 buf[used_size++] = '\r';
2857 buf[used_size++] = '\n';
2858
2859 c->write_buffer_append_offset += used_size;
2861
2862 return MHD_YES;
2863}
2864
2865
2882static void
2884 unsigned int status_code,
2885 const char *message,
2886 size_t message_len,
2887 char *header_name,
2888 size_t header_name_len,
2889 char *header_value,
2890 size_t header_value_len)
2891{
2892 struct MHD_Response *response;
2893 enum MHD_Result iret;
2894
2895 mhd_assert (! connection->stop_with_error); /* Do not send error twice */
2896 if (connection->stop_with_error)
2897 { /* Should not happen */
2898 if (MHD_CONNECTION_CLOSED > connection->state)
2899 connection->state = MHD_CONNECTION_CLOSED;
2900 free (header_name);
2901 free (header_value);
2902 return;
2903 }
2904 connection->stop_with_error = true;
2905 connection->discard_request = true;
2906#ifdef HAVE_MESSAGES
2907 MHD_DLOG (connection->daemon,
2908 _ ("Error processing request (HTTP response code is %u ('%s')). " \
2909 "Closing connection.\n"),
2910 status_code,
2911 message);
2912#endif
2914 {
2915#ifdef HAVE_MESSAGES
2916 MHD_DLOG (connection->daemon,
2917 _ ("Too late to send an error response, " \
2918 "response is being sent already.\n"),
2919 status_code,
2920 message);
2921#endif
2922 CONNECTION_CLOSE_ERROR (connection,
2923 _ ("Too late for error response."));
2924 free (header_name);
2925 free (header_value);
2926 return;
2927 }
2928 /* TODO: remove when special error queue function is implemented */
2930 if (0 != connection->read_buffer_size)
2931 {
2932 /* Read buffer is not needed anymore, discard it
2933 * to free some space for error response. */
2934 MHD_pool_deallocate (connection->pool,
2935 connection->read_buffer,
2936 connection->read_buffer_size);
2937 connection->read_buffer = NULL;
2938 connection->read_buffer_size = 0;
2939 connection->read_buffer_offset = 0;
2940 }
2941 if (NULL != connection->rp.response)
2942 {
2943 MHD_destroy_response (connection->rp.response);
2944 connection->rp.response = NULL;
2945 }
2946 response = MHD_create_response_from_buffer_static (message_len,
2947 message);
2948 if (NULL == response)
2949 {
2950#ifdef HAVE_MESSAGES
2951 MHD_DLOG (connection->daemon,
2952 _ ("Failed to create error response.\n"),
2953 status_code,
2954 message);
2955#endif
2956 /* can't even send a reply, at least close the connection */
2957 connection->state = MHD_CONNECTION_CLOSED;
2958 free (header_name);
2959 free (header_value);
2960 return;
2961 }
2962 mhd_assert ((0 == header_name_len) || (NULL != header_name));
2963 mhd_assert ((NULL == header_name) || (0 != header_name_len));
2964 mhd_assert ((0 == header_value_len) || (NULL != header_value));
2965 mhd_assert ((NULL == header_value) || (0 != header_value_len));
2966 mhd_assert ((NULL == header_name) || (NULL != header_value));
2967 mhd_assert ((NULL != header_value) || (NULL == header_name));
2968 if (NULL != header_name)
2969 {
2970 iret = MHD_add_response_entry_no_alloc_ (response,
2972 header_name, header_name_len,
2973 header_value, header_value_len);
2974 if (MHD_NO == iret)
2975 {
2976 free (header_name);
2977 free (header_value);
2978 }
2979 }
2980 else
2981 iret = MHD_YES;
2982
2983 if (MHD_NO != iret)
2984 {
2985 bool before = connection->in_access_handler;
2986
2987 /* Fake the flag for the internal call */
2988 connection->in_access_handler = true;
2989 iret = MHD_queue_response (connection,
2990 status_code,
2991 response);
2992 connection->in_access_handler = before;
2993 }
2994 MHD_destroy_response (response);
2995 if (MHD_NO == iret)
2996 {
2997 /* can't even send a reply, at least close the connection */
2998 CONNECTION_CLOSE_ERROR (connection,
2999 _ ("Closing connection " \
3000 "(failed to queue error response)."));
3001 return;
3002 }
3003 mhd_assert (NULL != connection->rp.response);
3004 /* Do not reuse this connection. */
3005 connection->keepalive = MHD_CONN_MUST_CLOSE;
3006 if (MHD_NO == build_header_response (connection))
3007 {
3008 /* No memory. Release everything. */
3009 connection->rq.version = NULL;
3010 connection->rq.method = NULL;
3011 connection->rq.url = NULL;
3012 connection->rq.url_len = 0;
3013 connection->rq.url_for_callback = NULL;
3014 connection->rq.headers_received = NULL;
3015 connection->rq.headers_received_tail = NULL;
3016 connection->write_buffer = NULL;
3017 connection->write_buffer_size = 0;
3018 connection->write_buffer_send_offset = 0;
3019 connection->write_buffer_append_offset = 0;
3020 connection->read_buffer
3021 = MHD_pool_reset (connection->pool,
3022 NULL,
3023 0,
3024 0);
3025 connection->read_buffer_size = 0;
3026
3027 /* Retry with empty buffer */
3028 if (MHD_NO == build_header_response (connection))
3029 {
3030 CONNECTION_CLOSE_ERROR (connection,
3031 _ ("Closing connection " \
3032 "(failed to create error response header)."));
3033 return;
3034 }
3035 }
3037}
3038
3039
3043#ifdef HAVE_MESSAGES
3044# define transmit_error_response_static(c, code, msg) \
3045 transmit_error_response_len (c, code, \
3046 msg, MHD_STATICSTR_LEN_ (msg), \
3047 NULL, 0, NULL, 0)
3048#else /* ! HAVE_MESSAGES */
3049# define transmit_error_response_static(c, code, msg) \
3050 transmit_error_response_len (c, code, \
3051 "", 0, \
3052 NULL, 0, NULL, 0)
3053#endif /* ! HAVE_MESSAGES */
3054
3058#ifdef HAVE_MESSAGES
3059# define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l) \
3060 transmit_error_response_len (c, code, \
3061 m, MHD_STATICSTR_LEN_ (m), \
3062 hd_n, hd_n_l, \
3063 hd_v, hd_v_l)
3064#else /* ! HAVE_MESSAGES */
3065# define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l) \
3066 transmit_error_response_len (c, code, \
3067 "", 0, \
3068 hd_n, hd_n_l, \
3069 hd_v, hd_v_l)
3070#endif /* ! HAVE_MESSAGES */
3071
3072
3083static bool
3085{
3087 if (! c->rq.have_chunked_upload)
3088 return 0 != c->read_buffer_offset;
3089
3090 /* Chunked upload */
3091 mhd_assert (0 != c->rq.remaining_upload_size); /* Must not be possible in MHD_CONNECTION_BODY_RECEIVING state */
3093 {
3094 /* 0 == c->rq.current_chunk_size: Waiting the chunk size (chunk header).
3095 0 != c->rq.current_chunk_size: Waiting for chunk-closing CRLF. */
3096 return false;
3097 }
3098 return 0 != c->read_buffer_offset; /* Chunk payload data in the read buffer */
3099}
3100
3101
3118
3119
3120#ifndef MHD_MAX_REASONABLE_HEADERS_SIZE_
3130# define MHD_MAX_REASONABLE_HEADERS_SIZE_ (6 * 1024)
3131#endif /* ! MHD_MAX_REASONABLE_HEADERS_SIZE_ */
3132
3133#ifndef MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
3144# define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 8000
3145#endif /* ! MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ */
3146
3147#ifndef MHD_MIN_REASONABLE_HEADERS_SIZE_
3155# define MHD_MIN_REASONABLE_HEADERS_SIZE_ 26
3156#endif /* ! MHD_MIN_REASONABLE_HEADERS_SIZE_ */
3157
3158#ifndef MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
3166# define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 40
3167#endif /* ! MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ */
3168
3169#ifndef MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
3177# define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 16
3178#endif /* ! MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ */
3179
3180#ifndef MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
3186# define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 4
3187#endif /* ! MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ */
3188
3189
3201static unsigned int
3203 enum MHD_ProcRecvDataStage stage,
3204 const char *add_element,
3205 size_t add_element_size)
3206{
3207 size_t method_size;
3208 size_t uri_size;
3209 size_t opt_headers_size;
3210 size_t host_field_line_size;
3211
3214 mhd_assert ((0 == add_element_size) || (NULL != add_element));
3215
3217 {
3219 opt_headers_size =
3220 (size_t) ((c->read_buffer + c->read_buffer_offset)
3221 - c->rq.field_lines.start);
3222 }
3223 else
3224 opt_headers_size = c->rq.field_lines.size;
3225
3226 /* The read buffer is fully used by the request line, the field lines
3227 (headers) and internal information.
3228 The return status code works as a suggestion for the client to reduce
3229 one of the request elements. */
3230
3231 if ((MHD_PROC_RECV_BODY_CHUNKED == stage) &&
3232 (MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ < add_element_size))
3233 {
3234 /* Request could be re-tried easily with smaller chunk sizes */
3236 }
3237
3238 host_field_line_size = 0;
3239 /* The "Host:" field line is mandatory.
3240 The total size of the field lines (headers) cannot be smaller than
3241 the size of the "Host:" field line. */
3242 if ((MHD_PROC_RECV_HEADERS == stage)
3243 && (0 != add_element_size))
3244 {
3245 static const size_t header_host_key_len =
3247 const bool is_host_header =
3248 (header_host_key_len + 1 <= add_element_size)
3249 && ( (0 == add_element[header_host_key_len])
3250 || (':' == add_element[header_host_key_len]) )
3252 add_element,
3253 header_host_key_len);
3254 if (is_host_header)
3255 {
3256 const bool is_parsed = ! (
3258 (add_element_size == c->read_buffer_offset) &&
3259 (c->read_buffer == add_element) );
3260 size_t actual_element_size;
3261
3262 mhd_assert (! is_parsed || (0 == add_element[header_host_key_len]));
3263 /* The actual size should be larger due to CRLF or LF chars,
3264 however the exact termination sequence is not known here and
3265 as perfect precision is not required, to simplify the code
3266 assume the minimal length. */
3267 if (is_parsed)
3268 actual_element_size = add_element_size + 1; /* "1" for LF */
3269 else
3270 actual_element_size = add_element_size;
3271
3272 host_field_line_size = actual_element_size;
3273 mhd_assert (opt_headers_size >= actual_element_size);
3274 opt_headers_size -= actual_element_size;
3275 }
3276 }
3277 if (0 == host_field_line_size)
3278 {
3279 static const size_t host_field_name_len =
3281 size_t host_field_name_value_len;
3285 host_field_name_len,
3286 NULL,
3287 &host_field_name_value_len))
3288 {
3289 /* Calculate the minimal size of the field line: no space between
3290 colon and the field value, line terminated by LR */
3291 host_field_line_size =
3292 host_field_name_len + host_field_name_value_len + 2; /* "2" for ':' and LF */
3293
3294 /* The "Host:" field could be added by application */
3295 if (opt_headers_size >= host_field_line_size)
3296 {
3297 opt_headers_size -= host_field_line_size;
3298 /* Take into account typical space after colon and CR at the end of the line */
3299 if (opt_headers_size >= 2)
3300 opt_headers_size -= 2;
3301 }
3302 else
3303 host_field_line_size = 0; /* No "Host:" field line set by the client */
3304 }
3305 }
3306
3307 uri_size = c->rq.req_target_len;
3309 method_size = 0; /* Do not recommend shorter request method */
3310 else
3311 {
3312 mhd_assert (NULL != c->rq.method);
3313 method_size = strlen (c->rq.method);
3314 }
3315
3316 if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3317 {
3318 /* Typically the easiest way to reduce request header size is
3319 a removal of some optional headers. */
3320 if (opt_headers_size > (uri_size / 8))
3321 {
3322 if ((opt_headers_size / 2) > method_size)
3324 else
3325 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3326 }
3327 else
3328 { /* Request target is MUCH larger than headers */
3329 if ((uri_size / 16) > method_size)
3330 return MHD_HTTP_URI_TOO_LONG;
3331 else
3332 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3333 }
3334 }
3335 if ((size_t) MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3336 {
3337 /* If request target size if larger than maximum reasonable size
3338 recommend client to reduce the request target size (length). */
3339 if ((uri_size / 16) > method_size)
3340 return MHD_HTTP_URI_TOO_LONG; /* Request target is MUCH larger than headers */
3341 else
3342 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3343 }
3344
3345 /* The read buffer is too small to handle reasonably large requests */
3346
3347 if ((size_t) MHD_MIN_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3348 {
3349 /* Recommend application to retry with minimal headers */
3350 if ((opt_headers_size * 4) > uri_size)
3351 {
3352 if (opt_headers_size > method_size)
3354 else
3355 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3356 }
3357 else
3358 { /* Request target is significantly larger than headers */
3359 if (uri_size > method_size * 4)
3360 return MHD_HTTP_URI_TOO_LONG;
3361 else
3362 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3363 }
3364 }
3365 if ((size_t) MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3366 {
3367 /* Recommend application to retry with a shorter request target */
3368 if (uri_size > method_size * 4)
3369 return MHD_HTTP_URI_TOO_LONG;
3370 else
3371 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3372 }
3373
3374 if ((size_t) MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ < method_size)
3375 {
3376 /* The request target (URI) and headers are (reasonably) very small.
3377 Some non-standard long request method is used. */
3378 /* The last resort response as it means "the method is not supported
3379 by the server for any URI". */
3381 }
3382
3383 /* The almost impossible situation: all elements are small, but cannot
3384 fit the buffer. The application set the buffer size to
3385 critically low value? */
3386
3387 if ((1 < opt_headers_size) || (1 < uri_size))
3388 {
3389 if (opt_headers_size >= uri_size)
3391 else
3392 return MHD_HTTP_URI_TOO_LONG;
3393 }
3394
3395 /* Nothing to reduce in the request.
3396 Reply with some status. */
3397 if (0 != host_field_line_size)
3399
3400 return MHD_HTTP_URI_TOO_LONG;
3401}
3402
3403
3414static void
3416 const char *add_header,
3417 size_t add_header_size)
3418{
3419 unsigned int err_code;
3420
3421 err_code = get_no_space_err_status_code (c,
3423 add_header,
3424 add_header_size);
3426 err_code,
3428}
3429
3430
3431#ifdef COOKIE_SUPPORT
3437static void
3438handle_req_cookie_no_space (struct MHD_Connection *c)
3439{
3440 unsigned int err_code;
3441
3442 err_code = get_no_space_err_status_code (c,
3444 NULL,
3445 0);
3447 err_code,
3449}
3450
3451
3452#endif /* COOKIE_SUPPORT */
3453
3454
3465static void
3467 const char *chunk_size_line,
3468 size_t chunk_size_line_size)
3469{
3470 unsigned int err_code;
3471
3472 if (NULL != chunk_size_line)
3473 {
3474 const char *semicol;
3475 /* Check for chunk extension */
3476 semicol = memchr (chunk_size_line, ';', chunk_size_line_size);
3477 if (NULL != semicol)
3478 { /* Chunk extension present. It could be removed without any loss of the
3479 details of the request. */
3483 return; /* The error response has been queued already */
3484 }
3485 }
3486 err_code = get_no_space_err_status_code (c,
3488 chunk_size_line,
3489 chunk_size_line_size);
3491 err_code,
3493}
3494
3495
3506static void
3508 const char *add_footer,
3509 size_t add_footer_size)
3510{
3511 (void) add_footer; (void) add_footer_size; /* Unused */
3513
3514 /* Footers should be optional */
3518}
3519
3520
3531static void
3533 enum MHD_ProcRecvDataStage stage)
3534{
3535 mhd_assert (MHD_PROC_RECV_INIT <= stage);
3538 mhd_assert ((MHD_PROC_RECV_INIT != stage) || \
3539 (MHD_CONNECTION_INIT == c->state));
3540 mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \
3542 mhd_assert ((MHD_PROC_RECV_URI != stage) || \
3544 mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \
3546 mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \
3548 mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */
3549 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3551 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3553 mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \
3555 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3556 (! c->rq.have_chunked_upload));
3557 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3558 (c->rq.have_chunked_upload));
3559 switch (stage)
3560 {
3561 case MHD_PROC_RECV_INIT:
3563 /* Some data has been received, but it is not clear yet whether
3564 * the received data is an valid HTTP request */
3566 _ ("No space left in the read buffer when " \
3567 "receiving the initial part of " \
3568 "the request line."));
3569 return;
3570 case MHD_PROC_RECV_URI:
3572 /* Some data has been received, but the request line is incomplete */
3575 /* A quick simple check whether the incomplete line looks
3576 * like an HTTP request */
3577 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
3579 {
3583 return;
3584 }
3586 _ ("No space left in the read buffer when " \
3587 "receiving the URI in " \
3588 "the request line. " \
3589 "The request uses non-standard HTTP request " \
3590 "method token."));
3591 return;
3594 return;
3597 /* The 'some_payload_processed' flag reflects the *last* application
3598 callback only. After that callback more data may have been received
3599 from the network, in particular a chunk-size line with a chunk
3600 extension that does not fit into the read buffer. Therefore the flag
3601 alone does not imply that free space is available; it does so only as
3602 long as unprocessed payload is still sitting in the buffer. */
3603 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3604 (! c->rq.some_payload_processed) || \
3607 {
3608 /* The connection must not be in MHD_EVENT_LOOP_INFO_READ state
3609 when external polling is used and some data left unprocessed. */
3611 /* failed to grow the read buffer, and the
3612 client which is supposed to handle the
3613 received data in a *blocking* fashion
3614 (in this mode) did not handle the data as
3615 it was supposed to!
3616 => we would either have to do busy-waiting
3617 (on the client, which would likely fail),
3618 or if we do nothing, we would just timeout
3619 on the connection (if a timeout is even
3620 set!).
3621 Solution: we kill the connection with an error */
3625 }
3626 else
3627 {
3628 if (MHD_PROC_RECV_BODY_NORMAL == stage)
3629 {
3630 /* A header probably has been added to a suspended connection and
3631 it took precisely all the space in the buffer.
3632 Very low probability. */
3635 }
3636 else
3637 {
3640 { /* Receiving content of the chunk */
3641 /* A header probably has been added to a suspended connection and
3642 it took precisely all the space in the buffer.
3643 Very low probability. */
3645 }
3646 else
3647 {
3648 if (0 != c->rq.current_chunk_size)
3649 { /* Waiting for chunk-closing CRLF */
3650 /* Not really possible as some payload should be
3651 processed and the space used by payload should be available. */
3653 }
3654 else
3655 { /* Reading the line with the chunk size */
3657 c->read_buffer,
3659 }
3660 }
3661 }
3662 }
3663 return;
3666 return;
3667 /* The next cases should not be possible */
3669 default:
3670 break;
3671 }
3672 mhd_assert (0);
3673}
3674
3675
3689static bool
3691{
3695 bool rbuff_grow_desired;
3699 bool rbuff_grow_required;
3700
3703
3704 rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size);
3705 if (rbuff_grow_required)
3706 rbuff_grow_desired = true;
3707 else
3708 {
3709 rbuff_grow_desired = (c->read_buffer_offset + c->daemon->pool_increment >
3710 c->read_buffer_size);
3711
3712 if ((rbuff_grow_desired) &&
3714 {
3715 if (! c->rq.have_chunked_upload)
3716 {
3718 /* Do not grow read buffer more than necessary to process the current
3719 request. */
3720 rbuff_grow_desired =
3722 }
3723 else
3724 {
3726 if (0 == c->rq.current_chunk_size)
3727 rbuff_grow_desired = /* Reading value of the next chunk size */
3729 c->read_buffer_size);
3730 else
3731 {
3732 const uint64_t cur_chunk_left =
3734 /* Do not grow read buffer more than necessary to process the current
3735 chunk with terminating CRLF. */
3737 rbuff_grow_desired =
3738 ((cur_chunk_left + 2) > (uint64_t) (c->read_buffer_size));
3739 }
3740 }
3741 }
3742 }
3743
3744 if (! rbuff_grow_desired)
3745 return true; /* No need to increase the buffer */
3746
3747 if (try_grow_read_buffer (c, rbuff_grow_required))
3748 return true; /* Buffer increase succeed */
3749
3750 if (! rbuff_grow_required)
3751 return true; /* Can continue without buffer increase */
3752
3753 /* Failed to increase the read buffer size, but need to read the data
3754 from the network.
3755 No more space left in the buffer, no more space to increase the buffer. */
3756
3757 /* 'PROCESS_READ' event state flag must be set only if the last application
3758 callback has processed some data. If any data is processed then some
3759 space in the read buffer must be available. */
3761
3762 if ((! MHD_D_IS_USING_THREADS_ (c->daemon))
3765 {
3766 /* The application is handling processing cycles.
3767 The data could be processed later. */
3769 return true;
3770 }
3771 else
3772 {
3773 enum MHD_ProcRecvDataStage stage;
3774
3775 switch (c->state)
3776 {
3778 stage = MHD_PROC_RECV_INIT;
3779 break;
3782 stage = MHD_PROC_RECV_METHOD;
3783 else if (0 == c->rq.req_target_len)
3784 stage = MHD_PROC_RECV_URI;
3785 else
3786 stage = MHD_PROC_RECV_HTTPVER;
3787 break;
3789 stage = MHD_PROC_RECV_HEADERS;
3790 break;
3792 stage = c->rq.have_chunked_upload ?
3794 break;
3796 stage = MHD_PROC_RECV_FOOTERS;
3797 break;
3816#ifdef UPGRADE_SUPPORT
3817 case MHD_CONNECTION_UPGRADE:
3818#endif
3819 default:
3821 mhd_assert (0);
3822 }
3823
3824 handle_recv_no_space (c, stage);
3825 }
3826 return false;
3827}
3828
3829
3838static void
3840{
3841 /* Do not update states of suspended connection */
3842 if (connection->suspended)
3843 return; /* States will be updated after resume. */
3844#ifdef HTTPS_SUPPORT
3845 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
3846 { /* HTTPS connection. */
3847 switch (connection->tls_state)
3848 {
3849 case MHD_TLS_CONN_INIT:
3851 return;
3854 if (0 == gnutls_record_get_direction (connection->tls_session))
3856 else
3858 return;
3860 break; /* Do normal processing */
3864 return;
3865 case MHD_TLS_CONN_TLS_CLOSING: /* Not implemented yet */
3866 case MHD_TLS_CONN_TLS_CLOSED: /* Not implemented yet */
3868 case MHD_TLS_CONN_NO_TLS: /* Not possible */
3869 default:
3870 MHD_PANIC (_ ("Invalid TLS state value.\n"));
3871 }
3872 }
3873#endif /* HTTPS_SUPPORT */
3874 while (1)
3875 {
3876#if DEBUG_STATES
3877 MHD_DLOG (connection->daemon,
3878 _ ("In function %s handling connection at state: %s\n"),
3879 MHD_FUNC_,
3880 MHD_state_to_string (connection->state));
3881#endif
3882 switch (connection->state)
3883 {
3887 break;
3889 mhd_assert (0);
3890 break;
3893 break;
3896 mhd_assert (0);
3897 break;
3900 break;
3902 if ((connection->rq.some_payload_processed) &&
3904 {
3905 /* Some data was processed, the buffer must have some free space */
3906 mhd_assert (connection->read_buffer_offset < \
3907 connection->read_buffer_size);
3908 if (! connection->rq.have_chunked_upload)
3909 {
3910 /* Not a chunked upload. Do not read more than necessary to
3911 process the current request. */
3912 if (connection->rq.remaining_upload_size >=
3913 connection->read_buffer_offset)
3915 else
3917 }
3918 else
3919 {
3920 /* Chunked upload. The size of the current request is unknown.
3921 Continue reading as the space in the read buffer is available. */
3923 }
3924 }
3925 else
3927 break;
3929 mhd_assert (0);
3930 break;
3933 break;
3935 mhd_assert (0);
3936 break;
3939 break;
3941 mhd_assert (0);
3942 break;
3944 /* headers in buffer, keep writing */
3946 break;
3948 mhd_assert (0);
3949 break;
3952 break;
3955 break;
3958 break;
3961 break;
3963 mhd_assert (0);
3964 break;
3967 break;
3969 mhd_assert (0);
3970 break;
3973 return; /* do nothing, not even reading */
3974#ifdef UPGRADE_SUPPORT
3975 case MHD_CONNECTION_UPGRADE:
3976 mhd_assert (0);
3977 break;
3978#endif /* UPGRADE_SUPPORT */
3979 default:
3980 mhd_assert (0);
3981 }
3982
3983 if (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info))
3984 {
3985 /* Check whether the space is available to receive data */
3986 if (! check_and_grow_read_buffer_space (connection))
3987 {
3988 mhd_assert (connection->discard_request);
3989 continue;
3990 }
3991 }
3992 break; /* Everything was processed. */
3993 }
3994}
3995
3996
4009static enum MHD_Result
4011 const char *key,
4012 size_t key_size,
4013 const char *value,
4014 size_t value_size,
4015 enum MHD_ValueKind kind)
4016{
4017 struct MHD_Connection *connection = (struct MHD_Connection *) cls;
4018 if (MHD_NO ==
4019 MHD_set_connection_value_n (connection,
4020 kind,
4021 key,
4022 key_size,
4023 value,
4024 value_size))
4025 {
4026#ifdef HAVE_MESSAGES
4027 MHD_DLOG (connection->daemon,
4028 _ ("Not enough memory in pool to allocate header record!\n"));
4029#endif
4033 return MHD_NO;
4034 }
4035 return MHD_YES;
4036}
4037
4038
4039#ifdef COOKIE_SUPPORT
4040
4044enum _MHD_ParseCookie
4045{
4046 MHD_PARSE_COOKIE_OK = MHD_YES,
4047 MHD_PARSE_COOKIE_OK_LAX = 2,
4048 MHD_PARSE_COOKIE_MALFORMED = -1,
4049 MHD_PARSE_COOKIE_NO_MEMORY = MHD_NO
4050};
4051
4052
4065static enum _MHD_ParseCookie
4066parse_cookies_string (char *str,
4067 const size_t str_len,
4068 struct MHD_Connection *connection)
4069{
4070 size_t i;
4071 bool non_strict;
4072 /* Skip extra whitespaces and empty cookies */
4073 const bool allow_wsp_empty = (0 >= connection->daemon->client_discipline);
4074 /* Allow whitespaces around '=' character */
4075 const bool wsp_around_eq = (-3 >= connection->daemon->client_discipline);
4076 /* Allow whitespaces in quoted cookie value */
4077 const bool wsp_in_quoted = (-2 >= connection->daemon->client_discipline);
4078 /* Allow tab as space after semicolon between cookies */
4079 const bool tab_as_sp = (0 >= connection->daemon->client_discipline);
4080 /* Allow no space after semicolon between cookies */
4081 const bool allow_no_space = (0 >= connection->daemon->client_discipline);
4082
4083 non_strict = false;
4084 i = 0;
4085 while (i < str_len)
4086 {
4087 size_t name_start;
4088 size_t name_len;
4089 size_t value_start;
4090 size_t value_len;
4091 bool val_quoted;
4092 /* Skip any whitespaces and empty cookies */
4093 while (' ' == str[i] || '\t' == str[i] || ';' == str[i])
4094 {
4095 if (! allow_wsp_empty)
4096 return MHD_PARSE_COOKIE_MALFORMED;
4097 non_strict = true;
4098 i++;
4099 if (i == str_len)
4100 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
4101 }
4102 /* 'i' must point to the first char of cookie-name */
4103 name_start = i;
4104 /* Find the end of the cookie-name */
4105 do
4106 {
4107 const char l = str[i];
4108 if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) ||
4109 (';' == l) || (0 == l))
4110 break;
4111 } while (str_len > ++i);
4112 name_len = i - name_start;
4113 /* Skip any whitespaces */
4114 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
4115 {
4116 if (! wsp_around_eq)
4117 return MHD_PARSE_COOKIE_MALFORMED;
4118 non_strict = true;
4119 i++;
4120 }
4121 if ((str_len == i) || ('=' != str[i]) || (0 == name_len))
4122 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */
4123 /* 'i' must point to the '=' char */
4124 mhd_assert ('=' == str[i]);
4125 i++;
4126 /* Skip any whitespaces */
4127 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
4128 {
4129 if (! wsp_around_eq)
4130 return MHD_PARSE_COOKIE_MALFORMED;
4131 non_strict = true;
4132 i++;
4133 }
4134 /* 'i' must point to the first char of cookie-value */
4135 if (str_len == i)
4136 {
4137 value_start = 0;
4138 value_len = 0;
4139#ifdef _DEBUG
4140 val_quoted = false; /* This assignment used in assert */
4141#endif
4142 }
4143 else
4144 {
4145 bool valid_cookie;
4146 val_quoted = ('"' == str[i]);
4147 if (val_quoted)
4148 i++;
4149 value_start = i;
4150 /* Find the end of the cookie-value */
4151 while (str_len > i)
4152 {
4153 const char l = str[i];
4154 if ((';' == l) || ('"' == l) || (',' == l) || (';' == l) ||
4155 ('\\' == l) || (0 == l))
4156 break;
4157 if ((' ' == l) || ('\t' == l))
4158 {
4159 if (! val_quoted)
4160 break;
4161 if (! wsp_in_quoted)
4162 return MHD_PARSE_COOKIE_MALFORMED;
4163 non_strict = true;
4164 }
4165 i++;
4166 }
4167 value_len = i - value_start;
4168 if (val_quoted)
4169 {
4170 if ((str_len == i) || ('"' != str[i]))
4171 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */
4172 i++;
4173 }
4174 /* Skip any whitespaces */
4175 if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i])))
4176 {
4177 do
4178 {
4179 i++;
4180 } while (str_len > i && (' ' == str[i] || '\t' == str[i]));
4181 /* Whitespace at the end? */
4182 if (str_len > i)
4183 {
4184 if (! allow_wsp_empty)
4185 return MHD_PARSE_COOKIE_MALFORMED;
4186 non_strict = true;
4187 }
4188 }
4189 if (str_len == i)
4190 valid_cookie = true;
4191 else if (';' == str[i])
4192 valid_cookie = true;
4193 else
4194 valid_cookie = false;
4195
4196 if (! valid_cookie)
4197 return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */
4198 }
4199 mhd_assert (0 != name_len);
4200 str[name_start + name_len] = 0; /* Zero-terminate the name */
4201 if (0 != value_len)
4202 {
4203 mhd_assert (value_start + value_len <= str_len);
4204 str[value_start + value_len] = 0; /* Zero-terminate the value */
4205 if (MHD_NO ==
4208 str + name_start,
4209 name_len,
4210 str + value_start,
4211 value_len))
4212 return MHD_PARSE_COOKIE_NO_MEMORY;
4213 }
4214 else
4215 {
4216 if (MHD_NO ==
4219 str + name_start,
4220 name_len,
4221 "",
4222 0))
4223 return MHD_PARSE_COOKIE_NO_MEMORY;
4224 }
4225 if (str_len > i)
4226 {
4227 mhd_assert (0 == str[i] || ';' == str[i]);
4228 mhd_assert (! val_quoted || ';' == str[i]);
4229 mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len);
4230 i++;
4231 if (str_len == i)
4232 { /* No next cookie after semicolon */
4233 if (! allow_wsp_empty)
4234 return MHD_PARSE_COOKIE_MALFORMED;
4235 non_strict = true;
4236 }
4237 else if (' ' != str[i])
4238 {/* No space after semicolon */
4239 if (('\t' == str[i]) && tab_as_sp)
4240 i++;
4241 else if (! allow_no_space)
4242 return MHD_PARSE_COOKIE_MALFORMED;
4243 non_strict = true;
4244 }
4245 else
4246 {
4247 i++;
4248 if (str_len == i)
4249 {
4250 if (! allow_wsp_empty)
4251 return MHD_PARSE_COOKIE_MALFORMED;
4252 non_strict = true;
4253 }
4254 }
4255 }
4256 }
4257 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
4258}
4259
4260
4269static enum _MHD_ParseCookie
4270parse_cookie_header (struct MHD_Connection *connection,
4271 const char *hdr,
4272 size_t hdr_len)
4273{
4274 char *cpy;
4275 size_t i;
4276 enum _MHD_ParseCookie parse_res;
4277 struct MHD_HTTP_Req_Header *const saved_tail =
4278 connection->rq.headers_received_tail;
4279 const bool allow_partially_correct_cookie =
4280 (1 >= connection->daemon->client_discipline);
4281
4282 if (0 == hdr_len)
4283 return MHD_PARSE_COOKIE_OK;
4284
4285 cpy = MHD_connection_alloc_memory_ (connection,
4286 hdr_len + 1);
4287 if (NULL == cpy)
4288 parse_res = MHD_PARSE_COOKIE_NO_MEMORY;
4289 else
4290 {
4291 memcpy (cpy,
4292 hdr,
4293 hdr_len);
4294 cpy[hdr_len] = '\0';
4295
4296 i = 0;
4297 /* Skip all initial whitespaces */
4298 while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i]))
4299 i++;
4300
4301 parse_res = parse_cookies_string (cpy + i, hdr_len - i, connection);
4302 }
4303
4304 switch (parse_res)
4305 {
4306 case MHD_PARSE_COOKIE_OK:
4307 break;
4308 case MHD_PARSE_COOKIE_OK_LAX:
4309#ifdef HAVE_MESSAGES
4310 if (saved_tail != connection->rq.headers_received_tail)
4311 MHD_DLOG (connection->daemon,
4312 _ ("The Cookie header has been parsed, but it is not fully "
4313 "compliant with the standard.\n"));
4314#endif /* HAVE_MESSAGES */
4315 break;
4316 case MHD_PARSE_COOKIE_MALFORMED:
4317 if (saved_tail != connection->rq.headers_received_tail)
4318 {
4319 if (! allow_partially_correct_cookie)
4320 {
4321 /* Remove extracted values from partially broken cookie */
4322 /* Memory remains allocated until the end of the request processing */
4323 connection->rq.headers_received_tail = saved_tail;
4324 saved_tail->next = NULL;
4325#ifdef HAVE_MESSAGES
4326 MHD_DLOG (connection->daemon,
4327 _ ("The Cookie header has been ignored as it contains "
4328 "malformed data.\n"));
4329#endif /* HAVE_MESSAGES */
4330 }
4331#ifdef HAVE_MESSAGES
4332 else
4333 MHD_DLOG (connection->daemon,
4334 _ ("The Cookie header has been only partially parsed as it "
4335 "contains malformed data.\n"));
4336#endif /* HAVE_MESSAGES */
4337 }
4338#ifdef HAVE_MESSAGES
4339 else
4340 MHD_DLOG (connection->daemon,
4341 _ ("The Cookie header has malformed data.\n"));
4342#endif /* HAVE_MESSAGES */
4343 break;
4344 case MHD_PARSE_COOKIE_NO_MEMORY:
4345#ifdef HAVE_MESSAGES
4346 MHD_DLOG (connection->daemon,
4347 _ ("Not enough memory in the connection pool to "
4348 "parse client cookies!\n"));
4349#endif /* HAVE_MESSAGES */
4350 break;
4351 default:
4352 mhd_assert (0);
4353 break;
4354 }
4355#ifndef HAVE_MESSAGES
4356 (void) saved_tail; /* Mute compiler warning */
4357#endif /* ! HAVE_MESSAGES */
4358
4359 return parse_res;
4360}
4361
4362
4363#endif /* COOKIE_SUPPORT */
4364
4365
4369#define HTTP_VER_LEN (MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1))
4370
4380static bool
4382 const char *http_string,
4383 size_t len)
4384{
4385 const char *const h = http_string;
4386 mhd_assert (NULL != http_string);
4387
4388 /* String must start with 'HTTP/d.d', case-sensitive match.
4389 * See https://www.rfc-editor.org/rfc/rfc9112#name-http-version */
4390 if ((HTTP_VER_LEN != len) ||
4391 ('H' != h[0]) || ('T' != h[1]) || ('T' != h[2]) || ('P' != h[3]) ||
4392 ('/' != h[4])
4393 || ('.' != h[6]) ||
4394 (('0' > h[5]) || ('9' < h[5])) ||
4395 (('0' > h[7]) || ('9' < h[7])))
4396 {
4397 connection->rq.http_ver = MHD_HTTP_VER_INVALID;
4401 return false;
4402 }
4403 if (1 == h[5] - '0')
4404 {
4405 /* HTTP/1.x */
4406 if (1 == h[7] - '0')
4407 connection->rq.http_ver = MHD_HTTP_VER_1_1;
4408 else if (0 == h[7] - '0')
4409 connection->rq.http_ver = MHD_HTTP_VER_1_0;
4410 else
4411 connection->rq.http_ver = MHD_HTTP_VER_1_2__1_9;
4412
4413 return true;
4414 }
4415
4416 if (0 == h[5] - '0')
4417 {
4418 /* Too old major version */
4419 connection->rq.http_ver = MHD_HTTP_VER_TOO_OLD;
4423 return false;
4424 }
4425
4426 connection->rq.http_ver = MHD_HTTP_VER_FUTURE;
4430 return false;
4431}
4432
4433
4441static void
4443 const char *method,
4444 size_t len)
4445{
4446 const char *const m = method;
4447 mhd_assert (NULL != m);
4448 mhd_assert (0 != len);
4449
4450 if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET) == len) &&
4451 (0 == memcmp (m, MHD_HTTP_METHOD_GET, len)))
4452 connection->rq.http_mthd = MHD_HTTP_MTHD_GET;
4453 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_HEAD) == len) &&
4454 (0 == memcmp (m, MHD_HTTP_METHOD_HEAD, len)))
4455 connection->rq.http_mthd = MHD_HTTP_MTHD_HEAD;
4456 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_POST) == len) &&
4457 (0 == memcmp (m, MHD_HTTP_METHOD_POST, len)))
4458 connection->rq.http_mthd = MHD_HTTP_MTHD_POST;
4459 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_PUT) == len) &&
4460 (0 == memcmp (m, MHD_HTTP_METHOD_PUT, len)))
4461 connection->rq.http_mthd = MHD_HTTP_MTHD_PUT;
4462 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_DELETE) == len) &&
4463 (0 == memcmp (m, MHD_HTTP_METHOD_DELETE, len)))
4464 connection->rq.http_mthd = MHD_HTTP_MTHD_DELETE;
4465 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_CONNECT) == len) &&
4466 (0 == memcmp (m, MHD_HTTP_METHOD_CONNECT, len)))
4467 connection->rq.http_mthd = MHD_HTTP_MTHD_CONNECT;
4468 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_OPTIONS) == len) &&
4469 (0 == memcmp (m, MHD_HTTP_METHOD_OPTIONS, len)))
4470 connection->rq.http_mthd = MHD_HTTP_MTHD_OPTIONS;
4471 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_TRACE) == len) &&
4472 (0 == memcmp (m, MHD_HTTP_METHOD_TRACE, len)))
4473 connection->rq.http_mthd = MHD_HTTP_MTHD_TRACE;
4474 else
4475 connection->rq.http_mthd = MHD_HTTP_MTHD_OTHER;
4476}
4477
4478
4486static void
4488{
4489 struct MHD_Daemon *daemon = connection->daemon;
4490 size_t processed;
4491
4492 if (NULL != connection->rp.response)
4493 return; /* already queued a response */
4494 processed = 0;
4495 connection->rq.client_aware = true;
4496 connection->in_access_handler = true;
4497 if (MHD_NO ==
4498 daemon->default_handler (daemon->default_handler_cls,
4499 connection,
4500 connection->rq.url_for_callback,
4501 connection->rq.method,
4502 connection->rq.version,
4503 NULL,
4504 &processed,
4505 &connection->rq.client_context))
4506 {
4507 connection->in_access_handler = false;
4508 /* serious internal error, close connection */
4509 CONNECTION_CLOSE_ERROR (connection,
4510 _ ("Application reported internal error, " \
4511 "closing connection."));
4512 return;
4513 }
4514 connection->in_access_handler = false;
4515}
4516
4517
4525static void
4527{
4528 struct MHD_Daemon *daemon = connection->daemon;
4529 size_t available;
4530 bool instant_retry;
4531 char *buffer_head;
4532 const int discp_lvl = daemon->client_discipline;
4533 /* RFC does not allow LF as the line termination in chunk headers.
4534 See RFC 9112, section 7.1 and section 2.2-3 */
4535 const bool bare_lf_as_crlf = (-2 > discp_lvl);
4536 /* Allow "Bad WhiteSpace" in chunk extension.
4537 RFC 9112, Section 7.1.1, Paragraph 2 */
4538 const bool allow_bws = (2 > discp_lvl);
4539
4540 mhd_assert (NULL == connection->rp.response);
4541
4542 buffer_head = connection->read_buffer;
4543 available = connection->read_buffer_offset;
4544 do
4545 {
4546 size_t to_be_processed;
4547 size_t left_unprocessed;
4548 size_t processed_size;
4549
4550 instant_retry = false;
4551 if (connection->rq.have_chunked_upload)
4552 {
4554 if ( (connection->rq.current_chunk_offset ==
4555 connection->rq.current_chunk_size) &&
4556 (0 != connection->rq.current_chunk_size) )
4557 {
4558 /* Skip CRLF chunk termination */
4559 size_t i;
4560 mhd_assert (0 != available);
4561 /* skip new line at the *end* of a chunk */
4562 i = 0;
4563 if ( (2 <= available) &&
4564 ('\r' == buffer_head[0]) &&
4565 ('\n' == buffer_head[1]) )
4566 i += 2; /* skip CRLF */
4567 else if (bare_lf_as_crlf && ('\n' == buffer_head[0]))
4568 i++; /* skip bare LF */
4569 else if (2 > available)
4570 break; /* need more upload data */
4571 if (0 == i)
4572 {
4573 /* malformed encoding */
4577 return;
4578 }
4579 available -= i;
4580 buffer_head += i;
4581 connection->rq.current_chunk_offset = 0;
4582 connection->rq.current_chunk_size = 0;
4583 if (0 == available)
4584 break;
4585 }
4586 if (0 != connection->rq.current_chunk_size)
4587 {
4588 /* Process chunk "content" */
4589 uint64_t cur_chunk_left;
4590 mhd_assert (connection->rq.current_chunk_offset < \
4591 connection->rq.current_chunk_size);
4592 cur_chunk_left
4593 = connection->rq.current_chunk_size
4594 - connection->rq.current_chunk_offset;
4595 if (cur_chunk_left > available)
4596 to_be_processed = available;
4597 else
4598 { /* cur_chunk_left <= (size_t)available */
4599 to_be_processed = (size_t) cur_chunk_left;
4600 if (available > to_be_processed)
4601 instant_retry = true;
4602 }
4603 }
4604 else
4605 { /* Need the parse the chunk size line */
4607 size_t num_dig;
4608 uint64_t chunk_size;
4609 bool broken;
4610 bool overflow;
4611
4612 mhd_assert (0 != available);
4613
4614 overflow = false;
4615 chunk_size = 0; /* Mute possible compiler warning.
4616 The real value will be set later. */
4617
4618 num_dig = MHD_strx_to_uint64_n_ (buffer_head,
4619 available,
4620 &chunk_size);
4621 mhd_assert (num_dig <= available);
4622 if (num_dig == available)
4623 continue; /* Need line delimiter */
4624
4625 broken = (0 == num_dig);
4626 if (broken)
4627 {
4628 uint64_t dummy;
4629 /* Check whether result is invalid due to uint64_t overflow */
4630 overflow = (0 != MHD_strx_to_uint64_n_ (buffer_head,
4631 1,
4632 &dummy));
4633 }
4634 else
4635 {
4640 size_t chunk_size_line_len;
4641
4642 chunk_size_line_len = 0;
4643 if ((';' == buffer_head[num_dig]) ||
4644 (allow_bws &&
4645 ((' ' == buffer_head[num_dig]) ||
4646 ('\t' == buffer_head[num_dig]))))
4647 { /* Chunk extension or "bad whitespace" after chunk length */
4648 size_t i;
4649
4650 /* Skip bad whitespaces (if any) */
4651 for (i = num_dig; i < available; ++i)
4652 {
4653 if ((' ' != buffer_head[i]) && ('\t' != buffer_head[i]))
4654 break;
4655 }
4656 if (i == available)
4657 break; /* need more data */
4658 if (';' == buffer_head[i])
4659 {
4660 /* Chunk extension */
4661 for (++i; i < available; ++i)
4662 {
4663 if (('\r' == buffer_head[i]) ||
4664 ('\n' == buffer_head[i]))
4665 break;
4666 }
4667 if (i == available)
4668 break; /* need more data */
4669 mhd_assert (i > num_dig);
4670 mhd_assert (1 <= i);
4671 if ('\r' == buffer_head[i])
4672 {
4673 if (i + 1 == available)
4674 break; /* need more data */
4675 if ('\n' == buffer_head[i + 1])
4676 chunk_size_line_len = i + 2; /* Valid chunk header */
4677 }
4678 else
4679 {
4680 mhd_assert ('\n' == buffer_head[i]);
4681 if (bare_lf_as_crlf)
4682 chunk_size_line_len = i + 1; /* Valid chunk header */
4683 }
4684 /* The chunk header is broken
4685 if chunk_size_line_len is zero here. */
4686 }
4687 else
4688 { /* No ';' after "bad whitespace" */
4689 mhd_assert (allow_bws);
4690 mhd_assert (0 == chunk_size_line_len);
4691 }
4692 }
4693 else
4694 {
4695 /* No chunk extension */
4696 mhd_assert (available >= num_dig);
4697 if ((2 <= (available - num_dig)) &&
4698 ('\r' == buffer_head[num_dig]) &&
4699 ('\n' == buffer_head[num_dig + 1]))
4700 chunk_size_line_len = num_dig + 2;
4701 else if (bare_lf_as_crlf &&
4702 ('\n' == buffer_head[num_dig]))
4703 chunk_size_line_len = num_dig + 1;
4704 else if (2 > (available - num_dig))
4705 break; /* need more data */
4706 }
4707
4708 if (0 != chunk_size_line_len)
4709 { /* Valid termination of the chunk size line */
4710 mhd_assert (chunk_size_line_len <= available);
4711 /* Start reading payload data of the chunk */
4712 connection->rq.current_chunk_offset = 0;
4713 connection->rq.current_chunk_size = chunk_size;
4714
4715 available -= chunk_size_line_len;
4716 buffer_head += chunk_size_line_len;
4717
4718 if (0 == chunk_size)
4719 { /* The final (termination) chunk */
4720 connection->rq.remaining_upload_size = 0;
4721 break;
4722 }
4723 if (available > 0)
4724 instant_retry = true;
4725 continue;
4726 }
4727 /* Invalid chunk size line */
4728 }
4729
4730 if (! overflow)
4734 else
4738 return;
4739 }
4740 }
4741 else
4742 {
4743 /* no chunked encoding, give all to the client */
4745 mhd_assert (0 != connection->rq.remaining_upload_size);
4746 if (connection->rq.remaining_upload_size < available)
4747 to_be_processed = (size_t) connection->rq.remaining_upload_size;
4748 else
4749 to_be_processed = available;
4750 }
4751 left_unprocessed = to_be_processed;
4752 connection->rq.client_aware = true;
4753 connection->in_access_handler = true;
4754 if (MHD_NO ==
4755 daemon->default_handler (daemon->default_handler_cls,
4756 connection,
4757 connection->rq.url_for_callback,
4758 connection->rq.method,
4759 connection->rq.version,
4760 buffer_head,
4761 &left_unprocessed,
4762 &connection->rq.client_context))
4763 {
4764 connection->in_access_handler = false;
4765 /* serious internal error, close connection */
4766 CONNECTION_CLOSE_ERROR (connection,
4767 _ ("Application reported internal error, " \
4768 "closing connection."));
4769 return;
4770 }
4771 connection->in_access_handler = false;
4772
4773 if (left_unprocessed > to_be_processed)
4774 MHD_PANIC (_ ("libmicrohttpd API violation.\n"));
4775
4776 connection->rq.some_payload_processed =
4777 (left_unprocessed != to_be_processed);
4778
4779 if (0 != left_unprocessed)
4780 {
4781 instant_retry = false; /* client did not process everything */
4782#ifdef HAVE_MESSAGES
4783 if ((! connection->rq.some_payload_processed) &&
4784 (! connection->suspended))
4785 {
4786 /* client did not process any upload data, complain if
4787 the setup was incorrect, which may prevent us from
4788 handling the rest of the request */
4789 if (MHD_D_IS_USING_THREADS_ (daemon))
4790 MHD_DLOG (daemon,
4791 _ ("WARNING: Access Handler Callback has not processed " \
4792 "any upload data and connection is not suspended. " \
4793 "This may result in hung connection.\n"));
4794 }
4795#endif /* HAVE_MESSAGES */
4796 }
4797 processed_size = to_be_processed - left_unprocessed;
4798 /* dh left "processed" bytes in buffer for next time... */
4799 buffer_head += processed_size;
4800 available -= processed_size;
4801 if (! connection->rq.have_chunked_upload)
4802 {
4804 connection->rq.remaining_upload_size -= processed_size;
4805 }
4806 else
4807 {
4809 connection->rq.current_chunk_offset += processed_size;
4810 }
4811 } while (instant_retry);
4812 /* TODO: zero out reused memory region */
4813 if ( (available > 0) &&
4814 (buffer_head != connection->read_buffer) )
4815 memmove (connection->read_buffer,
4816 buffer_head,
4817 available);
4818 else
4819 mhd_assert ((0 == available) || \
4820 (connection->read_buffer_offset == available));
4821 connection->read_buffer_offset = available;
4822}
4823
4824
4833static enum MHD_Result
4835 enum MHD_CONNECTION_STATE next_state)
4836{
4837 if ( (connection->write_buffer_append_offset !=
4838 connection->write_buffer_send_offset)
4839 /* || data_in_tls_buffers == true */
4840 )
4841 return MHD_NO;
4842 connection->write_buffer_append_offset = 0;
4843 connection->write_buffer_send_offset = 0;
4844 connection->state = next_state;
4845 return MHD_YES;
4846}
4847
4848
4856static void
4858{
4859 struct MHD_HTTP_Req_Header *pos;
4860 bool have_hdr_host;
4861 bool have_cntn_len;
4862
4863 have_hdr_host = false;
4864 have_cntn_len = false;
4865
4866 /* The presence of the request body is indicated by "Content-Length:" or
4867 "Transfer-Encoding:" request headers.
4868 See RFC 9112 section 6.1, 6.2, 6.3; RFC 9110 Section 8.6. */
4869
4872
4873 for (pos = c->rq.headers_received; NULL != pos; pos = pos->next)
4874 {
4875 if (MHD_HEADER_KIND != pos->kind)
4876 continue;
4877
4879 pos->header,
4880 pos->header_size))
4881 {
4882 if (have_hdr_host)
4883 {
4884 if (-3 < c->daemon->client_discipline)
4885 {
4889 return;
4890 }
4891 }
4892 have_hdr_host = true;
4893 }
4894#ifdef COOKIE_SUPPORT
4896 pos->header,
4897 pos->header_size))
4898 {
4899 if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (c,
4900 pos->value,
4901 pos->value_size))
4902 {
4903 handle_req_cookie_no_space (c);
4904 return;
4905 }
4906 }
4907#endif /* COOKIE_SUPPORT */
4909 pos->header,
4910 pos->header_size))
4911 {
4912 const char *clen;
4913 size_t val_len;
4914 size_t num_digits;
4915 uint64_t decoded_val;
4916
4917 val_len = pos->value_size;
4918 clen = pos->value;
4919
4920 mhd_assert ('\0' == clen[val_len]);
4921
4922 if ((have_cntn_len)
4923 && (0 < c->daemon->client_discipline))
4924 {
4928 return;
4929 }
4930
4931 num_digits = MHD_str_to_uint64_n_ (clen,
4932 val_len,
4933 &decoded_val);
4934
4935 if ((0 == num_digits) ||
4936 (val_len != num_digits) ||
4937 (MHD_SIZE_UNKNOWN == decoded_val))
4938 { /* Bad or too large value */
4939
4940 if (have_cntn_len)
4941 {
4945 return;
4946 }
4947
4948 if ((val_len != num_digits)
4949 || ('0' > clen[0]) || ('9' < clen[0]))
4950 {
4951#ifdef HAVE_MESSAGES
4952 MHD_DLOG (c->daemon,
4953 _ ("Malformed 'Content-Length' header. " \
4954 "Closing connection.\n"));
4955#endif
4959 return;
4960 }
4961
4962#ifdef HAVE_MESSAGES
4963 MHD_DLOG (c->daemon,
4964 _ ("Too large value of 'Content-Length' header. " \
4965 "Closing connection.\n"));
4966#endif
4970 return;
4971 }
4972
4973 if ((have_cntn_len) &&
4974 (c->rq.remaining_upload_size != decoded_val))
4975 {
4976 if (-3 < c->daemon->client_discipline)
4977 {
4981 return;
4982 }
4983 /* The HTTP framing is broken.
4984 Use smallest (safest) length value and force-close
4985 after processing of this request. */
4986 if (c->rq.remaining_upload_size > decoded_val)
4987 c->rq.remaining_upload_size = decoded_val;
4989 }
4990 else
4991 c->rq.remaining_upload_size = decoded_val;
4992
4993 have_cntn_len = true;
4994 }
4997 pos->header,
4998 pos->header_size))
4999 {
5000
5001 if (MHD_HTTP_VER_1_1 > c->rq.http_ver)
5002 {
5003 /* RFC 9112, 6.1, last paragraph */
5004 if (0 < c->daemon->client_discipline)
5005 {
5009 return;
5010 }
5011 /* HTTP framing potentially broken */
5013 }
5014
5015 if (c->rq.have_chunked_upload
5016 || ! MHD_str_equal_caseless_s_bin_n_ ("chunked",
5017 pos->value,
5018 pos->value_size))
5019 {
5025 return;
5026 }
5027 c->rq.have_chunked_upload = true;
5029 }
5030 }
5031
5032 if (c->rq.have_chunked_upload && have_cntn_len)
5033 {
5034 if (0 < c->daemon->client_discipline)
5035 {
5039 return;
5040 }
5041 else
5042 {
5043#ifdef HAVE_MESSAGES
5044 MHD_DLOG (c->daemon,
5045 _ ("The 'Content-Length' request header is ignored "
5046 "as chunked Transfer-Encoding is set in the "
5047 "same request.\n"));
5048#endif /* HAVE_MESSAGES */
5050 /* Must close connection after reply to prevent potential attack */
5052 }
5053 }
5054
5057 mhd_assert ((0 == c->rq.remaining_upload_size) ||
5058 have_cntn_len || c->rq.have_chunked_upload);
5059
5060 if (! have_hdr_host
5062 && (-3 < c->daemon->client_discipline))
5063 {
5064#ifdef HAVE_MESSAGES
5065 MHD_DLOG (c->daemon,
5066 _ ("Received HTTP/1.1 request without `Host' header.\n"));
5067#endif
5071 return;
5072 }
5073}
5074
5075
5083_MHD_static_inline void
5085{
5086 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
5087}
5088
5089
5094_MHD_static_inline void
5096{
5098 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
5100}
5101
5102
5103#ifndef MHD_MAX_EMPTY_LINES_SKIP
5108#define MHD_MAX_EMPTY_LINES_SKIP 1024
5109#endif /* ! MHD_MAX_EMPTY_LINES_SKIP */
5110
5118static bool
5120{
5121 size_t p;
5122 const int discp_lvl = c->daemon->client_discipline;
5123 /* Allow to skip one or more empty lines before the request line.
5124 RFC 9112, section 2.2 */
5125 const bool skip_empty_lines = (1 >= discp_lvl);
5126 /* Allow to skip more then one empty line before the request line.
5127 RFC 9112, section 2.2 */
5128 const bool skip_several_empty_lines = (skip_empty_lines && (0 >= discp_lvl));
5129 /* Allow to skip number of unlimited empty lines before the request line.
5130 RFC 9112, section 2.2 */
5131 const bool skip_unlimited_empty_lines =
5132 (skip_empty_lines && (-3 >= discp_lvl));
5133 /* Treat bare LF as the end of the line.
5134 RFC 9112, section 2.2 */
5135 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
5136 /* Treat tab as whitespace delimiter.
5137 RFC 9112, section 3 */
5138 const bool tab_as_wsp = (0 >= discp_lvl);
5139 /* Treat VT (vertical tab) and FF (form feed) as whitespace delimiters.
5140 RFC 9112, section 3 */
5141 const bool other_wsp_as_wsp = (-1 >= discp_lvl);
5142 /* Treat continuous whitespace block as a single space.
5143 RFC 9112, section 3 */
5144 const bool wsp_blocks = (-1 >= discp_lvl);
5145 /* Parse whitespace in URI, special parsing of the request line.
5146 RFC 9112, section 3.2 */
5147 const bool wsp_in_uri = (0 >= discp_lvl);
5148 /* Keep whitespace in URI, give app URI with whitespace instead of
5149 automatic redirect to fixed URI.
5150 Violates RFC 9112, section 3.2 */
5151 const bool wsp_in_uri_keep = (-2 >= discp_lvl);
5152 /* Keep bare CR character as is.
5153 Violates RFC 9112, section 2.2 */
5154 const bool bare_cr_keep = (wsp_in_uri_keep && (-3 >= discp_lvl));
5155 /* Treat bare CR as space; replace it with space before processing.
5156 RFC 9112, section 2.2 */
5157 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
5158
5161 mhd_assert (NULL == c->rq.method || \
5166 0 != c->rq.hdrs.rq_line.proc_pos);
5167
5168 if (0 == c->read_buffer_offset)
5169 {
5171 return false; /* No data to process */
5172 }
5173 p = c->rq.hdrs.rq_line.proc_pos;
5174 mhd_assert (p <= c->read_buffer_offset);
5175
5176 /* Skip empty lines, if any (and if allowed) */
5177 /* See RFC 9112, section 2.2 */
5178 if ((0 == p)
5179 && (skip_empty_lines))
5180 {
5181 /* Skip empty lines before the request line.
5182 See RFC 9112, section 2.2 */
5183 bool is_empty_line;
5185 mhd_assert (NULL == c->rq.method);
5186 mhd_assert (NULL == c->rq.url);
5187 mhd_assert (0 == c->rq.url_len);
5189 mhd_assert (0 == c->rq.req_target_len);
5190 mhd_assert (NULL == c->rq.version);
5191 do
5192 {
5193 is_empty_line = false;
5194 if ('\r' == c->read_buffer[0])
5195 {
5196 if (1 == c->read_buffer_offset)
5197 return false; /* Not enough data yet */
5198 if ('\n' == c->read_buffer[1])
5199 {
5200 is_empty_line = true;
5201 c->read_buffer += 2;
5202 c->read_buffer_size -= 2;
5203 c->read_buffer_offset -= 2;
5205 }
5206 }
5207 else if (('\n' == c->read_buffer[0]) &&
5208 (bare_lf_as_crlf))
5209 {
5210 is_empty_line = true;
5211 c->read_buffer += 1;
5212 c->read_buffer_size -= 1;
5213 c->read_buffer_offset -= 1;
5215 }
5216 if (is_empty_line)
5217 {
5218 if ((! skip_unlimited_empty_lines) &&
5219 (((unsigned int) ((skip_several_empty_lines) ?
5222 {
5224 _ ("Too many meaningless extra empty lines " \
5225 "received before the request"));
5226 return true; /* Process connection closure */
5227 }
5228 if (0 == c->read_buffer_offset)
5229 return false; /* No more data to process */
5230 }
5231 } while (is_empty_line);
5232 }
5233 /* All empty lines are skipped */
5234
5236 /* Read and parse the request line */
5238
5239 while (p < c->read_buffer_offset)
5240 {
5241 const char chr = c->read_buffer[p];
5242 bool end_of_line;
5243 /*
5244 The processing logic is different depending on the configured strictness:
5245
5246 When whitespace BLOCKS are NOT ALLOWED, the end of the whitespace is
5247 processed BEFORE processing of the current character.
5248 When whitespace BLOCKS are ALLOWED, the end of the whitespace is
5249 processed AFTER processing of the current character.
5250
5251 When space char in the URI is ALLOWED, the delimiter between the URI and
5252 the HTTP version string is processed only at the END of the line.
5253 When space in the URI is NOT ALLOWED, the delimiter between the URI and
5254 the HTTP version string is processed as soon as the FIRST whitespace is
5255 found after URI start.
5256 */
5257
5258 end_of_line = false;
5259
5260 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5261 (c->rq.hdrs.rq_line.last_ws_end > \
5263 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_start) || \
5264 (0 != c->rq.hdrs.rq_line.last_ws_end));
5265
5266 /* Check for the end of the line */
5267 if ('\r' == chr)
5268 {
5269 if (p + 1 == c->read_buffer_offset)
5270 {
5271 c->rq.hdrs.rq_line.proc_pos = p;
5272 return false; /* Not enough data yet */
5273 }
5274 else if ('\n' == c->read_buffer[p + 1])
5275 end_of_line = true;
5276 else
5277 {
5278 /* Bare CR alone */
5279 /* Must be rejected or replaced with space char.
5280 See RFC 9112, section 2.2 */
5281 if (bare_cr_as_sp)
5282 {
5283 c->read_buffer[p] = ' ';
5285 continue; /* Re-start processing of the current character */
5286 }
5287 else if (! bare_cr_keep)
5288 {
5289 /* A quick simple check whether this line looks like an HTTP request */
5290 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5292 {
5296 }
5297 else
5299 _ ("Bare CR characters are not allowed " \
5300 "in the request line.\n"));
5301 return true; /* Error in the request */
5302 }
5303 }
5304 }
5305 else if ('\n' == chr)
5306 {
5307 /* Bare LF may be recognised as a line delimiter.
5308 See RFC 9112, section 2.2 */
5309 if (bare_lf_as_crlf)
5310 end_of_line = true;
5311 else
5312 {
5313 /* While RFC does not enforce error for bare LF character,
5314 if this char is not treated as a line delimiter, it should be
5315 rejected to avoid any security weakness due to request smuggling. */
5316 /* A quick simple check whether this line looks like an HTTP request */
5317 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5319 {
5323 }
5324 else
5326 _ ("Bare LF characters are not allowed " \
5327 "in the request line.\n"));
5328 return true; /* Error in the request */
5329 }
5330 }
5331
5332 if (end_of_line)
5333 {
5334 /* Handle the end of the request line */
5335
5336 if (NULL != c->rq.method)
5337 {
5338 if (wsp_in_uri)
5339 {
5340 /* The end of the URI and the start of the HTTP version string
5341 should be determined now. */
5342 mhd_assert (NULL == c->rq.version);
5343 mhd_assert (0 == c->rq.req_target_len);
5344 if (0 != c->rq.hdrs.rq_line.last_ws_end)
5345 {
5346 /* Determine the end and the length of the URI */
5347 if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5348 {
5349 c->read_buffer [c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5350 c->rq.req_target_len =
5352 - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5353 }
5354 else if ((c->rq.hdrs.rq_line.last_ws_start + 1 <
5355 c->rq.hdrs.rq_line.last_ws_end) &&
5356 (HTTP_VER_LEN == (p - c->rq.hdrs.rq_line.last_ws_end)))
5357 {
5358 /* Found only HTTP method and HTTP version and more than one
5359 whitespace between them. Assume zero-length URI. */
5360 mhd_assert (wsp_blocks);
5362 c->read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5363 c->rq.hdrs.rq_line.rq_tgt =
5365 c->rq.req_target_len = 0;
5368 }
5369 /* Determine the start of the HTTP version string */
5370 if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5371 {
5373 }
5374 }
5375 }
5376 else
5377 {
5378 /* The end of the URI and the start of the HTTP version string
5379 should be already known. */
5380 if ((NULL == c->rq.version)
5381 && (NULL != c->rq.hdrs.rq_line.rq_tgt)
5382 && (HTTP_VER_LEN == p - (size_t) (c->rq.hdrs.rq_line.rq_tgt
5383 - c->read_buffer))
5384 && (0 != c->read_buffer[(size_t)
5385 (c->rq.hdrs.rq_line.rq_tgt
5386 - c->read_buffer) - 1]))
5387 {
5388 /* Found only HTTP method and HTTP version and more than one
5389 whitespace between them. Assume zero-length URI. */
5390 size_t uri_pos;
5391 mhd_assert (wsp_blocks);
5392 mhd_assert (0 == c->rq.req_target_len);
5393 uri_pos = (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer) - 1;
5394 mhd_assert (uri_pos < p);
5395 c->rq.version = c->rq.hdrs.rq_line.rq_tgt;
5396 c->read_buffer[uri_pos] = 0; /* Zero terminate the URI */
5397 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + uri_pos;
5398 c->rq.req_target_len = 0;
5401 }
5402 }
5403
5404 if (NULL != c->rq.version)
5405 {
5407 if (! parse_http_version (c, c->rq.version,
5408 p
5409 - (size_t) (c->rq.version
5410 - c->read_buffer)))
5411 {
5413 return true; /* Unsupported / broken HTTP version */
5414 }
5415 c->read_buffer[p] = 0; /* Zero terminate the HTTP version strings */
5416 if ('\r' == chr)
5417 {
5418 p++; /* Consume CR */
5419 mhd_assert (p < c->read_buffer_offset); /* The next character has been already checked */
5420 }
5421 p++; /* Consume LF */
5422 c->read_buffer += p;
5423 c->read_buffer_size -= p;
5424 c->read_buffer_offset -= p;
5426 c->rq.req_target_len);
5428 (0 != c->rq.req_target_len));
5430 ((size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5431 - c->rq.hdrs.rq_line.rq_tgt) < \
5432 c->rq.req_target_len));
5434 (c->rq.hdrs.rq_line.rq_tgt_qmark >= \
5435 c->rq.hdrs.rq_line.rq_tgt));
5436 return true; /* The request line is successfully parsed */
5437 }
5438 }
5439 /* Error in the request line */
5440
5441 /* A quick simple check whether this line looks like an HTTP request */
5442 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5444 {
5448 }
5449 else
5451 _ ("The request line is malformed.\n"));
5452
5453 return true;
5454 }
5455
5456 /* Process possible end of the previously found whitespace delimiter */
5457 if ((! wsp_blocks) &&
5458 (p == c->rq.hdrs.rq_line.last_ws_end) &&
5459 (0 != c->rq.hdrs.rq_line.last_ws_end))
5460 {
5461 /* Previous character was a whitespace char and whitespace blocks
5462 are not allowed. */
5463 /* The current position is the next character after
5464 a whitespace delimiter */
5465 if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5466 {
5467 /* The current position is the start of the URI */
5468 mhd_assert (0 == c->rq.req_target_len);
5469 mhd_assert (NULL == c->rq.version);
5470 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5471 /* Reset the whitespace marker */
5473 c->rq.hdrs.rq_line.last_ws_end = 0;
5474 }
5475 else
5476 {
5477 /* It was a whitespace after the start of the URI */
5478 if (! wsp_in_uri)
5479 {
5480 mhd_assert ((0 != c->rq.req_target_len) || \
5481 (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5482 mhd_assert (NULL == c->rq.version); /* Too many whitespaces? This error is handled at whitespace start */
5483 c->rq.version = c->read_buffer + p;
5484 /* Reset the whitespace marker */
5486 c->rq.hdrs.rq_line.last_ws_end = 0;
5487 }
5488 }
5489 }
5490
5491 /* Process the current character.
5492 Is it not the end of the line. */
5493 if ((' ' == chr)
5494 || (('\t' == chr) && (tab_as_wsp))
5495 || ((other_wsp_as_wsp) && ((0xb == chr) || (0xc == chr))))
5496 {
5497 /* A whitespace character */
5498 if ((0 == c->rq.hdrs.rq_line.last_ws_end) ||
5499 (p != c->rq.hdrs.rq_line.last_ws_end) ||
5500 (! wsp_blocks))
5501 {
5502 /* Found first whitespace char of the new whitespace block */
5503 if (NULL == c->rq.method)
5504 {
5505 /* Found the end of the HTTP method string */
5509 mhd_assert (0 == c->rq.req_target_len);
5510 mhd_assert (NULL == c->rq.version);
5511 if (0 == p)
5512 {
5514 _ ("The request line starts with "
5515 "a whitespace.\n"));
5516 return true; /* Error in the request */
5517 }
5518 c->read_buffer[p] = 0; /* Zero-terminate the request method string */
5519 c->rq.method = c->read_buffer;
5520 parse_http_std_method (c, c->rq.method, p);
5521 }
5522 else
5523 {
5524 /* A whitespace after the start of the URI */
5525 if (! wsp_in_uri)
5526 {
5527 /* Whitespace in URI is not allowed to be parsed */
5528 if (NULL == c->rq.version)
5529 {
5531 /* This is a delimiter between URI and HTTP version string */
5532 c->read_buffer[p] = 0; /* Zero-terminate request URI string */
5533 mhd_assert (((size_t) (c->rq.hdrs.rq_line.rq_tgt \
5534 - c->read_buffer)) <= p);
5535 c->rq.req_target_len =
5536 p - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5537 }
5538 else
5539 {
5540 /* This is a delimiter AFTER version string */
5541
5542 /* A quick simple check whether this line looks like an HTTP request */
5543 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5545 {
5549 }
5550 else
5552 _ ("The request line has more than "
5553 "two whitespaces.\n"));
5554 return true; /* Error in the request */
5555 }
5556 }
5557 else
5558 {
5559 /* Whitespace in URI is allowed to be parsed */
5560 if (0 != c->rq.hdrs.rq_line.last_ws_end)
5561 {
5562 /* The whitespace after the start of the URI has been found already */
5566 }
5567 }
5568 }
5570 c->rq.hdrs.rq_line.last_ws_end = p + 1; /* Will be updated on the next char parsing */
5571 }
5572 else
5573 {
5574 /* Continuation of the whitespace block */
5576 mhd_assert (0 != p);
5577 c->rq.hdrs.rq_line.last_ws_end = p + 1;
5578 }
5579 }
5580 else
5581 {
5582 /* Non-whitespace char, not the end of the line */
5583 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5584 (c->rq.hdrs.rq_line.last_ws_end == p) || \
5585 wsp_in_uri);
5586
5587 if ((p == c->rq.hdrs.rq_line.last_ws_end) &&
5588 (0 != c->rq.hdrs.rq_line.last_ws_end) &&
5589 (wsp_blocks))
5590 {
5591 /* The end of the whitespace block */
5592 if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5593 {
5594 /* This is the first character of the URI */
5595 mhd_assert (0 == c->rq.req_target_len);
5596 mhd_assert (NULL == c->rq.version);
5597 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5598 /* Reset the whitespace marker */
5600 c->rq.hdrs.rq_line.last_ws_end = 0;
5601 }
5602 else
5603 {
5604 if (! wsp_in_uri)
5605 {
5606 /* This is the first character of the HTTP version */
5608 mhd_assert ((0 != c->rq.req_target_len) || \
5609 (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5610 mhd_assert (NULL == c->rq.version); /* Handled at whitespace start */
5611 c->rq.version = c->read_buffer + p;
5612 /* Reset the whitespace marker */
5614 c->rq.hdrs.rq_line.last_ws_end = 0;
5615 }
5616 }
5617 }
5618
5619 /* Handle other special characters */
5620 if ('?' == chr)
5621 {
5622 if ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) &&
5623 (NULL != c->rq.hdrs.rq_line.rq_tgt))
5624 {
5626 }
5627 }
5628 else if ((0xb == chr) || (0xc == chr))
5629 {
5630 /* VT or LF characters */
5631 mhd_assert (! other_wsp_as_wsp);
5632 if ((NULL != c->rq.hdrs.rq_line.rq_tgt) &&
5633 (NULL == c->rq.version) &&
5634 (wsp_in_uri))
5635 {
5637 }
5638 else
5639 {
5641 _ ("Invalid character is in the "
5642 "request line.\n"));
5643 return true; /* Error in the request */
5644 }
5645 }
5646 else if (0 == chr)
5647 {
5648 /* NUL character */
5650 _ ("The NUL character is in the "
5651 "request line.\n"));
5652 return true; /* Error in the request */
5653 }
5654 }
5655
5656 p++;
5657 }
5658
5659 c->rq.hdrs.rq_line.proc_pos = p;
5660 return false; /* Not enough data yet */
5661}
5662
5663
5664#ifndef MHD_MAX_FIXED_URI_LEN
5668#define MHD_MAX_FIXED_URI_LEN (64 * 1024)
5669#endif /* ! MHD_MAX_FIXED_URI_LEN */
5670
5678static void
5680{
5681 char *b;
5682 size_t fixed_uri_len;
5683 size_t i;
5684 size_t o;
5685 char *hdr_name;
5686 size_t hdr_name_len;
5687
5691 c->rq.req_target_len);
5692 fixed_uri_len = c->rq.req_target_len
5693 + 2 * c->rq.hdrs.rq_line.num_ws_in_uri;
5694 if ( (fixed_uri_len + 200 > c->daemon->pool_size) ||
5695 (fixed_uri_len > MHD_MAX_FIXED_URI_LEN) ||
5696 (NULL == (b = malloc (fixed_uri_len + 1))) )
5697 {
5699 _ ("The request has whitespace character is " \
5700 "in the URI and the URI is too large to " \
5701 "send automatic redirect to fixed URI.\n"));
5702 return;
5703 }
5704 i = 0;
5705 o = 0;
5706
5707 do
5708 {
5709 const char chr = c->rq.hdrs.rq_line.rq_tgt[i++];
5710 /* The number of bytes that this iteration appends to the buffer. */
5711 const size_t add_size =
5712 ((' ' == chr) || ('\t' == chr) || (0x0B == chr) || (0x0C == chr)) ? 3 : 1;
5713
5714 mhd_assert ('\r' != chr); /* Replaced during request line parsing */
5715 mhd_assert ('\n' != chr); /* Rejected during request line parsing */
5716 mhd_assert (0 != chr); /* Rejected during request line parsing */
5717 /* Check the remaining space before write. */
5718 if (MHD_CHECK_FAILED_ (add_size <= fixed_uri_len - o))
5719 {
5721 "add_size <= fixed_uri_len - o");
5722 free (b);
5724 NULL);
5725 return;
5726 }
5727 switch (chr)
5728 {
5729 case ' ':
5730 b[o++] = '%';
5731 b[o++] = '2';
5732 b[o++] = '0';
5733 break;
5734 case '\t':
5735 b[o++] = '%';
5736 b[o++] = '0';
5737 b[o++] = '9';
5738 break;
5739 case 0x0B: /* VT (vertical tab) */
5740 b[o++] = '%';
5741 b[o++] = '0';
5742 b[o++] = 'B';
5743 break;
5744 case 0x0C: /* FF (form feed) */
5745 b[o++] = '%';
5746 b[o++] = '0';
5747 b[o++] = 'C';
5748 break;
5749 default:
5750 b[o++] = chr;
5751 break;
5752 }
5753 } while (i < c->rq.req_target_len);
5754 mhd_assert (fixed_uri_len == o);
5755 b[o] = 0; /* Zero-terminate the result */
5756
5758 hdr_name = malloc (hdr_name_len + 1);
5759 if (NULL != hdr_name)
5760 {
5761 memcpy (hdr_name,
5763 hdr_name_len + 1);
5764 /* hdr_name and b are free()d within this call */
5768 hdr_name,
5769 hdr_name_len,
5770 b,
5771 o);
5772 return;
5773 }
5774 free (b);
5776 _ ("The request has whitespace character is in the " \
5777 "URI.\n"));
5778 return;
5779}
5780
5781
5788static bool
5790{
5791#ifdef _DEBUG
5792 size_t params_len;
5793#endif /* _DEBUG */
5795 mhd_assert (NULL == c->rq.url);
5796 mhd_assert (0 == c->rq.url_len);
5802 (c->rq.req_target_len > \
5803 (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5804 - c->rq.hdrs.rq_line.rq_tgt)));
5805
5806 /* Log callback before the request-target is modified/decoded */
5807 if (NULL != c->daemon->uri_log_callback)
5808 {
5809 c->rq.client_aware = true;
5810 c->rq.client_context =
5812 c->rq.hdrs.rq_line.rq_tgt,
5813 c);
5814 }
5815
5816 if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark)
5817 {
5818#ifdef _DEBUG
5819 params_len =
5821 - (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark - c->rq.hdrs.rq_line.rq_tgt);
5822#endif /* _DEBUG */
5823 c->rq.hdrs.rq_line.rq_tgt_qmark[0] = 0; /* Replace '?' with zero termination */
5824 if (MHD_NO == MHD_parse_arguments_ (c,
5826 c->rq.hdrs.rq_line.rq_tgt_qmark + 1,
5828 c))
5829 {
5831 return false;
5832 }
5833 }
5834#ifdef _DEBUG
5835 else
5836 params_len = 0;
5837#endif /* _DEBUG */
5838
5840 mhd_assert (strlen (c->rq.hdrs.rq_line.rq_tgt) == \
5841 c->rq.req_target_len - params_len);
5842
5843 /* Finally unescape URI itself */
5844 c->rq.url_len =
5846 c,
5847 c->rq.hdrs.rq_line.rq_tgt);
5848 c->rq.url = c->rq.hdrs.rq_line.rq_tgt;
5849
5850 if (2 == c->daemon->allow_bzero_in_url)
5851 c->rq.url_for_callback = c->rq.url;
5852 else if (strlen (c->rq.url) == c->rq.url_len)
5853 c->rq.url_for_callback = c->rq.url;
5854 else if (0 == c->daemon->allow_bzero_in_url)
5855 {
5859 return false;
5860 }
5861
5862 return true;
5863}
5864
5865
5873static bool
5875{
5876 const int discp_lvl = c->daemon->client_discipline;
5877 /* Parse whitespace in URI, special parsing of the request line */
5878 const bool wsp_in_uri = (0 >= discp_lvl);
5879 /* Keep whitespace in URI, give app URI with whitespace instead of
5880 automatic redirect to fixed URI */
5881 const bool wsp_in_uri_keep = (-2 >= discp_lvl);
5882
5883 if (! get_request_line_inner (c))
5884 {
5885 /* End of the request line has not been found yet */
5886 mhd_assert ((! wsp_in_uri) || NULL == c->rq.version);
5887 if ((NULL != c->rq.version) &&
5888 (HTTP_VER_LEN <
5890 - (size_t) (c->rq.version - c->read_buffer))))
5891 {
5896 return true; /* Error in the request */
5897 }
5898 return false;
5899 }
5901 return true; /* Error in the request */
5902
5904 mhd_assert (NULL == c->rq.url);
5905 mhd_assert (0 == c->rq.url_len);
5908 if (0 != c->rq.hdrs.rq_line.num_ws_in_uri)
5909 {
5910 if (! wsp_in_uri)
5911 {
5915 return true; /* Error in the request */
5916 }
5917 if (! wsp_in_uri_keep)
5918 {
5920 return true; /* Error in the request */
5921 }
5922 }
5923 if (! process_request_target (c))
5924 return true; /* Error in processing */
5925
5927 return true;
5928}
5929
5930
5953
5954
5962static bool
5964{
5965 switch (chr)
5966 {
5967 case '!':
5968 case '#':
5969 case '$':
5970 case '%':
5971 case '&':
5972 case '\'':
5973 case '*':
5974 case '+':
5975 case '-':
5976 case '.':
5977 case '^':
5978 case '_':
5979 case '`':
5980 case '|':
5981 case '~':
5982 case 'a':
5983 case 'b':
5984 case 'c':
5985 case 'd':
5986 case 'e':
5987 case 'f':
5988 case 'g':
5989 case 'h':
5990 case 'i':
5991 case 'j':
5992 case 'k':
5993 case 'l':
5994 case 'm':
5995 case 'n':
5996 case 'o':
5997 case 'p':
5998 case 'q':
5999 case 'r':
6000 case 's':
6001 case 't':
6002 case 'u':
6003 case 'v':
6004 case 'w':
6005 case 'x':
6006 case 'y':
6007 case 'z':
6008 case 'A':
6009 case 'B':
6010 case 'C':
6011 case 'D':
6012 case 'E':
6013 case 'F':
6014 case 'G':
6015 case 'H':
6016 case 'I':
6017 case 'J':
6018 case 'K':
6019 case 'L':
6020 case 'M':
6021 case 'N':
6022 case 'O':
6023 case 'P':
6024 case 'Q':
6025 case 'R':
6026 case 'S':
6027 case 'T':
6028 case 'U':
6029 case 'V':
6030 case 'W':
6031 case 'X':
6032 case 'Y':
6033 case 'Z':
6034 case '0':
6035 case '1':
6036 case '2':
6037 case '3':
6038 case '4':
6039 case '5':
6040 case '6':
6041 case '7':
6042 case '8':
6043 case '9':
6044 return true;
6045 default:
6046 return false;
6047 }
6048}
6049
6050
6062static enum MHD_HdrLineReadRes_
6064 bool process_footers,
6065 struct _MHD_str_w_len *hdr_name,
6066 struct _MHD_str_w_len *hdr_value)
6067{
6068 const int discp_lvl = c->daemon->client_discipline;
6069 /* Treat bare LF as the end of the line.
6070 RFC 9112, section 2.2-3
6071 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
6072 Bare LF is processed as end of the line or rejected as broken request. */
6073 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
6074 /* Keep bare CR character as is.
6075 Violates RFC 9112, section 2.2-4 */
6076 const bool bare_cr_keep = (-3 >= discp_lvl);
6077 /* Treat bare CR as space; replace it with space before processing.
6078 RFC 9112, section 2.2-4 */
6079 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
6080 /* Treat NUL as space; replace it with space before processing.
6081 RFC 9110, section 5.5-5 */
6082 const bool nul_as_sp = (-1 >= discp_lvl);
6083 /* Allow folded header lines.
6084 RFC 9112, section 5.2-4 */
6085 const bool allow_folded = (0 >= discp_lvl);
6086 /* Do not reject headers with the whitespace at the start of the first line.
6087 When allowed, the first line with whitespace character at the first
6088 position is ignored (as well as all possible line foldings of the first
6089 line).
6090 RFC 9112, section 2.2-8 */
6091 const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl);
6092 /* Allow whitespace in header (field) name.
6093 Violates RFC 9110, section 5.1-2 */
6094 const bool allow_wsp_in_name = (-2 >= discp_lvl);
6095 /* Allow zero-length header (field) name.
6096 Violates RFC 9110, section 5.1-2 */
6097 const bool allow_empty_name = (-2 >= discp_lvl);
6098 /* Allow non-tchar characters in header (field) name.
6099 Violates RFC 9110, section 5.1 */
6100 const bool allow_extended_charset = (-2 >= discp_lvl);
6101 /* Allow whitespace before colon.
6102 Violates RFC 9112, section 5.1-2 */
6103 const bool allow_wsp_before_colon = (-3 >= discp_lvl);
6104 /* Do not abort the request when header line has no colon, just skip such
6105 bad lines.
6106 RFC 9112, section 5-1 */
6107 const bool allow_line_without_colon = (-2 >= discp_lvl);
6108
6109 size_t p;
6111#if ! defined (HAVE_MESSAGES) && ! defined(_DEBUG)
6112 (void) process_footers; /* Unused parameter */
6113#endif /* !HAVE_MESSAGES && !_DEBUG */
6114
6115 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6117 c->state);
6118
6119 p = c->rq.hdrs.hdr.proc_pos;
6120
6121 mhd_assert (p <= c->read_buffer_offset);
6122 while (p < c->read_buffer_offset)
6123 {
6124 const char chr = c->read_buffer[p];
6125 bool end_of_line;
6126
6127 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
6128 (c->rq.hdrs.hdr.name_len < p));
6129 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p));
6130 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
6131 (c->rq.hdrs.hdr.name_end_found));
6132 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
6133 (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start));
6134 /* A zero-length header (field) name is possible in two deliberately
6135 non-conformant modes: a first line starting with whitespace (which is
6136 discarded as a whole when its end is reached) and an empty field name
6137 allowed by 'allow_empty_name'. */
6138 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
6139 (0 != c->rq.hdrs.hdr.name_len) || \
6140 (c->rq.hdrs.hdr.starts_with_ws) || \
6141 (allow_empty_name && c->rq.hdrs.hdr.name_end_found));
6142 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
6143 (0 == c->rq.hdrs.hdr.name_len) || \
6144 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len));
6145 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
6146 (0 == c->rq.hdrs.hdr.value_start) || \
6147 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start));
6148
6149 /* Check for the end of the line */
6150 if ('\r' == chr)
6151 {
6152 if (0 != p)
6153 {
6154 /* Line is not empty, need to check for possible line folding */
6155 if (p + 2 >= c->read_buffer_offset)
6156 break; /* Not enough data yet to check for folded line */
6157 }
6158 else
6159 {
6160 /* Line is empty, no need to check for possible line folding */
6161 if (p + 2 > c->read_buffer_offset)
6162 break; /* Not enough data yet to check for the end of the line */
6163 }
6164 if ('\n' == c->read_buffer[p + 1])
6165 end_of_line = true;
6166 else
6167 {
6168 /* Bare CR alone */
6169 /* Must be rejected or replaced with space char.
6170 See RFC 9112, section 2.2-4 */
6171 if (bare_cr_as_sp)
6172 {
6173 c->read_buffer[p] = ' ';
6175 continue; /* Re-start processing of the current character */
6176 }
6177 else if (! bare_cr_keep)
6178 {
6179 if (! process_footers)
6183 else
6187 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6188 }
6189 end_of_line = false;
6190 }
6191 }
6192 else if ('\n' == chr)
6193 {
6194 /* Bare LF may be recognised as a line delimiter.
6195 See RFC 9112, section 2.2-3 */
6196 if (bare_lf_as_crlf)
6197 {
6198 if (0 != p)
6199 {
6200 /* Line is not empty, need to check for possible line folding */
6201 if (p + 1 >= c->read_buffer_offset)
6202 break; /* Not enough data yet to check for folded line */
6203 }
6204 end_of_line = true;
6205 }
6206 else
6207 {
6208 if (! process_footers)
6212 else
6216 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6217 }
6218 }
6219 else
6220 end_of_line = false;
6221
6222 if (end_of_line)
6223 {
6224 /* Handle the end of the line */
6228 const size_t line_len = p + (('\r' == chr) ? 2 : 1);
6229 char next_line_char;
6230 mhd_assert (line_len <= c->read_buffer_offset);
6231
6232 if (0 == p)
6233 {
6234 /* Zero-length header line. This is the end of the request header
6235 section.
6236 RFC 9112, Section 2.1-1 */
6239 mhd_assert (0 == c->rq.hdrs.hdr.name_len);
6240 mhd_assert (0 == c->rq.hdrs.hdr.ws_start);
6241 mhd_assert (0 == c->rq.hdrs.hdr.value_start);
6242 /* Consume the line with CRLF (or bare LF) */
6243 c->read_buffer += line_len;
6244 c->read_buffer_offset -= line_len;
6245 c->read_buffer_size -= line_len;
6247 }
6248
6249 mhd_assert (line_len < c->read_buffer_offset);
6250 mhd_assert (0 != line_len);
6251 mhd_assert ('\n' == c->read_buffer[line_len - 1]);
6252 next_line_char = c->read_buffer[line_len];
6253 if ((' ' == next_line_char) ||
6254 ('\t' == next_line_char))
6255 {
6256 /* Folded line */
6257 if (! allow_folded)
6258 {
6259 if (! process_footers)
6263 else
6267
6268 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6269 }
6270 /* Replace CRLF (or bare LF) character(s) with space characters.
6271 See RFC 9112, Section 5.2-4 */
6272 c->read_buffer[p] = ' ';
6273 if ('\r' == chr)
6274 c->read_buffer[p + 1] = ' ';
6275 continue; /* Re-start processing of the current character */
6276 }
6277 else
6278 {
6279 /* It is not a folded line, it's the real end of the non-empty line */
6280 bool skip_line = false;
6281 mhd_assert (0 != p);
6282 if (c->rq.hdrs.hdr.starts_with_ws)
6283 {
6284 /* This is the first line and it starts with whitespace. This line
6285 must be discarded completely.
6286 See RFC 9112, Section 2.2-8 */
6287 mhd_assert (allow_wsp_at_start);
6288#ifdef HAVE_MESSAGES
6289 MHD_DLOG (c->daemon,
6290 _ ("Whitespace-prefixed first header line " \
6291 "has been skipped.\n"));
6292#endif /* HAVE_MESSAGES */
6293 skip_line = true;
6294 }
6295 else if (! c->rq.hdrs.hdr.name_end_found)
6296 {
6297 if (! allow_line_without_colon)
6298 {
6299 if (! process_footers)
6303 else
6307
6308 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6309 }
6310 /* Skip broken line completely */
6312 skip_line = true;
6313 }
6314 if (skip_line)
6315 {
6316 /* Skip the entire line */
6317 c->read_buffer += line_len;
6318 c->read_buffer_offset -= line_len;
6319 c->read_buffer_size -= line_len;
6320 p = 0;
6321 /* Reset processing state */
6322 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
6323 /* Start processing of the next line */
6324 continue;
6325 }
6326 else
6327 {
6328 /* This line should be valid header line */
6329 size_t value_len;
6330 mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name);
6331
6332 hdr_name->str = c->read_buffer + 0; /* The name always starts at the first character */
6333 hdr_name->len = c->rq.hdrs.hdr.name_len;
6334 mhd_assert (0 == hdr_name->str[hdr_name->len]);
6335
6336 if (0 == c->rq.hdrs.hdr.value_start)
6337 {
6338 c->rq.hdrs.hdr.value_start = p;
6339 c->read_buffer[p] = 0;
6340 value_len = 0;
6341 }
6342 else if (0 != c->rq.hdrs.hdr.ws_start)
6343 {
6344 mhd_assert (p > c->rq.hdrs.hdr.ws_start);
6346 c->read_buffer[c->rq.hdrs.hdr.ws_start] = 0;
6347 value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start;
6348 }
6349 else
6350 {
6351 mhd_assert (p > c->rq.hdrs.hdr.ws_start);
6352 c->read_buffer[p] = 0;
6353 value_len = p - c->rq.hdrs.hdr.value_start;
6354 }
6355 hdr_value->str = c->read_buffer + c->rq.hdrs.hdr.value_start;
6356 hdr_value->len = value_len;
6357 mhd_assert (0 == hdr_value->str[hdr_value->len]);
6358 /* Consume the entire line */
6359 c->read_buffer += line_len;
6360 c->read_buffer_offset -= line_len;
6361 c->read_buffer_size -= line_len;
6363 }
6364 }
6365 }
6366 else if ((' ' == chr) || ('\t' == chr))
6367 {
6368 if (0 == p)
6369 {
6370 if (! allow_wsp_at_start)
6371 {
6372 if (! process_footers)
6376 else
6380 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6381 }
6382 c->rq.hdrs.hdr.starts_with_ws = true;
6383 }
6384 else if ((! c->rq.hdrs.hdr.name_end_found) &&
6385 (! c->rq.hdrs.hdr.starts_with_ws))
6386 {
6387 /* Whitespace in header name / between header name and colon */
6388 if (allow_wsp_in_name || allow_wsp_before_colon)
6389 {
6390 if (0 == c->rq.hdrs.hdr.ws_start)
6391 c->rq.hdrs.hdr.ws_start = p;
6392 }
6393 else
6394 {
6395 if (! process_footers)
6399 else
6403
6404 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6405 }
6406 }
6407 else
6408 {
6409 /* Whitespace before/inside/after header (field) value */
6410 if (0 == c->rq.hdrs.hdr.ws_start)
6411 c->rq.hdrs.hdr.ws_start = p;
6412 }
6413 }
6414 else if (0 == chr)
6415 {
6416 if (! nul_as_sp)
6417 {
6418 if (! process_footers)
6422 else
6426
6427 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6428 }
6429 c->read_buffer[p] = ' ';
6430 continue; /* Re-start processing of the current character */
6431 }
6432 else
6433 {
6434 /* Not a whitespace, not the end of the header line */
6435 /* A bare CR reaches this point when it is kept as an ordinary
6436 character ('bare_cr_keep', MHD_OPTION_CLIENT_DISCIPLINE_LVL -3);
6437 in every other mode it is either replaced with a space or
6438 rejected before. */
6439 mhd_assert (('\r' != chr) || bare_cr_keep);
6440 mhd_assert ('\n' != chr);
6441 mhd_assert ('\0' != chr);
6442 if ( (! c->rq.hdrs.hdr.name_end_found) &&
6443 (! c->rq.hdrs.hdr.starts_with_ws) )
6444 {
6445 /* Processing the header (field) name */
6446 if ( (! allow_extended_charset) &&
6447 (':' != chr) &&
6448 (! char_legal_in_field_name (chr)) )
6449 {
6454 }
6455
6456 if (':' == chr)
6457 {
6458 if (0 == c->rq.hdrs.hdr.ws_start)
6459 c->rq.hdrs.hdr.name_len = p;
6460 else
6461 {
6462 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6463 if (! allow_wsp_before_colon)
6464 {
6465 if (! process_footers)
6469 else
6473 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6474 }
6476#ifndef MHD_FAVOR_SMALL_CODE
6477 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6478#endif /* ! MHD_FAVOR_SMALL_CODE */
6479 }
6480 if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name)
6481 {
6482 if (! process_footers)
6486 else
6490 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6491 }
6492 c->rq.hdrs.hdr.name_end_found = true;
6493 c->read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */
6494 }
6495 else
6496 {
6497 if (0 != c->rq.hdrs.hdr.ws_start)
6498 {
6499 /* End of the whitespace in header (field) name */
6500 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6501 if (! allow_wsp_in_name)
6502 {
6503 if (! process_footers)
6507 else
6511
6512 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6513 }
6514#ifndef MHD_FAVOR_SMALL_CODE
6515 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6516#endif /* ! MHD_FAVOR_SMALL_CODE */
6517 }
6518 }
6519 }
6520 else
6521 {
6522 /* Processing the header (field) value */
6523 if (0 == c->rq.hdrs.hdr.value_start)
6524 c->rq.hdrs.hdr.value_start = p;
6525#ifndef MHD_FAVOR_SMALL_CODE
6526 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6527#endif /* ! MHD_FAVOR_SMALL_CODE */
6528 }
6529#ifdef MHD_FAVOR_SMALL_CODE
6530 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6531#endif /* MHD_FAVOR_SMALL_CODE */
6532 }
6533 p++;
6534 }
6535 c->rq.hdrs.hdr.proc_pos = p;
6536 return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */
6537}
6538
6539
6550static bool
6551get_req_headers (struct MHD_Connection *c, bool process_footers)
6552{
6553 do
6554 {
6555 struct _MHD_str_w_len hdr_name;
6556 struct _MHD_str_w_len hdr_value;
6557 enum MHD_HdrLineReadRes_ res;
6558
6559 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6561 c->state);
6562
6563 #ifdef _DEBUG
6564 hdr_name.str = NULL;
6565 hdr_value.str = NULL;
6566#endif /* _DEBUG */
6567 res = get_req_header (c, process_footers, &hdr_name, &hdr_value);
6569 {
6570 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6572 c->state);
6573 mhd_assert (NULL != hdr_name.str);
6574 mhd_assert (NULL != hdr_value.str);
6575 /* Values must be zero-terminated and must not have binary zeros */
6576 mhd_assert (strlen (hdr_name.str) == hdr_name.len);
6577 mhd_assert (strlen (hdr_value.str) == hdr_value.len);
6578 /* Values must not have whitespaces at the start or at the end */
6579 mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != ' '));
6580 mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != '\t'));
6581 mhd_assert ((hdr_name.len == 0) || \
6582 (hdr_name.str[hdr_name.len - 1] != ' '));
6583 mhd_assert ((hdr_name.len == 0) || \
6584 (hdr_name.str[hdr_name.len - 1] != '\t'));
6585 mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != ' '));
6586 mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != '\t'));
6587 mhd_assert ((hdr_value.len == 0) || \
6588 (hdr_value.str[hdr_value.len - 1] != ' '));
6589 mhd_assert ((hdr_value.len == 0) || \
6590 (hdr_value.str[hdr_value.len - 1] != '\t'));
6591
6592 if (MHD_NO ==
6594 (! process_footers) ?
6597 hdr_name.str, hdr_name.len,
6598 hdr_value.str, hdr_value.len))
6599 {
6600 size_t add_element_size;
6601
6602 mhd_assert (hdr_name.str < hdr_value.str);
6603
6604#ifdef HAVE_MESSAGES
6605 MHD_DLOG (c->daemon,
6606 _ ("Failed to allocate memory in the connection memory " \
6607 "pool to store %s.\n"),
6608 (! process_footers) ? _ ("header") : _ ("footer"));
6609#endif /* HAVE_MESSAGES */
6610
6611 add_element_size = hdr_value.len
6612 + (size_t) (hdr_value.str - hdr_name.str);
6613
6614 if (! process_footers)
6615 handle_req_headers_no_space (c, hdr_name.str, add_element_size);
6616 else
6617 handle_req_footers_no_space (c, hdr_name.str, add_element_size);
6618
6620 return true;
6621 }
6622 /* Reset processing state */
6624 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6626 c->state);
6627 /* Read the next header (field) line */
6628 continue;
6629 }
6631 {
6632 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6634 c->state);
6635 return false;
6636 }
6637 else if (MHD_HDR_LINE_READING_DATA_ERROR == res)
6638 {
6639 mhd_assert ((process_footers ? \
6644 return true;
6645 }
6647 break;
6648 } while (1);
6649
6650#ifdef HAVE_MESSAGES
6651 if (1 == c->rq.num_cr_sp_replaced)
6652 {
6653 MHD_DLOG (c->daemon,
6654 _ ("One bare CR character has been replaced with space " \
6655 "in %s.\n"),
6656 (! process_footers) ?
6657 _ ("the request line or in the request headers") :
6658 _ ("the request footers"));
6659 }
6660 else if (0 != c->rq.num_cr_sp_replaced)
6661 {
6662 MHD_DLOG (c->daemon,
6663 _ ("%" PRIu64 " bare CR characters have been replaced with " \
6664 "spaces in the request line and/or in the request %s.\n"),
6665 (uint64_t) c->rq.num_cr_sp_replaced,
6666 (! process_footers) ? _ ("headers") : _ ("footers"));
6667 }
6668 if (1 == c->rq.skipped_broken_lines)
6669 {
6670 MHD_DLOG (c->daemon,
6671 _ ("One %s line without colon has been skipped.\n"),
6672 (! process_footers) ? _ ("header") : _ ("footer"));
6673 }
6674 else if (0 != c->rq.skipped_broken_lines)
6675 {
6676 MHD_DLOG (c->daemon,
6677 _ ("%" PRIu64 " %s lines without colons has been skipped.\n"),
6678 (uint64_t) c->rq.skipped_broken_lines,
6679 (! process_footers) ? _ ("header") : _ ("footer"));
6680 }
6681#endif /* HAVE_MESSAGES */
6682
6683 mhd_assert (c->rq.method < c->read_buffer);
6684 if (! process_footers)
6685 {
6686 c->rq.header_size = (size_t) (c->read_buffer - c->rq.method);
6688 c->rq.field_lines.size =
6689 (size_t) ((c->read_buffer - c->rq.field_lines.start) - 1);
6690 if ('\r' == *(c->read_buffer - 2))
6691 c->rq.field_lines.size--;
6693
6695 {
6696 /* Try to re-use some of the last bytes of the request header */
6697 /* Do this only if space in the read buffer is limited AND
6698 amount of read ahead data is small. */
6703 const char *last_elmnt_end;
6704 size_t shift_back_size;
6705
6706 if (NULL != c->rq.headers_received_tail)
6707 {
6709 {
6710 /* Tailing query argument without '=', we only have the header */
6711 last_elmnt_end =
6714 }
6715 else
6716 {
6717 last_elmnt_end =
6720 }
6721 }
6722 else
6723 {
6724 last_elmnt_end = c->rq.version + HTTP_VER_LEN;
6725 }
6726 /* The request line strings (method, url, version) remain visible to
6727 the application for the whole request, and the last received header
6728 is not necessarily above them: when it is not, reclaiming down to
6729 the header end puts the read buffer on top of the version string,
6730 and the next recv() overwrites the terminator the application is
6731 about to read through. Never reclaim below the end of the request
6732 line. */
6733 if ((NULL != c->rq.version) &&
6734 (last_elmnt_end < c->rq.version + HTTP_VER_LEN))
6735 last_elmnt_end = c->rq.version + HTTP_VER_LEN;
6736 /* Check that @a last_elmnt_end points into the request that has
6737 just been parsed, which lives entirely
6738 between the start of the request line and the current read buffer
6739 position. */
6741 (NULL != last_elmnt_end) &&
6742 (c->rq.method <= last_elmnt_end) &&
6743 (last_elmnt_end < c->read_buffer - 1),
6744 true);
6745 shift_back_size = (size_t) (c->read_buffer - (last_elmnt_end + 1));
6746 if (0 != c->read_buffer_offset)
6747 memmove (c->read_buffer - shift_back_size,
6748 c->read_buffer,
6750 c->read_buffer -= shift_back_size;
6751 c->read_buffer_size += shift_back_size;
6752 }
6753 }
6754 else
6756
6757 return true;
6758}
6759
6760
6768void
6770{
6771 struct MHD_Daemon *daemon = connection->daemon;
6772#if defined(MHD_USE_THREADS)
6773 mhd_assert (NULL == daemon->worker_pool);
6774#endif /* MHD_USE_THREADS */
6775
6776 if (0 == connection->connection_timeout_ms)
6777 return; /* Skip update of activity for connections
6778 without timeout timer. */
6779 if (connection->suspended)
6780 return; /* no activity on suspended connections */
6781
6784 return; /* each connection has personal timeout */
6785
6786 if (connection->connection_timeout_ms != daemon->connection_timeout_ms)
6787 return; /* custom timeout, no need to move it in "normal" DLL */
6788#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6789 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
6790#endif
6791 /* move connection to head of timeout list (by remove + add operation) */
6793 daemon->normal_timeout_tail,
6794 connection);
6796 daemon->normal_timeout_tail,
6797 connection);
6798#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6799 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
6800#endif
6801}
6802
6803
6813void
6815 bool socket_error)
6816{
6817 ssize_t bytes_read;
6818
6819 if ( (MHD_CONNECTION_CLOSED == connection->state) ||
6820 (connection->suspended) )
6821 return;
6822#ifdef HTTPS_SUPPORT
6823 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6824 { /* HTTPS connection. */
6825 if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6826 {
6827 if (! MHD_run_tls_handshake_ (connection))
6828 return;
6829 }
6830 }
6831#endif /* HTTPS_SUPPORT */
6832
6833 mhd_assert (NULL != connection->read_buffer);
6834 if (connection->read_buffer_size == connection->read_buffer_offset)
6835 return; /* No space for receiving data. */
6836
6837 bytes_read = connection->recv_cls (connection,
6838 &connection->read_buffer
6839 [connection->read_buffer_offset],
6840 connection->read_buffer_size
6841 - connection->read_buffer_offset);
6842 if ((bytes_read < 0) || socket_error)
6843 {
6844 if ((MHD_ERR_AGAIN_ == bytes_read) && ! socket_error)
6845 return; /* No new data to process. */
6846 if ((bytes_read > 0) && connection->sk_nonblck)
6847 { /* Try to detect the socket error */
6848 int dummy;
6849 bytes_read = connection->recv_cls (connection, &dummy, sizeof (dummy));
6850 }
6851 if (MHD_ERR_CONNRESET_ == bytes_read)
6852 {
6854 (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6855 {
6856#ifdef HAVE_MESSAGES
6857 MHD_DLOG (connection->daemon,
6858 _ ("Socket has been disconnected when reading request.\n"));
6859#endif
6860 connection->discard_request = true;
6861 }
6862 MHD_connection_close_ (connection,
6864 return;
6865 }
6866
6867#ifdef HAVE_MESSAGES
6868 if (MHD_CONNECTION_INIT != connection->state)
6869 MHD_DLOG (connection->daemon,
6870 _ ("Connection socket is closed when reading " \
6871 "request due to the error: %s\n"),
6872 (bytes_read < 0) ? str_conn_error_ (bytes_read) :
6873 "detected connection closure");
6874#endif
6875 CONNECTION_CLOSE_ERROR (connection,
6876 NULL);
6877 return;
6878 }
6879
6880 if (0 == bytes_read)
6881 { /* Remote side closed connection. */
6882 connection->read_closed = true;
6884 (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6885 {
6886#ifdef HAVE_MESSAGES
6887 MHD_DLOG (connection->daemon,
6888 _ ("Connection was closed by remote side with incomplete "
6889 "request.\n"));
6890#endif
6891 connection->discard_request = true;
6892 MHD_connection_close_ (connection,
6894 }
6895 else if (MHD_CONNECTION_INIT == connection->state)
6896 /* This termination code cannot be reported to the application
6897 * because application has not been informed yet about this request */
6898 MHD_connection_close_ (connection,
6900 else
6901 MHD_connection_close_ (connection,
6903 return;
6904 }
6905 connection->read_buffer_offset += (size_t) bytes_read;
6906 MHD_update_last_activity_ (connection);
6907#if DEBUG_STATES
6908 MHD_DLOG (connection->daemon,
6909 _ ("In function %s handling connection at state: %s\n"),
6910 MHD_FUNC_,
6911 MHD_state_to_string (connection->state));
6912#endif
6913 /* TODO: check whether the next 'switch()' really needed */
6914 switch (connection->state)
6915 {
6922 /* nothing to do but default action */
6923 if (connection->read_closed)
6924 {
6925 /* TODO: check whether this really needed */
6926 MHD_connection_close_ (connection,
6928 }
6929 return;
6931 return;
6932#ifdef UPGRADE_SUPPORT
6933 case MHD_CONNECTION_UPGRADE:
6934 mhd_assert (0);
6935 return;
6936#endif /* UPGRADE_SUPPORT */
6938 /* shrink read buffer to how much is actually used */
6939 /* TODO: remove shrink as it handled in special function */
6940 if ((0 != connection->read_buffer_size) &&
6941 (connection->read_buffer_size != connection->read_buffer_offset))
6942 {
6943 mhd_assert (NULL != connection->read_buffer);
6944 connection->read_buffer =
6945 MHD_pool_reallocate (connection->pool,
6946 connection->read_buffer,
6947 connection->read_buffer_size,
6948 connection->read_buffer_offset);
6949 connection->read_buffer_size = connection->read_buffer_offset;
6950 }
6951 break;
6957 /* Milestone state, no data should be read */
6958 mhd_assert (0); /* Should not be possible */
6959 break;
6970 default:
6971 mhd_assert (0); /* Should not be possible */
6972 break;
6973 }
6974 return;
6975}
6976
6977
6986void
6988{
6989 struct MHD_Response *response;
6990 ssize_t ret;
6991 if (connection->suspended)
6992 return;
6993
6994#ifdef HTTPS_SUPPORT
6995 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6996 { /* HTTPS connection. */
6997 if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6998 {
6999 if (! MHD_run_tls_handshake_ (connection))
7000 return;
7001 }
7002 }
7003#endif /* HTTPS_SUPPORT */
7004
7005#if DEBUG_STATES
7006 MHD_DLOG (connection->daemon,
7007 _ ("In function %s handling connection at state: %s\n"),
7008 MHD_FUNC_,
7009 MHD_state_to_string (connection->state));
7010#endif
7011 switch (connection->state)
7012 {
7019 mhd_assert (0);
7020 return;
7022 ret = MHD_send_data_ (connection,
7024 [connection->continue_message_write_offset],
7026 - connection->continue_message_write_offset,
7027 true);
7028 if (ret < 0)
7029 {
7030 if (MHD_ERR_AGAIN_ == ret)
7031 return;
7032#ifdef HAVE_MESSAGES
7033 MHD_DLOG (connection->daemon,
7034 _ ("Failed to send data in request for %s.\n"),
7035 connection->rq.url);
7036#endif
7037 CONNECTION_CLOSE_ERROR (connection,
7038 NULL);
7039 return;
7040 }
7041#if _MHD_DEBUG_SEND_DATA
7042 fprintf (stderr,
7043 _ ("Sent 100 continue response: `%.*s'\n"),
7044 (int) ret,
7046#endif
7047 connection->continue_message_write_offset += (size_t) ret;
7048 MHD_update_last_activity_ (connection);
7049 return;
7055 mhd_assert (0);
7056 return;
7058 mhd_assert (0);
7059 return;
7061 {
7062 struct MHD_Response *const resp = connection->rp.response;
7063 const size_t wb_ready = connection->write_buffer_append_offset
7064 - connection->write_buffer_send_offset;
7065 mhd_assert (connection->write_buffer_append_offset >= \
7066 connection->write_buffer_send_offset);
7067 mhd_assert (NULL != resp);
7068 mhd_assert ( (0 == resp->data_size) || \
7069 (0 == resp->data_start) || \
7070 (NULL != resp->crc) );
7071 mhd_assert ( (0 == connection->rp.rsp_write_position) || \
7072 (resp->total_size ==
7073 connection->rp.rsp_write_position) );
7074 mhd_assert ((MHD_CONN_MUST_UPGRADE != connection->keepalive) || \
7075 (! connection->rp.props.send_reply_body));
7076
7077 if ( (connection->rp.props.send_reply_body) &&
7078 (NULL == resp->crc) &&
7079 (NULL == resp->data_iov) &&
7080 /* TODO: remove the next check as 'send_reply_body' is used */
7081 (0 == connection->rp.rsp_write_position) &&
7082 (! connection->rp.props.chunked) )
7083 {
7084 mhd_assert (resp->total_size >= resp->data_size);
7085 mhd_assert (0 == resp->data_start);
7086 /* Send response headers alongside the response body, if the body
7087 * data is available. */
7088 ret = MHD_send_hdr_and_body_ (connection,
7089 &connection->write_buffer
7090 [connection->write_buffer_send_offset],
7091 wb_ready,
7092 false,
7093 resp->data,
7094 resp->data_size,
7095 (resp->total_size == resp->data_size));
7096 }
7097 else
7098 {
7099 /* This is response for HEAD request or reply body is not allowed
7100 * for any other reason or reply body is dynamically generated. */
7101 /* Do not send the body data even if it's available. */
7102 ret = MHD_send_hdr_and_body_ (connection,
7103 &connection->write_buffer
7104 [connection->write_buffer_send_offset],
7105 wb_ready,
7106 false,
7107 NULL,
7108 0,
7109 ((0 == resp->total_size) ||
7110 (! connection->rp.props.send_reply_body)
7111 ));
7112 }
7113
7114 if (ret < 0)
7115 {
7116 if (MHD_ERR_AGAIN_ == ret)
7117 return;
7118#ifdef HAVE_MESSAGES
7119 MHD_DLOG (connection->daemon,
7120 _ ("Failed to send the response headers for the " \
7121 "request for `%s'. Error: %s\n"),
7122 connection->rq.url,
7123 str_conn_error_ (ret));
7124#endif
7125 CONNECTION_CLOSE_ERROR (connection,
7126 NULL);
7127 return;
7128 }
7129 /* 'ret' is not negative, it's safe to cast it to 'size_t'. */
7130 if (((size_t) ret) > wb_ready)
7131 {
7132 /* The complete header and some response data have been sent,
7133 * update both offsets. */
7134 mhd_assert (0 == connection->rp.rsp_write_position);
7135 mhd_assert (! connection->rp.props.chunked);
7136 mhd_assert (connection->rp.props.send_reply_body);
7137 connection->write_buffer_send_offset += wb_ready;
7138 connection->rp.rsp_write_position = ((size_t) ret) - wb_ready;
7139 }
7140 else
7141 connection->write_buffer_send_offset += (size_t) ret;
7142 MHD_update_last_activity_ (connection);
7143 if (MHD_CONNECTION_HEADERS_SENDING != connection->state)
7144 return;
7145 check_write_done (connection,
7147 return;
7148 }
7150 return;
7152 response = connection->rp.response;
7153 if (connection->rp.rsp_write_position <
7154 connection->rp.response->total_size)
7155 {
7156 uint64_t data_write_offset;
7157
7158#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7159 if (NULL != response->crc)
7160 MHD_mutex_lock_chk_ (&response->mutex);
7161#endif
7162 if (MHD_NO == try_ready_normal_body (connection))
7163 {
7164 /* mutex was already unlocked by try_ready_normal_body */
7165 return;
7166 }
7167#if defined(_MHD_HAVE_SENDFILE)
7168 if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
7169 {
7170 mhd_assert (NULL == response->data_iov);
7171 ret = MHD_send_sendfile_ (connection);
7172 }
7173 else /* combined with the next 'if' */
7174#endif /* _MHD_HAVE_SENDFILE */
7175 if (NULL != response->data_iov)
7176 {
7177 ret = MHD_send_iovec_ (connection,
7178 &connection->rp.resp_iov,
7179 true);
7180 }
7181 else
7182 {
7183 data_write_offset = connection->rp.rsp_write_position
7184 - response->data_start;
7185 if (data_write_offset > (uint64_t) SIZE_MAX)
7186 MHD_PANIC (_ ("Data offset exceeds limit.\n"));
7187 ret = MHD_send_data_ (connection,
7188 &response->data
7189 [(size_t) data_write_offset],
7190 response->data_size
7191 - (size_t) data_write_offset,
7192 true);
7193#if _MHD_DEBUG_SEND_DATA
7194 if (ret > 0)
7195 fprintf (stderr,
7196 _ ("Sent %d-byte DATA response: `%.*s'\n"),
7197 (int) ret,
7198 (int) ret,
7199 &rp.response->data[connection->rp.rsp_write_position
7200 - rp.response->data_start]);
7201#endif
7202 }
7203#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7204 if (NULL != response->crc)
7205 MHD_mutex_unlock_chk_ (&response->mutex);
7206#endif
7207 if (ret < 0)
7208 {
7209 if (MHD_ERR_AGAIN_ == ret)
7210 return;
7211#ifdef HAVE_MESSAGES
7212 MHD_DLOG (connection->daemon,
7213 _ ("Failed to send the response body for the " \
7214 "request for `%s'. Error: %s\n"),
7215 connection->rq.url,
7216 str_conn_error_ (ret));
7217#endif
7218 CONNECTION_CLOSE_ERROR (connection,
7219 NULL);
7220 return;
7221 }
7222 connection->rp.rsp_write_position += (size_t) ret;
7223 MHD_update_last_activity_ (connection);
7224 }
7225 if (connection->rp.rsp_write_position ==
7226 connection->rp.response->total_size)
7228 return;
7230 mhd_assert (0);
7231 return;
7233 ret = MHD_send_data_ (connection,
7234 &connection->write_buffer
7235 [connection->write_buffer_send_offset],
7236 connection->write_buffer_append_offset
7237 - connection->write_buffer_send_offset,
7238 true);
7239 if (ret < 0)
7240 {
7241 if (MHD_ERR_AGAIN_ == ret)
7242 return;
7243#ifdef HAVE_MESSAGES
7244 MHD_DLOG (connection->daemon,
7245 _ ("Failed to send the chunked response body for the " \
7246 "request for `%s'. Error: %s\n"),
7247 connection->rq.url,
7248 str_conn_error_ (ret));
7249#endif
7250 CONNECTION_CLOSE_ERROR (connection,
7251 NULL);
7252 return;
7253 }
7254 connection->write_buffer_send_offset += (size_t) ret;
7255 MHD_update_last_activity_ (connection);
7256 if (MHD_CONNECTION_CHUNKED_BODY_READY != connection->state)
7257 return;
7258 check_write_done (connection,
7259 (connection->rp.response->total_size ==
7260 connection->rp.rsp_write_position) ?
7263 return;
7266 mhd_assert (0);
7267 return;
7269 ret = MHD_send_data_ (connection,
7270 &connection->write_buffer
7271 [connection->write_buffer_send_offset],
7272 connection->write_buffer_append_offset
7273 - connection->write_buffer_send_offset,
7274 true);
7275 if (ret < 0)
7276 {
7277 if (MHD_ERR_AGAIN_ == ret)
7278 return;
7279#ifdef HAVE_MESSAGES
7280 MHD_DLOG (connection->daemon,
7281 _ ("Failed to send the footers for the " \
7282 "request for `%s'. Error: %s\n"),
7283 connection->rq.url,
7284 str_conn_error_ (ret));
7285#endif
7286 CONNECTION_CLOSE_ERROR (connection,
7287 NULL);
7288 return;
7289 }
7290 connection->write_buffer_send_offset += (size_t) ret;
7291 MHD_update_last_activity_ (connection);
7292 if (MHD_CONNECTION_FOOTERS_SENDING != connection->state)
7293 return;
7294 check_write_done (connection,
7296 return;
7298 mhd_assert (0);
7299 return;
7301 return;
7302#ifdef UPGRADE_SUPPORT
7303 case MHD_CONNECTION_UPGRADE:
7304 mhd_assert (0);
7305 return;
7306#endif /* UPGRADE_SUPPORT */
7307 default:
7308 mhd_assert (0);
7309 CONNECTION_CLOSE_ERROR (connection,
7310 _ ("Internal error.\n"));
7311 break;
7312 }
7313 return;
7314}
7315
7316
7323static bool
7325{
7326 const uint64_t timeout = c->connection_timeout_ms;
7327 uint64_t now;
7328 uint64_t since_actv;
7329
7330 if (c->suspended)
7331 return false;
7332 if (0 == timeout)
7333 return false;
7335 since_actv = now - c->last_activity;
7336 /* Keep the next lines in sync with #connection_get_wait() to avoid
7337 * undesired side-effects like busy-waiting. */
7338 if (timeout < since_actv)
7339 {
7340 if (UINT64_MAX / 2 < since_actv)
7341 {
7342 const uint64_t jump_back = c->last_activity - now;
7343 /* Very unlikely that it is more than quarter-million years pause.
7344 * More likely that system clock jumps back. */
7345 if (5000 >= jump_back)
7346 {
7347#ifdef HAVE_MESSAGES
7348 MHD_DLOG (c->daemon,
7349 _ ("Detected system clock %u milliseconds jump back.\n"),
7350 (unsigned int) jump_back);
7351#endif
7352 return false;
7353 }
7354#ifdef HAVE_MESSAGES
7355 MHD_DLOG (c->daemon,
7356 _ ("Detected too large system clock %" PRIu64 " milliseconds "
7357 "jump back.\n"),
7358 jump_back);
7359#endif
7360 }
7361 return true;
7362 }
7363 return false;
7364}
7365
7366
7375static void
7377{
7378 struct MHD_Daemon *daemon = connection->daemon;
7379#ifdef MHD_USE_THREADS
7380 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
7381 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
7382 mhd_assert (NULL == daemon->worker_pool);
7383#endif /* MHD_USE_THREADS */
7384
7385 if (connection->in_cleanup)
7386 return; /* Prevent double cleanup. */
7387 connection->in_cleanup = true;
7388 if (NULL != connection->rp.response)
7389 {
7390 MHD_destroy_response (connection->rp.response);
7391 connection->rp.response = NULL;
7392 }
7393#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7394 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
7395#endif
7396 if (connection->suspended)
7397 {
7400 connection);
7401 connection->suspended = false;
7402 }
7403 else
7404 {
7405 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
7406 {
7407 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7409 daemon->normal_timeout_tail,
7410 connection);
7411 else
7413 daemon->manual_timeout_tail,
7414 connection);
7415 }
7417 daemon->connections_tail,
7418 connection);
7419 }
7420 DLL_insert (daemon->cleanup_head,
7421 daemon->cleanup_tail,
7422 connection);
7423 connection->resuming = false;
7424 connection->in_idle = false;
7425#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7426 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
7427#endif
7429 {
7430 /* if we were at the connection limit before and are in
7431 thread-per-connection mode, signal the main thread
7432 to resume accepting connections */
7433 if ( (MHD_ITC_IS_VALID_ (daemon->itc)) &&
7434 (! MHD_itc_activate_ (daemon->itc, "c")) )
7435 {
7436#ifdef HAVE_MESSAGES
7437 MHD_DLOG (daemon,
7438 _ ("Failed to signal end of connection via inter-thread " \
7439 "communication channel.\n"));
7440#endif
7441 }
7442 }
7443}
7444
7445
7451void
7453{
7454 size_t read_buf_size;
7455
7456#ifdef HTTPS_SUPPORT
7457 mhd_assert ( (0 == (c->daemon->options & MHD_USE_TLS)) || \
7458 (MHD_TLS_CONN_INIT == c->tls_state) );
7459 mhd_assert ( (0 != (c->daemon->options & MHD_USE_TLS)) || \
7460 (MHD_TLS_CONN_NO_TLS == c->tls_state) );
7461#endif /* HTTPS_SUPPORT */
7463
7466
7467 memset (&c->rq, 0, sizeof(c->rq));
7468 memset (&c->rp, 0, sizeof(c->rp));
7469
7470 c->write_buffer = NULL;
7471 c->write_buffer_size = 0;
7474
7476
7477 c->read_buffer_offset = 0;
7478 read_buf_size = c->daemon->pool_size / 2;
7479 c->read_buffer
7480 = MHD_pool_allocate (c->pool,
7481 read_buf_size,
7482 false);
7483 c->read_buffer_size = read_buf_size;
7484}
7485
7486
7493static void
7495 bool reuse)
7496{
7497 struct MHD_Connection *const c = connection;
7498 struct MHD_Daemon *const d = connection->daemon;
7499
7500 if (! reuse)
7501 {
7502 /* Next function will destroy response, notify client,
7503 * destroy memory pool, and set connection state to "CLOSED" */
7505 c->stop_with_error ?
7508 c->read_buffer = NULL;
7509 c->read_buffer_size = 0;
7510 c->read_buffer_offset = 0;
7511 c->write_buffer = NULL;
7512 c->write_buffer_size = 0;
7515 }
7516 else
7517 {
7518 /* Reset connection to process the next request */
7519 size_t new_read_buf_size;
7522
7523 if ( (NULL != d->notify_completed) &&
7524 (c->rq.client_aware) )
7526 c,
7527 &c->rq.client_context,
7529 c->rq.client_aware = false;
7530
7531 if (NULL != c->rp.response)
7533 c->rp.response = NULL;
7534
7537 c->event_loop_info =
7538 (0 == c->read_buffer_offset) ?
7540
7541 memset (&c->rq, 0, sizeof(c->rq));
7542
7543 /* iov (if any) will be deallocated by MHD_pool_reset */
7544 memset (&c->rp, 0, sizeof(c->rp));
7545
7546 c->write_buffer = NULL;
7547 c->write_buffer_size = 0;
7551
7552 /* Reset the read buffer to the starting size,
7553 preserving the bytes we have already read. */
7554 new_read_buf_size = c->daemon->pool_size / 2;
7555 if (c->read_buffer_offset > new_read_buf_size)
7556 new_read_buf_size = c->read_buffer_offset;
7557
7558 c->read_buffer
7559 = MHD_pool_reset (c->pool,
7560 c->read_buffer,
7562 new_read_buf_size);
7563 c->read_buffer_size = new_read_buf_size;
7564 }
7565 c->rq.client_context = NULL;
7566}
7567
7568
7581enum MHD_Result
7583{
7584 struct MHD_Daemon *daemon = connection->daemon;
7585 enum MHD_Result ret;
7586#ifdef MHD_USE_THREADS
7587 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
7588 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
7589#endif /* MHD_USE_THREADS */
7590 /* 'daemon' is not used if epoll is not available and asserts are disabled */
7591 (void) daemon; /* Mute compiler warning */
7592
7593 connection->in_idle = true;
7594 while (! connection->suspended)
7595 {
7596#ifdef HTTPS_SUPPORT
7597 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
7598 { /* HTTPS connection. */
7599 if ((MHD_TLS_CONN_INIT <= connection->tls_state) &&
7600 (MHD_TLS_CONN_CONNECTED > connection->tls_state))
7601 break;
7602 }
7603#endif /* HTTPS_SUPPORT */
7604#if DEBUG_STATES
7605 MHD_DLOG (daemon,
7606 _ ("In function %s handling connection at state: %s\n"),
7607 MHD_FUNC_,
7608 MHD_state_to_string (connection->state));
7609#endif
7610 switch (connection->state)
7611 {
7614 if (get_request_line (connection))
7615 {
7618 || (connection->discard_request));
7619 continue;
7620 }
7622 break;
7626 continue;
7628 if (get_req_headers (connection, false))
7629 {
7631 mhd_assert ((MHD_CONNECTION_HEADERS_RECEIVED == connection->state) || \
7632 (connection->discard_request));
7633 continue;
7634 }
7636 break;
7638 parse_connection_headers (connection);
7639 if (MHD_CONNECTION_HEADERS_RECEIVED != connection->state)
7640 continue;
7642 if (connection->suspended)
7643 break;
7644 continue;
7646 call_connection_handler (connection); /* first call */
7647 if (MHD_CONNECTION_HEADERS_PROCESSED != connection->state)
7648 continue;
7649 if (connection->suspended)
7650 continue;
7651
7652 if ( (NULL == connection->rp.response) &&
7653 (need_100_continue (connection)) &&
7654 /* If the client is already sending the payload (body)
7655 there is no need to send "100 Continue" */
7656 (0 == connection->read_buffer_offset) )
7657 {
7659 break;
7660 }
7661 if ( (NULL != connection->rp.response) &&
7662 (0 != connection->rq.remaining_upload_size) )
7663 {
7664 /* we refused (no upload allowed!) */
7665 connection->rq.remaining_upload_size = 0;
7666 /* force close, in case client still tries to upload... */
7667 connection->discard_request = true;
7668 }
7669 connection->state = (0 == connection->rq.remaining_upload_size)
7672 if (connection->suspended)
7673 break;
7674 continue;
7676 if (connection->continue_message_write_offset ==
7678 {
7680 continue;
7681 }
7682 break;
7684 mhd_assert (0 != connection->rq.remaining_upload_size);
7685 mhd_assert (! connection->discard_request);
7686 mhd_assert (NULL == connection->rp.response);
7687 if (0 != connection->read_buffer_offset)
7688 {
7689 process_request_body (connection); /* loop call */
7690 if (MHD_CONNECTION_BODY_RECEIVING != connection->state)
7691 continue;
7692 }
7693 /* Modify here when queueing of the response during data processing
7694 will be supported */
7695 mhd_assert (! connection->discard_request);
7696 mhd_assert (NULL == connection->rp.response);
7697 if (0 == connection->rq.remaining_upload_size)
7698 {
7699 connection->state = MHD_CONNECTION_BODY_RECEIVED;
7700 continue;
7701 }
7702 break;
7704 mhd_assert (! connection->discard_request);
7705 mhd_assert (NULL == connection->rp.response);
7706 if (0 == connection->rq.remaining_upload_size)
7707 {
7708 if (connection->rq.have_chunked_upload)
7709 {
7710 /* Reset counter variables reused for footers */
7711 connection->rq.num_cr_sp_replaced = 0;
7712 connection->rq.skipped_broken_lines = 0;
7715 }
7716 else
7718 continue;
7719 }
7720 break;
7722 if (get_req_headers (connection, true))
7723 {
7725 mhd_assert ((MHD_CONNECTION_FOOTERS_RECEIVED == connection->state) || \
7726 (connection->discard_request));
7727 continue;
7728 }
7730 break;
7732 /* The header, the body, and the footers of the request has been received,
7733 * switch to the final processing of the request. */
7735 continue;
7737 call_connection_handler (connection); /* "final" call */
7738 if (connection->state != MHD_CONNECTION_FULL_REQ_RECEIVED)
7739 continue;
7740 if (NULL == connection->rp.response)
7741 break; /* try again next time */
7742 /* Response is ready, start reply */
7743 connection->state = MHD_CONNECTION_START_REPLY;
7744 continue;
7746 mhd_assert (NULL != connection->rp.response);
7748 if (MHD_NO == build_header_response (connection))
7749 {
7750 /* oops - close! */
7751 CONNECTION_CLOSE_ERROR (connection,
7752 _ ("Closing connection (failed to create "
7753 "response header).\n"));
7754 continue;
7755 }
7757 break;
7758
7760 /* no default action */
7761 break;
7763#ifdef UPGRADE_SUPPORT
7764 if (NULL != connection->rp.response->upgrade_handler)
7765 {
7766 connection->state = MHD_CONNECTION_UPGRADE;
7767 /* This connection is "upgraded". Pass socket to application. */
7768 if (MHD_NO ==
7770 connection))
7771 {
7772 /* upgrade failed, fail hard */
7773 CONNECTION_CLOSE_ERROR (connection,
7774 NULL);
7775 continue;
7776 }
7777 /* Response is not required anymore for this connection. */
7778 if (1)
7779 {
7780 struct MHD_Response *const resp = connection->rp.response;
7781
7782 connection->rp.response = NULL;
7783 MHD_destroy_response (resp);
7784 }
7785 continue;
7786 }
7787#endif /* UPGRADE_SUPPORT */
7788
7789 if (connection->rp.props.send_reply_body)
7790 {
7791 if (connection->rp.props.chunked)
7793 else
7795 }
7796 else
7798 continue;
7800 mhd_assert (connection->rp.props.send_reply_body);
7801 mhd_assert (! connection->rp.props.chunked);
7802 /* nothing to do here */
7803 break;
7805 mhd_assert (connection->rp.props.send_reply_body);
7806 mhd_assert (! connection->rp.props.chunked);
7807#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7808 if (NULL != connection->rp.response->crc)
7809 MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7810#endif
7811 if (0 == connection->rp.response->total_size)
7812 {
7813#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7814 if (NULL != connection->rp.response->crc)
7815 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7816#endif
7817 if (connection->rp.props.chunked)
7819 else
7821 continue;
7822 }
7823 if (MHD_NO != try_ready_normal_body (connection))
7824 {
7825#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7826 if (NULL != connection->rp.response->crc)
7827 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7828#endif
7830 /* Buffering for flushable socket was already enabled*/
7831
7832 break;
7833 }
7834 /* mutex was already unlocked by "try_ready_normal_body */
7835 /* not ready, no socket action */
7836 break;
7838 mhd_assert (connection->rp.props.send_reply_body);
7839 mhd_assert (connection->rp.props.chunked);
7840 /* nothing to do here */
7841 break;
7843 mhd_assert (connection->rp.props.send_reply_body);
7844 mhd_assert (connection->rp.props.chunked);
7845#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7846 if (NULL != connection->rp.response->crc)
7847 MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7848#endif
7849 if ( (0 == connection->rp.response->total_size) ||
7850 (connection->rp.rsp_write_position ==
7851 connection->rp.response->total_size) )
7852 {
7853#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7854 if (NULL != connection->rp.response->crc)
7855 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7856#endif
7858 continue;
7859 }
7860 if (1)
7861 { /* pseudo-branch for local variables scope */
7862 bool finished;
7863 if (MHD_NO != try_ready_chunked_body (connection, &finished))
7864 {
7865#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7866 if (NULL != connection->rp.response->crc)
7867 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7868#endif
7869 connection->state = finished ? MHD_CONNECTION_CHUNKED_BODY_SENT :
7871 continue;
7872 }
7873 /* mutex was already unlocked by try_ready_chunked_body */
7874 }
7875 break;
7877 mhd_assert (connection->rp.props.send_reply_body);
7878 mhd_assert (connection->rp.props.chunked);
7879 mhd_assert (connection->write_buffer_send_offset <= \
7880 connection->write_buffer_append_offset);
7881
7883 {
7884 /* oops - close! */
7885 CONNECTION_CLOSE_ERROR (connection,
7886 _ ("Closing connection (failed to create " \
7887 "response footer)."));
7888 continue;
7889 }
7890 mhd_assert (connection->write_buffer_send_offset < \
7891 connection->write_buffer_append_offset);
7893 continue;
7895 mhd_assert (connection->rp.props.send_reply_body);
7896 mhd_assert (connection->rp.props.chunked);
7897 /* no default action */
7898 break;
7900 if (MHD_HTTP_PROCESSING == connection->rp.responseCode)
7901 {
7902 /* After this type of response, we allow sending another! */
7904 MHD_destroy_response (connection->rp.response);
7905 connection->rp.response = NULL;
7906 /* FIXME: maybe partially reset memory pool? */
7907 continue;
7908 }
7909 /* Reset connection after complete reply */
7910 connection_reset (connection,
7911 MHD_CONN_USE_KEEPALIVE == connection->keepalive &&
7912 ! connection->read_closed &&
7913 ! connection->discard_request);
7914 continue;
7916 cleanup_connection (connection);
7917 connection->in_idle = false;
7918 return MHD_NO;
7919#ifdef UPGRADE_SUPPORT
7920 case MHD_CONNECTION_UPGRADE:
7921 connection->in_idle = false;
7922 return MHD_YES; /* keep open */
7923#endif /* UPGRADE_SUPPORT */
7924 default:
7925 mhd_assert (0);
7926 break;
7927 }
7928 break;
7929 }
7930 if (connection_check_timedout (connection))
7931 {
7932 MHD_connection_close_ (connection,
7934 connection->in_idle = false;
7935 return MHD_YES;
7936 }
7938 ret = MHD_YES;
7939#ifdef EPOLL_SUPPORT
7940 if ( (! connection->suspended) &&
7941 MHD_D_IS_USING_EPOLL_ (daemon) )
7942 {
7943 ret = MHD_connection_epoll_update_ (connection);
7944 }
7945#endif /* EPOLL_SUPPORT */
7946 connection->in_idle = false;
7947 return ret;
7948}
7949
7950
7951#ifdef EPOLL_SUPPORT
7960enum MHD_Result
7961MHD_connection_epoll_update_ (struct MHD_Connection *connection)
7962{
7963 struct MHD_Daemon *const daemon = connection->daemon;
7964
7966
7967 if ((0 != (MHD_EVENT_LOOP_INFO_PROCESS & connection->event_loop_info)) &&
7968 (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)))
7969 {
7970 /* Make sure that connection waiting for processing will be processed */
7971 EDLL_insert (daemon->eready_head,
7972 daemon->eready_tail,
7973 connection);
7974 connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
7975 }
7976
7977 if ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) &&
7978 (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
7979 ( ( (MHD_EVENT_LOOP_INFO_WRITE == connection->event_loop_info) &&
7980 (0 == (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY))) ||
7981 ( (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info)) &&
7982 (0 == (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) ) ) )
7983 {
7984 /* add to epoll set */
7985 struct epoll_event event;
7986
7987 event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
7988 event.data.ptr = connection;
7989 if (0 != epoll_ctl (daemon->epoll_fd,
7990 EPOLL_CTL_ADD,
7991 connection->socket_fd,
7992 &event))
7993 {
7994#ifdef HAVE_MESSAGES
7995 if (0 != (daemon->options & MHD_USE_ERROR_LOG))
7996 MHD_DLOG (daemon,
7997 _ ("Call to epoll_ctl failed: %s\n"),
7999#endif
8000 connection->state = MHD_CONNECTION_CLOSED;
8001 cleanup_connection (connection);
8002 return MHD_NO;
8003 }
8004 connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
8005 }
8006 return MHD_YES;
8007}
8008
8009
8010#endif
8011
8012
8018void
8020{
8021 connection->recv_cls = &recv_param_adapter;
8022}
8023
8024
8037_MHD_EXTERN const union MHD_ConnectionInfo *
8039 enum MHD_ConnectionInfoType info_type,
8040 ...)
8041{
8042 switch (info_type)
8043 {
8044#ifdef HTTPS_SUPPORT
8046 if (NULL == connection->tls_session)
8047 return NULL;
8048 if (1)
8049 { /* Workaround to mute compiler warning */
8050 gnutls_cipher_algorithm_t res;
8051 res = gnutls_cipher_get (connection->tls_session);
8052 connection->connection_info_dummy.cipher_algorithm = (int) res;
8053 }
8054 return &connection->connection_info_dummy;
8056 if (NULL == connection->tls_session)
8057 return NULL;
8058 if (1)
8059 { /* Workaround to mute compiler warning */
8060 gnutls_protocol_t res;
8061 res = gnutls_protocol_get_version (connection->tls_session);
8062 connection->connection_info_dummy.protocol = (int) res;
8063 }
8064 return &connection->connection_info_dummy;
8066 if (NULL == connection->tls_session)
8067 return NULL;
8068 connection->connection_info_dummy.tls_session = connection->tls_session;
8069 return &connection->connection_info_dummy;
8070#else /* ! HTTPS_SUPPORT */
8074#endif /* ! HTTPS_SUPPORT */
8076 return NULL; /* Not implemented */
8078 if (0 < connection->addr_len)
8079 {
8080 mhd_assert (sizeof (connection->addr) == \
8081 sizeof (connection->connection_info_dummy.client_addr));
8082 memcpy (&connection->connection_info_dummy.client_addr,
8083 &connection->addr,
8084 sizeof(connection->addr));
8085 return &connection->connection_info_dummy;
8086 }
8087 return NULL;
8089 connection->connection_info_dummy.daemon =
8090 MHD_get_master (connection->daemon);
8091 return &connection->connection_info_dummy;
8093 connection->connection_info_dummy.connect_fd = connection->socket_fd;
8094 return &connection->connection_info_dummy;
8097 connection->socket_context;
8098 return &connection->connection_info_dummy;
8100 connection->connection_info_dummy.suspended =
8101 connection->suspended ? MHD_YES : MHD_NO;
8102 return &connection->connection_info_dummy;
8104#if SIZEOF_UNSIGNED_INT <= (SIZEOF_UINT64_T - 2)
8105 if (UINT_MAX < connection->connection_timeout_ms / 1000)
8107 else
8108#endif /* SIZEOF_UNSIGNED_INT <=(SIZEOF_UINT64_T - 2) */
8110 (unsigned int) (connection->connection_timeout_ms / 1000);
8111 return &connection->connection_info_dummy;
8113 if ( (MHD_CONNECTION_HEADERS_RECEIVED > connection->state) ||
8114 (MHD_CONNECTION_CLOSED == connection->state) )
8115 return NULL; /* invalid, too early! */
8116 connection->connection_info_dummy.header_size = connection->rq.header_size;
8117 return &connection->connection_info_dummy;
8119 if (NULL == connection->rp.response)
8120 return NULL;
8121 connection->connection_info_dummy.http_status = connection->rp.responseCode;
8122 return &connection->connection_info_dummy;
8123 default:
8124 return NULL;
8125 }
8126}
8127
8128
8140 enum MHD_CONNECTION_OPTION option,
8141 ...)
8142{
8143 va_list ap;
8144 struct MHD_Daemon *daemon;
8145 unsigned int ui_val;
8146
8147 daemon = connection->daemon;
8148 switch (option)
8149 {
8151 if (0 == connection->connection_timeout_ms)
8153 va_start (ap, option);
8154 ui_val = va_arg (ap, unsigned int);
8155 va_end (ap);
8156#if (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT
8157 if ((UINT64_MAX / 4000 - 1) < ui_val)
8158 {
8159#ifdef HAVE_MESSAGES
8160 MHD_DLOG (connection->daemon,
8161 _ ("The specified connection timeout (%u) is too " \
8162 "large. Maximum allowed value (%" PRIu64 ") will be used " \
8163 "instead.\n"),
8164 ui_val,
8165 (UINT64_MAX / 4000 - 1));
8166#endif
8167 ui_val = UINT64_MAX / 4000 - 1;
8168 }
8169#endif /* (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT */
8170 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
8171 {
8172#if defined(MHD_USE_THREADS)
8173 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
8174#endif
8175 if (! connection->suspended)
8176 {
8177 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
8179 daemon->normal_timeout_tail,
8180 connection);
8181 else
8183 daemon->manual_timeout_tail,
8184 connection);
8185 connection->connection_timeout_ms = ((uint64_t) ui_val) * 1000;
8186 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
8188 daemon->normal_timeout_tail,
8189 connection);
8190 else
8192 daemon->manual_timeout_tail,
8193 connection);
8194 }
8195#if defined(MHD_USE_THREADS)
8196 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
8197#endif
8198 }
8199 return MHD_YES;
8200 default:
8201 return MHD_NO;
8202 }
8203}
8204
8205
8253 unsigned int status_code,
8254 struct MHD_Response *response)
8255{
8256 struct MHD_Daemon *daemon;
8257 bool reply_icy;
8258
8259 if ((NULL == connection) || (NULL == response))
8260 return MHD_NO;
8261
8262 daemon = connection->daemon;
8263 if ((! connection->in_access_handler) && (! connection->suspended) &&
8264 MHD_D_IS_USING_THREADS_ (daemon))
8265 return MHD_NO;
8266
8267 reply_icy = (0 != (status_code & MHD_ICY_FLAG));
8268 status_code &= ~MHD_ICY_FLAG;
8269
8270#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
8271 if ( (! connection->suspended) &&
8272 MHD_D_IS_USING_THREADS_ (daemon) &&
8273 (! MHD_thread_handle_ID_is_current_thread_ (connection->tid)) )
8274 {
8275#ifdef HAVE_MESSAGES
8276 MHD_DLOG (daemon,
8277 _ ("Attempted to queue response on wrong thread!\n"));
8278#endif
8279 return MHD_NO;
8280 }
8281#endif
8282
8283 if (NULL != connection->rp.response)
8284 return MHD_NO; /* The response was already set */
8285
8286 if ( (MHD_CONNECTION_HEADERS_PROCESSED != connection->state) &&
8287 (MHD_CONNECTION_FULL_REQ_RECEIVED != connection->state) )
8288 return MHD_NO; /* Wrong connection state */
8289
8290 if (daemon->shutdown)
8291 return MHD_NO;
8292
8293#ifdef UPGRADE_SUPPORT
8294 if (NULL != response->upgrade_handler)
8295 {
8296 if (0 == (daemon->options & MHD_ALLOW_UPGRADE))
8297 {
8298#ifdef HAVE_MESSAGES
8299 MHD_DLOG (daemon,
8300 _ ("Attempted 'upgrade' connection on daemon without" \
8301 " MHD_ALLOW_UPGRADE option!\n"));
8302#endif
8303 return MHD_NO;
8304 }
8305 if (MHD_HTTP_SWITCHING_PROTOCOLS != status_code)
8306 {
8307#ifdef HAVE_MESSAGES
8308 MHD_DLOG (daemon,
8309 _ ("Application used invalid status code for" \
8310 " 'upgrade' response!\n"));
8311#endif
8312 return MHD_NO;
8313 }
8314 if (0 == (response->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
8315 {
8316#ifdef HAVE_MESSAGES
8317 MHD_DLOG (daemon,
8318 _ ("Application used invalid response" \
8319 " without \"Connection\" header!\n"));
8320#endif
8321 return MHD_NO;
8322 }
8323 /* MHD_add_response_header() keeps the "Connection" header first, so
8324 response->first_header could be read directly here; look it up by
8325 name instead, so that this does not silently break if that ever
8326 stops being true. */
8327 mhd_assert (NULL != response->first_header);
8332 "upgrade"))
8333 {
8334#ifdef HAVE_MESSAGES
8335 MHD_DLOG (daemon,
8336 _ ("Application used invalid response" \
8337 " without \"upgrade\" token in" \
8338 " \"Connection\" header!\n"));
8339#endif
8340 return MHD_NO;
8341 }
8342 if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
8343 {
8344#ifdef HAVE_MESSAGES
8345 MHD_DLOG (daemon,
8346 _ ("Connection \"Upgrade\" can be used only " \
8347 "with HTTP/1.1 connections!\n"));
8348#endif
8349 return MHD_NO;
8350 }
8351 if (MHD_CONN_MUST_CLOSE == connection->keepalive)
8352 {
8353 /* MHD has already decided, while parsing the request, that this
8354 * connection cannot be reused; 'keepalive_possible()' therefore
8355 * returns MHD_CONN_MUST_CLOSE rather than MHD_CONN_MUST_UPGRADE
8356 * for it, and 'build_header_response()' asserts that an upgrade
8357 * reply is only ever built for MHD_CONN_MUST_UPGRADE. Refuse the
8358 * response here instead of aborting there.
8359 *
8360 * The application cannot test for this itself: the request that
8361 * triggers it looks perfectly well-formed to the access handler.
8362 * A request carrying both "Content-Length" and
8363 * "Transfer-Encoding: chunked" is the shortest way in and needs
8364 * no non-default daemon options at all. */
8365#ifdef HAVE_MESSAGES
8366 MHD_DLOG (daemon,
8367 _ ("Connection cannot be upgraded: it has already been " \
8368 "marked as \"must close\" while the request was " \
8369 "being parsed.\n"));
8370#endif
8371 return MHD_NO;
8372 }
8373 }
8374#endif /* UPGRADE_SUPPORT */
8375 if (MHD_HTTP_SWITCHING_PROTOCOLS == status_code)
8376 {
8377#ifdef UPGRADE_SUPPORT
8378 if (NULL == response->upgrade_handler)
8379 {
8380#ifdef HAVE_MESSAGES
8381 MHD_DLOG (daemon,
8382 _ ("Application used status code 101 \"Switching Protocols\" " \
8383 "with non-'upgrade' response!\n"));
8384#endif /* HAVE_MESSAGES */
8385 return MHD_NO;
8386 }
8387#else /* ! UPGRADE_SUPPORT */
8388#ifdef HAVE_MESSAGES
8389 MHD_DLOG (daemon,
8390 _ ("Application used status code 101 \"Switching Protocols\", " \
8391 "but this MHD was built without \"Upgrade\" support!\n"));
8392#endif /* HAVE_MESSAGES */
8393 return MHD_NO;
8394#endif /* ! UPGRADE_SUPPORT */
8395 }
8396 if ( (100 > status_code) ||
8397 (999 < status_code) )
8398 {
8399#ifdef HAVE_MESSAGES
8400 MHD_DLOG (daemon,
8401 _ ("Refused wrong status code (%u). " \
8402 "HTTP requires three digits status code!\n"),
8403 status_code);
8404#endif
8405 return MHD_NO;
8406 }
8407 if (200 > status_code)
8408 {
8409 if (MHD_HTTP_VER_1_0 == connection->rq.http_ver)
8410 {
8411#ifdef HAVE_MESSAGES
8412 MHD_DLOG (daemon,
8413 _ ("Wrong status code (%u) refused. " \
8414 "HTTP/1.0 clients do not support 1xx status codes!\n"),
8415 (status_code));
8416#endif
8417 return MHD_NO;
8418 }
8419 if (0 != (response->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
8421 {
8422#ifdef HAVE_MESSAGES
8423 MHD_DLOG (daemon,
8424 _ ("Wrong status code (%u) refused. " \
8425 "HTTP/1.0 reply mode does not support 1xx status codes!\n"),
8426 (status_code));
8427#endif
8428 return MHD_NO;
8429 }
8430 }
8431 if ( (MHD_HTTP_MTHD_CONNECT == connection->rq.http_mthd) &&
8432 (2 == status_code / 100) )
8433 {
8434#ifdef HAVE_MESSAGES
8435 MHD_DLOG (daemon,
8436 _ ("Successful (%u) response code cannot be used to answer " \
8437 "\"CONNECT\" request!\n"),
8438 (status_code));
8439#endif
8440 return MHD_NO;
8441 }
8442
8443 if ( (0 != (MHD_RF_HEAD_ONLY_RESPONSE & response->flags)) &&
8444 (RP_BODY_HEADERS_ONLY < is_reply_body_needed (connection, status_code)) )
8445 {
8446#ifdef HAVE_MESSAGES
8447 MHD_DLOG (daemon,
8448 _ ("HEAD-only response cannot be used when the request requires "
8449 "reply body to be sent!\n"));
8450#endif
8451 return MHD_NO;
8452 }
8453
8454#ifdef HAVE_MESSAGES
8455 if ( (0 != (MHD_RF_INSANITY_HEADER_CONTENT_LENGTH & response->flags)) &&
8456 (0 != (MHD_RAF_HAS_CONTENT_LENGTH & response->flags_auto)) )
8457 {
8458 MHD_DLOG (daemon,
8459 _ ("The response has application-defined \"Content-Length\" " \
8460 "header. The reply to the request will be not " \
8461 "HTTP-compliant and may result in hung connection or " \
8462 "other problems!\n"));
8463 }
8464#endif
8465
8466 MHD_increment_response_rc (response);
8467 connection->rp.response = response;
8468 connection->rp.responseCode = status_code;
8469 connection->rp.responseIcy = reply_icy;
8470#if defined(_MHD_HAVE_SENDFILE)
8471 if ( (response->fd == -1) ||
8472 (response->is_pipe) ||
8473 (0 != (connection->daemon->options & MHD_USE_TLS))
8474#if defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED) && \
8475 defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE)
8476 || (! daemon->sigpipe_blocked && ! connection->sk_spipe_suppress)
8477#endif /* MHD_SEND_SPIPE_SUPPRESS_NEEDED &&
8478 MHD_SEND_SPIPE_SUPPRESS_POSSIBLE */
8479 )
8480 connection->rp.resp_sender = MHD_resp_sender_std;
8481 else
8482 connection->rp.resp_sender = MHD_resp_sender_sendfile;
8483#endif /* _MHD_HAVE_SENDFILE */
8484 /* FIXME: if 'is_pipe' is set, TLS is off, and we have *splice*, we could use splice()
8485 to avoid two user-space copies... */
8486
8487 if ( (MHD_HTTP_MTHD_HEAD == connection->rq.http_mthd) ||
8488 (MHD_HTTP_OK > status_code) ||
8489 (MHD_HTTP_NO_CONTENT == status_code) ||
8490 (MHD_HTTP_NOT_MODIFIED == status_code) )
8491 {
8492 /* if this is a "HEAD" request, or a status code for
8493 which a body is not allowed, pretend that we
8494 have already sent the full message body. */
8495 /* TODO: remove the next assignment, use 'rp_props.send_reply_body' in
8496 * checks */
8497 connection->rp.rsp_write_position = response->total_size;
8498 }
8499 if (MHD_CONNECTION_HEADERS_PROCESSED == connection->state)
8500 {
8501 /* response was queued "early", refuse to read body / footers or
8502 further requests! */
8503 connection->discard_request = true;
8504 connection->state = MHD_CONNECTION_START_REPLY;
8505 connection->rq.remaining_upload_size = 0;
8506 }
8507 if (! connection->in_idle)
8508 (void) MHD_connection_handle_idle (connection);
8509 MHD_update_last_activity_ (connection);
8510 return MHD_YES;
8511}
8512
8513
8514/* end of connection.c */
#define ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG
Definition connection.c:168
#define REQUEST_CONTENTLENGTH_TOOLARGE
Definition connection.c:588
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:780
#define ERR_RSP_EMPTY_FOOTER_NAME
Definition connection.c:452
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:76
#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:576
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:706
#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:564
#define REQ_HTTP_VER_IS_NOT_SUPPORTED
Definition connection.c:640
#define MHD_MAX_REASONABLE_HEADERS_SIZE_
#define RQ_LINE_TOO_MANY_WSP
Definition connection.c:199
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:400
static void setup_reply_properties(struct MHD_Connection *connection)
#define ERR_RSP_INVALID_CHAR_IN_FIELD_NAME
Definition connection.c:374
#define HTTP_100_CONTINUE
Definition connection.c:81
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:545
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:413
static void connection_shrink_read_buffer(struct MHD_Connection *connection)
#define ERR_RSP_WSP_IN_FOOTER_NAME
Definition connection.c:359
#define MHD_ALLOW_BARE_LF_AS_CRLF_(discp_lvl)
Definition connection.c:66
#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:629
#define ERR_MSG_REQUEST_TOO_BIG
Definition connection.c:95
#define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
#define ERR_MSG_REQUEST_FOOTER_TOO_BIG
Definition connection.c:186
#define ERR_MSG_REQUEST_CHUNK_LINE_EXT_TOO_BIG
Definition connection.c:149
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:470
#define BARE_CR_IN_FOOTER
Definition connection.c:229
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:244
#define ERR_RSP_EMPTY_HEADER_NAME
Definition connection.c:439
static bool need_100_continue(struct MHD_Connection *connection)
#define ERR_RSP_WSP_IN_HEADER_NAME
Definition connection.c:344
#define REQUEST_HTTP1_0_TR_ENCODING
Definition connection.c:528
static bool get_req_headers(struct MHD_Connection *c, bool process_footers)
#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP
Definition connection.c:617
#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:507
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:299
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:286
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:259
static void connection_reset(struct MHD_Connection *connection, bool reuse)
#define REQUEST_LENGTH_WITH_TR_ENCODING
Definition connection.c:522
#define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
#define RQ_TARGET_INVALID_CHAR
Definition connection.c:273
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:494
static void connection_switch_from_recv_to_send(struct MHD_Connection *connection)
#define ERR_RSP_WSP_BEFORE_FOOTER
Definition connection.c:329
#define ERR_RSP_INVALID_CHR_IN_HEADER
Definition connection.c:387
#define REQUEST_MULTIPLE_HOST_HDR
Definition connection.c:476
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:551
#define BARE_CR_IN_HEADER
Definition connection.c:214
#define ERR_MSG_REQUEST_HEADER_TOO_BIG
Definition connection.c:112
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:601
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:314
#define ERR_MSG_REQUEST_HEADER_WITH_COOKIES_TOO_BIG
Definition connection.c:130
_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:426
#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:913
_MHD_EXTERN int MHD_get_connection_values(struct MHD_Connection *connection, enum MHD_ValueKind kind, MHD_KeyValueIterator iterator, void *iterator_cls)
Definition connection.c:874
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:966
_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:836
_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)
MHD_RequestTerminationCode
@ MHD_CONNECTION_INFO_CONNECTION_TIMEOUT
@ MHD_CONNECTION_INFO_SOCKET_CONTEXT
@ MHD_CONNECTION_INFO_GNUTLS_SESSION
@ MHD_CONNECTION_INFO_REQUEST_HEADER_SIZE
@ MHD_CONNECTION_INFO_CIPHER_ALGO
@ MHD_CONNECTION_INFO_CONNECTION_SUSPENDED
@ MHD_CONNECTION_INFO_CLIENT_ADDRESS
@ MHD_CONNECTION_INFO_DAEMON
@ MHD_CONNECTION_INFO_GNUTLS_CLIENT_CERT
@ MHD_CONNECTION_INFO_HTTP_STATUS
@ MHD_CONNECTION_INFO_CONNECTION_FD
@ MHD_CONNECTION_INFO_PROTOCOL
@ MHD_REQUEST_TERMINATED_TIMEOUT_REACHED
@ MHD_REQUEST_TERMINATED_COMPLETED_OK
@ MHD_REQUEST_TERMINATED_WITH_ERROR
@ MHD_REQUEST_TERMINATED_READ_ERROR
@ MHD_REQUEST_TERMINATED_CLIENT_ABORT
_MHD_EXTERN enum MHD_Result MHD_queue_response(struct MHD_Connection *connection, unsigned int status_code, struct MHD_Response *response)
_MHD_EXTERN void MHD_destroy_response(struct MHD_Response *response)
Definition response.c:2294
enum MHD_HdrLineReadRes_ _MHD_FIXED_ENUM
_MHD_EXTERN struct MHD_Response * MHD_create_response_from_buffer_static(size_t size, const void *buffer)
Definition response.c:1512
_MHD_EXTERN enum MHD_Result MHD_set_connection_option(struct MHD_Connection *connection, enum MHD_CONNECTION_OPTION option,...)
#define MHD_ICY_FLAG
Definition microhttpd.h:556
_MHD_EXTERN const union MHD_ConnectionInfo * MHD_get_connection_info(struct MHD_Connection *connection, enum MHD_ConnectionInfoType info_type,...)
#define MHD_HTTP_VERSION_1_0
#define MHD_HTTP_VERSION_1_1
enum MHD_Result MHD_parse_arguments_(struct MHD_Connection *connection, enum MHD_ValueKind kind, char *args, MHD_ArgumentIterator_ cb, void *cls)
Definition internal.c:169
MHD internal shared structures.
MHD_CONNECTION_STATE
Definition internal.h:613
@ MHD_CONNECTION_BODY_RECEIVED
Definition internal.h:663
@ MHD_CONNECTION_CHUNKED_BODY_SENT
Definition internal.h:725
@ MHD_CONNECTION_REQ_HEADERS_RECEIVING
Definition internal.h:636
@ MHD_CONNECTION_BODY_RECEIVING
Definition internal.h:656
@ MHD_CONNECTION_HEADERS_SENDING
Definition internal.h:694
@ MHD_CONNECTION_FOOTERS_SENDING
Definition internal.h:730
@ MHD_CONNECTION_FOOTERS_RECEIVED
Definition internal.h:675
@ MHD_CONNECTION_FULL_REPLY_SENT
Definition internal.h:736
@ MHD_CONNECTION_HEADERS_SENT
Definition internal.h:699
@ MHD_CONNECTION_HEADERS_PROCESSED
Definition internal.h:646
@ MHD_CONNECTION_INIT
Definition internal.h:618
@ MHD_CONNECTION_CLOSED
Definition internal.h:741
@ MHD_CONNECTION_REQ_LINE_RECEIVED
Definition internal.h:631
@ MHD_CONNECTION_NORMAL_BODY_UNREADY
Definition internal.h:705
@ MHD_CONNECTION_HEADERS_RECEIVED
Definition internal.h:641
@ MHD_CONNECTION_NORMAL_BODY_READY
Definition internal.h:710
@ MHD_CONNECTION_START_REPLY
Definition internal.h:688
@ MHD_CONNECTION_FOOTERS_RECEIVING
Definition internal.h:668
@ MHD_CONNECTION_CHUNKED_BODY_READY
Definition internal.h:720
@ MHD_CONNECTION_FULL_REQ_RECEIVED
Definition internal.h:681
@ MHD_CONNECTION_CHUNKED_BODY_UNREADY
Definition internal.h:715
@ MHD_CONNECTION_CONTINUE_SENDING
Definition internal.h:651
@ MHD_CONNECTION_REQ_LINE_RECEIVING
Definition internal.h:624
#define XDLL_insert(head, tail, element)
Definition internal.h:2735
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
#define MHD_check_response_header_s_token_ci(r, k, tkn)
Definition internal.h:2904
@ 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:2688
@ 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:2782
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:2930
@ 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:2802
#define PRIu64
Definition internal.h:53
#define XDLL_remove(head, tail, element)
Definition internal.h:2757
#define MHD_D_IS_USING_THREAD_PER_CONN_(d)
Definition internal.h:2608
#define DLL_remove(head, tail, element)
Definition internal.h:2710
#define MHD_D_IS_USING_THREADS_(d)
Definition internal.h:2604
#define MHD_D_IS_USING_EPOLL_(d)
Definition internal.h:2580
void * MHD_pool_reallocate(struct MemoryPool *pool, void *old, size_t old_size, size_t new_size)
Definition memorypool.c:553
void MHD_pool_destroy(struct MemoryPool *pool)
Definition memorypool.c:361
bool MHD_pool_is_resizable_inplace(struct MemoryPool *pool, void *block, size_t block_size)
Definition memorypool.c:460
void MHD_pool_deallocate(struct MemoryPool *pool, void *block, size_t block_size)
Definition memorypool.c:678
void * MHD_pool_try_alloc(struct MemoryPool *pool, size_t size, size_t *required_bytes)
Definition memorypool.c:501
size_t MHD_pool_get_free(struct MemoryPool *pool)
Definition memorypool.c:394
void * MHD_pool_reset(struct MemoryPool *pool, void *keep, size_t copy_bytes, size_t new_size)
Definition memorypool.c:798
void * MHD_pool_allocate(struct MemoryPool *pool, size_t size, bool from_end)
Definition memorypool.c:419
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
macros for MHD_CHECK_(), always-compiled memory-safety invariants
#define MHD_CHECK_LOG_(daemon, expr_str)
Definition mhd_check.h:99
#define MHD_CHECK_CONN_CLOSE_RET_(c, expr, retval)
Definition mhd_check.h:153
#define MHD_CHECK_FAILED_(expr)
Definition mhd_check.h:81
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:684
size_t MHD_uint8_to_str_pad(uint8_t val, uint8_t min_digits, char *buf, size_t buf_size)
Definition mhd_str.c:1630
size_t MHD_uint16_to_str(uint16_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1551
size_t MHD_str_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition mhd_str.c:1239
size_t MHD_uint64_to_str(uint64_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1592
size_t MHD_strx_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition mhd_str.c:1416
int MHD_str_equal_caseless_n_(const char *const str1, const char *const str2, size_t maxlen)
Definition mhd_str.c:718
bool MHD_str_has_token_caseless_(const char *str, const char *const token, size_t token_len)
Definition mhd_str.c:783
bool MHD_str_equal_caseless_bin_n_(const char *const str1, const char *const str2, size_t len)
Definition mhd_str.c:750
size_t MHD_uint32_to_strx(uint32_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1517
Header for string manipulating helpers.
#define MHD_str_equal_caseless_s_bin_n_(a, s, l)
Definition mhd_str.h:122
#define MHD_STATICSTR_LEN_(macro)
#define MHD_thread_handle_ID_is_current_thread_(hndl_id)
#define MHD_SIZE_UNKNOWN
Definition microhttpd.h:183
MHD_Result
Definition microhttpd.h:163
@ MHD_YES
Definition microhttpd.h:172
@ MHD_NO
Definition microhttpd.h:167
#define MHD_CONTENT_READER_END_OF_STREAM
Definition microhttpd.h:186
_MHD_EXTERN size_t MHD_get_reason_phrase_len_for(unsigned int code)
#define MHD_INVALID_SOCKET
Definition microhttpd.h:207
_MHD_EXTERN const char * MHD_get_reason_phrase_for(unsigned int code)
#define MHD_CONTENT_READER_END_WITH_ERROR
Definition microhttpd.h:187
MHD_ValueKind
@ MHD_FOOTER_KIND
@ MHD_COOKIE_KIND
@ MHD_HEADER_KIND
@ MHD_GET_ARGUMENT_KIND
@ MHD_USE_TURBO
@ MHD_USE_SUPPRESS_DATE_NO_CLOCK
@ MHD_USE_TLS
@ MHD_ALLOW_UPGRADE
@ MHD_USE_ERROR_LOG
@ MHD_RF_SEND_KEEP_ALIVE_HEADER
@ MHD_RF_HEAD_ONLY_RESPONSE
@ MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
@ MHD_RF_HTTP_1_0_SERVER
@ MHD_RF_INSANITY_HEADER_CONTENT_LENGTH
MHD_CONNECTION_OPTION
@ MHD_CONNECTION_OPTION_TIMEOUT
bool MHD_add_response_entry_no_alloc_(struct MHD_Response *response, enum MHD_ValueKind kind, char *header, size_t header_len, char *content, size_t content_len)
Definition response.c:167
void MHD_increment_response_rc(struct MHD_Response *response)
Definition response.c:2340
Methods for managing response objects.
enum MHD_Result MHD_response_execute_upgrade_(struct MHD_Response *response, struct MHD_Connection *connection)
MHD_socket socket_fd
Definition internal.h:1489
size_t write_buffer_size
Definition internal.h:1447
size_t write_buffer_send_offset
Definition internal.h:1452
socklen_t addr_len
Definition internal.h:1469
enum MHD_ConnectionEventLoopInfo event_loop_info
Definition internal.h:1576
size_t write_buffer_append_offset
Definition internal.h:1458
char * write_buffer
Definition internal.h:1415
bool stop_with_error
Definition internal.h:1532
bool discard_request
Definition internal.h:1541
ReceiveCallback recv_cls
Definition internal.h:1581
volatile bool resuming
Definition internal.h:1627
void * socket_context
Definition internal.h:1395
uint64_t last_activity
Definition internal.h:1475
enum MHD_ConnKeepAlive keepalive
Definition internal.h:1402
struct MHD_Request rq
Definition internal.h:1371
union MHD_ConnectionInfo connection_info_dummy
Definition internal.h:1632
size_t continue_message_write_offset
Definition internal.h:1464
size_t read_buffer_offset
Definition internal.h:1442
struct MHD_Reply rp
Definition internal.h:1376
struct MemoryPool * pool
Definition internal.h:1386
enum MHD_CONNECTION_STATE state
Definition internal.h:1571
char * read_buffer
Definition internal.h:1409
struct MHD_Daemon * daemon
Definition internal.h:1366
bool in_access_handler
Definition internal.h:1622
bool sk_spipe_suppress
Definition internal.h:1505
uint64_t connection_timeout_ms
Definition internal.h:1482
struct sockaddr_storage * addr
Definition internal.h:1421
size_t read_buffer_size
Definition internal.h:1436
size_t pool_size
Definition internal.h:2142
MHD_AccessHandlerCallback default_handler
Definition internal.h:1873
LogCallback uri_log_callback
Definition internal.h:2063
struct MHD_Connection * normal_timeout_tail
Definition internal.h:2006
int client_discipline
Definition internal.h:2288
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:2221
uint64_t connection_timeout_ms
Definition internal.h:2276
struct MHD_Connection * manual_timeout_tail
Definition internal.h:2021
volatile bool shutdown
Definition internal.h:2226
enum MHD_FLAG options
Definition internal.h:1886
struct MHD_Connection * cleanup_tail
Definition internal.h:1926
bool sigpipe_blocked
Definition internal.h:2316
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:2295
void * default_handler_cls
Definition internal.h:1878
struct MHD_Connection * connections_head
Definition internal.h:1901
size_t pool_increment
Definition internal.h:2147
void * uri_log_callback_cls
Definition internal.h:2068
enum MHD_ValueKind kind
Definition internal.h:396
struct MHD_HTTP_Req_Header * prev
Definition internal.h:371
const char * value
Definition internal.h:386
struct MHD_HTTP_Req_Header * next
Definition internal.h:366
const char * header
Definition internal.h:376
struct MHD_HTTP_Res_Header * next
Definition internal.h:323
enum MHD_ValueKind kind
Definition internal.h:353
uint64_t rsp_write_position
Definition internal.h:1301
bool responseIcy
Definition internal.h:1294
struct MHD_iovec_track_ resp_iov
Definition internal.h:1309
struct MHD_Response * response
Definition internal.h:1282
unsigned int responseCode
Definition internal.h:1288
struct MHD_Reply_Properties props
Definition internal.h:1318
unsigned int skipped_empty_lines
Definition internal.h:955
struct MHD_HTTP_Req_Header * headers_received
Definition internal.h:1117
union MHD_HeadersProcessing hdrs
Definition internal.h:1247
uint64_t current_chunk_size
Definition internal.h:1167
uint64_t current_chunk_offset
Definition internal.h:1173
bool some_payload_processed
Definition internal.h:1190
size_t skipped_broken_lines
Definition internal.h:1242
void * client_context
Definition internal.h:1198
const char * url
Definition internal.h:1096
size_t url_len
Definition internal.h:1101
const char * version
Definition internal.h:1075
size_t num_cr_sp_replaced
Definition internal.h:1237
enum MHD_HTTP_Version http_ver
Definition internal.h:1080
size_t req_target_len
Definition internal.h:1106
const char * method
Definition internal.h:1085
size_t header_size
Definition internal.h:1130
union MHD_StartOrSize field_lines
Definition internal.h:1140
struct MHD_HTTP_Req_Header * headers_received_tail
Definition internal.h:1122
enum MHD_HTTP_Method http_mthd
Definition internal.h:1090
bool client_aware
Definition internal.h:1205
bool have_chunked_upload
Definition internal.h:1158
const char * url_for_callback
Definition internal.h:1112
uint64_t remaining_upload_size
Definition internal.h:1146
size_t data_buffer_size
Definition internal.h:557
MHD_iovec_ * data_iov
Definition internal.h:588
const char * data
Definition internal.h:489
uint64_t data_start
Definition internal.h:541
MHD_ContentReaderCallback crc
Definition internal.h:501
enum MHD_ResponseAutoFlags flags_auto
Definition internal.h:578
unsigned int data_iovcnt
Definition internal.h:593
size_t data_size
Definition internal.h:552
enum MHD_ResponseFlags flags
Definition internal.h:573
void * crc_cls
Definition internal.h:495
struct MHD_HTTP_Res_Header * first_header
Definition internal.h:478
uint64_t total_size
Definition internal.h:535
MHD_iovec_ * iov
Definition internal.h:453
const char * str
unsigned int connection_timeout
struct MHD_Daemon * daemon
unsigned int http_status
struct sockaddr * client_addr
MHD_socket connect_fd
struct MHD_RequestLineProcessing rq_line
Definition internal.h:1036
struct MHD_HeaderProcessing hdr
Definition internal.h:1041
const char * start
Definition internal.h:1055