GNU libmicrohttpd 1.0.3
Loading...
Searching...
No Matches
connection.c
Go to the documentation of this file.
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2007-2020 Daniel Pittman and Christian Grothoff
4 Copyright (C) 2015-2024 Evgeny Grin (Karlson2k)
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20*/
28#include "internal.h"
29#include "mhd_limits.h"
30#include "connection.h"
31#include "memorypool.h"
32#include "response.h"
33#include "mhd_mono_clock.h"
34#include "mhd_str.h"
35#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
36#include "mhd_locks.h"
37#endif
38#include "mhd_sockets.h"
39#include "mhd_compat.h"
40#include "mhd_itc.h"
41#ifdef MHD_LINUX_SOLARIS_SENDFILE
42#include <sys/sendfile.h>
43#endif /* MHD_LINUX_SOLARIS_SENDFILE */
44#if defined(HAVE_FREEBSD_SENDFILE) || defined(HAVE_DARWIN_SENDFILE)
45#include <sys/types.h>
46#include <sys/socket.h>
47#include <sys/uio.h>
48#endif /* HAVE_FREEBSD_SENDFILE || HAVE_DARWIN_SENDFILE */
49#ifdef HTTPS_SUPPORT
50#include "connection_https.h"
51#endif /* HTTPS_SUPPORT */
52#ifdef HAVE_SYS_PARAM_H
53/* For FreeBSD version identification */
54#include <sys/param.h>
55#endif /* HAVE_SYS_PARAM_H */
56#include "mhd_send.h"
57#include "mhd_assert.h"
58
65#define MHD_ALLOW_BARE_LF_AS_CRLF_(discp_lvl) (0 >= discp_lvl)
66
75#define MHD_CHUNK_HEADER_REASONABLE_LEN 24
76
80#define HTTP_100_CONTINUE "HTTP/1.1 100 Continue\r\n\r\n"
81
86#ifdef HAVE_MESSAGES
87#define ERR_MSG_REQUEST_TOO_BIG \
88 "<html>" \
89 "<head><title>Request too big</title></head>" \
90 "<body>Request HTTP header is too big for the memory constraints " \
91 "of this webserver.</body>" \
92 "</html>"
93#else
94#define ERR_MSG_REQUEST_TOO_BIG ""
95#endif
96
100#ifdef HAVE_MESSAGES
101#define ERR_MSG_REQUEST_HEADER_TOO_BIG \
102 "<html>" \
103 "<head><title>Request too big</title></head>" \
104 "<body><p>The total size of the request headers, which includes the " \
105 "request target and the request field lines, exceeds the memory " \
106 "constraints of this web server.</p>" \
107 "<p>The request could be re-tried with shorter field lines, a shorter " \
108 "request target or a shorter request method token.</p></body>" \
109 "</html>"
110#else
111#define ERR_MSG_REQUEST_HEADER_TOO_BIG ""
112#endif
113
117#ifdef HAVE_MESSAGES
118#define ERR_MSG_REQUEST_HEADER_WITH_COOKIES_TOO_BIG \
119 "<html>" \
120 "<head><title>Request too big</title></head>" \
121 "<body><p>The total size of the request headers, which includes the " \
122 "request target and the request field lines, exceeds the memory " \
123 "constraints of this web server.</p> " \
124 "<p>The request could be re-tried with smaller " \
125 "<b>&quot;Cookie:&quot;</b> field value, shorter other field lines, " \
126 "a shorter request target or a shorter request method token.</p></body> " \
127 "</html>"
128#else
129#define ERR_MSG_REQUEST_HEADER_WITH_COOKIES_TOO_BIG ""
130#endif
131
136#ifdef HAVE_MESSAGES
137#define ERR_MSG_REQUEST_CHUNK_LINE_EXT_TOO_BIG \
138 "<html>" \
139 "<head><title>Request too big</title></head>" \
140 "<body><p>The total size of the request target, the request field lines " \
141 "and the chunk size line exceeds the memory constraints of this web " \
142 "server.</p>" \
143 "<p>The request could be re-tried without chunk extensions, with a smaller " \
144 "chunk size, shorter field lines, a shorter request target or a shorter " \
145 "request method token.</p></body>" \
146 "</html>"
147#else
148#define ERR_MSG_REQUEST_CHUNK_LINE_EXT_TOO_BIG ""
149#endif
150
155#ifdef HAVE_MESSAGES
156#define ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG \
157 "<html>" \
158 "<head><title>Request too big</title></head>" \
159 "<body><p>The total size of the request target, the request field lines " \
160 "and the chunk size line exceeds the memory constraints of this web " \
161 "server.</p>" \
162 "<p>The request could be re-tried with a smaller " \
163 "chunk size, shorter field lines, a shorter request target or a shorter " \
164 "request method token.</p></body>" \
165 "</html>"
166#else
167#define ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG ""
168#endif
169
173#ifdef HAVE_MESSAGES
174#define ERR_MSG_REQUEST_FOOTER_TOO_BIG \
175 "<html>" \
176 "<head><title>Request too big</title></head>" \
177 "<body><p>The total size of the request headers, which includes the " \
178 "request target, the request field lines and the chunked trailer " \
179 "section exceeds the memory constraints of this web server.</p>" \
180 "<p>The request could be re-tried with a shorter chunked trailer " \
181 "section, shorter field lines, a shorter request target or " \
182 "a shorter request method token.</p></body>" \
183 "</html>"
184#else
185#define ERR_MSG_REQUEST_FOOTER_TOO_BIG ""
186#endif
187
191#ifdef HAVE_MESSAGES
192#define RQ_LINE_TOO_MANY_WSP \
193 "<html>" \
194 "<head><title>Request broken</title></head>" \
195 "<body>The request line has more then two whitespaces.</body>" \
196 "</html>"
197#else
198#define RQ_LINE_TOO_MANY_WSP ""
199#endif
200
205#ifdef HAVE_MESSAGES
206#define BARE_CR_IN_HEADER \
207 "<html>" \
208 "<head><title>Request broken</title></head>" \
209 "<body>Request HTTP header has bare CR character without " \
210 "following LF character.</body>" \
211 "</html>"
212#else
213#define BARE_CR_IN_HEADER ""
214#endif
215
220#ifdef HAVE_MESSAGES
221#define BARE_CR_IN_FOOTER \
222 "<html>" \
223 "<head><title>Request broken</title></head>" \
224 "<body>Request HTTP footer has bare CR character without " \
225 "following LF character.</body>" \
226 "</html>"
227#else
228#define BARE_CR_IN_FOOTER ""
229#endif
230
235#ifdef HAVE_MESSAGES
236#define BARE_LF_IN_HEADER \
237 "<html>" \
238 "<head><title>Request broken</title></head>" \
239 "<body>Request HTTP header has bare LF character without " \
240 "preceding CR character.</body>" \
241 "</html>"
242#else
243#define BARE_LF_IN_HEADER ""
244#endif
245
250#ifdef HAVE_MESSAGES
251#define BARE_LF_IN_FOOTER \
252 "<html>" \
253 "<head><title>Request broken</title></head>" \
254 "<body>Request HTTP footer has bare LF character without " \
255 "preceding CR character.</body>" \
256 "</html>"
257#else
258#define BARE_LF_IN_FOOTER ""
259#endif
260
264#ifdef HAVE_MESSAGES
265#define RQ_TARGET_INVALID_CHAR \
266 "<html>" \
267 "<head><title>Request broken</title></head>" \
268 "<body>HTTP request has invalid characters in " \
269 "the request-target.</body>" \
270 "</html>"
271#else
272#define RQ_TARGET_INVALID_CHAR ""
273#endif
274
278#ifdef HAVE_MESSAGES
279#define ERR_RSP_OBS_FOLD \
280 "<html>" \
281 "<head><title>Request broken</title></head>" \
282 "<body>Obsolete line folding is used in HTTP request header.</body>" \
283 "</html>"
284#else
285#define ERR_RSP_OBS_FOLD ""
286#endif
287
291#ifdef HAVE_MESSAGES
292#define ERR_RSP_OBS_FOLD_FOOTER \
293 "<html>" \
294 "<head><title>Request broken</title></head>" \
295 "<body>Obsolete line folding is used in HTTP request footer.</body>" \
296 "</html>"
297#else
298#define ERR_RSP_OBS_FOLD_FOOTER ""
299#endif
300
305#ifdef HAVE_MESSAGES
306#define ERR_RSP_WSP_BEFORE_HEADER \
307 "<html>" \
308 "<head><title>Request broken</title></head>" \
309 "<body>HTTP request has whitespace between the request line and " \
310 "the first header.</body>" \
311 "</html>"
312#else
313#define ERR_RSP_WSP_BEFORE_HEADER ""
314#endif
315
320#ifdef HAVE_MESSAGES
321#define ERR_RSP_WSP_BEFORE_FOOTER \
322 "<html>" \
323 "<head><title>Request broken</title></head>" \
324 "<body>First HTTP footer line has whitespace at the first " \
325 "position.</body>" \
326 "</html>"
327#else
328#define ERR_RSP_WSP_BEFORE_FOOTER ""
329#endif
330
335#ifdef HAVE_MESSAGES
336#define ERR_RSP_WSP_IN_HEADER_NAME \
337 "<html>" \
338 "<head><title>Request broken</title></head>" \
339 "<body>HTTP request has whitespace before the first colon " \
340 "in header line.</body>" \
341 "</html>"
342#else
343#define ERR_RSP_WSP_IN_HEADER_NAME ""
344#endif
345
350#ifdef HAVE_MESSAGES
351#define ERR_RSP_WSP_IN_FOOTER_NAME \
352 "<html>" \
353 "<head><title>Request broken</title></head>" \
354 "<body>HTTP request has whitespace before the first colon " \
355 "in footer line.</body>" \
356 "</html>"
357#else
358#define ERR_RSP_WSP_IN_FOOTER_NAME ""
359#endif
360
361
366#ifdef HAVE_MESSAGES
367#define ERR_RSP_INVALID_CHAR_IN_FIELD_NAME \
368 "<html>" \
369 "<head><title>Request broken</title></head>" \
370 "<body>HTTP request has invalid character in field name.</body>" \
371 "</html>"
372#else
373#define ERR_RSP_INVALID_CHAR_IN_FIELD_NAME ""
374#endif
375
379#ifdef HAVE_MESSAGES
380#define ERR_RSP_INVALID_CHR_IN_HEADER \
381 "<html>" \
382 "<head><title>Request broken</title></head>" \
383 "<body>HTTP request has invalid character in header.</body>" \
384 "</html>"
385#else
386#define ERR_RSP_INVALID_CHR_IN_HEADER ""
387#endif
388
392#ifdef HAVE_MESSAGES
393#define ERR_RSP_INVALID_CHR_IN_FOOTER \
394 "<html>" \
395 "<head><title>Request broken</title></head>" \
396 "<body>HTTP request has invalid character in footer.</body>" \
397 "</html>"
398#else
399#define ERR_RSP_INVALID_CHR_IN_FOOTER ""
400#endif
401
405#ifdef HAVE_MESSAGES
406#define ERR_RSP_HEADER_WITHOUT_COLON \
407 "<html>" \
408 "<head><title>Request broken</title></head>" \
409 "<body>HTTP request header line has no colon character.</body>" \
410 "</html>"
411#else
412#define ERR_RSP_HEADER_WITHOUT_COLON ""
413#endif
414
418#ifdef HAVE_MESSAGES
419#define ERR_RSP_FOOTER_WITHOUT_COLON \
420 "<html>" \
421 "<head><title>Request broken</title></head>" \
422 "<body>HTTP request footer line has no colon character.</body>" \
423 "</html>"
424#else
425#define ERR_RSP_FOOTER_WITHOUT_COLON ""
426#endif
427
431#ifdef HAVE_MESSAGES
432#define ERR_RSP_EMPTY_HEADER_NAME \
433 "<html>" \
434 "<head><title>Request broken</title></head>" \
435 "<body>HTTP request header has empty header name.</body>" \
436 "</html>"
437#else
438#define ERR_RSP_EMPTY_HEADER_NAME ""
439#endif
440
444#ifdef HAVE_MESSAGES
445#define ERR_RSP_EMPTY_FOOTER_NAME \
446 "<html>" \
447 "<head><title>Request broken</title></head>" \
448 "<body>HTTP request footer has empty footer name.</body>" \
449 "</html>"
450#else
451#define ERR_RSP_EMPTY_FOOTER_NAME ""
452#endif
453
461#ifdef HAVE_MESSAGES
462#define REQUEST_LACKS_HOST \
463 "<html>" \
464 "<head><title>&quot;Host:&quot; header required</title></head>" \
465 "<body>HTTP/1.1 request without <b>&quot;Host:&quot;</b>.</body>" \
466 "</html>"
467
468#else
469#define REQUEST_LACKS_HOST ""
470#endif
471
475#ifdef HAVE_MESSAGES
476#define REQUEST_UNSUPPORTED_TR_ENCODING \
477 "<html>" \
478 "<head><title>Unsupported Transfer-Encoding</title></head>" \
479 "<body>The Transfer-Encoding used in request is not supported.</body>" \
480 "</html>"
481#else
482#define REQUEST_UNSUPPORTED_TR_ENCODING ""
483#endif
484
489#ifdef HAVE_MESSAGES
490#define REQUEST_LENGTH_WITH_TR_ENCODING \
491 "<html>" \
492 "<head><title>Malformed request</title></head>" \
493 "<body>Wrong combination of the request headers: both Transfer-Encoding " \
494 "and Content-Length headers are used at the same time.</body>" \
495 "</html>"
496#else
497#define REQUEST_LENGTH_WITH_TR_ENCODING ""
498#endif
499
507#ifdef HAVE_MESSAGES
508#define REQUEST_MALFORMED \
509 "<html><head><title>Request malformed</title></head>" \
510 "<body>HTTP request is syntactically incorrect.</body></html>"
511#else
512#define REQUEST_MALFORMED ""
513#endif
514
518#define REQUEST_HAS_NUL_CHAR_IN_PATH \
519 "<html><head><title>Bad Request Path</title></head>" \
520 "<body>The request path contains invalid characters.</body></html>"
521
526#ifdef HAVE_MESSAGES
527#define REQUEST_CHUNKED_MALFORMED \
528 "<html><head><title>Request malformed</title></head>" \
529 "<body>HTTP chunked encoding is syntactically incorrect.</body></html>"
530#else
531#define REQUEST_CHUNKED_MALFORMED ""
532#endif
533
537#ifdef HAVE_MESSAGES
538#define REQUEST_CHUNK_TOO_LARGE \
539 "<html><head><title>Request content too large</title></head>" \
540 "<body>The chunk size used in HTTP chunked encoded " \
541 "request is too large.</body></html>"
542#else
543#define REQUEST_CHUNK_TOO_LARGE ""
544#endif
545
549#ifdef HAVE_MESSAGES
550#define REQUEST_CONTENTLENGTH_TOOLARGE \
551 "<html><head><title>Request content too large</title></head>" \
552 "<body>HTTP request has too large value for " \
553 "<b>Content-Length</b> header.</body></html>"
554#else
555#define REQUEST_CONTENTLENGTH_TOOLARGE ""
556#endif
557
562#ifdef HAVE_MESSAGES
563#define REQUEST_CONTENTLENGTH_MALFORMED \
564 "<html><head><title>Request malformed</title></head>" \
565 "<body>HTTP request has wrong value for " \
566 "<b>Content-Length</b> header.</body></html>"
567#else
568#define REQUEST_CONTENTLENGTH_MALFORMED ""
569#endif
570
577#ifdef HAVE_MESSAGES
578#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP \
579 "<html><head><title>Internal server error</title></head>" \
580 "<body>Please ask the developer of this Web server to carefully " \
581 "read the GNU libmicrohttpd documentation about connection " \
582 "management and blocking.</body></html>"
583#else
584#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP ""
585#endif
586
590#ifdef HAVE_MESSAGES
591#define REQ_HTTP_VER_IS_TOO_OLD \
592 "<html><head><title>Requested HTTP version is not supported</title></head>" \
593 "<body>Requested HTTP version is too old and not " \
594 "supported.</body></html>"
595#else
596#define REQ_HTTP_VER_IS_TOO_OLD ""
597#endif
598
602#ifdef HAVE_MESSAGES
603#define REQ_HTTP_VER_IS_NOT_SUPPORTED \
604 "<html><head><title>Requested HTTP version is not supported</title></head>" \
605 "<body>Requested HTTP version is not supported.</body></html>"
606#else
607#define REQ_HTTP_VER_IS_NOT_SUPPORTED ""
608#endif
609
610
614#define MHD_SENFILE_CHUNK_ (0x20000)
615
619#define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000)
620
621#ifdef HAVE_MESSAGES
627static const char *
628str_conn_error_ (ssize_t mhd_err_code)
629{
630 switch (mhd_err_code)
631 {
632 case MHD_ERR_AGAIN_:
633 return _ ("The operation would block, retry later");
635 return _ ("The connection was forcibly closed by remote peer");
636 case MHD_ERR_NOTCONN_:
637 return _ ("The socket is not connected");
638 case MHD_ERR_NOMEM_:
639 return _ ("Not enough system resources to serve the request");
640 case MHD_ERR_BADF_:
641 return _ ("Bad FD value");
642 case MHD_ERR_INVAL_:
643 return _ ("Argument value is invalid");
645 return _ ("Argument value is not supported");
646 case MHD_ERR_PIPE_:
647 return _ ("The socket is no longer available for sending");
648 case MHD_ERR_TLS_:
649 return _ ("TLS encryption or decryption error");
650 default:
651 break; /* Mute compiler warning */
652 }
653 if (0 <= mhd_err_code)
654 return _ ("Not an error code");
655
656 mhd_assert (0); /* Should never be reachable */
657 return _ ("Wrong error code value");
658}
659
660
661#endif /* HAVE_MESSAGES */
662
672void *
674 size_t size)
675{
676 struct MHD_Connection *const c = connection; /* a short alias */
677 struct MemoryPool *const pool = c->pool; /* a short alias */
678 size_t need_to_be_freed = 0;
679 void *res;
680
681 res = MHD_pool_try_alloc (pool,
682 size,
683 &need_to_be_freed);
684 if (NULL != res)
685 return res;
686
688 c->write_buffer,
690 {
692 need_to_be_freed)
693 {
694 char *buf;
695 const size_t new_buf_size = c->write_buffer_size - need_to_be_freed;
696 buf = MHD_pool_reallocate (pool,
697 c->write_buffer,
699 new_buf_size);
700 mhd_assert (c->write_buffer == buf);
701 mhd_assert (c->write_buffer_append_offset <= new_buf_size);
702 mhd_assert (c->write_buffer_send_offset <= new_buf_size);
703 c->write_buffer_size = new_buf_size;
704 c->write_buffer = buf;
705 }
706 else
707 return NULL;
708 }
709 else if (MHD_pool_is_resizable_inplace (pool,
710 c->read_buffer,
712 {
713 if (c->read_buffer_size - c->read_buffer_offset >= need_to_be_freed)
714 {
715 char *buf;
716 const size_t new_buf_size = c->read_buffer_size - need_to_be_freed;
717 buf = MHD_pool_reallocate (pool,
718 c->read_buffer,
720 new_buf_size);
721 mhd_assert (c->read_buffer == buf);
722 mhd_assert (c->read_buffer_offset <= new_buf_size);
723 c->read_buffer_size = new_buf_size;
724 c->read_buffer = buf;
725 }
726 else
727 return NULL;
728 }
729 else
730 return NULL;
731 res = MHD_pool_allocate (pool, size, true);
732 mhd_assert (NULL != res); /* It has been checked that pool has enough space */
733 return res;
734}
735
736
746static ssize_t
748 void *other,
749 size_t i)
750{
751 ssize_t ret;
752
753 if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
754 (MHD_CONNECTION_CLOSED == connection->state) )
755 {
756 return MHD_ERR_NOTCONN_;
757 }
759 i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */
760
761 ret = MHD_recv_ (connection->socket_fd,
762 other,
763 i);
764 if (0 > ret)
765 {
766 const int err = MHD_socket_get_error_ ();
767 if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
768 {
769#ifdef EPOLL_SUPPORT
770 /* Got EAGAIN --- no longer read-ready */
771 connection->epoll_state &=
773#endif /* EPOLL_SUPPORT */
774 return MHD_ERR_AGAIN_;
775 }
776 if (MHD_SCKT_ERR_IS_EINTR_ (err))
777 return MHD_ERR_AGAIN_;
779 return MHD_ERR_CONNRESET_;
781 return MHD_ERR_OPNOTSUPP_;
783 return MHD_ERR_NOTCONN_;
785 return MHD_ERR_INVAL_;
787 return MHD_ERR_NOMEM_;
789 return MHD_ERR_BADF_;
790 /* Treat any other error as a hard error. */
791 return MHD_ERR_NOTCONN_;
792 }
793#ifdef EPOLL_SUPPORT
794 else if (i > (size_t) ret)
795 connection->epoll_state &=
797#endif /* EPOLL_SUPPORT */
798 return ret;
799}
800
801
804 const char **uri,
805 size_t *uri_size)
806{
807 if (NULL != uri)
808 *uri = NULL;
809 if (NULL != uri_size)
810 *uri_size = 0u;
811
812 if (connection->state < MHD_CONNECTION_REQ_LINE_RECEIVED)
813 return MHD_NO;
814 if (connection->state >= MHD_CONNECTION_START_REPLY)
815 return MHD_NO;
816 if (NULL == connection->rq.url)
817 return MHD_NO;
818
819 if (NULL != uri)
820 *uri = connection->rq.url;
821 if (NULL != uri_size)
822 *uri_size = connection->rq.url_len;
823
824 return MHD_YES;
825}
826
827
840_MHD_EXTERN int
842 enum MHD_ValueKind kind,
843 MHD_KeyValueIterator iterator,
844 void *iterator_cls)
845{
846 int ret;
847 struct MHD_HTTP_Req_Header *pos;
848
849 if (NULL == connection)
850 return -1;
851 ret = 0;
852 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
853 if (0 != (pos->kind & kind))
854 {
855 ret++;
856 if ( (NULL != iterator) &&
857 (MHD_NO == iterator (iterator_cls,
858 pos->kind,
859 pos->header,
860 pos->value)) )
861 return ret;
862 }
863 return ret;
864}
865
866
879_MHD_EXTERN int
881 enum MHD_ValueKind kind,
882 MHD_KeyValueIteratorN iterator,
883 void *iterator_cls)
884{
885 int ret;
886 struct MHD_HTTP_Req_Header *pos;
887
888 if (NULL == connection)
889 return -1;
890 ret = 0;
891
892 if (NULL == iterator)
893 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
894 {
895 if (0 != (kind & pos->kind))
896 ret++;
897 }
898 else
899 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
900 if (0 != (kind & pos->kind))
901 {
902 ret++;
903 if (MHD_NO == iterator (iterator_cls,
904 pos->kind,
905 pos->header,
906 pos->header_size,
907 pos->value,
908 pos->value_size))
909 return ret;
910 }
911 return ret;
912}
913
914
932static enum MHD_Result
934 enum MHD_ValueKind kind,
935 const char *key,
936 size_t key_size,
937 const char *value,
938 size_t value_size)
939{
940 struct MHD_HTTP_Req_Header *pos;
941
942 pos = MHD_connection_alloc_memory_ (connection,
943 sizeof (struct MHD_HTTP_Res_Header));
944 if (NULL == pos)
945 return MHD_NO;
946 pos->header = key;
947 pos->header_size = key_size;
948 pos->value = value;
949 pos->value_size = value_size;
950 pos->kind = kind;
951 pos->next = NULL;
952 /* append 'pos' to the linked list of headers */
953 if (NULL == connection->rq.headers_received_tail)
954 {
955 connection->rq.headers_received = pos;
956 connection->rq.headers_received_tail = pos;
957 }
958 else
959 {
960 connection->rq.headers_received_tail->next = pos;
961 connection->rq.headers_received_tail = pos;
962 }
963 return MHD_YES;
964}
965
966
994 enum MHD_ValueKind kind,
995 const char *key,
996 size_t key_size,
997 const char *value,
998 size_t value_size)
999{
1000 if ( (MHD_GET_ARGUMENT_KIND != kind) &&
1001 ( ((key ? strlen (key) : 0) != key_size) ||
1002 ((value ? strlen (value) : 0) != value_size) ) )
1003 return MHD_NO; /* binary zero is allowed only in GET arguments */
1004
1005 return MHD_set_connection_value_n_nocheck_ (connection,
1006 kind,
1007 key,
1008 key_size,
1009 value,
1010 value_size);
1011}
1012
1013
1041 enum MHD_ValueKind kind,
1042 const char *key,
1043 const char *value)
1044{
1045 return MHD_set_connection_value_n_nocheck_ (connection,
1046 kind,
1047 key,
1048 NULL != key
1049 ? strlen (key)
1050 : 0,
1051 value,
1052 NULL != value
1053 ? strlen (value)
1054 : 0);
1055}
1056
1057
1068_MHD_EXTERN const char *
1070 enum MHD_ValueKind kind,
1071 const char *key)
1072{
1073 const char *value;
1074
1075 value = NULL;
1076 (void) MHD_lookup_connection_value_n (connection,
1077 kind,
1078 key,
1079 (NULL == key) ? 0 : strlen (key),
1080 &value,
1081 NULL);
1082 return value;
1083}
1084
1085
1107 enum MHD_ValueKind kind,
1108 const char *key,
1109 size_t key_size,
1110 const char **value_ptr,
1111 size_t *value_size_ptr)
1112{
1113 struct MHD_HTTP_Req_Header *pos;
1114
1115 if (NULL == connection)
1116 return MHD_NO;
1117
1118 if (NULL == key)
1119 {
1120 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1121 {
1122 if ( (0 != (kind & pos->kind)) &&
1123 (NULL == pos->header) )
1124 break;
1125 }
1126 }
1127 else
1128 {
1129 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1130 {
1131 if ( (0 != (kind & pos->kind)) &&
1132 (key_size == pos->header_size) &&
1133 ( (key == pos->header) ||
1135 pos->header,
1136 key_size) ) ) )
1137 break;
1138 }
1139 }
1140
1141 if (NULL == pos)
1142 return MHD_NO;
1143
1144 if (NULL != value_ptr)
1145 *value_ptr = pos->value;
1146
1147 if (NULL != value_size_ptr)
1148 *value_size_ptr = pos->value_size;
1149
1150 return MHD_YES;
1151}
1152
1153
1169static bool
1171 const char *header,
1172 size_t header_len,
1173 const char *token,
1174 size_t token_len)
1175{
1176 struct MHD_HTTP_Req_Header *pos;
1177
1178 if ((NULL == connection) || (NULL == header) || (0 == header[0]) ||
1179 (NULL == token) || (0 == token[0]))
1180 return false;
1181
1182 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1183 {
1184 if ((0 != (pos->kind & MHD_HEADER_KIND)) &&
1185 (header_len == pos->header_size) &&
1186 ( (header == pos->header) ||
1188 pos->header,
1189 header_len)) ) &&
1190 (MHD_str_has_token_caseless_ (pos->value, token, token_len)))
1191 return true;
1192 }
1193 return false;
1194}
1195
1196
1208#define MHD_lookup_header_s_token_ci(c,h,tkn) \
1209 MHD_lookup_header_token_ci ((c),(h),MHD_STATICSTR_LEN_ (h), \
1210 (tkn),MHD_STATICSTR_LEN_ (tkn))
1211
1212
1220static bool
1222{
1223 const char *expect;
1224
1225 if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
1226 return false;
1227
1228 if (0 == connection->rq.remaining_upload_size)
1229 return false;
1230
1231 if (MHD_NO ==
1237 &expect,
1238 NULL))
1239 return false;
1240
1241 if (MHD_str_equal_caseless_ (expect,
1242 "100-continue"))
1243 return true;
1244
1245 return false;
1246}
1247
1248
1255void
1257{
1258 const struct MHD_Daemon *daemon = connection->daemon;
1259
1260 if (0 == (daemon->options & MHD_USE_TURBO))
1261 {
1262#ifdef HTTPS_SUPPORT
1263 /* For TLS connection use shutdown of TLS layer
1264 * and do not shutdown TCP socket. This give more
1265 * chances to send TLS closure data to remote side.
1266 * Closure of TLS layer will be interpreted by
1267 * remote side as end of transmission. */
1268 if (0 != (daemon->options & MHD_USE_TLS))
1269 {
1270 if (! MHD_tls_connection_shutdown (connection))
1271 shutdown (connection->socket_fd,
1272 SHUT_WR);
1273 }
1274 else /* Combined with next 'shutdown()'. */
1275#endif /* HTTPS_SUPPORT */
1276 shutdown (connection->socket_fd,
1277 SHUT_WR);
1278 }
1279 connection->state = MHD_CONNECTION_CLOSED;
1281}
1282
1283
1293void
1295 enum MHD_RequestTerminationCode termination_code)
1296{
1297 struct MHD_Daemon *daemon = connection->daemon;
1298 struct MHD_Response *resp = connection->rp.response;
1299
1300 mhd_assert (! connection->suspended);
1301#ifdef MHD_USE_THREADS
1302 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
1303 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
1304#endif /* MHD_USE_THREADS */
1305 if ( (NULL != daemon->notify_completed) &&
1306 (connection->rq.client_aware) )
1307 daemon->notify_completed (daemon->notify_completed_cls,
1308 connection,
1309 &connection->rq.client_context,
1310 termination_code);
1311 connection->rq.client_aware = false;
1312 if (NULL != resp)
1313 {
1314 connection->rp.response = NULL;
1315 MHD_destroy_response (resp);
1316 }
1317 if (NULL != connection->pool)
1318 {
1319 MHD_pool_destroy (connection->pool);
1320 connection->pool = NULL;
1321 }
1322
1323 MHD_connection_mark_closed_ (connection);
1324}
1325
1326
1327#if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
1338void
1340{
1341 struct MHD_Daemon *daemon = connection->daemon;
1342 struct MHD_UpgradeResponseHandle *urh = connection->urh;
1343
1344#ifdef MHD_USE_THREADS
1345 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
1348#endif /* MHD_USE_THREADS */
1349
1350 if (0 == (daemon->options & MHD_USE_TLS))
1351 return; /* Nothing to do with non-TLS connection. */
1352
1353 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
1354 DLL_remove (daemon->urh_head,
1355 daemon->urh_tail,
1356 urh);
1357#ifdef EPOLL_SUPPORT
1358 if (MHD_D_IS_USING_EPOLL_ (daemon) &&
1359 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
1360 EPOLL_CTL_DEL,
1361 connection->socket_fd,
1362 NULL)) )
1363 {
1364 MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
1365 }
1366 if (urh->in_eready_list)
1367 {
1368 EDLL_remove (daemon->eready_urh_head,
1369 daemon->eready_urh_tail,
1370 urh);
1371 urh->in_eready_list = false;
1372 }
1373#endif /* EPOLL_SUPPORT */
1374 if (MHD_INVALID_SOCKET != urh->mhd.socket)
1375 {
1376#ifdef EPOLL_SUPPORT
1377 if (MHD_D_IS_USING_EPOLL_ (daemon) &&
1378 (0 != epoll_ctl (daemon->epoll_upgrade_fd,
1379 EPOLL_CTL_DEL,
1380 urh->mhd.socket,
1381 NULL)) )
1382 {
1383 MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
1384 }
1385#endif /* EPOLL_SUPPORT */
1386 /* Reflect remote disconnect to application by breaking
1387 * socketpair connection. */
1388 shutdown (urh->mhd.socket, SHUT_RDWR);
1389 }
1390 /* Socketpair sockets will remain open as they will be
1391 * used with MHD_UPGRADE_ACTION_CLOSE. They will be
1392 * closed by cleanup_upgraded_connection() during
1393 * connection's final cleanup.
1394 */
1395}
1396
1397
1398#endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT*/
1399
1400
1408static void
1410 const char *emsg)
1411{
1412 connection->stop_with_error = true;
1413 connection->discard_request = true;
1414#ifdef HAVE_MESSAGES
1415 if (NULL != emsg)
1416 MHD_DLOG (connection->daemon,
1417 "%s\n",
1418 emsg);
1419#else /* ! HAVE_MESSAGES */
1420 (void) emsg; /* Mute compiler warning. */
1421#endif /* ! HAVE_MESSAGES */
1422 MHD_connection_close_ (connection,
1424}
1425
1426
1431#ifdef HAVE_MESSAGES
1432#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, emsg)
1433#else
1434#define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, NULL)
1435#endif
1436
1437
1450static enum MHD_Result
1452{
1453 ssize_t ret;
1454 struct MHD_Response *response;
1455
1456 response = connection->rp.response;
1457 mhd_assert (connection->rp.props.send_reply_body);
1458
1459 if ( (0 == response->total_size) ||
1460 /* TODO: replace the next check with assert */
1461 (connection->rp.rsp_write_position == response->total_size) )
1462 return MHD_YES; /* 0-byte response is always ready */
1463 if (NULL != response->data_iov)
1464 {
1465 size_t copy_size;
1466
1467 if (NULL != connection->rp.resp_iov.iov)
1468 return MHD_YES;
1469 copy_size = response->data_iovcnt * sizeof(MHD_iovec_);
1470 connection->rp.resp_iov.iov = MHD_connection_alloc_memory_ (connection,
1471 copy_size);
1472 if (NULL == connection->rp.resp_iov.iov)
1473 {
1474 MHD_mutex_unlock_chk_ (&response->mutex);
1475 /* not enough memory */
1476 CONNECTION_CLOSE_ERROR (connection,
1477 _ ("Closing connection (out of memory)."));
1478 return MHD_NO;
1479 }
1480 memcpy (connection->rp.resp_iov.iov,
1481 response->data_iov,
1482 copy_size);
1483 connection->rp.resp_iov.cnt = response->data_iovcnt;
1484 connection->rp.resp_iov.sent = 0;
1485 return MHD_YES;
1486 }
1487 if (NULL == response->crc)
1488 return MHD_YES;
1489 if ( (response->data_start <=
1490 connection->rp.rsp_write_position) &&
1491 (response->data_size + response->data_start >
1492 connection->rp.rsp_write_position) )
1493 return MHD_YES; /* response already ready */
1494#if defined(_MHD_HAVE_SENDFILE)
1495 if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
1496 {
1497 /* will use sendfile, no need to bother response crc */
1498 return MHD_YES;
1499 }
1500#endif /* _MHD_HAVE_SENDFILE */
1501
1502 ret = response->crc (response->crc_cls,
1503 connection->rp.rsp_write_position,
1504 (char *) response->data,
1505 (size_t) MHD_MIN ((uint64_t) response->data_buffer_size,
1506 response->total_size
1507 - connection->rp.rsp_write_position));
1508 if (0 > ret)
1509 {
1510 /* either error or http 1.0 transfer, close socket! */
1511 /* TODO: do not update total size, check whether response
1512 * was really with unknown size */
1513 response->total_size = connection->rp.rsp_write_position;
1514#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1515 MHD_mutex_unlock_chk_ (&response->mutex);
1516#endif
1518 MHD_connection_close_ (connection,
1520 else
1521 CONNECTION_CLOSE_ERROR (connection,
1522 _ ("Closing connection (application reported " \
1523 "error generating data)."));
1524 return MHD_NO;
1525 }
1526 response->data_start = connection->rp.rsp_write_position;
1527 response->data_size = (size_t) ret;
1528 if (0 == ret)
1529 {
1531#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1532 MHD_mutex_unlock_chk_ (&response->mutex);
1533#endif
1534 return MHD_NO;
1535 }
1536 return MHD_YES;
1537}
1538
1539
1552static enum MHD_Result
1554 bool *p_finished)
1555{
1556 ssize_t ret;
1557 struct MHD_Response *response;
1558 static const size_t max_chunk = 0xFFFFFF;
1559 char chunk_hdr[6]; /* 6: max strlen of "FFFFFF" */
1560 /* "FFFFFF" + "\r\n" */
1561 static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2;
1562 /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */
1563 static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2;
1564 size_t chunk_hdr_len;
1565 uint64_t left_to_send;
1566 size_t size_to_fill;
1567
1568 response = connection->rp.response;
1569 mhd_assert (NULL != response->crc || NULL != response->data);
1570
1571 mhd_assert (0 == connection->write_buffer_append_offset);
1572
1573 /* The buffer must be reasonably large enough */
1574 if (128 > connection->write_buffer_size)
1575 {
1576 size_t size;
1577
1578 size = connection->write_buffer_size + MHD_pool_get_free (connection->pool);
1579 if (128 > size)
1580 {
1581#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1582 MHD_mutex_unlock_chk_ (&response->mutex);
1583#endif
1584 /* not enough memory */
1585 CONNECTION_CLOSE_ERROR (connection,
1586 _ ("Closing connection (out of memory)."));
1587 return MHD_NO;
1588 }
1589 /* Limit the buffer size to the largest usable size for chunks */
1590 if ( (max_chunk + max_chunk_overhead) < size)
1591 size = max_chunk + max_chunk_overhead;
1592 mhd_assert ((NULL == connection->write_buffer) || \
1593 MHD_pool_is_resizable_inplace (connection->pool, \
1594 connection->write_buffer, \
1595 connection->write_buffer_size));
1596 connection->write_buffer =
1597 MHD_pool_reallocate (connection->pool,
1598 connection->write_buffer,
1599 connection->write_buffer_size,
1600 size);
1601 mhd_assert (NULL != connection->write_buffer);
1602 connection->write_buffer_size = size;
1603 }
1604 mhd_assert (max_chunk_overhead < connection->write_buffer_size);
1605
1606 if (MHD_SIZE_UNKNOWN == response->total_size)
1607 left_to_send = MHD_SIZE_UNKNOWN;
1608 else
1609 left_to_send = response->total_size
1610 - connection->rp.rsp_write_position;
1611
1612 size_to_fill = connection->write_buffer_size - max_chunk_overhead;
1613 /* Limit size for the callback to the max usable size */
1614 if (max_chunk < size_to_fill)
1615 size_to_fill = max_chunk;
1616 if (left_to_send < size_to_fill)
1617 size_to_fill = (size_t) left_to_send;
1618
1619 if (0 == left_to_send)
1620 /* nothing to send, don't bother calling crc */
1622 else if ( (response->data_start <=
1623 connection->rp.rsp_write_position) &&
1624 (response->data_start + response->data_size >
1625 connection->rp.rsp_write_position) )
1626 {
1627 /* difference between rsp_write_position and data_start is less
1628 than data_size which is size_t type, no need to check for overflow */
1629 const size_t data_write_offset
1630 = (size_t) (connection->rp.rsp_write_position
1631 - response->data_start);
1632 /* buffer already ready, use what is there for the chunk */
1633 mhd_assert (SSIZE_MAX >= (response->data_size - data_write_offset));
1634 mhd_assert (response->data_size >= data_write_offset);
1635 ret = (ssize_t) (response->data_size - data_write_offset);
1636 if ( ((size_t) ret) > size_to_fill)
1637 ret = (ssize_t) size_to_fill;
1638 memcpy (&connection->write_buffer[max_chunk_hdr_len],
1639 &response->data[data_write_offset],
1640 (size_t) ret);
1641 }
1642 else
1643 {
1644 if (NULL == response->crc)
1645 { /* There is no way to reach this code */
1646#if defined(MHD_USE_THREADS)
1647 MHD_mutex_unlock_chk_ (&response->mutex);
1648#endif
1649 CONNECTION_CLOSE_ERROR (connection,
1650 _ ("No callback for the chunked data."));
1651 return MHD_NO;
1652 }
1653 ret = response->crc (response->crc_cls,
1654 connection->rp.rsp_write_position,
1655 &connection->write_buffer[max_chunk_hdr_len],
1656 size_to_fill);
1657 }
1659 {
1660 /* error, close socket! */
1661 /* TODO: remove update of the response size */
1662 response->total_size = connection->rp.rsp_write_position;
1663#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1664 MHD_mutex_unlock_chk_ (&response->mutex);
1665#endif
1666 CONNECTION_CLOSE_ERROR (connection,
1667 _ ("Closing connection (application error " \
1668 "generating response)."));
1669 return MHD_NO;
1670 }
1672 {
1673 *p_finished = true;
1674 /* TODO: remove update of the response size */
1675 response->total_size = connection->rp.rsp_write_position;
1676 return MHD_YES;
1677 }
1678 if (0 == ret)
1679 {
1681#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1682 MHD_mutex_unlock_chk_ (&response->mutex);
1683#endif
1684 return MHD_NO;
1685 }
1686 if (size_to_fill < (size_t) ret)
1687 {
1688#if defined(MHD_USE_THREADS)
1689 MHD_mutex_unlock_chk_ (&response->mutex);
1690#endif
1691 CONNECTION_CLOSE_ERROR (connection,
1692 _ ("Closing connection (application returned " \
1693 "more data than requested)."));
1694 return MHD_NO;
1695 }
1696 chunk_hdr_len = MHD_uint32_to_strx ((uint32_t) ret, chunk_hdr,
1697 sizeof(chunk_hdr));
1698 mhd_assert (chunk_hdr_len != 0);
1699 mhd_assert (chunk_hdr_len < sizeof(chunk_hdr));
1700 *p_finished = false;
1701 connection->write_buffer_send_offset =
1702 (max_chunk_hdr_len - (chunk_hdr_len + 2));
1703 memcpy (connection->write_buffer + connection->write_buffer_send_offset,
1704 chunk_hdr,
1705 chunk_hdr_len);
1706 connection->write_buffer[max_chunk_hdr_len - 2] = '\r';
1707 connection->write_buffer[max_chunk_hdr_len - 1] = '\n';
1708 connection->write_buffer[max_chunk_hdr_len + (size_t) ret] = '\r';
1709 connection->write_buffer[max_chunk_hdr_len + (size_t) ret + 1] = '\n';
1710 connection->rp.rsp_write_position += (size_t) ret;
1711 connection->write_buffer_append_offset = max_chunk_hdr_len + (size_t) ret + 2;
1712 return MHD_YES;
1713}
1714
1715
1738static enum MHD_ConnKeepAlive
1740{
1741 struct MHD_Connection *const c = connection;
1742 struct MHD_Response *const r = c->rp.response;
1743
1744 mhd_assert (NULL != r);
1746 return MHD_CONN_MUST_CLOSE;
1747
1748#ifdef UPGRADE_SUPPORT
1749 /* TODO: Move below the next check when MHD stops closing connections
1750 * when response is queued in first callback */
1751 if (NULL != r->upgrade_handler)
1752 {
1753 /* No "close" token is enforced by 'add_response_header_connection()' */
1755 /* Valid HTTP version is enforced by 'MHD_queue_response()' */
1758 return MHD_CONN_MUST_UPGRADE;
1759 }
1760#endif /* UPGRADE_SUPPORT */
1761
1762 mhd_assert ( (! c->stop_with_error) || (c->discard_request));
1763 if ((c->read_closed) || (c->discard_request))
1764 return MHD_CONN_MUST_CLOSE;
1765
1767 return MHD_CONN_MUST_CLOSE;
1769 return MHD_CONN_MUST_CLOSE;
1770
1772 return MHD_CONN_MUST_CLOSE;
1773
1776 "close"))
1777 return MHD_CONN_MUST_CLOSE;
1778
1779 if ((MHD_HTTP_VER_1_0 == connection->rq.http_ver) ||
1780 (0 != (connection->rp.response->flags & MHD_RF_HTTP_1_0_SERVER)))
1781 {
1782 if (MHD_lookup_header_s_token_ci (connection,
1784 "Keep-Alive"))
1786
1787 return MHD_CONN_MUST_CLOSE;
1788 }
1789
1792
1793 return MHD_CONN_MUST_CLOSE;
1794}
1795
1796
1806static bool
1807get_date_str (char *date)
1808{
1809 static const char *const days[] = {
1810 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1811 };
1812 static const char *const mons[] = {
1813 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1814 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1815 };
1816 static const size_t buf_len = 29;
1817 struct tm now;
1818 time_t t;
1819 const char *src;
1820#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
1821 ! defined(HAVE_GMTIME_R)
1822 struct tm *pNow;
1823#endif
1824
1825 if ((time_t) -1 == time (&t))
1826 return false;
1827#if defined(HAVE_C11_GMTIME_S)
1828 if (NULL == gmtime_s (&t,
1829 &now))
1830 return false;
1831#elif defined(HAVE_W32_GMTIME_S)
1832 if (0 != gmtime_s (&now,
1833 &t))
1834 return false;
1835#elif defined(HAVE_GMTIME_R)
1836 if (NULL == gmtime_r (&t,
1837 &now))
1838 return false;
1839#else
1840 pNow = gmtime (&t);
1841 if (NULL == pNow)
1842 return false;
1843 now = *pNow;
1844#endif
1845
1846 /* Day of the week */
1847 src = days[now.tm_wday % 7];
1848 date[0] = src[0];
1849 date[1] = src[1];
1850 date[2] = src[2];
1851 date[3] = ',';
1852 date[4] = ' ';
1853 /* Day of the month */
1854 if (2 != MHD_uint8_to_str_pad ((uint8_t) now.tm_mday, 2,
1855 date + 5, buf_len - 5))
1856 return false;
1857 date[7] = ' ';
1858 /* Month */
1859 src = mons[now.tm_mon % 12];
1860 date[8] = src[0];
1861 date[9] = src[1];
1862 date[10] = src[2];
1863 date[11] = ' ';
1864 /* Year */
1865 if (4 != MHD_uint16_to_str ((uint16_t) (1900 + now.tm_year), date + 12,
1866 buf_len - 12))
1867 return false;
1868 date[16] = ' ';
1869 /* Time */
1870 MHD_uint8_to_str_pad ((uint8_t) now.tm_hour, 2, date + 17, buf_len - 17);
1871 date[19] = ':';
1872 MHD_uint8_to_str_pad ((uint8_t) now.tm_min, 2, date + 20, buf_len - 20);
1873 date[22] = ':';
1874 MHD_uint8_to_str_pad ((uint8_t) now.tm_sec, 2, date + 23, buf_len - 23);
1875 date[25] = ' ';
1876 date[26] = 'G';
1877 date[27] = 'M';
1878 date[28] = 'T';
1879
1880 return true;
1881}
1882
1883
1891static bool
1892get_date_header (char *header)
1893{
1894 if (! get_date_str (header + 6))
1895 {
1896 header[0] = 0;
1897 return false;
1898 }
1899 header[0] = 'D';
1900 header[1] = 'a';
1901 header[2] = 't';
1902 header[3] = 'e';
1903 header[4] = ':';
1904 header[5] = ' ';
1905 header[35] = '\r';
1906 header[36] = '\n';
1907 header[37] = 0;
1908 return true;
1909}
1910
1911
1924static bool
1926 bool required)
1927{
1928 size_t new_size;
1929 size_t avail_size;
1930 const size_t def_grow_size = connection->daemon->pool_increment;
1931 void *rb;
1932
1933 avail_size = MHD_pool_get_free (connection->pool);
1934 if (0 == avail_size)
1935 return false; /* No more space available */
1936 if (0 == connection->read_buffer_size)
1937 new_size = avail_size / 2; /* Use half of available buffer for reading */
1938 else
1939 {
1940 size_t grow_size;
1941
1942 grow_size = avail_size / 8;
1943 if (def_grow_size > grow_size)
1944 { /* Shortage of space */
1945 const size_t left_free =
1946 connection->read_buffer_size - connection->read_buffer_offset;
1947 mhd_assert (connection->read_buffer_size >= \
1948 connection->read_buffer_offset);
1949 if ((def_grow_size <= grow_size + left_free)
1950 && (left_free < def_grow_size))
1951 grow_size = def_grow_size - left_free; /* Use precise 'def_grow_size' for new free space */
1952 else if (! required)
1953 return false; /* Grow is not mandatory, leave some space in pool */
1954 else
1955 {
1956 /* Shortage of space, but grow is mandatory */
1957 const size_t small_inc =
1958 ((MHD_BUF_INC_SIZE > def_grow_size) ?
1959 def_grow_size : MHD_BUF_INC_SIZE) / 8;
1960 if (small_inc < avail_size)
1961 grow_size = small_inc;
1962 else
1963 grow_size = avail_size;
1964 }
1965 }
1966 new_size = connection->read_buffer_size + grow_size;
1967 }
1968 /* Make sure that read buffer will not be moved */
1969 if ((NULL != connection->read_buffer) &&
1970 ! MHD_pool_is_resizable_inplace (connection->pool,
1971 connection->read_buffer,
1972 connection->read_buffer_size))
1973 {
1974 mhd_assert (0);
1975 return false;
1976 }
1977 /* we can actually grow the buffer, do it! */
1978 rb = MHD_pool_reallocate (connection->pool,
1979 connection->read_buffer,
1980 connection->read_buffer_size,
1981 new_size);
1982 if (NULL == rb)
1983 {
1984 /* This should NOT be possible: we just computed 'new_size' so that
1985 it should fit. If it happens, somehow our read buffer is not in
1986 the right position in the pool, say because someone called
1987 MHD_pool_allocate() without 'from_end' set to 'true'? Anyway,
1988 should be investigated! (Ideally provide all data from
1989 *pool and connection->read_buffer and new_size for debugging). */
1990 mhd_assert (0);
1991 return false;
1992 }
1993 mhd_assert (connection->read_buffer == rb);
1994 connection->read_buffer = rb;
1995 mhd_assert (NULL != connection->read_buffer);
1996 connection->read_buffer_size = new_size;
1997 return true;
1998}
1999
2000
2005static void
2007{
2008 struct MHD_Connection *const c = connection;
2009 void *new_buf;
2010
2011 if ((NULL == c->read_buffer) || (0 == c->read_buffer_size))
2012 {
2013 mhd_assert (0 == c->read_buffer_size);
2015 return;
2016 }
2017
2019 if (0 == c->read_buffer_offset)
2020 {
2022 c->read_buffer = NULL;
2023 c->read_buffer_size = 0;
2024 }
2025 else
2026 {
2028 c->read_buffer_size));
2031 mhd_assert (c->read_buffer == new_buf);
2032 c->read_buffer = new_buf;
2034 }
2035}
2036
2037
2044static size_t
2046{
2047 struct MHD_Connection *const c = connection;
2048 struct MemoryPool *const pool = connection->pool;
2049 void *new_buf;
2050 size_t new_size;
2051 size_t free_size;
2052
2053 mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
2056
2057 free_size = MHD_pool_get_free (pool);
2058 if (0 != free_size)
2059 {
2060 new_size = c->write_buffer_size + free_size;
2061 /* This function must not move the buffer position.
2062 * MHD_pool_reallocate () may return the new position only if buffer was
2063 * allocated 'from_end' or is not the last allocation,
2064 * which should not happen. */
2065 mhd_assert ((NULL == c->write_buffer) || \
2067 c->write_buffer_size));
2068 new_buf = MHD_pool_reallocate (pool,
2069 c->write_buffer,
2071 new_size);
2072 mhd_assert ((c->write_buffer == new_buf) || (NULL == c->write_buffer));
2073 c->write_buffer = new_buf;
2074 c->write_buffer_size = new_size;
2076 {
2077 /* All data have been sent, reset offsets to zero. */
2080 }
2081 }
2082
2084}
2085
2086
2087#if 0 /* disable unused function */
2096static void
2097connection_shrink_write_buffer (struct MHD_Connection *connection)
2098{
2099 struct MHD_Connection *const c = connection;
2100 struct MemoryPool *const pool = connection->pool;
2101 void *new_buf;
2102
2103 mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
2106
2107 if ( (NULL == c->write_buffer) || (0 == c->write_buffer_size))
2108 {
2111 c->write_buffer = NULL;
2112 return;
2113 }
2115 return;
2116
2117 new_buf = MHD_pool_reallocate (pool, c->write_buffer, c->write_buffer_size,
2119 mhd_assert ((c->write_buffer == new_buf) || \
2120 (0 == c->write_buffer_append_offset));
2122 if (0 == c->write_buffer_size)
2123 c->write_buffer = NULL;
2124 else
2125 c->write_buffer = new_buf;
2126}
2127
2128
2129#endif /* unused function */
2130
2131
2139static void
2141{
2142 /* Read buffer is not needed for this request, shrink it.*/
2143 connection_shrink_read_buffer (connection);
2144}
2145
2146
2174
2175
2187static enum replyBodyUse
2189 unsigned int rcode)
2190{
2191 struct MHD_Connection *const c = connection;
2192
2193 mhd_assert (100 <= rcode);
2194 mhd_assert (999 >= rcode);
2195
2196 if (199 >= rcode)
2197 return RP_BODY_NONE;
2198
2199 if (MHD_HTTP_NO_CONTENT == rcode)
2200 return RP_BODY_NONE;
2201
2202#if 0
2203 /* This check is not needed as upgrade handler is used only with code 101 */
2204#ifdef UPGRADE_SUPPORT
2205 if (NULL != rp.response->upgrade_handler)
2206 return RP_BODY_NONE;
2207#endif /* UPGRADE_SUPPORT */
2208#endif
2209
2210#if 0
2211 /* CONNECT is not supported by MHD */
2212 /* Successful responses for connect requests are filtered by
2213 * MHD_queue_response() */
2214 if ( (MHD_HTTP_MTHD_CONNECT == c->rq.http_mthd) &&
2215 (2 == rcode / 100) )
2216 return false; /* Actually pass-through CONNECT is not supported by MHD */
2217#endif
2218
2219 /* Reply body headers could be used.
2220 * Check whether reply body itself must be used. */
2221
2223 return RP_BODY_HEADERS_ONLY;
2224
2225 if (MHD_HTTP_NOT_MODIFIED == rcode)
2226 return RP_BODY_HEADERS_ONLY;
2227
2228 /* Reply body must be sent. The body may have zero length, but body size
2229 * must be indicated by headers ('Content-Length:' or
2230 * 'Transfer-Encoding: chunked'). */
2231 return RP_BODY_SEND;
2232}
2233
2234
2243static void
2245{
2246 struct MHD_Connection *const c = connection;
2247 struct MHD_Response *const r = c->rp.response;
2248 enum replyBodyUse use_rp_body;
2249 bool use_chunked;
2250
2251 mhd_assert (NULL != r);
2252
2253 /* ** Adjust reply properties ** */
2254
2256 use_rp_body = is_reply_body_needed (c, c->rp.responseCode);
2257 c->rp.props.send_reply_body = (use_rp_body > RP_BODY_HEADERS_ONLY);
2259
2260#ifdef UPGRADE_SUPPORT
2261 mhd_assert ( (NULL == r->upgrade_handler) ||
2262 (RP_BODY_NONE == use_rp_body) );
2263#endif /* UPGRADE_SUPPORT */
2264
2266 {
2267 if ((MHD_SIZE_UNKNOWN == r->total_size) ||
2269 { /* Use chunked reply encoding if possible */
2270
2271 /* Check whether chunked encoding is supported by the client */
2273 use_chunked = false;
2274 /* Check whether chunked encoding is allowed for the reply */
2275 else if (0 != (r->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
2277 use_chunked = false;
2278 else
2279 /* If chunked encoding is supported and allowed, and response size
2280 * is unknown, use chunked even for non-Keep-Alive connections.
2281 * See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3
2282 * Also use chunked if it is enforced by application and supported by
2283 * the client. */
2284 use_chunked = true;
2285 }
2286 else
2287 use_chunked = false;
2288
2289 if ( (MHD_SIZE_UNKNOWN == r->total_size) &&
2290 (! use_chunked) )
2291 {
2292 /* End of the stream is indicated by closure */
2294 }
2295 }
2296 else
2297 use_chunked = false; /* chunked encoding cannot be used without body */
2298
2299 c->rp.props.chunked = use_chunked;
2300#ifdef _DEBUG
2301 c->rp.props.set = true;
2302#endif /* _DEBUG */
2303}
2304
2305
2310static void
2312{
2313 struct MHD_Connection *const c = connection;
2314 struct MHD_Response *const r = c->rp.response;
2315
2316 mhd_assert (c->rp.props.set);
2317#ifdef HAVE_MESSAGES
2318 if ( (! c->rp.props.use_reply_body_headers) &&
2319 (0 != r->total_size) )
2320 {
2321 MHD_DLOG (c->daemon,
2322 _ ("This reply with response code %u cannot use reply body. "
2323 "Non-empty response body is ignored and not used.\n"),
2324 (unsigned) (c->rp.responseCode));
2325 }
2326 if ( (! c->rp.props.use_reply_body_headers) &&
2328 {
2329 MHD_DLOG (c->daemon,
2330 _ ("This reply with response code %u cannot use reply body. "
2331 "Application defined \"Content-Length\" header violates"
2332 "HTTP specification.\n"),
2333 (unsigned) (c->rp.responseCode));
2334 }
2335#else
2336 (void) c; /* Mute compiler warning */
2337 (void) r; /* Mute compiler warning */
2338#endif
2339}
2340
2341
2353static bool
2354buffer_append (char *buf,
2355 size_t *ppos,
2356 size_t buf_size,
2357 const char *append,
2358 size_t append_size)
2359{
2360 mhd_assert (NULL != buf); /* Mute static analyzer */
2361 if (buf_size < *ppos + append_size)
2362 return false;
2363 memcpy (buf + *ppos, append, append_size);
2364 *ppos += append_size;
2365 return true;
2366}
2367
2368
2379#define buffer_append_s(buf,ppos,buf_size,str) \
2380 buffer_append (buf,ppos,buf_size,str, MHD_STATICSTR_LEN_ (str))
2381
2382
2402static bool
2404 size_t *ppos,
2405 size_t buf_size,
2406 struct MHD_Response *response,
2407 bool filter_transf_enc,
2408 bool filter_content_len,
2409 bool add_close,
2410 bool add_keep_alive)
2411{
2412 struct MHD_Response *const r = response;
2413 struct MHD_HTTP_Res_Header *hdr;
2414 size_t el_size;
2415
2416 mhd_assert (! add_close || ! add_keep_alive);
2417
2419 filter_transf_enc = false; /* No such header */
2420 if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2421 filter_content_len = false; /* No such header */
2422 if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2423 {
2424 add_close = false; /* No such header */
2425 add_keep_alive = false; /* No such header */
2426 }
2427 else if (0 != (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE))
2428 add_close = false; /* "close" token was already set */
2429
2430 for (hdr = r->first_header; NULL != hdr; hdr = hdr->next)
2431 {
2432 size_t initial_pos = *ppos;
2433 if (MHD_HEADER_KIND != hdr->kind)
2434 continue;
2435 if (filter_transf_enc)
2436 { /* Need to filter-out "Transfer-Encoding" */
2438 hdr->header_size) &&
2440 hdr->header, hdr->header_size)) )
2441 {
2442 filter_transf_enc = false; /* There is the only one such header */
2443 continue; /* Skip "Transfer-Encoding" header */
2444 }
2445 }
2446 if (filter_content_len)
2447 { /* Need to filter-out "Content-Length" */
2449 hdr->header_size) &&
2451 hdr->header, hdr->header_size)) )
2452 {
2453 /* Reset filter flag if only one header is allowed */
2454 filter_transf_enc =
2456 continue; /* Skip "Content-Length" header */
2457 }
2458 }
2459
2460 /* Add user header */
2461 el_size = hdr->header_size + 2 + hdr->value_size + 2;
2462 if (buf_size < *ppos + el_size)
2463 return false;
2464 memcpy (buf + *ppos, hdr->header, hdr->header_size);
2465 (*ppos) += hdr->header_size;
2466 buf[(*ppos)++] = ':';
2467 buf[(*ppos)++] = ' ';
2468 if (add_close || add_keep_alive)
2469 {
2470 /* "Connection:" header must be always the first one */
2473 hdr->header_size));
2474
2475 if (add_close)
2476 {
2477 el_size += MHD_STATICSTR_LEN_ ("close, ");
2478 if (buf_size < initial_pos + el_size)
2479 return false;
2480 memcpy (buf + *ppos, "close, ",
2481 MHD_STATICSTR_LEN_ ("close, "));
2482 *ppos += MHD_STATICSTR_LEN_ ("close, ");
2483 }
2484 else
2485 {
2486 el_size += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2487 if (buf_size < initial_pos + el_size)
2488 return false;
2489 memcpy (buf + *ppos, "Keep-Alive, ",
2490 MHD_STATICSTR_LEN_ ("Keep-Alive, "));
2491 *ppos += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2492 }
2493 add_close = false;
2494 add_keep_alive = false;
2495 }
2496 if (0 != hdr->value_size)
2497 memcpy (buf + *ppos, hdr->value, hdr->value_size);
2498 *ppos += hdr->value_size;
2499 buf[(*ppos)++] = '\r';
2500 buf[(*ppos)++] = '\n';
2501 mhd_assert (initial_pos + el_size == (*ppos));
2502 }
2503 return true;
2504}
2505
2506
2515static enum MHD_Result
2517{
2518 struct MHD_Connection *const c = connection;
2519 struct MHD_Response *const r = c->rp.response;
2520 char *buf;
2521 size_t pos;
2522 size_t buf_size;
2523 size_t el_size;
2524 unsigned rcode;
2525 bool use_conn_close;
2526 bool use_conn_k_alive;
2527
2528 mhd_assert (NULL != r);
2529
2530 /* ** Adjust response properties ** */
2532
2533 mhd_assert (c->rp.props.set);
2537#ifdef UPGRADE_SUPPORT
2538 mhd_assert ((NULL == r->upgrade_handler) || \
2540#else /* ! UPGRADE_SUPPORT */
2542#endif /* ! UPGRADE_SUPPORT */
2544 mhd_assert ((! c->rp.props.send_reply_body) || \
2546#ifdef UPGRADE_SUPPORT
2547 mhd_assert (NULL == r->upgrade_handler || \
2549#endif /* UPGRADE_SUPPORT */
2550
2552
2553 rcode = (unsigned) c->rp.responseCode;
2555 {
2556 /* The closure of connection must be always indicated by header
2557 * to avoid hung connections */
2558 use_conn_close = true;
2559 use_conn_k_alive = false;
2560 }
2561 else if (MHD_CONN_USE_KEEPALIVE == c->keepalive)
2562 {
2563 use_conn_close = false;
2564 /* Add "Connection: keep-alive" if request is HTTP/1.0 or
2565 * if reply is HTTP/1.0
2566 * For HTTP/1.1 add header only if explicitly requested by app
2567 * (by response flag), as "Keep-Alive" is default for HTTP/1.1. */
2568 if ((0 != (r->flags & MHD_RF_SEND_KEEP_ALIVE_HEADER)) ||
2569 (MHD_HTTP_VER_1_0 == c->rq.http_ver) ||
2570 (0 != (r->flags & MHD_RF_HTTP_1_0_SERVER)))
2571 use_conn_k_alive = true;
2572 else
2573 use_conn_k_alive = false;
2574 }
2575 else
2576 {
2577 use_conn_close = false;
2578 use_conn_k_alive = false;
2579 }
2580
2581 /* ** Actually build the response header ** */
2582
2583 /* Get all space available */
2585 buf = c->write_buffer;
2587 buf_size = c->write_buffer_size;
2588 if (0 == buf_size)
2589 return MHD_NO;
2590 mhd_assert (NULL != buf);
2591
2592 /* * The status line * */
2593
2594 /* The HTTP version */
2595 if (! c->rp.responseIcy)
2596 { /* HTTP reply */
2597 if (0 == (r->flags & MHD_RF_HTTP_1_0_SERVER))
2598 { /* HTTP/1.1 reply */
2599 /* Use HTTP/1.1 responses for HTTP/1.0 clients.
2600 * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
2601 if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_1))
2602 return MHD_NO;
2603 }
2604 else
2605 { /* HTTP/1.0 reply */
2606 if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_0))
2607 return MHD_NO;
2608 }
2609 }
2610 else
2611 { /* ICY reply */
2612 if (! buffer_append_s (buf, &pos, buf_size, "ICY"))
2613 return MHD_NO;
2614 }
2615
2616 /* The response code */
2617 if (buf_size < pos + 5) /* space + code + space */
2618 return MHD_NO;
2619 buf[pos++] = ' ';
2620 pos += MHD_uint16_to_str ((uint16_t) rcode, buf + pos,
2621 buf_size - pos);
2622 buf[pos++] = ' ';
2623
2624 /* The reason phrase */
2625 el_size = MHD_get_reason_phrase_len_for (rcode);
2626 if (0 == el_size)
2627 {
2628 if (! buffer_append_s (buf, &pos, buf_size, "Non-Standard Status"))
2629 return MHD_NO;
2630 }
2631 else if (! buffer_append (buf, &pos, buf_size,
2633 el_size))
2634 return MHD_NO;
2635
2636 /* The linefeed */
2637 if (buf_size < pos + 2)
2638 return MHD_NO;
2639 buf[pos++] = '\r';
2640 buf[pos++] = '\n';
2641
2642 /* * The headers * */
2643
2644 /* Main automatic headers */
2645
2646 /* The "Date:" header */
2647 if ( (0 == (r->flags_auto & MHD_RAF_HAS_DATE_HDR)) &&
2649 {
2650 /* Additional byte for unused zero-termination */
2651 if (buf_size < pos + 38)
2652 return MHD_NO;
2653 if (get_date_header (buf + pos))
2654 pos += 37;
2655 }
2656 /* The "Connection:" header */
2657 mhd_assert (! use_conn_close || ! use_conn_k_alive);
2658 mhd_assert (! use_conn_k_alive || ! use_conn_close);
2659 if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2660 {
2661 if (use_conn_close)
2662 {
2663 if (! buffer_append_s (buf, &pos, buf_size,
2664 MHD_HTTP_HEADER_CONNECTION ": close\r\n"))
2665 return MHD_NO;
2666 }
2667 else if (use_conn_k_alive)
2668 {
2669 if (! buffer_append_s (buf, &pos, buf_size,
2670 MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n"))
2671 return MHD_NO;
2672 }
2673 }
2674
2675 /* User-defined headers */
2676
2677 if (! add_user_headers (buf, &pos, buf_size, r,
2678 ! c->rp.props.chunked,
2680 (0 ==
2682 use_conn_close,
2683 use_conn_k_alive))
2684 return MHD_NO;
2685
2686 /* Other automatic headers */
2687
2688 if ( (c->rp.props.use_reply_body_headers) &&
2689 (0 == (r->flags & MHD_RF_HEAD_ONLY_RESPONSE)) )
2690 {
2691 /* Body-specific headers */
2692
2693 if (c->rp.props.chunked)
2694 { /* Chunked encoding is used */
2696 { /* No chunked encoding header set by user */
2697 if (! buffer_append_s (buf, &pos, buf_size,
2699 "chunked\r\n"))
2700 return MHD_NO;
2701 }
2702 }
2703 else /* Chunked encoding is not used */
2704 {
2705 if (MHD_SIZE_UNKNOWN != r->total_size)
2706 { /* The size is known */
2707 if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2708 { /* The response does not have "Content-Length" header */
2709 if (! buffer_append_s (buf, &pos, buf_size,
2711 return MHD_NO;
2712 el_size = MHD_uint64_to_str (r->total_size, buf + pos,
2713 buf_size - pos);
2714 if (0 == el_size)
2715 return MHD_NO;
2716 pos += el_size;
2717
2718 if (buf_size < pos + 2)
2719 return MHD_NO;
2720 buf[pos++] = '\r';
2721 buf[pos++] = '\n';
2722 }
2723 }
2724 }
2725 }
2726
2727 /* * Header termination * */
2728 if (buf_size < pos + 2)
2729 return MHD_NO;
2730 buf[pos++] = '\r';
2731 buf[pos++] = '\n';
2732
2734 return MHD_YES;
2735}
2736
2737
2747static enum MHD_Result
2749{
2750 char *buf;
2751 size_t buf_size;
2752 size_t used_size;
2753 struct MHD_Connection *const c = connection;
2754 struct MHD_HTTP_Res_Header *pos;
2755
2756 mhd_assert (connection->rp.props.chunked);
2757 /* TODO: allow combining of the final footer with the last chunk,
2758 * modify the next assert. */
2760 mhd_assert (NULL != c->rp.response);
2761
2762 buf_size = connection_maximize_write_buffer (c);
2763 /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */
2764 if (buf_size < 5)
2765 return MHD_NO;
2768 mhd_assert (NULL != buf);
2769 used_size = 0;
2770 buf[used_size++] = '0';
2771 buf[used_size++] = '\r';
2772 buf[used_size++] = '\n';
2773
2774 for (pos = c->rp.response->first_header; NULL != pos; pos = pos->next)
2775 {
2776 if (MHD_FOOTER_KIND == pos->kind)
2777 {
2778 size_t new_used_size; /* resulting size with this header */
2779 /* '4' is colon, space, linefeeds */
2780 new_used_size = used_size + pos->header_size + pos->value_size + 4;
2781 if (new_used_size > buf_size)
2782 return MHD_NO;
2783 memcpy (buf + used_size, pos->header, pos->header_size);
2784 used_size += pos->header_size;
2785 buf[used_size++] = ':';
2786 buf[used_size++] = ' ';
2787 memcpy (buf + used_size, pos->value, pos->value_size);
2788 used_size += pos->value_size;
2789 buf[used_size++] = '\r';
2790 buf[used_size++] = '\n';
2791 mhd_assert (used_size == new_used_size);
2792 }
2793 }
2794 if (used_size + 2 > buf_size)
2795 return MHD_NO;
2796 buf[used_size++] = '\r';
2797 buf[used_size++] = '\n';
2798
2799 c->write_buffer_append_offset += used_size;
2801
2802 return MHD_YES;
2803}
2804
2805
2822static void
2824 unsigned int status_code,
2825 const char *message,
2826 size_t message_len,
2827 char *header_name,
2828 size_t header_name_len,
2829 char *header_value,
2830 size_t header_value_len)
2831{
2832 struct MHD_Response *response;
2833 enum MHD_Result iret;
2834
2835 mhd_assert (! connection->stop_with_error); /* Do not send error twice */
2836 if (connection->stop_with_error)
2837 { /* Should not happen */
2838 if (MHD_CONNECTION_CLOSED > connection->state)
2839 connection->state = MHD_CONNECTION_CLOSED;
2840 free (header_name);
2841 free (header_value);
2842 return;
2843 }
2844 connection->stop_with_error = true;
2845 connection->discard_request = true;
2846#ifdef HAVE_MESSAGES
2847 MHD_DLOG (connection->daemon,
2848 _ ("Error processing request (HTTP response code is %u ('%s')). " \
2849 "Closing connection.\n"),
2850 status_code,
2851 message);
2852#endif
2854 {
2855#ifdef HAVE_MESSAGES
2856 MHD_DLOG (connection->daemon,
2857 _ ("Too late to send an error response, " \
2858 "response is being sent already.\n"),
2859 status_code,
2860 message);
2861#endif
2862 CONNECTION_CLOSE_ERROR (connection,
2863 _ ("Too late for error response."));
2864 free (header_name);
2865 free (header_value);
2866 return;
2867 }
2868 /* TODO: remove when special error queue function is implemented */
2870 if (0 != connection->read_buffer_size)
2871 {
2872 /* Read buffer is not needed anymore, discard it
2873 * to free some space for error response. */
2874 MHD_pool_deallocate (connection->pool,
2875 connection->read_buffer,
2876 connection->read_buffer_size);
2877 connection->read_buffer = NULL;
2878 connection->read_buffer_size = 0;
2879 connection->read_buffer_offset = 0;
2880 }
2881 if (NULL != connection->rp.response)
2882 {
2883 MHD_destroy_response (connection->rp.response);
2884 connection->rp.response = NULL;
2885 }
2886 response = MHD_create_response_from_buffer_static (message_len,
2887 message);
2888 if (NULL == response)
2889 {
2890#ifdef HAVE_MESSAGES
2891 MHD_DLOG (connection->daemon,
2892 _ ("Failed to create error response.\n"),
2893 status_code,
2894 message);
2895#endif
2896 /* can't even send a reply, at least close the connection */
2897 connection->state = MHD_CONNECTION_CLOSED;
2898 free (header_name);
2899 free (header_value);
2900 return;
2901 }
2902 mhd_assert ((0 == header_name_len) || (NULL != header_name));
2903 mhd_assert ((NULL == header_name) || (0 != header_name_len));
2904 mhd_assert ((0 == header_value_len) || (NULL != header_value));
2905 mhd_assert ((NULL == header_value) || (0 != header_value_len));
2906 mhd_assert ((NULL == header_name) || (NULL != header_value));
2907 mhd_assert ((NULL != header_value) || (NULL == header_name));
2908 if (NULL != header_name)
2909 {
2910 iret = MHD_add_response_entry_no_alloc_ (response,
2912 header_name, header_name_len,
2913 header_value, header_value_len);
2914 if (MHD_NO == iret)
2915 {
2916 free (header_name);
2917 free (header_value);
2918 }
2919 }
2920 else
2921 iret = MHD_YES;
2922
2923 if (MHD_NO != iret)
2924 {
2925 bool before = connection->in_access_handler;
2926
2927 /* Fake the flag for the internal call */
2928 connection->in_access_handler = true;
2929 iret = MHD_queue_response (connection,
2930 status_code,
2931 response);
2932 connection->in_access_handler = before;
2933 }
2934 MHD_destroy_response (response);
2935 if (MHD_NO == iret)
2936 {
2937 /* can't even send a reply, at least close the connection */
2938 CONNECTION_CLOSE_ERROR (connection,
2939 _ ("Closing connection " \
2940 "(failed to queue error response)."));
2941 return;
2942 }
2943 mhd_assert (NULL != connection->rp.response);
2944 /* Do not reuse this connection. */
2945 connection->keepalive = MHD_CONN_MUST_CLOSE;
2946 if (MHD_NO == build_header_response (connection))
2947 {
2948 /* No memory. Release everything. */
2949 connection->rq.version = NULL;
2950 connection->rq.method = NULL;
2951 connection->rq.url = NULL;
2952 connection->rq.url_len = 0;
2953 connection->rq.url_for_callback = NULL;
2954 connection->rq.headers_received = NULL;
2955 connection->rq.headers_received_tail = NULL;
2956 connection->write_buffer = NULL;
2957 connection->write_buffer_size = 0;
2958 connection->write_buffer_send_offset = 0;
2959 connection->write_buffer_append_offset = 0;
2960 connection->read_buffer
2961 = MHD_pool_reset (connection->pool,
2962 NULL,
2963 0,
2964 0);
2965 connection->read_buffer_size = 0;
2966
2967 /* Retry with empty buffer */
2968 if (MHD_NO == build_header_response (connection))
2969 {
2970 CONNECTION_CLOSE_ERROR (connection,
2971 _ ("Closing connection " \
2972 "(failed to create error response header)."));
2973 return;
2974 }
2975 }
2977}
2978
2979
2983#ifdef HAVE_MESSAGES
2984# define transmit_error_response_static(c, code, msg) \
2985 transmit_error_response_len (c, code, \
2986 msg, MHD_STATICSTR_LEN_ (msg), \
2987 NULL, 0, NULL, 0)
2988#else /* ! HAVE_MESSAGES */
2989# define transmit_error_response_static(c, code, msg) \
2990 transmit_error_response_len (c, code, \
2991 "", 0, \
2992 NULL, 0, NULL, 0)
2993#endif /* ! HAVE_MESSAGES */
2994
2998#ifdef HAVE_MESSAGES
2999# define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l) \
3000 transmit_error_response_len (c, code, \
3001 m, MHD_STATICSTR_LEN_ (m), \
3002 hd_n, hd_n_l, \
3003 hd_v, hd_v_l)
3004#else /* ! HAVE_MESSAGES */
3005# define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l) \
3006 transmit_error_response_len (c, code, \
3007 "", 0, \
3008 hd_n, hd_n_l, \
3009 hd_v, hd_v_l)
3010#endif /* ! HAVE_MESSAGES */
3011
3012
3023static bool
3025{
3027 if (! c->rq.have_chunked_upload)
3028 return 0 != c->read_buffer_offset;
3029
3030 /* Chunked upload */
3031 mhd_assert (0 != c->rq.remaining_upload_size); /* Must not be possible in MHD_CONNECTION_BODY_RECEIVING state */
3033 {
3034 /* 0 == c->rq.current_chunk_size: Waiting the chunk size (chunk header).
3035 0 != c->rq.current_chunk_size: Waiting for chunk-closing CRLF. */
3036 return false;
3037 }
3038 return 0 != c->read_buffer_offset; /* Chunk payload data in the read buffer */
3039}
3040
3041
3058
3059
3060#ifndef MHD_MAX_REASONABLE_HEADERS_SIZE_
3070# define MHD_MAX_REASONABLE_HEADERS_SIZE_ (6 * 1024)
3071#endif /* ! MHD_MAX_REASONABLE_HEADERS_SIZE_ */
3072
3073#ifndef MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
3084# define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 8000
3085#endif /* ! MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ */
3086
3087#ifndef MHD_MIN_REASONABLE_HEADERS_SIZE_
3095# define MHD_MIN_REASONABLE_HEADERS_SIZE_ 26
3096#endif /* ! MHD_MIN_REASONABLE_HEADERS_SIZE_ */
3097
3098#ifndef MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
3106# define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 40
3107#endif /* ! MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ */
3108
3109#ifndef MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
3117# define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 16
3118#endif /* ! MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ */
3119
3120#ifndef MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
3126# define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 4
3127#endif /* ! MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ */
3128
3129
3141static unsigned int
3143 enum MHD_ProcRecvDataStage stage,
3144 const char *add_element,
3145 size_t add_element_size)
3146{
3147 size_t method_size;
3148 size_t uri_size;
3149 size_t opt_headers_size;
3150 size_t host_field_line_size;
3151
3154 mhd_assert ((0 == add_element_size) || (NULL != add_element));
3155
3157 {
3159 opt_headers_size =
3160 (size_t) ((c->read_buffer + c->read_buffer_offset)
3161 - c->rq.field_lines.start);
3162 }
3163 else
3164 opt_headers_size = c->rq.field_lines.size;
3165
3166 /* The read buffer is fully used by the request line, the field lines
3167 (headers) and internal information.
3168 The return status code works as a suggestion for the client to reduce
3169 one of the request elements. */
3170
3171 if ((MHD_PROC_RECV_BODY_CHUNKED == stage) &&
3172 (MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ < add_element_size))
3173 {
3174 /* Request could be re-tried easily with smaller chunk sizes */
3176 }
3177
3178 host_field_line_size = 0;
3179 /* The "Host:" field line is mandatory.
3180 The total size of the field lines (headers) cannot be smaller than
3181 the size of the "Host:" field line. */
3182 if ((MHD_PROC_RECV_HEADERS == stage)
3183 && (0 != add_element_size))
3184 {
3185 static const size_t header_host_key_len =
3187 const bool is_host_header =
3188 (header_host_key_len + 1 <= add_element_size)
3189 && ( (0 == add_element[header_host_key_len])
3190 || (':' == add_element[header_host_key_len]) )
3192 add_element,
3193 header_host_key_len);
3194 if (is_host_header)
3195 {
3196 const bool is_parsed = ! (
3198 (add_element_size == c->read_buffer_offset) &&
3199 (c->read_buffer == add_element) );
3200 size_t actual_element_size;
3201
3202 mhd_assert (! is_parsed || (0 == add_element[header_host_key_len]));
3203 /* The actual size should be larger due to CRLF or LF chars,
3204 however the exact termination sequence is not known here and
3205 as perfect precision is not required, to simplify the code
3206 assume the minimal length. */
3207 if (is_parsed)
3208 actual_element_size = add_element_size + 1; /* "1" for LF */
3209 else
3210 actual_element_size = add_element_size;
3211
3212 host_field_line_size = actual_element_size;
3213 mhd_assert (opt_headers_size >= actual_element_size);
3214 opt_headers_size -= actual_element_size;
3215 }
3216 }
3217 if (0 == host_field_line_size)
3218 {
3219 static const size_t host_field_name_len =
3221 size_t host_field_name_value_len;
3225 host_field_name_len,
3226 NULL,
3227 &host_field_name_value_len))
3228 {
3229 /* Calculate the minimal size of the field line: no space between
3230 colon and the field value, line terminated by LR */
3231 host_field_line_size =
3232 host_field_name_len + host_field_name_value_len + 2; /* "2" for ':' and LF */
3233
3234 /* The "Host:" field could be added by application */
3235 if (opt_headers_size >= host_field_line_size)
3236 {
3237 opt_headers_size -= host_field_line_size;
3238 /* Take into account typical space after colon and CR at the end of the line */
3239 if (opt_headers_size >= 2)
3240 opt_headers_size -= 2;
3241 }
3242 else
3243 host_field_line_size = 0; /* No "Host:" field line set by the client */
3244 }
3245 }
3246
3247 uri_size = c->rq.req_target_len;
3249 method_size = 0; /* Do not recommend shorter request method */
3250 else
3251 {
3252 mhd_assert (NULL != c->rq.method);
3253 method_size = strlen (c->rq.method);
3254 }
3255
3256 if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3257 {
3258 /* Typically the easiest way to reduce request header size is
3259 a removal of some optional headers. */
3260 if (opt_headers_size > (uri_size / 8))
3261 {
3262 if ((opt_headers_size / 2) > method_size)
3264 else
3265 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3266 }
3267 else
3268 { /* Request target is MUCH larger than headers */
3269 if ((uri_size / 16) > method_size)
3270 return MHD_HTTP_URI_TOO_LONG;
3271 else
3272 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3273 }
3274 }
3275 if ((size_t) MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3276 {
3277 /* If request target size if larger than maximum reasonable size
3278 recommend client to reduce the request target size (length). */
3279 if ((uri_size / 16) > method_size)
3280 return MHD_HTTP_URI_TOO_LONG; /* Request target is MUCH larger than headers */
3281 else
3282 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3283 }
3284
3285 /* The read buffer is too small to handle reasonably large requests */
3286
3287 if ((size_t) MHD_MIN_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3288 {
3289 /* Recommend application to retry with minimal headers */
3290 if ((opt_headers_size * 4) > uri_size)
3291 {
3292 if (opt_headers_size > method_size)
3294 else
3295 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3296 }
3297 else
3298 { /* Request target is significantly larger than headers */
3299 if (uri_size > method_size * 4)
3300 return MHD_HTTP_URI_TOO_LONG;
3301 else
3302 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3303 }
3304 }
3305 if ((size_t) MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3306 {
3307 /* Recommend application to retry with a shorter request target */
3308 if (uri_size > method_size * 4)
3309 return MHD_HTTP_URI_TOO_LONG;
3310 else
3311 return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3312 }
3313
3314 if ((size_t) MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ < method_size)
3315 {
3316 /* The request target (URI) and headers are (reasonably) very small.
3317 Some non-standard long request method is used. */
3318 /* The last resort response as it means "the method is not supported
3319 by the server for any URI". */
3321 }
3322
3323 /* The almost impossible situation: all elements are small, but cannot
3324 fit the buffer. The application set the buffer size to
3325 critically low value? */
3326
3327 if ((1 < opt_headers_size) || (1 < uri_size))
3328 {
3329 if (opt_headers_size >= uri_size)
3331 else
3332 return MHD_HTTP_URI_TOO_LONG;
3333 }
3334
3335 /* Nothing to reduce in the request.
3336 Reply with some status. */
3337 if (0 != host_field_line_size)
3339
3340 return MHD_HTTP_URI_TOO_LONG;
3341}
3342
3343
3354static void
3356 const char *add_header,
3357 size_t add_header_size)
3358{
3359 unsigned int err_code;
3360
3361 err_code = get_no_space_err_status_code (c,
3363 add_header,
3364 add_header_size);
3366 err_code,
3368}
3369
3370
3371#ifdef COOKIE_SUPPORT
3377static void
3378handle_req_cookie_no_space (struct MHD_Connection *c)
3379{
3380 unsigned int err_code;
3381
3382 err_code = get_no_space_err_status_code (c,
3384 NULL,
3385 0);
3387 err_code,
3389}
3390
3391
3392#endif /* COOKIE_SUPPORT */
3393
3394
3405static void
3407 const char *chunk_size_line,
3408 size_t chunk_size_line_size)
3409{
3410 unsigned int err_code;
3411
3412 if (NULL != chunk_size_line)
3413 {
3414 const char *semicol;
3415 /* Check for chunk extension */
3416 semicol = memchr (chunk_size_line, ';', chunk_size_line_size);
3417 if (NULL != semicol)
3418 { /* Chunk extension present. It could be removed without any loss of the
3419 details of the request. */
3423 }
3424 }
3425 err_code = get_no_space_err_status_code (c,
3427 chunk_size_line,
3428 chunk_size_line_size);
3430 err_code,
3432}
3433
3434
3445static void
3447 const char *add_footer,
3448 size_t add_footer_size)
3449{
3450 (void) add_footer; (void) add_footer_size; /* Unused */
3452
3453 /* Footers should be optional */
3457}
3458
3459
3470static void
3472 enum MHD_ProcRecvDataStage stage)
3473{
3474 mhd_assert (MHD_PROC_RECV_INIT <= stage);
3477 mhd_assert ((MHD_PROC_RECV_INIT != stage) || \
3478 (MHD_CONNECTION_INIT == c->state));
3479 mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \
3481 mhd_assert ((MHD_PROC_RECV_URI != stage) || \
3483 mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \
3485 mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \
3487 mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */
3488 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3490 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3492 mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \
3494 mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3495 (! c->rq.have_chunked_upload));
3496 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3497 (c->rq.have_chunked_upload));
3498 switch (stage)
3499 {
3500 case MHD_PROC_RECV_INIT:
3502 /* Some data has been received, but it is not clear yet whether
3503 * the received data is an valid HTTP request */
3505 _ ("No space left in the read buffer when " \
3506 "receiving the initial part of " \
3507 "the request line."));
3508 return;
3509 case MHD_PROC_RECV_URI:
3511 /* Some data has been received, but the request line is incomplete */
3514 /* A quick simple check whether the incomplete line looks
3515 * like an HTTP request */
3516 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
3518 {
3522 return;
3523 }
3525 _ ("No space left in the read buffer when " \
3526 "receiving the URI in " \
3527 "the request line. " \
3528 "The request uses non-standard HTTP request " \
3529 "method token."));
3530 return;
3533 return;
3536 mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3539 {
3540 /* The connection must not be in MHD_EVENT_LOOP_INFO_READ state
3541 when external polling is used and some data left unprocessed. */
3543 /* failed to grow the read buffer, and the
3544 client which is supposed to handle the
3545 received data in a *blocking* fashion
3546 (in this mode) did not handle the data as
3547 it was supposed to!
3548 => we would either have to do busy-waiting
3549 (on the client, which would likely fail),
3550 or if we do nothing, we would just timeout
3551 on the connection (if a timeout is even
3552 set!).
3553 Solution: we kill the connection with an error */
3557 }
3558 else
3559 {
3560 if (MHD_PROC_RECV_BODY_NORMAL == stage)
3561 {
3562 /* A header probably has been added to a suspended connection and
3563 it took precisely all the space in the buffer.
3564 Very low probability. */
3567 }
3568 else
3569 {
3572 { /* Receiving content of the chunk */
3573 /* A header probably has been added to a suspended connection and
3574 it took precisely all the space in the buffer.
3575 Very low probability. */
3577 }
3578 else
3579 {
3580 if (0 != c->rq.current_chunk_size)
3581 { /* Waiting for chunk-closing CRLF */
3582 /* Not really possible as some payload should be
3583 processed and the space used by payload should be available. */
3585 }
3586 else
3587 { /* Reading the line with the chunk size */
3589 c->read_buffer,
3591 }
3592 }
3593 }
3594 }
3595 return;
3598 return;
3599 /* The next cases should not be possible */
3601 default:
3602 break;
3603 }
3604 mhd_assert (0);
3605}
3606
3607
3621static bool
3623{
3627 bool rbuff_grow_desired;
3631 bool rbuff_grow_required;
3632
3635
3636 rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size);
3637 if (rbuff_grow_required)
3638 rbuff_grow_desired = true;
3639 else
3640 {
3641 rbuff_grow_desired = (c->read_buffer_offset + c->daemon->pool_increment >
3642 c->read_buffer_size);
3643
3644 if ((rbuff_grow_desired) &&
3646 {
3647 if (! c->rq.have_chunked_upload)
3648 {
3650 /* Do not grow read buffer more than necessary to process the current
3651 request. */
3652 rbuff_grow_desired =
3654 }
3655 else
3656 {
3658 if (0 == c->rq.current_chunk_size)
3659 rbuff_grow_desired = /* Reading value of the next chunk size */
3661 c->read_buffer_size);
3662 else
3663 {
3664 const uint64_t cur_chunk_left =
3666 /* Do not grow read buffer more than necessary to process the current
3667 chunk with terminating CRLF. */
3669 rbuff_grow_desired =
3670 ((cur_chunk_left + 2) > (uint64_t) (c->read_buffer_size));
3671 }
3672 }
3673 }
3674 }
3675
3676 if (! rbuff_grow_desired)
3677 return true; /* No need to increase the buffer */
3678
3679 if (try_grow_read_buffer (c, rbuff_grow_required))
3680 return true; /* Buffer increase succeed */
3681
3682 if (! rbuff_grow_required)
3683 return true; /* Can continue without buffer increase */
3684
3685 /* Failed to increase the read buffer size, but need to read the data
3686 from the network.
3687 No more space left in the buffer, no more space to increase the buffer. */
3688
3689 /* 'PROCESS_READ' event state flag must be set only if the last application
3690 callback has processed some data. If any data is processed then some
3691 space in the read buffer must be available. */
3693
3694 if ((! MHD_D_IS_USING_THREADS_ (c->daemon))
3697 {
3698 /* The application is handling processing cycles.
3699 The data could be processed later. */
3701 return true;
3702 }
3703 else
3704 {
3705 enum MHD_ProcRecvDataStage stage;
3706
3707 switch (c->state)
3708 {
3710 stage = MHD_PROC_RECV_INIT;
3711 break;
3714 stage = MHD_PROC_RECV_METHOD;
3715 else if (0 == c->rq.req_target_len)
3716 stage = MHD_PROC_RECV_URI;
3717 else
3718 stage = MHD_PROC_RECV_HTTPVER;
3719 break;
3721 stage = MHD_PROC_RECV_HEADERS;
3722 break;
3724 stage = c->rq.have_chunked_upload ?
3726 break;
3728 stage = MHD_PROC_RECV_FOOTERS;
3729 break;
3748#ifdef UPGRADE_SUPPORT
3749 case MHD_CONNECTION_UPGRADE:
3750#endif
3751 default:
3753 mhd_assert (0);
3754 }
3755
3756 handle_recv_no_space (c, stage);
3757 }
3758 return false;
3759}
3760
3761
3770static void
3772{
3773 /* Do not update states of suspended connection */
3774 if (connection->suspended)
3775 return; /* States will be updated after resume. */
3776#ifdef HTTPS_SUPPORT
3777 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
3778 { /* HTTPS connection. */
3779 switch (connection->tls_state)
3780 {
3781 case MHD_TLS_CONN_INIT:
3783 return;
3786 if (0 == gnutls_record_get_direction (connection->tls_session))
3788 else
3790 return;
3792 break; /* Do normal processing */
3796 return;
3797 case MHD_TLS_CONN_TLS_CLOSING: /* Not implemented yet */
3798 case MHD_TLS_CONN_TLS_CLOSED: /* Not implemented yet */
3800 case MHD_TLS_CONN_NO_TLS: /* Not possible */
3801 default:
3802 MHD_PANIC (_ ("Invalid TLS state value.\n"));
3803 }
3804 }
3805#endif /* HTTPS_SUPPORT */
3806 while (1)
3807 {
3808#if DEBUG_STATES
3809 MHD_DLOG (connection->daemon,
3810 _ ("In function %s handling connection at state: %s\n"),
3811 MHD_FUNC_,
3812 MHD_state_to_string (connection->state));
3813#endif
3814 switch (connection->state)
3815 {
3819 break;
3821 mhd_assert (0);
3822 break;
3825 break;
3828 mhd_assert (0);
3829 break;
3832 break;
3834 if ((connection->rq.some_payload_processed) &&
3836 {
3837 /* Some data was processed, the buffer must have some free space */
3838 mhd_assert (connection->read_buffer_offset < \
3839 connection->read_buffer_size);
3840 if (! connection->rq.have_chunked_upload)
3841 {
3842 /* Not a chunked upload. Do not read more than necessary to
3843 process the current request. */
3844 if (connection->rq.remaining_upload_size >=
3845 connection->read_buffer_offset)
3847 else
3849 }
3850 else
3851 {
3852 /* Chunked upload. The size of the current request is unknown.
3853 Continue reading as the space in the read buffer is available. */
3855 }
3856 }
3857 else
3859 break;
3861 mhd_assert (0);
3862 break;
3865 break;
3867 mhd_assert (0);
3868 break;
3871 break;
3873 mhd_assert (0);
3874 break;
3876 /* headers in buffer, keep writing */
3878 break;
3880 mhd_assert (0);
3881 break;
3884 break;
3887 break;
3890 break;
3893 break;
3895 mhd_assert (0);
3896 break;
3899 break;
3901 mhd_assert (0);
3902 break;
3905 return; /* do nothing, not even reading */
3906#ifdef UPGRADE_SUPPORT
3907 case MHD_CONNECTION_UPGRADE:
3908 mhd_assert (0);
3909 break;
3910#endif /* UPGRADE_SUPPORT */
3911 default:
3912 mhd_assert (0);
3913 }
3914
3915 if (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info))
3916 {
3917 /* Check whether the space is available to receive data */
3918 if (! check_and_grow_read_buffer_space (connection))
3919 {
3920 mhd_assert (connection->discard_request);
3921 continue;
3922 }
3923 }
3924 break; /* Everything was processed. */
3925 }
3926}
3927
3928
3941static enum MHD_Result
3943 const char *key,
3944 size_t key_size,
3945 const char *value,
3946 size_t value_size,
3947 enum MHD_ValueKind kind)
3948{
3949 struct MHD_Connection *connection = (struct MHD_Connection *) cls;
3950 if (MHD_NO ==
3951 MHD_set_connection_value_n (connection,
3952 kind,
3953 key,
3954 key_size,
3955 value,
3956 value_size))
3957 {
3958#ifdef HAVE_MESSAGES
3959 MHD_DLOG (connection->daemon,
3960 _ ("Not enough memory in pool to allocate header record!\n"));
3961#endif
3965 return MHD_NO;
3966 }
3967 return MHD_YES;
3968}
3969
3970
3971#ifdef COOKIE_SUPPORT
3972
3976enum _MHD_ParseCookie
3977{
3978 MHD_PARSE_COOKIE_OK = MHD_YES,
3979 MHD_PARSE_COOKIE_OK_LAX = 2,
3980 MHD_PARSE_COOKIE_MALFORMED = -1,
3981 MHD_PARSE_COOKIE_NO_MEMORY = MHD_NO
3982};
3983
3984
3997static enum _MHD_ParseCookie
3998parse_cookies_string (char *str,
3999 const size_t str_len,
4000 struct MHD_Connection *connection)
4001{
4002 size_t i;
4003 bool non_strict;
4004 /* Skip extra whitespaces and empty cookies */
4005 const bool allow_wsp_empty = (0 >= connection->daemon->client_discipline);
4006 /* Allow whitespaces around '=' character */
4007 const bool wsp_around_eq = (-3 >= connection->daemon->client_discipline);
4008 /* Allow whitespaces in quoted cookie value */
4009 const bool wsp_in_quoted = (-2 >= connection->daemon->client_discipline);
4010 /* Allow tab as space after semicolon between cookies */
4011 const bool tab_as_sp = (0 >= connection->daemon->client_discipline);
4012 /* Allow no space after semicolon between cookies */
4013 const bool allow_no_space = (0 >= connection->daemon->client_discipline);
4014
4015 non_strict = false;
4016 i = 0;
4017 while (i < str_len)
4018 {
4019 size_t name_start;
4020 size_t name_len;
4021 size_t value_start;
4022 size_t value_len;
4023 bool val_quoted;
4024 /* Skip any whitespaces and empty cookies */
4025 while (' ' == str[i] || '\t' == str[i] || ';' == str[i])
4026 {
4027 if (! allow_wsp_empty)
4028 return MHD_PARSE_COOKIE_MALFORMED;
4029 non_strict = true;
4030 i++;
4031 if (i == str_len)
4032 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
4033 }
4034 /* 'i' must point to the first char of cookie-name */
4035 name_start = i;
4036 /* Find the end of the cookie-name */
4037 do
4038 {
4039 const char l = str[i];
4040 if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) ||
4041 (';' == l) || (0 == l))
4042 break;
4043 } while (str_len > ++i);
4044 name_len = i - name_start;
4045 /* Skip any whitespaces */
4046 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
4047 {
4048 if (! wsp_around_eq)
4049 return MHD_PARSE_COOKIE_MALFORMED;
4050 non_strict = true;
4051 i++;
4052 }
4053 if ((str_len == i) || ('=' != str[i]) || (0 == name_len))
4054 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */
4055 /* 'i' must point to the '=' char */
4056 mhd_assert ('=' == str[i]);
4057 i++;
4058 /* Skip any whitespaces */
4059 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
4060 {
4061 if (! wsp_around_eq)
4062 return MHD_PARSE_COOKIE_MALFORMED;
4063 non_strict = true;
4064 i++;
4065 }
4066 /* 'i' must point to the first char of cookie-value */
4067 if (str_len == i)
4068 {
4069 value_start = 0;
4070 value_len = 0;
4071#ifdef _DEBUG
4072 val_quoted = false; /* This assignment used in assert */
4073#endif
4074 }
4075 else
4076 {
4077 bool valid_cookie;
4078 val_quoted = ('"' == str[i]);
4079 if (val_quoted)
4080 i++;
4081 value_start = i;
4082 /* Find the end of the cookie-value */
4083 while (str_len > i)
4084 {
4085 const char l = str[i];
4086 if ((';' == l) || ('"' == l) || (',' == l) || (';' == l) ||
4087 ('\\' == l) || (0 == l))
4088 break;
4089 if ((' ' == l) || ('\t' == l))
4090 {
4091 if (! val_quoted)
4092 break;
4093 if (! wsp_in_quoted)
4094 return MHD_PARSE_COOKIE_MALFORMED;
4095 non_strict = true;
4096 }
4097 i++;
4098 }
4099 value_len = i - value_start;
4100 if (val_quoted)
4101 {
4102 if ((str_len == i) || ('"' != str[i]))
4103 return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */
4104 i++;
4105 }
4106 /* Skip any whitespaces */
4107 if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i])))
4108 {
4109 do
4110 {
4111 i++;
4112 } while (str_len > i && (' ' == str[i] || '\t' == str[i]));
4113 /* Whitespace at the end? */
4114 if (str_len > i)
4115 {
4116 if (! allow_wsp_empty)
4117 return MHD_PARSE_COOKIE_MALFORMED;
4118 non_strict = true;
4119 }
4120 }
4121 if (str_len == i)
4122 valid_cookie = true;
4123 else if (';' == str[i])
4124 valid_cookie = true;
4125 else
4126 valid_cookie = false;
4127
4128 if (! valid_cookie)
4129 return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */
4130 }
4131 mhd_assert (0 != name_len);
4132 str[name_start + name_len] = 0; /* Zero-terminate the name */
4133 if (0 != value_len)
4134 {
4135 mhd_assert (value_start + value_len <= str_len);
4136 str[value_start + value_len] = 0; /* Zero-terminate the value */
4137 if (MHD_NO ==
4140 str + name_start,
4141 name_len,
4142 str + value_start,
4143 value_len))
4144 return MHD_PARSE_COOKIE_NO_MEMORY;
4145 }
4146 else
4147 {
4148 if (MHD_NO ==
4151 str + name_start,
4152 name_len,
4153 "",
4154 0))
4155 return MHD_PARSE_COOKIE_NO_MEMORY;
4156 }
4157 if (str_len > i)
4158 {
4159 mhd_assert (0 == str[i] || ';' == str[i]);
4160 mhd_assert (! val_quoted || ';' == str[i]);
4161 mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len);
4162 i++;
4163 if (str_len == i)
4164 { /* No next cookie after semicolon */
4165 if (! allow_wsp_empty)
4166 return MHD_PARSE_COOKIE_MALFORMED;
4167 non_strict = true;
4168 }
4169 else if (' ' != str[i])
4170 {/* No space after semicolon */
4171 if (('\t' == str[i]) && tab_as_sp)
4172 i++;
4173 else if (! allow_no_space)
4174 return MHD_PARSE_COOKIE_MALFORMED;
4175 non_strict = true;
4176 }
4177 else
4178 {
4179 i++;
4180 if (str_len == i)
4181 {
4182 if (! allow_wsp_empty)
4183 return MHD_PARSE_COOKIE_MALFORMED;
4184 non_strict = true;
4185 }
4186 }
4187 }
4188 }
4189 return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
4190}
4191
4192
4199static enum _MHD_ParseCookie
4200parse_cookie_header (struct MHD_Connection *connection)
4201{
4202 const char *hdr;
4203 size_t hdr_len;
4204 char *cpy;
4205 size_t i;
4206 enum _MHD_ParseCookie parse_res;
4207 struct MHD_HTTP_Req_Header *const saved_tail =
4208 connection->rq.headers_received_tail;
4209 const bool allow_partially_correct_cookie =
4210 (1 >= connection->daemon->client_discipline);
4211
4212 if (MHD_NO ==
4218 &hdr,
4219 &hdr_len))
4220 return MHD_PARSE_COOKIE_OK;
4221 if (0 == hdr_len)
4222 return MHD_PARSE_COOKIE_OK;
4223
4224 cpy = MHD_connection_alloc_memory_ (connection,
4225 hdr_len + 1);
4226 if (NULL == cpy)
4227 parse_res = MHD_PARSE_COOKIE_NO_MEMORY;
4228 else
4229 {
4230 memcpy (cpy,
4231 hdr,
4232 hdr_len);
4233 cpy[hdr_len] = '\0';
4234
4235 i = 0;
4236 /* Skip all initial whitespaces */
4237 while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i]))
4238 i++;
4239
4240 parse_res = parse_cookies_string (cpy + i, hdr_len - i, connection);
4241 }
4242
4243 switch (parse_res)
4244 {
4245 case MHD_PARSE_COOKIE_OK:
4246 break;
4247 case MHD_PARSE_COOKIE_OK_LAX:
4248#ifdef HAVE_MESSAGES
4249 if (saved_tail != connection->rq.headers_received_tail)
4250 MHD_DLOG (connection->daemon,
4251 _ ("The Cookie header has been parsed, but it is not fully "
4252 "compliant with the standard.\n"));
4253#endif /* HAVE_MESSAGES */
4254 break;
4255 case MHD_PARSE_COOKIE_MALFORMED:
4256 if (saved_tail != connection->rq.headers_received_tail)
4257 {
4258 if (! allow_partially_correct_cookie)
4259 {
4260 /* Remove extracted values from partially broken cookie */
4261 /* Memory remains allocated until the end of the request processing */
4262 connection->rq.headers_received_tail = saved_tail;
4263 saved_tail->next = NULL;
4264#ifdef HAVE_MESSAGES
4265 MHD_DLOG (connection->daemon,
4266 _ ("The Cookie header has been ignored as it contains "
4267 "malformed data.\n"));
4268#endif /* HAVE_MESSAGES */
4269 }
4270#ifdef HAVE_MESSAGES
4271 else
4272 MHD_DLOG (connection->daemon,
4273 _ ("The Cookie header has been only partially parsed as it "
4274 "contains malformed data.\n"));
4275#endif /* HAVE_MESSAGES */
4276 }
4277#ifdef HAVE_MESSAGES
4278 else
4279 MHD_DLOG (connection->daemon,
4280 _ ("The Cookie header has malformed data.\n"));
4281#endif /* HAVE_MESSAGES */
4282 break;
4283 case MHD_PARSE_COOKIE_NO_MEMORY:
4284#ifdef HAVE_MESSAGES
4285 MHD_DLOG (connection->daemon,
4286 _ ("Not enough memory in the connection pool to "
4287 "parse client cookies!\n"));
4288#endif /* HAVE_MESSAGES */
4289 break;
4290 default:
4291 mhd_assert (0);
4292 break;
4293 }
4294#ifndef HAVE_MESSAGES
4295 (void) saved_tail; /* Mute compiler warning */
4296#endif /* ! HAVE_MESSAGES */
4297
4298 return parse_res;
4299}
4300
4301
4302#endif /* COOKIE_SUPPORT */
4303
4304
4308#define HTTP_VER_LEN (MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1))
4309
4319static bool
4321 const char *http_string,
4322 size_t len)
4323{
4324 const char *const h = http_string;
4325 mhd_assert (NULL != http_string);
4326
4327 /* String must start with 'HTTP/d.d', case-sensetive match.
4328 * See https://www.rfc-editor.org/rfc/rfc9112#name-http-version */
4329 if ((HTTP_VER_LEN != len) ||
4330 ('H' != h[0]) || ('T' != h[1]) || ('T' != h[2]) || ('P' != h[3]) ||
4331 ('/' != h[4])
4332 || ('.' != h[6]) ||
4333 (('0' > h[5]) || ('9' < h[5])) ||
4334 (('0' > h[7]) || ('9' < h[7])))
4335 {
4336 connection->rq.http_ver = MHD_HTTP_VER_INVALID;
4340 return false;
4341 }
4342 if (1 == h[5] - '0')
4343 {
4344 /* HTTP/1.x */
4345 if (1 == h[7] - '0')
4346 connection->rq.http_ver = MHD_HTTP_VER_1_1;
4347 else if (0 == h[7] - '0')
4348 connection->rq.http_ver = MHD_HTTP_VER_1_0;
4349 else
4350 connection->rq.http_ver = MHD_HTTP_VER_1_2__1_9;
4351
4352 return true;
4353 }
4354
4355 if (0 == h[5] - '0')
4356 {
4357 /* Too old major version */
4358 connection->rq.http_ver = MHD_HTTP_VER_TOO_OLD;
4362 return false;
4363 }
4364
4365 connection->rq.http_ver = MHD_HTTP_VER_FUTURE;
4369 return false;
4370}
4371
4372
4380static void
4382 const char *method,
4383 size_t len)
4384{
4385 const char *const m = method;
4386 mhd_assert (NULL != m);
4387 mhd_assert (0 != len);
4388
4389 if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET) == len) &&
4390 (0 == memcmp (m, MHD_HTTP_METHOD_GET, len)))
4391 connection->rq.http_mthd = MHD_HTTP_MTHD_GET;
4392 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_HEAD) == len) &&
4393 (0 == memcmp (m, MHD_HTTP_METHOD_HEAD, len)))
4394 connection->rq.http_mthd = MHD_HTTP_MTHD_HEAD;
4395 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_POST) == len) &&
4396 (0 == memcmp (m, MHD_HTTP_METHOD_POST, len)))
4397 connection->rq.http_mthd = MHD_HTTP_MTHD_POST;
4398 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_PUT) == len) &&
4399 (0 == memcmp (m, MHD_HTTP_METHOD_PUT, len)))
4400 connection->rq.http_mthd = MHD_HTTP_MTHD_PUT;
4401 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_DELETE) == len) &&
4402 (0 == memcmp (m, MHD_HTTP_METHOD_DELETE, len)))
4403 connection->rq.http_mthd = MHD_HTTP_MTHD_DELETE;
4404 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_CONNECT) == len) &&
4405 (0 == memcmp (m, MHD_HTTP_METHOD_CONNECT, len)))
4406 connection->rq.http_mthd = MHD_HTTP_MTHD_CONNECT;
4407 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_OPTIONS) == len) &&
4408 (0 == memcmp (m, MHD_HTTP_METHOD_OPTIONS, len)))
4409 connection->rq.http_mthd = MHD_HTTP_MTHD_OPTIONS;
4410 else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_TRACE) == len) &&
4411 (0 == memcmp (m, MHD_HTTP_METHOD_TRACE, len)))
4412 connection->rq.http_mthd = MHD_HTTP_MTHD_TRACE;
4413 else
4414 connection->rq.http_mthd = MHD_HTTP_MTHD_OTHER;
4415}
4416
4417
4425static void
4427{
4428 struct MHD_Daemon *daemon = connection->daemon;
4429 size_t processed;
4430
4431 if (NULL != connection->rp.response)
4432 return; /* already queued a response */
4433 processed = 0;
4434 connection->rq.client_aware = true;
4435 connection->in_access_handler = true;
4436 if (MHD_NO ==
4437 daemon->default_handler (daemon->default_handler_cls,
4438 connection,
4439 connection->rq.url_for_callback,
4440 connection->rq.method,
4441 connection->rq.version,
4442 NULL,
4443 &processed,
4444 &connection->rq.client_context))
4445 {
4446 connection->in_access_handler = false;
4447 /* serious internal error, close connection */
4448 CONNECTION_CLOSE_ERROR (connection,
4449 _ ("Application reported internal error, " \
4450 "closing connection."));
4451 return;
4452 }
4453 connection->in_access_handler = false;
4454}
4455
4456
4464static void
4466{
4467 struct MHD_Daemon *daemon = connection->daemon;
4468 size_t available;
4469 bool instant_retry;
4470 char *buffer_head;
4471 const int discp_lvl = daemon->client_discipline;
4472 /* RFC does not allow LF as the line termination in chunk headers.
4473 See RFC 9112, section 7.1 and section 2.2-3 */
4474 const bool bare_lf_as_crlf = (-2 > discp_lvl);
4475 /* Allow "Bad WhiteSpace" in chunk extension.
4476 RFC 9112, Section 7.1.1, Paragraph 2 */
4477 const bool allow_bws = (2 > discp_lvl);
4478
4479 mhd_assert (NULL == connection->rp.response);
4480
4481 buffer_head = connection->read_buffer;
4482 available = connection->read_buffer_offset;
4483 do
4484 {
4485 size_t to_be_processed;
4486 size_t left_unprocessed;
4487 size_t processed_size;
4488
4489 instant_retry = false;
4490 if (connection->rq.have_chunked_upload)
4491 {
4493 if ( (connection->rq.current_chunk_offset ==
4494 connection->rq.current_chunk_size) &&
4495 (0 != connection->rq.current_chunk_size) )
4496 {
4497 /* Skip CRLF chunk termination */
4498 size_t i;
4499 mhd_assert (0 != available);
4500 /* skip new line at the *end* of a chunk */
4501 i = 0;
4502 if ( (2 <= available) &&
4503 ('\r' == buffer_head[0]) &&
4504 ('\n' == buffer_head[1]) )
4505 i += 2; /* skip CRLF */
4506 else if (bare_lf_as_crlf && ('\n' == buffer_head[0]))
4507 i++; /* skip bare LF */
4508 else if (2 > available)
4509 break; /* need more upload data */
4510 if (0 == i)
4511 {
4512 /* malformed encoding */
4516 return;
4517 }
4518 available -= i;
4519 buffer_head += i;
4520 connection->rq.current_chunk_offset = 0;
4521 connection->rq.current_chunk_size = 0;
4522 if (0 == available)
4523 break;
4524 }
4525 if (0 != connection->rq.current_chunk_size)
4526 {
4527 /* Process chunk "content" */
4528 uint64_t cur_chunk_left;
4529 mhd_assert (connection->rq.current_chunk_offset < \
4530 connection->rq.current_chunk_size);
4531 cur_chunk_left
4532 = connection->rq.current_chunk_size
4533 - connection->rq.current_chunk_offset;
4534 if (cur_chunk_left > available)
4535 to_be_processed = available;
4536 else
4537 { /* cur_chunk_left <= (size_t)available */
4538 to_be_processed = (size_t) cur_chunk_left;
4539 if (available > to_be_processed)
4540 instant_retry = true;
4541 }
4542 }
4543 else
4544 { /* Need the parse the chunk size line */
4546 size_t num_dig;
4547 uint64_t chunk_size;
4548 bool broken;
4549 bool overflow;
4550
4551 mhd_assert (0 != available);
4552
4553 overflow = false;
4554 chunk_size = 0; /* Mute possible compiler warning.
4555 The real value will be set later. */
4556
4557 num_dig = MHD_strx_to_uint64_n_ (buffer_head,
4558 available,
4559 &chunk_size);
4560 mhd_assert (num_dig <= available);
4561 if (num_dig == available)
4562 continue; /* Need line delimiter */
4563
4564 broken = (0 == num_dig);
4565 if (broken)
4566 {
4567 uint64_t dummy;
4568 /* Check whether result is invalid due to uint64_t overflow */
4569 overflow = (0 != MHD_strx_to_uint64_n_ (buffer_head,
4570 1,
4571 &dummy));
4572 }
4573 else
4574 {
4579 size_t chunk_size_line_len;
4580
4581 chunk_size_line_len = 0;
4582 if ((';' == buffer_head[num_dig]) ||
4583 (allow_bws &&
4584 ((' ' == buffer_head[num_dig]) ||
4585 ('\t' == buffer_head[num_dig]))))
4586 { /* Chunk extension or "bad whitespace" after chunk length */
4587 size_t i;
4588
4589 /* Skip bad whitespaces (if any) */
4590 for (i = num_dig; i < available; ++i)
4591 {
4592 if ((' ' != buffer_head[i]) && ('\t' != buffer_head[i]))
4593 break;
4594 }
4595 if (i == available)
4596 break; /* need more data */
4597 if (';' == buffer_head[i])
4598 {
4599 /* Chunk extension */
4600 for (++i; i < available; ++i)
4601 {
4602 if (('\r' == buffer_head[i]) ||
4603 ('\n' == buffer_head[i]))
4604 break;
4605 }
4606 if (i == available)
4607 break; /* need more data */
4608 mhd_assert (i > num_dig);
4609 mhd_assert (1 <= i);
4610 if ('\r' == buffer_head[i])
4611 {
4612 if (i + 1 == available)
4613 break; /* need more data */
4614 if ('\n' == buffer_head[i + 1])
4615 chunk_size_line_len = i; /* Valid chunk header */
4616 }
4617 else
4618 {
4619 mhd_assert ('\n' == buffer_head[i]);
4620 if (bare_lf_as_crlf)
4621 chunk_size_line_len = i; /* Valid chunk header */
4622 }
4623 /* The chunk header is broken
4624 if chunk_size_line_len is zero here. */
4625 }
4626 else
4627 { /* No ';' after "bad whitespace" */
4628 mhd_assert (allow_bws);
4629 mhd_assert (0 == chunk_size_line_len);
4630 }
4631 }
4632 else
4633 {
4634 /* No chunk extension */
4635 mhd_assert (available >= num_dig);
4636 if ((2 <= (available - num_dig)) &&
4637 ('\r' == buffer_head[num_dig]) &&
4638 ('\n' == buffer_head[num_dig + 1]))
4639 chunk_size_line_len = num_dig + 2;
4640 else if (bare_lf_as_crlf &&
4641 ('\n' == buffer_head[num_dig]))
4642 chunk_size_line_len = num_dig + 1;
4643 else if (2 > (available - num_dig))
4644 break; /* need more data */
4645 }
4646
4647 if (0 != chunk_size_line_len)
4648 { /* Valid termination of the chunk size line */
4649 mhd_assert (chunk_size_line_len <= available);
4650 /* Start reading payload data of the chunk */
4651 connection->rq.current_chunk_offset = 0;
4652 connection->rq.current_chunk_size = chunk_size;
4653
4654 available -= chunk_size_line_len;
4655 buffer_head += chunk_size_line_len;
4656
4657 if (0 == chunk_size)
4658 { /* The final (termination) chunk */
4659 connection->rq.remaining_upload_size = 0;
4660 break;
4661 }
4662 if (available > 0)
4663 instant_retry = true;
4664 continue;
4665 }
4666 /* Invalid chunk size line */
4667 }
4668
4669 if (! overflow)
4673 else
4677 return;
4678 }
4679 }
4680 else
4681 {
4682 /* no chunked encoding, give all to the client */
4684 mhd_assert (0 != connection->rq.remaining_upload_size);
4685 if (connection->rq.remaining_upload_size < available)
4686 to_be_processed = (size_t) connection->rq.remaining_upload_size;
4687 else
4688 to_be_processed = available;
4689 }
4690 left_unprocessed = to_be_processed;
4691 connection->rq.client_aware = true;
4692 connection->in_access_handler = true;
4693 if (MHD_NO ==
4694 daemon->default_handler (daemon->default_handler_cls,
4695 connection,
4696 connection->rq.url_for_callback,
4697 connection->rq.method,
4698 connection->rq.version,
4699 buffer_head,
4700 &left_unprocessed,
4701 &connection->rq.client_context))
4702 {
4703 connection->in_access_handler = false;
4704 /* serious internal error, close connection */
4705 CONNECTION_CLOSE_ERROR (connection,
4706 _ ("Application reported internal error, " \
4707 "closing connection."));
4708 return;
4709 }
4710 connection->in_access_handler = false;
4711
4712 if (left_unprocessed > to_be_processed)
4713 MHD_PANIC (_ ("libmicrohttpd API violation.\n"));
4714
4715 connection->rq.some_payload_processed =
4716 (left_unprocessed != to_be_processed);
4717
4718 if (0 != left_unprocessed)
4719 {
4720 instant_retry = false; /* client did not process everything */
4721#ifdef HAVE_MESSAGES
4722 if ((! connection->rq.some_payload_processed) &&
4723 (! connection->suspended))
4724 {
4725 /* client did not process any upload data, complain if
4726 the setup was incorrect, which may prevent us from
4727 handling the rest of the request */
4728 if (MHD_D_IS_USING_THREADS_ (daemon))
4729 MHD_DLOG (daemon,
4730 _ ("WARNING: Access Handler Callback has not processed " \
4731 "any upload data and connection is not suspended. " \
4732 "This may result in hung connection.\n"));
4733 }
4734#endif /* HAVE_MESSAGES */
4735 }
4736 processed_size = to_be_processed - left_unprocessed;
4737 /* dh left "processed" bytes in buffer for next time... */
4738 buffer_head += processed_size;
4739 available -= processed_size;
4740 if (! connection->rq.have_chunked_upload)
4741 {
4743 connection->rq.remaining_upload_size -= processed_size;
4744 }
4745 else
4746 {
4748 connection->rq.current_chunk_offset += processed_size;
4749 }
4750 } while (instant_retry);
4751 /* TODO: zero out reused memory region */
4752 if ( (available > 0) &&
4753 (buffer_head != connection->read_buffer) )
4754 memmove (connection->read_buffer,
4755 buffer_head,
4756 available);
4757 else
4758 mhd_assert ((0 == available) || \
4759 (connection->read_buffer_offset == available));
4760 connection->read_buffer_offset = available;
4761}
4762
4763
4772static enum MHD_Result
4774 enum MHD_CONNECTION_STATE next_state)
4775{
4776 if ( (connection->write_buffer_append_offset !=
4777 connection->write_buffer_send_offset)
4778 /* || data_in_tls_buffers == true */
4779 )
4780 return MHD_NO;
4781 connection->write_buffer_append_offset = 0;
4782 connection->write_buffer_send_offset = 0;
4783 connection->state = next_state;
4784 return MHD_YES;
4785}
4786
4787
4795static void
4797{
4798 const char *clen;
4799 const char *enc;
4800 size_t val_len;
4801
4802#ifdef COOKIE_SUPPORT
4803 if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (connection))
4804 {
4805 handle_req_cookie_no_space (connection);
4806 return;
4807 }
4808#endif /* COOKIE_SUPPORT */
4809 if ( (-3 < connection->daemon->client_discipline) &&
4810 (MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver)) &&
4811 (MHD_NO ==
4817 NULL,
4818 NULL)) )
4819 {
4820#ifdef HAVE_MESSAGES
4821 MHD_DLOG (connection->daemon,
4822 _ ("Received HTTP/1.1 request without `Host' header.\n"));
4823#endif
4827 return;
4828 }
4829
4830 /* The presence of the request body is indicated by "Content-Length:" or
4831 "Transfer-Encoding:" request headers.
4832 Unless one of these two headers is used, the request has no request body.
4833 See RFC9112, Section 6, paragraph 4. */
4834 connection->rq.remaining_upload_size = 0;
4835 if (MHD_NO !=
4841 &enc,
4842 NULL))
4843 {
4844 if (! MHD_str_equal_caseless_ (enc,
4845 "chunked"))
4846 {
4850 return;
4851 }
4852 else if (MHD_NO !=
4858 NULL,
4859 NULL))
4860 {
4861 /* TODO: add individual settings */
4862 if (1 <= connection->daemon->client_discipline)
4863 {
4867 return;
4868 }
4869 else
4870 {
4871 /* Must close connection after reply to prevent potential attack */
4872 connection->keepalive = MHD_CONN_MUST_CLOSE;
4873#ifdef HAVE_MESSAGES
4874 MHD_DLOG (connection->daemon,
4875 _ ("The 'Content-Length' request header is ignored "
4876 "as chunked Transfer-Encoding is used "
4877 "for this request.\n"));
4878#endif /* HAVE_MESSAGES */
4879 }
4880 }
4881 connection->rq.have_chunked_upload = true;
4883 }
4884 else if (MHD_NO !=
4890 &clen,
4891 &val_len))
4892 {
4893 size_t num_digits;
4894
4895 num_digits = MHD_str_to_uint64_n_ (clen,
4896 val_len,
4897 &connection->rq.remaining_upload_size);
4898
4899 if (((0 == num_digits) &&
4900 (0 != val_len) &&
4901 ('0' <= clen[0]) && ('9' >= clen[0]))
4902 || (MHD_SIZE_UNKNOWN == connection->rq.remaining_upload_size))
4903 {
4904 connection->rq.remaining_upload_size = 0;
4905#ifdef HAVE_MESSAGES
4906 MHD_DLOG (connection->daemon,
4907 _ ("Too large value of 'Content-Length' header. " \
4908 "Closing connection.\n"));
4909#endif
4913 }
4914 else if ((val_len != num_digits) ||
4915 (0 == num_digits))
4916 {
4917 connection->rq.remaining_upload_size = 0;
4918#ifdef HAVE_MESSAGES
4919 MHD_DLOG (connection->daemon,
4920 _ ("Failed to parse 'Content-Length' header. " \
4921 "Closing connection.\n"));
4922#endif
4926 }
4927 }
4928}
4929
4930
4938_MHD_static_inline void
4940{
4941 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
4942}
4943
4944
4949_MHD_static_inline void
4951{
4953 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
4955}
4956
4957
4958#ifndef MHD_MAX_EMPTY_LINES_SKIP
4963#define MHD_MAX_EMPTY_LINES_SKIP 1024
4964#endif /* ! MHD_MAX_EMPTY_LINES_SKIP */
4965
4973static bool
4975{
4976 size_t p;
4977 const int discp_lvl = c->daemon->client_discipline;
4978 /* Allow to skip one or more empty lines before the request line.
4979 RFC 9112, section 2.2 */
4980 const bool skip_empty_lines = (1 >= discp_lvl);
4981 /* Allow to skip more then one empty line before the request line.
4982 RFC 9112, section 2.2 */
4983 const bool skip_several_empty_lines = (skip_empty_lines && (0 >= discp_lvl));
4984 /* Allow to skip number of unlimited empty lines before the request line.
4985 RFC 9112, section 2.2 */
4986 const bool skip_unlimited_empty_lines =
4987 (skip_empty_lines && (-3 >= discp_lvl));
4988 /* Treat bare LF as the end of the line.
4989 RFC 9112, section 2.2 */
4990 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
4991 /* Treat tab as whitespace delimiter.
4992 RFC 9112, section 3 */
4993 const bool tab_as_wsp = (0 >= discp_lvl);
4994 /* Treat VT (vertical tab) and FF (form feed) as whitespace delimiters.
4995 RFC 9112, section 3 */
4996 const bool other_wsp_as_wsp = (-1 >= discp_lvl);
4997 /* Treat continuous whitespace block as a single space.
4998 RFC 9112, section 3 */
4999 const bool wsp_blocks = (-1 >= discp_lvl);
5000 /* Parse whitespace in URI, special parsing of the request line.
5001 RFC 9112, section 3.2 */
5002 const bool wsp_in_uri = (0 >= discp_lvl);
5003 /* Keep whitespace in URI, give app URI with whitespace instead of
5004 automatic redirect to fixed URI.
5005 Violates RFC 9112, section 3.2 */
5006 const bool wsp_in_uri_keep = (-2 >= discp_lvl);
5007 /* Keep bare CR character as is.
5008 Violates RFC 9112, section 2.2 */
5009 const bool bare_cr_keep = (wsp_in_uri_keep && (-3 >= discp_lvl));
5010 /* Treat bare CR as space; replace it with space before processing.
5011 RFC 9112, section 2.2 */
5012 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
5013
5016 mhd_assert (NULL == c->rq.method || \
5021 0 != c->rq.hdrs.rq_line.proc_pos);
5022
5023 if (0 == c->read_buffer_offset)
5024 {
5026 return false; /* No data to process */
5027 }
5028 p = c->rq.hdrs.rq_line.proc_pos;
5029 mhd_assert (p <= c->read_buffer_offset);
5030
5031 /* Skip empty lines, if any (and if allowed) */
5032 /* See RFC 9112, section 2.2 */
5033 if ((0 == p)
5034 && (skip_empty_lines))
5035 {
5036 /* Skip empty lines before the request line.
5037 See RFC 9112, section 2.2 */
5038 bool is_empty_line;
5040 mhd_assert (NULL == c->rq.method);
5041 mhd_assert (NULL == c->rq.url);
5042 mhd_assert (0 == c->rq.url_len);
5044 mhd_assert (0 == c->rq.req_target_len);
5045 mhd_assert (NULL == c->rq.version);
5046 do
5047 {
5048 is_empty_line = false;
5049 if ('\r' == c->read_buffer[0])
5050 {
5051 if (1 == c->read_buffer_offset)
5052 return false; /* Not enough data yet */
5053 if ('\n' == c->read_buffer[1])
5054 {
5055 is_empty_line = true;
5056 c->read_buffer += 2;
5057 c->read_buffer_size -= 2;
5058 c->read_buffer_offset -= 2;
5060 }
5061 }
5062 else if (('\n' == c->read_buffer[0]) &&
5063 (bare_lf_as_crlf))
5064 {
5065 is_empty_line = true;
5066 c->read_buffer += 1;
5067 c->read_buffer_size -= 1;
5068 c->read_buffer_offset -= 1;
5070 }
5071 if (is_empty_line)
5072 {
5073 if ((! skip_unlimited_empty_lines) &&
5074 (((unsigned int) ((skip_several_empty_lines) ?
5077 {
5079 _ ("Too many meaningless extra empty lines " \
5080 "received before the request"));
5081 return true; /* Process connection closure */
5082 }
5083 if (0 == c->read_buffer_offset)
5084 return false; /* No more data to process */
5085 }
5086 } while (is_empty_line);
5087 }
5088 /* All empty lines are skipped */
5089
5091 /* Read and parse the request line */
5093
5094 while (p < c->read_buffer_offset)
5095 {
5096 const char chr = c->read_buffer[p];
5097 bool end_of_line;
5098 /*
5099 The processing logic is different depending on the configured strictness:
5100
5101 When whitespace BLOCKS are NOT ALLOWED, the end of the whitespace is
5102 processed BEFORE processing of the current character.
5103 When whitespace BLOCKS are ALLOWED, the end of the whitespace is
5104 processed AFTER processing of the current character.
5105
5106 When space char in the URI is ALLOWED, the delimiter between the URI and
5107 the HTTP version string is processed only at the END of the line.
5108 When space in the URI is NOT ALLOWED, the delimiter between the URI and
5109 the HTTP version string is processed as soon as the FIRST whitespace is
5110 found after URI start.
5111 */
5112
5113 end_of_line = false;
5114
5115 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5116 (c->rq.hdrs.rq_line.last_ws_end > \
5118 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_start) || \
5119 (0 != c->rq.hdrs.rq_line.last_ws_end));
5120
5121 /* Check for the end of the line */
5122 if ('\r' == chr)
5123 {
5124 if (p + 1 == c->read_buffer_offset)
5125 {
5126 c->rq.hdrs.rq_line.proc_pos = p;
5127 return false; /* Not enough data yet */
5128 }
5129 else if ('\n' == c->read_buffer[p + 1])
5130 end_of_line = true;
5131 else
5132 {
5133 /* Bare CR alone */
5134 /* Must be rejected or replaced with space char.
5135 See RFC 9112, section 2.2 */
5136 if (bare_cr_as_sp)
5137 {
5138 c->read_buffer[p] = ' ';
5140 continue; /* Re-start processing of the current character */
5141 }
5142 else if (! bare_cr_keep)
5143 {
5144 /* A quick simple check whether this line looks like an HTTP request */
5145 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5147 {
5151 }
5152 else
5154 _ ("Bare CR characters are not allowed " \
5155 "in the request line.\n"));
5156 return true; /* Error in the request */
5157 }
5158 }
5159 }
5160 else if ('\n' == chr)
5161 {
5162 /* Bare LF may be recognised as a line delimiter.
5163 See RFC 9112, section 2.2 */
5164 if (bare_lf_as_crlf)
5165 end_of_line = true;
5166 else
5167 {
5168 /* While RFC does not enforce error for bare LF character,
5169 if this char is not treated as a line delimiter, it should be
5170 rejected to avoid any security weakness due to request smuggling. */
5171 /* A quick simple check whether this line looks like an HTTP request */
5172 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5174 {
5178 }
5179 else
5181 _ ("Bare LF characters are not allowed " \
5182 "in the request line.\n"));
5183 return true; /* Error in the request */
5184 }
5185 }
5186
5187 if (end_of_line)
5188 {
5189 /* Handle the end of the request line */
5190
5191 if (NULL != c->rq.method)
5192 {
5193 if (wsp_in_uri)
5194 {
5195 /* The end of the URI and the start of the HTTP version string
5196 should be determined now. */
5197 mhd_assert (NULL == c->rq.version);
5198 mhd_assert (0 == c->rq.req_target_len);
5199 if (0 != c->rq.hdrs.rq_line.last_ws_end)
5200 {
5201 /* Determine the end and the length of the URI */
5202 if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5203 {
5204 c->read_buffer [c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5205 c->rq.req_target_len =
5207 - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5208 }
5209 else if ((c->rq.hdrs.rq_line.last_ws_start + 1 <
5210 c->rq.hdrs.rq_line.last_ws_end) &&
5211 (HTTP_VER_LEN == (p - c->rq.hdrs.rq_line.last_ws_end)))
5212 {
5213 /* Found only HTTP method and HTTP version and more than one
5214 whitespace between them. Assume zero-length URI. */
5215 mhd_assert (wsp_blocks);
5217 c->read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5218 c->rq.hdrs.rq_line.rq_tgt =
5220 c->rq.req_target_len = 0;
5223 }
5224 /* Determine the start of the HTTP version string */
5225 if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5226 {
5228 }
5229 }
5230 }
5231 else
5232 {
5233 /* The end of the URI and the start of the HTTP version string
5234 should be already known. */
5235 if ((NULL == c->rq.version)
5236 && (NULL != c->rq.hdrs.rq_line.rq_tgt)
5237 && (HTTP_VER_LEN == p - (size_t) (c->rq.hdrs.rq_line.rq_tgt
5238 - c->read_buffer))
5239 && (0 != c->read_buffer[(size_t)
5240 (c->rq.hdrs.rq_line.rq_tgt
5241 - c->read_buffer) - 1]))
5242 {
5243 /* Found only HTTP method and HTTP version and more than one
5244 whitespace between them. Assume zero-length URI. */
5245 size_t uri_pos;
5246 mhd_assert (wsp_blocks);
5247 mhd_assert (0 == c->rq.req_target_len);
5248 uri_pos = (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer) - 1;
5249 mhd_assert (uri_pos < p);
5250 c->rq.version = c->rq.hdrs.rq_line.rq_tgt;
5251 c->read_buffer[uri_pos] = 0; /* Zero terminate the URI */
5252 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + uri_pos;
5253 c->rq.req_target_len = 0;
5256 }
5257 }
5258
5259 if (NULL != c->rq.version)
5260 {
5262 if (! parse_http_version (c, c->rq.version,
5263 p
5264 - (size_t) (c->rq.version
5265 - c->read_buffer)))
5266 {
5268 return true; /* Unsupported / broken HTTP version */
5269 }
5270 c->read_buffer[p] = 0; /* Zero terminate the HTTP version strings */
5271 if ('\r' == chr)
5272 {
5273 p++; /* Consume CR */
5274 mhd_assert (p < c->read_buffer_offset); /* The next character has been already checked */
5275 }
5276 p++; /* Consume LF */
5277 c->read_buffer += p;
5278 c->read_buffer_size -= p;
5279 c->read_buffer_offset -= p;
5281 c->rq.req_target_len);
5283 (0 != c->rq.req_target_len));
5285 ((size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5286 - c->rq.hdrs.rq_line.rq_tgt) < \
5287 c->rq.req_target_len));
5289 (c->rq.hdrs.rq_line.rq_tgt_qmark >= \
5290 c->rq.hdrs.rq_line.rq_tgt));
5291 return true; /* The request line is successfully parsed */
5292 }
5293 }
5294 /* Error in the request line */
5295
5296 /* A quick simple check whether this line looks like an HTTP request */
5297 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5299 {
5303 }
5304 else
5306 _ ("The request line is malformed.\n"));
5307
5308 return true;
5309 }
5310
5311 /* Process possible end of the previously found whitespace delimiter */
5312 if ((! wsp_blocks) &&
5313 (p == c->rq.hdrs.rq_line.last_ws_end) &&
5314 (0 != c->rq.hdrs.rq_line.last_ws_end))
5315 {
5316 /* Previous character was a whitespace char and whitespace blocks
5317 are not allowed. */
5318 /* The current position is the next character after
5319 a whitespace delimiter */
5320 if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5321 {
5322 /* The current position is the start of the URI */
5323 mhd_assert (0 == c->rq.req_target_len);
5324 mhd_assert (NULL == c->rq.version);
5325 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5326 /* Reset the whitespace marker */
5328 c->rq.hdrs.rq_line.last_ws_end = 0;
5329 }
5330 else
5331 {
5332 /* It was a whitespace after the start of the URI */
5333 if (! wsp_in_uri)
5334 {
5335 mhd_assert ((0 != c->rq.req_target_len) || \
5336 (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5337 mhd_assert (NULL == c->rq.version); /* Too many whitespaces? This error is handled at whitespace start */
5338 c->rq.version = c->read_buffer + p;
5339 /* Reset the whitespace marker */
5341 c->rq.hdrs.rq_line.last_ws_end = 0;
5342 }
5343 }
5344 }
5345
5346 /* Process the current character.
5347 Is it not the end of the line. */
5348 if ((' ' == chr)
5349 || (('\t' == chr) && (tab_as_wsp))
5350 || ((other_wsp_as_wsp) && ((0xb == chr) || (0xc == chr))))
5351 {
5352 /* A whitespace character */
5353 if ((0 == c->rq.hdrs.rq_line.last_ws_end) ||
5354 (p != c->rq.hdrs.rq_line.last_ws_end) ||
5355 (! wsp_blocks))
5356 {
5357 /* Found first whitespace char of the new whitespace block */
5358 if (NULL == c->rq.method)
5359 {
5360 /* Found the end of the HTTP method string */
5364 mhd_assert (0 == c->rq.req_target_len);
5365 mhd_assert (NULL == c->rq.version);
5366 if (0 == p)
5367 {
5369 _ ("The request line starts with "
5370 "a whitespace.\n"));
5371 return true; /* Error in the request */
5372 }
5373 c->read_buffer[p] = 0; /* Zero-terminate the request method string */
5374 c->rq.method = c->read_buffer;
5375 parse_http_std_method (c, c->rq.method, p);
5376 }
5377 else
5378 {
5379 /* A whitespace after the start of the URI */
5380 if (! wsp_in_uri)
5381 {
5382 /* Whitespace in URI is not allowed to be parsed */
5383 if (NULL == c->rq.version)
5384 {
5386 /* This is a delimiter between URI and HTTP version string */
5387 c->read_buffer[p] = 0; /* Zero-terminate request URI string */
5388 mhd_assert (((size_t) (c->rq.hdrs.rq_line.rq_tgt \
5389 - c->read_buffer)) <= p);
5390 c->rq.req_target_len =
5391 p - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5392 }
5393 else
5394 {
5395 /* This is a delimiter AFTER version string */
5396
5397 /* A quick simple check whether this line looks like an HTTP request */
5398 if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5400 {
5404 }
5405 else
5407 _ ("The request line has more than "
5408 "two whitespaces.\n"));
5409 return true; /* Error in the request */
5410 }
5411 }
5412 else
5413 {
5414 /* Whitespace in URI is allowed to be parsed */
5415 if (0 != c->rq.hdrs.rq_line.last_ws_end)
5416 {
5417 /* The whitespace after the start of the URI has been found already */
5421 }
5422 }
5423 }
5425 c->rq.hdrs.rq_line.last_ws_end = p + 1; /* Will be updated on the next char parsing */
5426 }
5427 else
5428 {
5429 /* Continuation of the whitespace block */
5431 mhd_assert (0 != p);
5432 c->rq.hdrs.rq_line.last_ws_end = p + 1;
5433 }
5434 }
5435 else
5436 {
5437 /* Non-whitespace char, not the end of the line */
5438 mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5439 (c->rq.hdrs.rq_line.last_ws_end == p) || \
5440 wsp_in_uri);
5441
5442 if ((p == c->rq.hdrs.rq_line.last_ws_end) &&
5443 (0 != c->rq.hdrs.rq_line.last_ws_end) &&
5444 (wsp_blocks))
5445 {
5446 /* The end of the whitespace block */
5447 if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5448 {
5449 /* This is the first character of the URI */
5450 mhd_assert (0 == c->rq.req_target_len);
5451 mhd_assert (NULL == c->rq.version);
5452 c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5453 /* Reset the whitespace marker */
5455 c->rq.hdrs.rq_line.last_ws_end = 0;
5456 }
5457 else
5458 {
5459 if (! wsp_in_uri)
5460 {
5461 /* This is the first character of the HTTP version */
5463 mhd_assert ((0 != c->rq.req_target_len) || \
5464 (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5465 mhd_assert (NULL == c->rq.version); /* Handled at whitespace start */
5466 c->rq.version = c->read_buffer + p;
5467 /* Reset the whitespace marker */
5469 c->rq.hdrs.rq_line.last_ws_end = 0;
5470 }
5471 }
5472 }
5473
5474 /* Handle other special characters */
5475 if ('?' == chr)
5476 {
5477 if ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) &&
5478 (NULL != c->rq.hdrs.rq_line.rq_tgt))
5479 {
5481 }
5482 }
5483 else if ((0xb == chr) || (0xc == chr))
5484 {
5485 /* VT or LF characters */
5486 mhd_assert (! other_wsp_as_wsp);
5487 if ((NULL != c->rq.hdrs.rq_line.rq_tgt) &&
5488 (NULL == c->rq.version) &&
5489 (wsp_in_uri))
5490 {
5492 }
5493 else
5494 {
5496 _ ("Invalid character is in the "
5497 "request line.\n"));
5498 return true; /* Error in the request */
5499 }
5500 }
5501 else if (0 == chr)
5502 {
5503 /* NUL character */
5505 _ ("The NUL character is in the "
5506 "request line.\n"));
5507 return true; /* Error in the request */
5508 }
5509 }
5510
5511 p++;
5512 }
5513
5514 c->rq.hdrs.rq_line.proc_pos = p;
5515 return false; /* Not enough data yet */
5516}
5517
5518
5519#ifndef MHD_MAX_FIXED_URI_LEN
5523#define MHD_MAX_FIXED_URI_LEN (64 * 1024)
5524#endif /* ! MHD_MAX_FIXED_URI_LEN */
5525
5533static void
5535{
5536 char *b;
5537 size_t fixed_uri_len;
5538 size_t i;
5539 size_t o;
5540 char *hdr_name;
5541 size_t hdr_name_len;
5542
5546 c->rq.req_target_len);
5547 fixed_uri_len = c->rq.req_target_len
5548 + 2 * c->rq.hdrs.rq_line.num_ws_in_uri;
5549 if ( (fixed_uri_len + 200 > c->daemon->pool_size) ||
5550 (fixed_uri_len > MHD_MAX_FIXED_URI_LEN) ||
5551 (NULL == (b = malloc (fixed_uri_len + 1))) )
5552 {
5554 _ ("The request has whitespace character is " \
5555 "in the URI and the URI is too large to " \
5556 "send automatic redirect to fixed URI.\n"));
5557 return;
5558 }
5559 i = 0;
5560 o = 0;
5561
5562 do
5563 {
5564 const char chr = c->rq.hdrs.rq_line.rq_tgt[i++];
5565
5566 mhd_assert ('\r' != chr); /* Replaced during request line parsing */
5567 mhd_assert ('\n' != chr); /* Rejected during request line parsing */
5568 mhd_assert (0 != chr); /* Rejected during request line parsing */
5569 switch (chr)
5570 {
5571 case ' ':
5572 b[o++] = '%';
5573 b[o++] = '2';
5574 b[o++] = '0';
5575 break;
5576 case '\t':
5577 b[o++] = '%';
5578 b[o++] = '0';
5579 b[o++] = '9';
5580 break;
5581 case 0x0B: /* VT (vertical tab) */
5582 b[o++] = '%';
5583 b[o++] = '0';
5584 b[o++] = 'B';
5585 break;
5586 case 0x0C: /* FF (form feed) */
5587 b[o++] = '%';
5588 b[o++] = '0';
5589 b[o++] = 'C';
5590 break;
5591 default:
5592 b[o++] = chr;
5593 break;
5594 }
5595 } while (i < c->rq.req_target_len);
5596 mhd_assert (fixed_uri_len == o);
5597 b[o] = 0; /* Zero-terminate the result */
5598
5600 hdr_name = malloc (hdr_name_len + 1);
5601 if (NULL != hdr_name)
5602 {
5603 memcpy (hdr_name,
5605 hdr_name_len + 1);
5606 /* hdr_name and b are free()d within this call */
5610 hdr_name,
5611 hdr_name_len,
5612 b,
5613 o);
5614 return;
5615 }
5616 free (b);
5618 _ ("The request has whitespace character is in the " \
5619 "URI.\n"));
5620 return;
5621}
5622
5623
5630static bool
5632{
5633#ifdef _DEBUG
5634 size_t params_len;
5635#endif /* _DEBUG */
5637 mhd_assert (NULL == c->rq.url);
5638 mhd_assert (0 == c->rq.url_len);
5644 (c->rq.req_target_len > \
5645 (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5646 - c->rq.hdrs.rq_line.rq_tgt)));
5647
5648 /* Log callback before the request-target is modified/decoded */
5649 if (NULL != c->daemon->uri_log_callback)
5650 {
5651 c->rq.client_aware = true;
5652 c->rq.client_context =
5654 c->rq.hdrs.rq_line.rq_tgt,
5655 c);
5656 }
5657
5658 if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark)
5659 {
5660#ifdef _DEBUG
5661 params_len =
5663 - (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark - c->rq.hdrs.rq_line.rq_tgt);
5664#endif /* _DEBUG */
5665 c->rq.hdrs.rq_line.rq_tgt_qmark[0] = 0; /* Replace '?' with zero termination */
5666 if (MHD_NO == MHD_parse_arguments_ (c,
5668 c->rq.hdrs.rq_line.rq_tgt_qmark + 1,
5670 c))
5671 {
5673 return false;
5674 }
5675 }
5676#ifdef _DEBUG
5677 else
5678 params_len = 0;
5679#endif /* _DEBUG */
5680
5682 mhd_assert (strlen (c->rq.hdrs.rq_line.rq_tgt) == \
5683 c->rq.req_target_len - params_len);
5684
5685 /* Finally unescape URI itself */
5686 c->rq.url_len =
5688 c,
5689 c->rq.hdrs.rq_line.rq_tgt);
5690 c->rq.url = c->rq.hdrs.rq_line.rq_tgt;
5691
5692 if (2 == c->daemon->allow_bzero_in_url)
5693 c->rq.url_for_callback = c->rq.url;
5694 else if (strlen (c->rq.url) == c->rq.url_len)
5695 c->rq.url_for_callback = c->rq.url;
5696 else if (0 == c->daemon->allow_bzero_in_url)
5697 {
5701 return false;
5702 }
5703
5704 return true;
5705}
5706
5707
5715static bool
5717{
5718 const int discp_lvl = c->daemon->client_discipline;
5719 /* Parse whitespace in URI, special parsing of the request line */
5720 const bool wsp_in_uri = (0 >= discp_lvl);
5721 /* Keep whitespace in URI, give app URI with whitespace instead of
5722 automatic redirect to fixed URI */
5723 const bool wsp_in_uri_keep = (-2 >= discp_lvl);
5724
5725 if (! get_request_line_inner (c))
5726 {
5727 /* End of the request line has not been found yet */
5728 mhd_assert ((! wsp_in_uri) || NULL == c->rq.version);
5729 if ((NULL != c->rq.version) &&
5730 (HTTP_VER_LEN <
5732 - (size_t) (c->rq.version - c->read_buffer))))
5733 {
5738 return true; /* Error in the request */
5739 }
5740 return false;
5741 }
5743 return true; /* Error in the request */
5744
5746 mhd_assert (NULL == c->rq.url);
5747 mhd_assert (0 == c->rq.url_len);
5750 if (0 != c->rq.hdrs.rq_line.num_ws_in_uri)
5751 {
5752 if (! wsp_in_uri)
5753 {
5757 return true; /* Error in the request */
5758 }
5759 if (! wsp_in_uri_keep)
5760 {
5762 return true; /* Error in the request */
5763 }
5764 }
5765 if (! process_request_target (c))
5766 return true; /* Error in processing */
5767
5769 return true;
5770}
5771
5772
5795
5796
5804static bool
5806{
5807 switch (chr)
5808 {
5809 case '!':
5810 case '#':
5811 case '$':
5812 case '%':
5813 case '&':
5814 case '\'':
5815 case '*':
5816 case '+':
5817 case '-':
5818 case '.':
5819 case '^':
5820 case '_':
5821 case '`':
5822 case '|':
5823 case '~':
5824 case 'a':
5825 case 'b':
5826 case 'c':
5827 case 'd':
5828 case 'e':
5829 case 'f':
5830 case 'g':
5831 case 'h':
5832 case 'i':
5833 case 'j':
5834 case 'k':
5835 case 'l':
5836 case 'm':
5837 case 'n':
5838 case 'o':
5839 case 'p':
5840 case 'q':
5841 case 'r':
5842 case 's':
5843 case 't':
5844 case 'u':
5845 case 'v':
5846 case 'w':
5847 case 'x':
5848 case 'y':
5849 case 'z':
5850 case 'A':
5851 case 'B':
5852 case 'C':
5853 case 'D':
5854 case 'E':
5855 case 'F':
5856 case 'G':
5857 case 'H':
5858 case 'I':
5859 case 'J':
5860 case 'K':
5861 case 'L':
5862 case 'M':
5863 case 'N':
5864 case 'O':
5865 case 'P':
5866 case 'Q':
5867 case 'R':
5868 case 'S':
5869 case 'T':
5870 case 'U':
5871 case 'V':
5872 case 'W':
5873 case 'X':
5874 case 'Y':
5875 case 'Z':
5876 case '0':
5877 case '1':
5878 case '2':
5879 case '3':
5880 case '4':
5881 case '5':
5882 case '6':
5883 case '7':
5884 case '8':
5885 case '9':
5886 return true;
5887 default:
5888 return false;
5889 }
5890}
5891
5892
5904static enum MHD_HdrLineReadRes_
5906 bool process_footers,
5907 struct _MHD_str_w_len *hdr_name,
5908 struct _MHD_str_w_len *hdr_value)
5909{
5910 const int discp_lvl = c->daemon->client_discipline;
5911 /* Treat bare LF as the end of the line.
5912 RFC 9112, section 2.2-3
5913 Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
5914 Bare LF is processed as end of the line or rejected as broken request. */
5915 const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
5916 /* Keep bare CR character as is.
5917 Violates RFC 9112, section 2.2-4 */
5918 const bool bare_cr_keep = (-3 >= discp_lvl);
5919 /* Treat bare CR as space; replace it with space before processing.
5920 RFC 9112, section 2.2-4 */
5921 const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
5922 /* Treat NUL as space; replace it with space before processing.
5923 RFC 9110, section 5.5-5 */
5924 const bool nul_as_sp = (-1 >= discp_lvl);
5925 /* Allow folded header lines.
5926 RFC 9112, section 5.2-4 */
5927 const bool allow_folded = (0 >= discp_lvl);
5928 /* Do not reject headers with the whitespace at the start of the first line.
5929 When allowed, the first line with whitespace character at the first
5930 position is ignored (as well as all possible line foldings of the first
5931 line).
5932 RFC 9112, section 2.2-8 */
5933 const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl);
5934 /* Allow whitespace in header (field) name.
5935 Violates RFC 9110, section 5.1-2 */
5936 const bool allow_wsp_in_name = (-2 >= discp_lvl);
5937 /* Allow zero-length header (field) name.
5938 Violates RFC 9110, section 5.1-2 */
5939 const bool allow_empty_name = (-2 >= discp_lvl);
5940 /* Allow non-tchar characters in header (field) name.
5941 Violates RFC 9110, section 5.1 */
5942 const bool allow_extended_charset = (-2 >= discp_lvl);
5943 /* Allow whitespace before colon.
5944 Violates RFC 9112, section 5.1-2 */
5945 const bool allow_wsp_before_colon = (-3 >= discp_lvl);
5946 /* Do not abort the request when header line has no colon, just skip such
5947 bad lines.
5948 RFC 9112, section 5-1 */
5949 const bool allow_line_without_colon = (-2 >= discp_lvl);
5950
5951 size_t p;
5952
5953#if ! defined (HAVE_MESSAGES) && ! defined(_DEBUG)
5954 (void) process_footers; /* Unused parameter */
5955#endif /* !HAVE_MESSAGES && !_DEBUG */
5956
5957 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
5959 c->state);
5960
5961 p = c->rq.hdrs.hdr.proc_pos;
5962
5963 mhd_assert (p <= c->read_buffer_offset);
5964 while (p < c->read_buffer_offset)
5965 {
5966 const char chr = c->read_buffer[p];
5967 bool end_of_line;
5968
5969 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
5970 (c->rq.hdrs.hdr.name_len < p));
5971 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p));
5972 mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
5973 (c->rq.hdrs.hdr.name_end_found));
5974 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
5975 (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start));
5976 mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
5977 (0 != c->rq.hdrs.hdr.name_len));
5978 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
5979 (0 == c->rq.hdrs.hdr.name_len) || \
5980 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len));
5981 mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
5982 (0 == c->rq.hdrs.hdr.value_start) || \
5983 (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start));
5984
5985 /* Check for the end of the line */
5986 if ('\r' == chr)
5987 {
5988 if (0 != p)
5989 {
5990 /* Line is not empty, need to check for possible line folding */
5991 if (p + 2 >= c->read_buffer_offset)
5992 break; /* Not enough data yet to check for folded line */
5993 }
5994 else
5995 {
5996 /* Line is empty, no need to check for possible line folding */
5997 if (p + 2 > c->read_buffer_offset)
5998 break; /* Not enough data yet to check for the end of the line */
5999 }
6000 if ('\n' == c->read_buffer[p + 1])
6001 end_of_line = true;
6002 else
6003 {
6004 /* Bare CR alone */
6005 /* Must be rejected or replaced with space char.
6006 See RFC 9112, section 2.2-4 */
6007 if (bare_cr_as_sp)
6008 {
6009 c->read_buffer[p] = ' ';
6011 continue; /* Re-start processing of the current character */
6012 }
6013 else if (! bare_cr_keep)
6014 {
6015 if (! process_footers)
6019 else
6023 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6024 }
6025 end_of_line = false;
6026 }
6027 }
6028 else if ('\n' == chr)
6029 {
6030 /* Bare LF may be recognised as a line delimiter.
6031 See RFC 9112, section 2.2-3 */
6032 if (bare_lf_as_crlf)
6033 {
6034 if (0 != p)
6035 {
6036 /* Line is not empty, need to check for possible line folding */
6037 if (p + 1 >= c->read_buffer_offset)
6038 break; /* Not enough data yet to check for folded line */
6039 }
6040 end_of_line = true;
6041 }
6042 else
6043 {
6044 if (! process_footers)
6048 else
6052 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6053 }
6054 }
6055 else
6056 end_of_line = false;
6057
6058 if (end_of_line)
6059 {
6060 /* Handle the end of the line */
6064 const size_t line_len = p + (('\r' == chr) ? 2 : 1);
6065 char next_line_char;
6066 mhd_assert (line_len <= c->read_buffer_offset);
6067
6068 if (0 == p)
6069 {
6070 /* Zero-length header line. This is the end of the request header
6071 section.
6072 RFC 9112, Section 2.1-1 */
6075 mhd_assert (0 == c->rq.hdrs.hdr.name_len);
6076 mhd_assert (0 == c->rq.hdrs.hdr.ws_start);
6077 mhd_assert (0 == c->rq.hdrs.hdr.value_start);
6078 /* Consume the line with CRLF (or bare LF) */
6079 c->read_buffer += line_len;
6080 c->read_buffer_offset -= line_len;
6081 c->read_buffer_size -= line_len;
6083 }
6084
6085 mhd_assert (line_len < c->read_buffer_offset);
6086 mhd_assert (0 != line_len);
6087 mhd_assert ('\n' == c->read_buffer[line_len - 1]);
6088 next_line_char = c->read_buffer[line_len];
6089 if ((' ' == next_line_char) ||
6090 ('\t' == next_line_char))
6091 {
6092 /* Folded line */
6093 if (! allow_folded)
6094 {
6095 if (! process_footers)
6099 else
6103
6104 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6105 }
6106 /* Replace CRLF (or bare LF) character(s) with space characters.
6107 See RFC 9112, Section 5.2-4 */
6108 c->read_buffer[p] = ' ';
6109 if ('\r' == chr)
6110 c->read_buffer[p + 1] = ' ';
6111 continue; /* Re-start processing of the current character */
6112 }
6113 else
6114 {
6115 /* It is not a folded line, it's the real end of the non-empty line */
6116 bool skip_line = false;
6117 mhd_assert (0 != p);
6118 if (c->rq.hdrs.hdr.starts_with_ws)
6119 {
6120 /* This is the first line and it starts with whitespace. This line
6121 must be discarded completely.
6122 See RFC 9112, Section 2.2-8 */
6123 mhd_assert (allow_wsp_at_start);
6124#ifdef HAVE_MESSAGES
6125 MHD_DLOG (c->daemon,
6126 _ ("Whitespace-prefixed first header line " \
6127 "has been skipped.\n"));
6128#endif /* HAVE_MESSAGES */
6129 skip_line = true;
6130 }
6131 else if (! c->rq.hdrs.hdr.name_end_found)
6132 {
6133 if (! allow_line_without_colon)
6134 {
6135 if (! process_footers)
6139 else
6143
6144 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6145 }
6146 /* Skip broken line completely */
6148 skip_line = true;
6149 }
6150 if (skip_line)
6151 {
6152 /* Skip the entire line */
6153 c->read_buffer += line_len;
6154 c->read_buffer_offset -= line_len;
6155 c->read_buffer_size -= line_len;
6156 p = 0;
6157 /* Reset processing state */
6158 memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
6159 /* Start processing of the next line */
6160 continue;
6161 }
6162 else
6163 {
6164 /* This line should be valid header line */
6165 size_t value_len;
6166 mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name);
6167
6168 hdr_name->str = c->read_buffer + 0; /* The name always starts at the first character */
6169 hdr_name->len = c->rq.hdrs.hdr.name_len;
6170 mhd_assert (0 == hdr_name->str[hdr_name->len]);
6171
6172 if (0 == c->rq.hdrs.hdr.value_start)
6173 {
6174 c->rq.hdrs.hdr.value_start = p;
6175 c->read_buffer[p] = 0;
6176 value_len = 0;
6177 }
6178 else if (0 != c->rq.hdrs.hdr.ws_start)
6179 {
6180 mhd_assert (p > c->rq.hdrs.hdr.ws_start);
6182 c->read_buffer[c->rq.hdrs.hdr.ws_start] = 0;
6183 value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start;
6184 }
6185 else
6186 {
6187 mhd_assert (p > c->rq.hdrs.hdr.ws_start);
6188 c->read_buffer[p] = 0;
6189 value_len = p - c->rq.hdrs.hdr.value_start;
6190 }
6191 hdr_value->str = c->read_buffer + c->rq.hdrs.hdr.value_start;
6192 hdr_value->len = value_len;
6193 mhd_assert (0 == hdr_value->str[hdr_value->len]);
6194 /* Consume the entire line */
6195 c->read_buffer += line_len;
6196 c->read_buffer_offset -= line_len;
6197 c->read_buffer_size -= line_len;
6199 }
6200 }
6201 }
6202 else if ((' ' == chr) || ('\t' == chr))
6203 {
6204 if (0 == p)
6205 {
6206 if (! allow_wsp_at_start)
6207 {
6208 if (! process_footers)
6212 else
6216 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6217 }
6218 c->rq.hdrs.hdr.starts_with_ws = true;
6219 }
6220 else if ((! c->rq.hdrs.hdr.name_end_found) &&
6221 (! c->rq.hdrs.hdr.starts_with_ws))
6222 {
6223 /* Whitespace in header name / between header name and colon */
6224 if (allow_wsp_in_name || allow_wsp_before_colon)
6225 {
6226 if (0 == c->rq.hdrs.hdr.ws_start)
6227 c->rq.hdrs.hdr.ws_start = p;
6228 }
6229 else
6230 {
6231 if (! process_footers)
6235 else
6239
6240 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6241 }
6242 }
6243 else
6244 {
6245 /* Whitespace before/inside/after header (field) value */
6246 if (0 == c->rq.hdrs.hdr.ws_start)
6247 c->rq.hdrs.hdr.ws_start = p;
6248 }
6249 }
6250 else if (0 == chr)
6251 {
6252 if (! nul_as_sp)
6253 {
6254 if (! process_footers)
6258 else
6262
6263 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6264 }
6265 c->read_buffer[p] = ' ';
6266 continue; /* Re-start processing of the current character */
6267 }
6268 else
6269 {
6270 /* Not a whitespace, not the end of the header line */
6271 mhd_assert ('\r' != chr);
6272 mhd_assert ('\n' != chr);
6273 mhd_assert ('\0' != chr);
6274 if ( (! c->rq.hdrs.hdr.name_end_found) &&
6275 (! c->rq.hdrs.hdr.starts_with_ws) )
6276 {
6277 /* Processing the header (field) name */
6278 if ( (! allow_extended_charset) &&
6279 (':' != chr) &&
6280 (! char_legal_in_field_name (chr)) )
6281 {
6286 }
6287
6288 if (':' == chr)
6289 {
6290 if (0 == c->rq.hdrs.hdr.ws_start)
6291 c->rq.hdrs.hdr.name_len = p;
6292 else
6293 {
6294 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6295 if (! allow_wsp_before_colon)
6296 {
6297 if (! process_footers)
6301 else
6305 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6306 }
6308#ifndef MHD_FAVOR_SMALL_CODE
6309 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6310#endif /* ! MHD_FAVOR_SMALL_CODE */
6311 }
6312 if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name)
6313 {
6314 if (! process_footers)
6318 else
6322 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6323 }
6324 c->rq.hdrs.hdr.name_end_found = true;
6325 c->read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */
6326 }
6327 else
6328 {
6329 if (0 != c->rq.hdrs.hdr.ws_start)
6330 {
6331 /* End of the whitespace in header (field) name */
6332 mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6333 if (! allow_wsp_in_name)
6334 {
6335 if (! process_footers)
6339 else
6343
6344 return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6345 }
6346#ifndef MHD_FAVOR_SMALL_CODE
6347 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6348#endif /* ! MHD_FAVOR_SMALL_CODE */
6349 }
6350 }
6351 }
6352 else
6353 {
6354 /* Processing the header (field) value */
6355 if (0 == c->rq.hdrs.hdr.value_start)
6356 c->rq.hdrs.hdr.value_start = p;
6357#ifndef MHD_FAVOR_SMALL_CODE
6358 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6359#endif /* ! MHD_FAVOR_SMALL_CODE */
6360 }
6361#ifdef MHD_FAVOR_SMALL_CODE
6362 c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6363#endif /* MHD_FAVOR_SMALL_CODE */
6364 }
6365 p++;
6366 }
6367 c->rq.hdrs.hdr.proc_pos = p;
6368 return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */
6369}
6370
6371
6382static bool
6383get_req_headers (struct MHD_Connection *c, bool process_footers)
6384{
6385 do
6386 {
6387 struct _MHD_str_w_len hdr_name;
6388 struct _MHD_str_w_len hdr_value;
6389 enum MHD_HdrLineReadRes_ res;
6390
6391 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6393 c->state);
6394
6395 #ifdef _DEBUG
6396 hdr_name.str = NULL;
6397 hdr_value.str = NULL;
6398#endif /* _DEBUG */
6399 res = get_req_header (c, process_footers, &hdr_name, &hdr_value);
6401 {
6402 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6404 c->state);
6405 mhd_assert (NULL != hdr_name.str);
6406 mhd_assert (NULL != hdr_value.str);
6407 /* Values must be zero-terminated and must not have binary zeros */
6408 mhd_assert (strlen (hdr_name.str) == hdr_name.len);
6409 mhd_assert (strlen (hdr_value.str) == hdr_value.len);
6410 /* Values must not have whitespaces at the start or at the end */
6411 mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != ' '));
6412 mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != '\t'));
6413 mhd_assert ((hdr_name.len == 0) || \
6414 (hdr_name.str[hdr_name.len - 1] != ' '));
6415 mhd_assert ((hdr_name.len == 0) || \
6416 (hdr_name.str[hdr_name.len - 1] != '\t'));
6417 mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != ' '));
6418 mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != '\t'));
6419 mhd_assert ((hdr_value.len == 0) || \
6420 (hdr_value.str[hdr_value.len - 1] != ' '));
6421 mhd_assert ((hdr_value.len == 0) || \
6422 (hdr_value.str[hdr_value.len - 1] != '\t'));
6423
6424 if (MHD_NO ==
6426 (! process_footers) ?
6429 hdr_name.str, hdr_name.len,
6430 hdr_value.str, hdr_value.len))
6431 {
6432 size_t add_element_size;
6433
6434 mhd_assert (hdr_name.str < hdr_value.str);
6435
6436#ifdef HAVE_MESSAGES
6437 MHD_DLOG (c->daemon,
6438 _ ("Failed to allocate memory in the connection memory " \
6439 "pool to store %s.\n"),
6440 (! process_footers) ? _ ("header") : _ ("footer"));
6441#endif /* HAVE_MESSAGES */
6442
6443 add_element_size = hdr_value.len
6444 + (size_t) (hdr_value.str - hdr_name.str);
6445
6446 if (! process_footers)
6447 handle_req_headers_no_space (c, hdr_name.str, add_element_size);
6448 else
6449 handle_req_footers_no_space (c, hdr_name.str, add_element_size);
6450
6452 return true;
6453 }
6454 /* Reset processing state */
6456 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6458 c->state);
6459 /* Read the next header (field) line */
6460 continue;
6461 }
6463 {
6464 mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6466 c->state);
6467 return false;
6468 }
6469 else if (MHD_HDR_LINE_READING_DATA_ERROR == res)
6470 {
6471 mhd_assert ((process_footers ? \
6476 return true;
6477 }
6479 break;
6480 } while (1);
6481
6482#ifdef HAVE_MESSAGES
6483 if (1 == c->rq.num_cr_sp_replaced)
6484 {
6485 MHD_DLOG (c->daemon,
6486 _ ("One bare CR character has been replaced with space " \
6487 "in %s.\n"),
6488 (! process_footers) ?
6489 _ ("the request line or in the request headers") :
6490 _ ("the request footers"));
6491 }
6492 else if (0 != c->rq.num_cr_sp_replaced)
6493 {
6494 MHD_DLOG (c->daemon,
6495 _ ("%" PRIu64 " bare CR characters have been replaced with " \
6496 "spaces in the request line and/or in the request %s.\n"),
6497 (uint64_t) c->rq.num_cr_sp_replaced,
6498 (! process_footers) ? _ ("headers") : _ ("footers"));
6499 }
6500 if (1 == c->rq.skipped_broken_lines)
6501 {
6502 MHD_DLOG (c->daemon,
6503 _ ("One %s line without colon has been skipped.\n"),
6504 (! process_footers) ? _ ("header") : _ ("footer"));
6505 }
6506 else if (0 != c->rq.skipped_broken_lines)
6507 {
6508 MHD_DLOG (c->daemon,
6509 _ ("%" PRIu64 " %s lines without colons has been skipped.\n"),
6510 (uint64_t) c->rq.skipped_broken_lines,
6511 (! process_footers) ? _ ("header") : _ ("footer"));
6512 }
6513#endif /* HAVE_MESSAGES */
6514
6515 mhd_assert (c->rq.method < c->read_buffer);
6516 if (! process_footers)
6517 {
6518 c->rq.header_size = (size_t) (c->read_buffer - c->rq.method);
6520 c->rq.field_lines.size =
6521 (size_t) ((c->read_buffer - c->rq.field_lines.start) - 1);
6522 if ('\r' == *(c->read_buffer - 2))
6523 c->rq.field_lines.size--;
6525
6527 {
6528 /* Try to re-use some of the last bytes of the request header */
6529 /* Do this only if space in the read buffer is limited AND
6530 amount of read ahead data is small. */
6535 const char *last_elmnt_end;
6536 size_t shift_back_size;
6537 if (NULL != c->rq.headers_received_tail)
6538 last_elmnt_end =
6541 else
6542 last_elmnt_end = c->rq.version + HTTP_VER_LEN;
6543 mhd_assert ((last_elmnt_end + 1) < c->read_buffer);
6544 shift_back_size = (size_t) (c->read_buffer - (last_elmnt_end + 1));
6545 if (0 != c->read_buffer_offset)
6546 memmove (c->read_buffer - shift_back_size,
6547 c->read_buffer,
6549 c->read_buffer -= shift_back_size;
6550 c->read_buffer_size += shift_back_size;
6551 }
6552 }
6553 else
6555
6556 return true;
6557}
6558
6559
6567void
6569{
6570 struct MHD_Daemon *daemon = connection->daemon;
6571#if defined(MHD_USE_THREADS)
6572 mhd_assert (NULL == daemon->worker_pool);
6573#endif /* MHD_USE_THREADS */
6574
6575 if (0 == connection->connection_timeout_ms)
6576 return; /* Skip update of activity for connections
6577 without timeout timer. */
6578 if (connection->suspended)
6579 return; /* no activity on suspended connections */
6580
6583 return; /* each connection has personal timeout */
6584
6585 if (connection->connection_timeout_ms != daemon->connection_timeout_ms)
6586 return; /* custom timeout, no need to move it in "normal" DLL */
6587#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6588 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
6589#endif
6590 /* move connection to head of timeout list (by remove + add operation) */
6592 daemon->normal_timeout_tail,
6593 connection);
6595 daemon->normal_timeout_tail,
6596 connection);
6597#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6598 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
6599#endif
6600}
6601
6602
6612void
6614 bool socket_error)
6615{
6616 ssize_t bytes_read;
6617
6618 if ( (MHD_CONNECTION_CLOSED == connection->state) ||
6619 (connection->suspended) )
6620 return;
6621#ifdef HTTPS_SUPPORT
6622 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6623 { /* HTTPS connection. */
6624 if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6625 {
6626 if (! MHD_run_tls_handshake_ (connection))
6627 return;
6628 }
6629 }
6630#endif /* HTTPS_SUPPORT */
6631
6632 mhd_assert (NULL != connection->read_buffer);
6633 if (connection->read_buffer_size == connection->read_buffer_offset)
6634 return; /* No space for receiving data. */
6635
6636 bytes_read = connection->recv_cls (connection,
6637 &connection->read_buffer
6638 [connection->read_buffer_offset],
6639 connection->read_buffer_size
6640 - connection->read_buffer_offset);
6641 if ((bytes_read < 0) || socket_error)
6642 {
6643 if ((MHD_ERR_AGAIN_ == bytes_read) && ! socket_error)
6644 return; /* No new data to process. */
6645 if ((bytes_read > 0) && connection->sk_nonblck)
6646 { /* Try to detect the socket error */
6647 int dummy;
6648 bytes_read = connection->recv_cls (connection, &dummy, sizeof (dummy));
6649 }
6650 if (MHD_ERR_CONNRESET_ == bytes_read)
6651 {
6653 (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6654 {
6655#ifdef HAVE_MESSAGES
6656 MHD_DLOG (connection->daemon,
6657 _ ("Socket has been disconnected when reading request.\n"));
6658#endif
6659 connection->discard_request = true;
6660 }
6661 MHD_connection_close_ (connection,
6663 return;
6664 }
6665
6666#ifdef HAVE_MESSAGES
6667 if (MHD_CONNECTION_INIT != connection->state)
6668 MHD_DLOG (connection->daemon,
6669 _ ("Connection socket is closed when reading " \
6670 "request due to the error: %s\n"),
6671 (bytes_read < 0) ? str_conn_error_ (bytes_read) :
6672 "detected connection closure");
6673#endif
6674 CONNECTION_CLOSE_ERROR (connection,
6675 NULL);
6676 return;
6677 }
6678
6679 if (0 == bytes_read)
6680 { /* Remote side closed connection. */
6681 connection->read_closed = true;
6683 (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6684 {
6685#ifdef HAVE_MESSAGES
6686 MHD_DLOG (connection->daemon,
6687 _ ("Connection was closed by remote side with incomplete "
6688 "request.\n"));
6689#endif
6690 connection->discard_request = true;
6691 MHD_connection_close_ (connection,
6693 }
6694 else if (MHD_CONNECTION_INIT == connection->state)
6695 /* This termination code cannot be reported to the application
6696 * because application has not been informed yet about this request */
6697 MHD_connection_close_ (connection,
6699 else
6700 MHD_connection_close_ (connection,
6702 return;
6703 }
6704 connection->read_buffer_offset += (size_t) bytes_read;
6705 MHD_update_last_activity_ (connection);
6706#if DEBUG_STATES
6707 MHD_DLOG (connection->daemon,
6708 _ ("In function %s handling connection at state: %s\n"),
6709 MHD_FUNC_,
6710 MHD_state_to_string (connection->state));
6711#endif
6712 /* TODO: check whether the next 'switch()' really needed */
6713 switch (connection->state)
6714 {
6721 /* nothing to do but default action */
6722 if (connection->read_closed)
6723 {
6724 /* TODO: check whether this really needed */
6725 MHD_connection_close_ (connection,
6727 }
6728 return;
6730 return;
6731#ifdef UPGRADE_SUPPORT
6732 case MHD_CONNECTION_UPGRADE:
6733 mhd_assert (0);
6734 return;
6735#endif /* UPGRADE_SUPPORT */
6737 /* shrink read buffer to how much is actually used */
6738 /* TODO: remove shrink as it handled in special function */
6739 if ((0 != connection->read_buffer_size) &&
6740 (connection->read_buffer_size != connection->read_buffer_offset))
6741 {
6742 mhd_assert (NULL != connection->read_buffer);
6743 connection->read_buffer =
6744 MHD_pool_reallocate (connection->pool,
6745 connection->read_buffer,
6746 connection->read_buffer_size,
6747 connection->read_buffer_offset);
6748 connection->read_buffer_size = connection->read_buffer_offset;
6749 }
6750 break;
6756 /* Milestone state, no data should be read */
6757 mhd_assert (0); /* Should not be possible */
6758 break;
6769 default:
6770 mhd_assert (0); /* Should not be possible */
6771 break;
6772 }
6773 return;
6774}
6775
6776
6785void
6787{
6788 struct MHD_Response *response;
6789 ssize_t ret;
6790 if (connection->suspended)
6791 return;
6792
6793#ifdef HTTPS_SUPPORT
6794 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6795 { /* HTTPS connection. */
6796 if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6797 {
6798 if (! MHD_run_tls_handshake_ (connection))
6799 return;
6800 }
6801 }
6802#endif /* HTTPS_SUPPORT */
6803
6804#if DEBUG_STATES
6805 MHD_DLOG (connection->daemon,
6806 _ ("In function %s handling connection at state: %s\n"),
6807 MHD_FUNC_,
6808 MHD_state_to_string (connection->state));
6809#endif
6810 switch (connection->state)
6811 {
6818 mhd_assert (0);
6819 return;
6821 ret = MHD_send_data_ (connection,
6823 [connection->continue_message_write_offset],
6825 - connection->continue_message_write_offset,
6826 true);
6827 if (ret < 0)
6828 {
6829 if (MHD_ERR_AGAIN_ == ret)
6830 return;
6831#ifdef HAVE_MESSAGES
6832 MHD_DLOG (connection->daemon,
6833 _ ("Failed to send data in request for %s.\n"),
6834 connection->rq.url);
6835#endif
6836 CONNECTION_CLOSE_ERROR (connection,
6837 NULL);
6838 return;
6839 }
6840#if _MHD_DEBUG_SEND_DATA
6841 fprintf (stderr,
6842 _ ("Sent 100 continue response: `%.*s'\n"),
6843 (int) ret,
6845#endif
6846 connection->continue_message_write_offset += (size_t) ret;
6847 MHD_update_last_activity_ (connection);
6848 return;
6854 mhd_assert (0);
6855 return;
6857 mhd_assert (0);
6858 return;
6860 {
6861 struct MHD_Response *const resp = connection->rp.response;
6862 const size_t wb_ready = connection->write_buffer_append_offset
6863 - connection->write_buffer_send_offset;
6864 mhd_assert (connection->write_buffer_append_offset >= \
6865 connection->write_buffer_send_offset);
6866 mhd_assert (NULL != resp);
6867 mhd_assert ( (0 == resp->data_size) || \
6868 (0 == resp->data_start) || \
6869 (NULL != resp->crc) );
6870 mhd_assert ( (0 == connection->rp.rsp_write_position) || \
6871 (resp->total_size ==
6872 connection->rp.rsp_write_position) );
6873 mhd_assert ((MHD_CONN_MUST_UPGRADE != connection->keepalive) || \
6874 (! connection->rp.props.send_reply_body));
6875
6876 if ( (connection->rp.props.send_reply_body) &&
6877 (NULL == resp->crc) &&
6878 (NULL == resp->data_iov) &&
6879 /* TODO: remove the next check as 'send_reply_body' is used */
6880 (0 == connection->rp.rsp_write_position) &&
6881 (! connection->rp.props.chunked) )
6882 {
6883 mhd_assert (resp->total_size >= resp->data_size);
6884 mhd_assert (0 == resp->data_start);
6885 /* Send response headers alongside the response body, if the body
6886 * data is available. */
6887 ret = MHD_send_hdr_and_body_ (connection,
6888 &connection->write_buffer
6889 [connection->write_buffer_send_offset],
6890 wb_ready,
6891 false,
6892 resp->data,
6893 resp->data_size,
6894 (resp->total_size == resp->data_size));
6895 }
6896 else
6897 {
6898 /* This is response for HEAD request or reply body is not allowed
6899 * for any other reason or reply body is dynamically generated. */
6900 /* Do not send the body data even if it's available. */
6901 ret = MHD_send_hdr_and_body_ (connection,
6902 &connection->write_buffer
6903 [connection->write_buffer_send_offset],
6904 wb_ready,
6905 false,
6906 NULL,
6907 0,
6908 ((0 == resp->total_size) ||
6909 (! connection->rp.props.send_reply_body)
6910 ));
6911 }
6912
6913 if (ret < 0)
6914 {
6915 if (MHD_ERR_AGAIN_ == ret)
6916 return;
6917#ifdef HAVE_MESSAGES
6918 MHD_DLOG (connection->daemon,
6919 _ ("Failed to send the response headers for the " \
6920 "request for `%s'. Error: %s\n"),
6921 connection->rq.url,
6922 str_conn_error_ (ret));
6923#endif
6924 CONNECTION_CLOSE_ERROR (connection,
6925 NULL);
6926 return;
6927 }
6928 /* 'ret' is not negative, it's safe to cast it to 'size_t'. */
6929 if (((size_t) ret) > wb_ready)
6930 {
6931 /* The complete header and some response data have been sent,
6932 * update both offsets. */
6933 mhd_assert (0 == connection->rp.rsp_write_position);
6934 mhd_assert (! connection->rp.props.chunked);
6935 mhd_assert (connection->rp.props.send_reply_body);
6936 connection->write_buffer_send_offset += wb_ready;
6937 connection->rp.rsp_write_position = ((size_t) ret) - wb_ready;
6938 }
6939 else
6940 connection->write_buffer_send_offset += (size_t) ret;
6941 MHD_update_last_activity_ (connection);
6942 if (MHD_CONNECTION_HEADERS_SENDING != connection->state)
6943 return;
6944 check_write_done (connection,
6946 return;
6947 }
6949 return;
6951 response = connection->rp.response;
6952 if (connection->rp.rsp_write_position <
6953 connection->rp.response->total_size)
6954 {
6955 uint64_t data_write_offset;
6956
6957#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6958 if (NULL != response->crc)
6959 MHD_mutex_lock_chk_ (&response->mutex);
6960#endif
6961 if (MHD_NO == try_ready_normal_body (connection))
6962 {
6963 /* mutex was already unlocked by try_ready_normal_body */
6964 return;
6965 }
6966#if defined(_MHD_HAVE_SENDFILE)
6967 if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
6968 {
6969 mhd_assert (NULL == response->data_iov);
6970 ret = MHD_send_sendfile_ (connection);
6971 }
6972 else /* combined with the next 'if' */
6973#endif /* _MHD_HAVE_SENDFILE */
6974 if (NULL != response->data_iov)
6975 {
6976 ret = MHD_send_iovec_ (connection,
6977 &connection->rp.resp_iov,
6978 true);
6979 }
6980 else
6981 {
6982 data_write_offset = connection->rp.rsp_write_position
6983 - response->data_start;
6984 if (data_write_offset > (uint64_t) SIZE_MAX)
6985 MHD_PANIC (_ ("Data offset exceeds limit.\n"));
6986 ret = MHD_send_data_ (connection,
6987 &response->data
6988 [(size_t) data_write_offset],
6989 response->data_size
6990 - (size_t) data_write_offset,
6991 true);
6992#if _MHD_DEBUG_SEND_DATA
6993 if (ret > 0)
6994 fprintf (stderr,
6995 _ ("Sent %d-byte DATA response: `%.*s'\n"),
6996 (int) ret,
6997 (int) ret,
6998 &rp.response->data[connection->rp.rsp_write_position
6999 - rp.response->data_start]);
7000#endif
7001 }
7002#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7003 if (NULL != response->crc)
7004 MHD_mutex_unlock_chk_ (&response->mutex);
7005#endif
7006 if (ret < 0)
7007 {
7008 if (MHD_ERR_AGAIN_ == ret)
7009 return;
7010#ifdef HAVE_MESSAGES
7011 MHD_DLOG (connection->daemon,
7012 _ ("Failed to send the response body for the " \
7013 "request for `%s'. Error: %s\n"),
7014 connection->rq.url,
7015 str_conn_error_ (ret));
7016#endif
7017 CONNECTION_CLOSE_ERROR (connection,
7018 NULL);
7019 return;
7020 }
7021 connection->rp.rsp_write_position += (size_t) ret;
7022 MHD_update_last_activity_ (connection);
7023 }
7024 if (connection->rp.rsp_write_position ==
7025 connection->rp.response->total_size)
7027 return;
7029 mhd_assert (0);
7030 return;
7032 ret = MHD_send_data_ (connection,
7033 &connection->write_buffer
7034 [connection->write_buffer_send_offset],
7035 connection->write_buffer_append_offset
7036 - connection->write_buffer_send_offset,
7037 true);
7038 if (ret < 0)
7039 {
7040 if (MHD_ERR_AGAIN_ == ret)
7041 return;
7042#ifdef HAVE_MESSAGES
7043 MHD_DLOG (connection->daemon,
7044 _ ("Failed to send the chunked response body for the " \
7045 "request for `%s'. Error: %s\n"),
7046 connection->rq.url,
7047 str_conn_error_ (ret));
7048#endif
7049 CONNECTION_CLOSE_ERROR (connection,
7050 NULL);
7051 return;
7052 }
7053 connection->write_buffer_send_offset += (size_t) ret;
7054 MHD_update_last_activity_ (connection);
7055 if (MHD_CONNECTION_CHUNKED_BODY_READY != connection->state)
7056 return;
7057 check_write_done (connection,
7058 (connection->rp.response->total_size ==
7059 connection->rp.rsp_write_position) ?
7062 return;
7065 mhd_assert (0);
7066 return;
7068 ret = MHD_send_data_ (connection,
7069 &connection->write_buffer
7070 [connection->write_buffer_send_offset],
7071 connection->write_buffer_append_offset
7072 - connection->write_buffer_send_offset,
7073 true);
7074 if (ret < 0)
7075 {
7076 if (MHD_ERR_AGAIN_ == ret)
7077 return;
7078#ifdef HAVE_MESSAGES
7079 MHD_DLOG (connection->daemon,
7080 _ ("Failed to send the footers for the " \
7081 "request for `%s'. Error: %s\n"),
7082 connection->rq.url,
7083 str_conn_error_ (ret));
7084#endif
7085 CONNECTION_CLOSE_ERROR (connection,
7086 NULL);
7087 return;
7088 }
7089 connection->write_buffer_send_offset += (size_t) ret;
7090 MHD_update_last_activity_ (connection);
7091 if (MHD_CONNECTION_FOOTERS_SENDING != connection->state)
7092 return;
7093 check_write_done (connection,
7095 return;
7097 mhd_assert (0);
7098 return;
7100 return;
7101#ifdef UPGRADE_SUPPORT
7102 case MHD_CONNECTION_UPGRADE:
7103 mhd_assert (0);
7104 return;
7105#endif /* UPGRADE_SUPPORT */
7106 default:
7107 mhd_assert (0);
7108 CONNECTION_CLOSE_ERROR (connection,
7109 _ ("Internal error.\n"));
7110 break;
7111 }
7112 return;
7113}
7114
7115
7122static bool
7124{
7125 const uint64_t timeout = c->connection_timeout_ms;
7126 uint64_t now;
7127 uint64_t since_actv;
7128
7129 if (c->suspended)
7130 return false;
7131 if (0 == timeout)
7132 return false;
7134 since_actv = now - c->last_activity;
7135 /* Keep the next lines in sync with #connection_get_wait() to avoid
7136 * undesired side-effects like busy-waiting. */
7137 if (timeout < since_actv)
7138 {
7139 if (UINT64_MAX / 2 < since_actv)
7140 {
7141 const uint64_t jump_back = c->last_activity - now;
7142 /* Very unlikely that it is more than quarter-million years pause.
7143 * More likely that system clock jumps back. */
7144 if (5000 >= jump_back)
7145 {
7146#ifdef HAVE_MESSAGES
7147 MHD_DLOG (c->daemon,
7148 _ ("Detected system clock %u milliseconds jump back.\n"),
7149 (unsigned int) jump_back);
7150#endif
7151 return false;
7152 }
7153#ifdef HAVE_MESSAGES
7154 MHD_DLOG (c->daemon,
7155 _ ("Detected too large system clock %" PRIu64 " milliseconds "
7156 "jump back.\n"),
7157 jump_back);
7158#endif
7159 }
7160 return true;
7161 }
7162 return false;
7163}
7164
7165
7174static void
7176{
7177 struct MHD_Daemon *daemon = connection->daemon;
7178#ifdef MHD_USE_THREADS
7179 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
7180 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
7181 mhd_assert (NULL == daemon->worker_pool);
7182#endif /* MHD_USE_THREADS */
7183
7184 if (connection->in_cleanup)
7185 return; /* Prevent double cleanup. */
7186 connection->in_cleanup = true;
7187 if (NULL != connection->rp.response)
7188 {
7189 MHD_destroy_response (connection->rp.response);
7190 connection->rp.response = NULL;
7191 }
7192#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7193 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
7194#endif
7195 if (connection->suspended)
7196 {
7199 connection);
7200 connection->suspended = false;
7201 }
7202 else
7203 {
7204 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
7205 {
7206 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7208 daemon->normal_timeout_tail,
7209 connection);
7210 else
7212 daemon->manual_timeout_tail,
7213 connection);
7214 }
7216 daemon->connections_tail,
7217 connection);
7218 }
7219 DLL_insert (daemon->cleanup_head,
7220 daemon->cleanup_tail,
7221 connection);
7222 connection->resuming = false;
7223 connection->in_idle = false;
7224#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7225 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
7226#endif
7228 {
7229 /* if we were at the connection limit before and are in
7230 thread-per-connection mode, signal the main thread
7231 to resume accepting connections */
7232 if ( (MHD_ITC_IS_VALID_ (daemon->itc)) &&
7233 (! MHD_itc_activate_ (daemon->itc, "c")) )
7234 {
7235#ifdef HAVE_MESSAGES
7236 MHD_DLOG (daemon,
7237 _ ("Failed to signal end of connection via inter-thread " \
7238 "communication channel.\n"));
7239#endif
7240 }
7241 }
7242}
7243
7244
7250void
7252{
7253 size_t read_buf_size;
7254
7255#ifdef HTTPS_SUPPORT
7256 mhd_assert ( (0 == (c->daemon->options & MHD_USE_TLS)) || \
7257 (MHD_TLS_CONN_INIT == c->tls_state) );
7258 mhd_assert ( (0 != (c->daemon->options & MHD_USE_TLS)) || \
7259 (MHD_TLS_CONN_NO_TLS == c->tls_state) );
7260#endif /* HTTPS_SUPPORT */
7262
7265
7266 memset (&c->rq, 0, sizeof(c->rq));
7267 memset (&c->rp, 0, sizeof(c->rp));
7268
7269 c->write_buffer = NULL;
7270 c->write_buffer_size = 0;
7273
7275
7276 c->read_buffer_offset = 0;
7277 read_buf_size = c->daemon->pool_size / 2;
7278 c->read_buffer
7279 = MHD_pool_allocate (c->pool,
7280 read_buf_size,
7281 false);
7282 c->read_buffer_size = read_buf_size;
7283}
7284
7285
7292static void
7294 bool reuse)
7295{
7296 struct MHD_Connection *const c = connection;
7297 struct MHD_Daemon *const d = connection->daemon;
7298
7299 if (! reuse)
7300 {
7301 /* Next function will destroy response, notify client,
7302 * destroy memory pool, and set connection state to "CLOSED" */
7304 c->stop_with_error ?
7307 c->read_buffer = NULL;
7308 c->read_buffer_size = 0;
7309 c->read_buffer_offset = 0;
7310 c->write_buffer = NULL;
7311 c->write_buffer_size = 0;
7314 }
7315 else
7316 {
7317 /* Reset connection to process the next request */
7318 size_t new_read_buf_size;
7321
7322 if ( (NULL != d->notify_completed) &&
7323 (c->rq.client_aware) )
7325 c,
7326 &c->rq.client_context,
7328 c->rq.client_aware = false;
7329
7330 if (NULL != c->rp.response)
7332 c->rp.response = NULL;
7333
7336 c->event_loop_info =
7337 (0 == c->read_buffer_offset) ?
7339
7340 memset (&c->rq, 0, sizeof(c->rq));
7341
7342 /* iov (if any) will be deallocated by MHD_pool_reset */
7343 memset (&c->rp, 0, sizeof(c->rp));
7344
7345 c->write_buffer = NULL;
7346 c->write_buffer_size = 0;
7350
7351 /* Reset the read buffer to the starting size,
7352 preserving the bytes we have already read. */
7353 new_read_buf_size = c->daemon->pool_size / 2;
7354 if (c->read_buffer_offset > new_read_buf_size)
7355 new_read_buf_size = c->read_buffer_offset;
7356
7357 c->read_buffer
7358 = MHD_pool_reset (c->pool,
7359 c->read_buffer,
7361 new_read_buf_size);
7362 c->read_buffer_size = new_read_buf_size;
7363 }
7364 c->rq.client_context = NULL;
7365}
7366
7367
7380enum MHD_Result
7382{
7383 struct MHD_Daemon *daemon = connection->daemon;
7384 enum MHD_Result ret;
7385#ifdef MHD_USE_THREADS
7386 mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
7387 MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
7388#endif /* MHD_USE_THREADS */
7389 /* 'daemon' is not used if epoll is not available and asserts are disabled */
7390 (void) daemon; /* Mute compiler warning */
7391
7392 connection->in_idle = true;
7393 while (! connection->suspended)
7394 {
7395#ifdef HTTPS_SUPPORT
7396 if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
7397 { /* HTTPS connection. */
7398 if ((MHD_TLS_CONN_INIT <= connection->tls_state) &&
7399 (MHD_TLS_CONN_CONNECTED > connection->tls_state))
7400 break;
7401 }
7402#endif /* HTTPS_SUPPORT */
7403#if DEBUG_STATES
7404 MHD_DLOG (daemon,
7405 _ ("In function %s handling connection at state: %s\n"),
7406 MHD_FUNC_,
7407 MHD_state_to_string (connection->state));
7408#endif
7409 switch (connection->state)
7410 {
7413 if (get_request_line (connection))
7414 {
7417 || (connection->discard_request));
7418 continue;
7419 }
7421 break;
7425 continue;
7427 if (get_req_headers (connection, false))
7428 {
7430 mhd_assert ((MHD_CONNECTION_HEADERS_RECEIVED == connection->state) || \
7431 (connection->discard_request));
7432 continue;
7433 }
7435 break;
7437 parse_connection_headers (connection);
7438 if (MHD_CONNECTION_HEADERS_RECEIVED != connection->state)
7439 continue;
7441 if (connection->suspended)
7442 break;
7443 continue;
7445 call_connection_handler (connection); /* first call */
7446 if (MHD_CONNECTION_HEADERS_PROCESSED != connection->state)
7447 continue;
7448 if (connection->suspended)
7449 continue;
7450
7451 if ( (NULL == connection->rp.response) &&
7452 (need_100_continue (connection)) &&
7453 /* If the client is already sending the payload (body)
7454 there is no need to send "100 Continue" */
7455 (0 == connection->read_buffer_offset) )
7456 {
7458 break;
7459 }
7460 if ( (NULL != connection->rp.response) &&
7461 (0 != connection->rq.remaining_upload_size) )
7462 {
7463 /* we refused (no upload allowed!) */
7464 connection->rq.remaining_upload_size = 0;
7465 /* force close, in case client still tries to upload... */
7466 connection->discard_request = true;
7467 }
7468 connection->state = (0 == connection->rq.remaining_upload_size)
7471 if (connection->suspended)
7472 break;
7473 continue;
7475 if (connection->continue_message_write_offset ==
7477 {
7479 continue;
7480 }
7481 break;
7483 mhd_assert (0 != connection->rq.remaining_upload_size);
7484 mhd_assert (! connection->discard_request);
7485 mhd_assert (NULL == connection->rp.response);
7486 if (0 != connection->read_buffer_offset)
7487 {
7488 process_request_body (connection); /* loop call */
7489 if (MHD_CONNECTION_BODY_RECEIVING != connection->state)
7490 continue;
7491 }
7492 /* Modify here when queueing of the response during data processing
7493 will be supported */
7494 mhd_assert (! connection->discard_request);
7495 mhd_assert (NULL == connection->rp.response);
7496 if (0 == connection->rq.remaining_upload_size)
7497 {
7498 connection->state = MHD_CONNECTION_BODY_RECEIVED;
7499 continue;
7500 }
7501 break;
7503 mhd_assert (! connection->discard_request);
7504 mhd_assert (NULL == connection->rp.response);
7505 if (0 == connection->rq.remaining_upload_size)
7506 {
7507 if (connection->rq.have_chunked_upload)
7508 {
7509 /* Reset counter variables reused for footers */
7510 connection->rq.num_cr_sp_replaced = 0;
7511 connection->rq.skipped_broken_lines = 0;
7514 }
7515 else
7517 continue;
7518 }
7519 break;
7521 if (get_req_headers (connection, true))
7522 {
7524 mhd_assert ((MHD_CONNECTION_FOOTERS_RECEIVED == connection->state) || \
7525 (connection->discard_request));
7526 continue;
7527 }
7529 break;
7531 /* The header, the body, and the footers of the request has been received,
7532 * switch to the final processing of the request. */
7534 continue;
7536 call_connection_handler (connection); /* "final" call */
7537 if (connection->state != MHD_CONNECTION_FULL_REQ_RECEIVED)
7538 continue;
7539 if (NULL == connection->rp.response)
7540 break; /* try again next time */
7541 /* Response is ready, start reply */
7542 connection->state = MHD_CONNECTION_START_REPLY;
7543 continue;
7545 mhd_assert (NULL != connection->rp.response);
7547 if (MHD_NO == build_header_response (connection))
7548 {
7549 /* oops - close! */
7550 CONNECTION_CLOSE_ERROR (connection,
7551 _ ("Closing connection (failed to create "
7552 "response header).\n"));
7553 continue;
7554 }
7556 break;
7557
7559 /* no default action */
7560 break;
7562#ifdef UPGRADE_SUPPORT
7563 if (NULL != connection->rp.response->upgrade_handler)
7564 {
7565 connection->state = MHD_CONNECTION_UPGRADE;
7566 /* This connection is "upgraded". Pass socket to application. */
7567 if (MHD_NO ==
7569 connection))
7570 {
7571 /* upgrade failed, fail hard */
7572 CONNECTION_CLOSE_ERROR (connection,
7573 NULL);
7574 continue;
7575 }
7576 /* Response is not required anymore for this connection. */
7577 if (1)
7578 {
7579 struct MHD_Response *const resp = connection->rp.response;
7580
7581 connection->rp.response = NULL;
7582 MHD_destroy_response (resp);
7583 }
7584 continue;
7585 }
7586#endif /* UPGRADE_SUPPORT */
7587
7588 if (connection->rp.props.send_reply_body)
7589 {
7590 if (connection->rp.props.chunked)
7592 else
7594 }
7595 else
7597 continue;
7599 mhd_assert (connection->rp.props.send_reply_body);
7600 mhd_assert (! connection->rp.props.chunked);
7601 /* nothing to do here */
7602 break;
7604 mhd_assert (connection->rp.props.send_reply_body);
7605 mhd_assert (! connection->rp.props.chunked);
7606#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7607 if (NULL != connection->rp.response->crc)
7608 MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7609#endif
7610 if (0 == connection->rp.response->total_size)
7611 {
7612#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7613 if (NULL != connection->rp.response->crc)
7614 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7615#endif
7616 if (connection->rp.props.chunked)
7618 else
7620 continue;
7621 }
7622 if (MHD_NO != try_ready_normal_body (connection))
7623 {
7624#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7625 if (NULL != connection->rp.response->crc)
7626 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7627#endif
7629 /* Buffering for flushable socket was already enabled*/
7630
7631 break;
7632 }
7633 /* mutex was already unlocked by "try_ready_normal_body */
7634 /* not ready, no socket action */
7635 break;
7637 mhd_assert (connection->rp.props.send_reply_body);
7638 mhd_assert (connection->rp.props.chunked);
7639 /* nothing to do here */
7640 break;
7642 mhd_assert (connection->rp.props.send_reply_body);
7643 mhd_assert (connection->rp.props.chunked);
7644#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7645 if (NULL != connection->rp.response->crc)
7646 MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7647#endif
7648 if ( (0 == connection->rp.response->total_size) ||
7649 (connection->rp.rsp_write_position ==
7650 connection->rp.response->total_size) )
7651 {
7652#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7653 if (NULL != connection->rp.response->crc)
7654 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7655#endif
7657 continue;
7658 }
7659 if (1)
7660 { /* pseudo-branch for local variables scope */
7661 bool finished;
7662 if (MHD_NO != try_ready_chunked_body (connection, &finished))
7663 {
7664#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7665 if (NULL != connection->rp.response->crc)
7666 MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7667#endif
7668 connection->state = finished ? MHD_CONNECTION_CHUNKED_BODY_SENT :
7670 continue;
7671 }
7672 /* mutex was already unlocked by try_ready_chunked_body */
7673 }
7674 break;
7676 mhd_assert (connection->rp.props.send_reply_body);
7677 mhd_assert (connection->rp.props.chunked);
7678 mhd_assert (connection->write_buffer_send_offset <= \
7679 connection->write_buffer_append_offset);
7680
7682 {
7683 /* oops - close! */
7684 CONNECTION_CLOSE_ERROR (connection,
7685 _ ("Closing connection (failed to create " \
7686 "response footer)."));
7687 continue;
7688 }
7689 mhd_assert (connection->write_buffer_send_offset < \
7690 connection->write_buffer_append_offset);
7692 continue;
7694 mhd_assert (connection->rp.props.send_reply_body);
7695 mhd_assert (connection->rp.props.chunked);
7696 /* no default action */
7697 break;
7699 if (MHD_HTTP_PROCESSING == connection->rp.responseCode)
7700 {
7701 /* After this type of response, we allow sending another! */
7703 MHD_destroy_response (connection->rp.response);
7704 connection->rp.response = NULL;
7705 /* FIXME: maybe partially reset memory pool? */
7706 continue;
7707 }
7708 /* Reset connection after complete reply */
7709 connection_reset (connection,
7710 MHD_CONN_USE_KEEPALIVE == connection->keepalive &&
7711 ! connection->read_closed &&
7712 ! connection->discard_request);
7713 continue;
7715 cleanup_connection (connection);
7716 connection->in_idle = false;
7717 return MHD_NO;
7718#ifdef UPGRADE_SUPPORT
7719 case MHD_CONNECTION_UPGRADE:
7720 connection->in_idle = false;
7721 return MHD_YES; /* keep open */
7722#endif /* UPGRADE_SUPPORT */
7723 default:
7724 mhd_assert (0);
7725 break;
7726 }
7727 break;
7728 }
7729 if (connection_check_timedout (connection))
7730 {
7731 MHD_connection_close_ (connection,
7733 connection->in_idle = false;
7734 return MHD_YES;
7735 }
7737 ret = MHD_YES;
7738#ifdef EPOLL_SUPPORT
7739 if ( (! connection->suspended) &&
7740 MHD_D_IS_USING_EPOLL_ (daemon) )
7741 {
7742 ret = MHD_connection_epoll_update_ (connection);
7743 }
7744#endif /* EPOLL_SUPPORT */
7745 connection->in_idle = false;
7746 return ret;
7747}
7748
7749
7750#ifdef EPOLL_SUPPORT
7759enum MHD_Result
7760MHD_connection_epoll_update_ (struct MHD_Connection *connection)
7761{
7762 struct MHD_Daemon *const daemon = connection->daemon;
7763
7765
7766 if ((0 != (MHD_EVENT_LOOP_INFO_PROCESS & connection->event_loop_info)) &&
7767 (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)))
7768 {
7769 /* Make sure that connection waiting for processing will be processed */
7770 EDLL_insert (daemon->eready_head,
7771 daemon->eready_tail,
7772 connection);
7773 connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
7774 }
7775
7776 if ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) &&
7777 (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
7778 ( ( (MHD_EVENT_LOOP_INFO_WRITE == connection->event_loop_info) &&
7779 (0 == (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY))) ||
7780 ( (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info)) &&
7781 (0 == (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) ) ) )
7782 {
7783 /* add to epoll set */
7784 struct epoll_event event;
7785
7786 event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
7787 event.data.ptr = connection;
7788 if (0 != epoll_ctl (daemon->epoll_fd,
7789 EPOLL_CTL_ADD,
7790 connection->socket_fd,
7791 &event))
7792 {
7793#ifdef HAVE_MESSAGES
7794 if (0 != (daemon->options & MHD_USE_ERROR_LOG))
7795 MHD_DLOG (daemon,
7796 _ ("Call to epoll_ctl failed: %s\n"),
7798#endif
7799 connection->state = MHD_CONNECTION_CLOSED;
7800 cleanup_connection (connection);
7801 return MHD_NO;
7802 }
7803 connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
7804 }
7805 return MHD_YES;
7806}
7807
7808
7809#endif
7810
7811
7817void
7819{
7820 connection->recv_cls = &recv_param_adapter;
7821}
7822
7823
7836_MHD_EXTERN const union MHD_ConnectionInfo *
7838 enum MHD_ConnectionInfoType info_type,
7839 ...)
7840{
7841 switch (info_type)
7842 {
7843#ifdef HTTPS_SUPPORT
7845 if (NULL == connection->tls_session)
7846 return NULL;
7847 if (1)
7848 { /* Workaround to mute compiler warning */
7849 gnutls_cipher_algorithm_t res;
7850 res = gnutls_cipher_get (connection->tls_session);
7851 connection->connection_info_dummy.cipher_algorithm = (int) res;
7852 }
7853 return &connection->connection_info_dummy;
7855 if (NULL == connection->tls_session)
7856 return NULL;
7857 if (1)
7858 { /* Workaround to mute compiler warning */
7859 gnutls_protocol_t res;
7860 res = gnutls_protocol_get_version (connection->tls_session);
7861 connection->connection_info_dummy.protocol = (int) res;
7862 }
7863 return &connection->connection_info_dummy;
7865 if (NULL == connection->tls_session)
7866 return NULL;
7867 connection->connection_info_dummy.tls_session = connection->tls_session;
7868 return &connection->connection_info_dummy;
7869#else /* ! HTTPS_SUPPORT */
7873#endif /* ! HTTPS_SUPPORT */
7875 return NULL; /* Not implemented */
7877 if (0 < connection->addr_len)
7878 {
7879 mhd_assert (sizeof (connection->addr) == \
7880 sizeof (connection->connection_info_dummy.client_addr));
7881 memcpy (&connection->connection_info_dummy.client_addr,
7882 &connection->addr,
7883 sizeof(connection->addr));
7884 return &connection->connection_info_dummy;
7885 }
7886 return NULL;
7888 connection->connection_info_dummy.daemon =
7889 MHD_get_master (connection->daemon);
7890 return &connection->connection_info_dummy;
7892 connection->connection_info_dummy.connect_fd = connection->socket_fd;
7893 return &connection->connection_info_dummy;
7896 connection->socket_context;
7897 return &connection->connection_info_dummy;
7899 connection->connection_info_dummy.suspended =
7900 connection->suspended ? MHD_YES : MHD_NO;
7901 return &connection->connection_info_dummy;
7903#if SIZEOF_UNSIGNED_INT <= (SIZEOF_UINT64_T - 2)
7904 if (UINT_MAX < connection->connection_timeout_ms / 1000)
7906 else
7907#endif /* SIZEOF_UNSIGNED_INT <=(SIZEOF_UINT64_T - 2) */
7909 (unsigned int) (connection->connection_timeout_ms / 1000);
7910 return &connection->connection_info_dummy;
7912 if ( (MHD_CONNECTION_HEADERS_RECEIVED > connection->state) ||
7913 (MHD_CONNECTION_CLOSED == connection->state) )
7914 return NULL; /* invalid, too early! */
7915 connection->connection_info_dummy.header_size = connection->rq.header_size;
7916 return &connection->connection_info_dummy;
7918 if (NULL == connection->rp.response)
7919 return NULL;
7920 connection->connection_info_dummy.http_status = connection->rp.responseCode;
7921 return &connection->connection_info_dummy;
7922 default:
7923 return NULL;
7924 }
7925}
7926
7927
7939 enum MHD_CONNECTION_OPTION option,
7940 ...)
7941{
7942 va_list ap;
7943 struct MHD_Daemon *daemon;
7944 unsigned int ui_val;
7945
7946 daemon = connection->daemon;
7947 switch (option)
7948 {
7950 if (0 == connection->connection_timeout_ms)
7952 va_start (ap, option);
7953 ui_val = va_arg (ap, unsigned int);
7954 va_end (ap);
7955#if (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT
7956 if ((UINT64_MAX / 4000 - 1) < ui_val)
7957 {
7958#ifdef HAVE_MESSAGES
7959 MHD_DLOG (connection->daemon,
7960 _ ("The specified connection timeout (%u) is too " \
7961 "large. Maximum allowed value (%" PRIu64 ") will be used " \
7962 "instead.\n"),
7963 ui_val,
7964 (UINT64_MAX / 4000 - 1));
7965#endif
7966 ui_val = UINT64_MAX / 4000 - 1;
7967 }
7968#endif /* (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT */
7969 if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
7970 {
7971#if defined(MHD_USE_THREADS)
7972 MHD_mutex_lock_chk_ (&daemon->cleanup_connection_mutex);
7973#endif
7974 if (! connection->suspended)
7975 {
7976 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7978 daemon->normal_timeout_tail,
7979 connection);
7980 else
7982 daemon->manual_timeout_tail,
7983 connection);
7984 connection->connection_timeout_ms = ((uint64_t) ui_val) * 1000;
7985 if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7987 daemon->normal_timeout_tail,
7988 connection);
7989 else
7991 daemon->manual_timeout_tail,
7992 connection);
7993 }
7994#if defined(MHD_USE_THREADS)
7995 MHD_mutex_unlock_chk_ (&daemon->cleanup_connection_mutex);
7996#endif
7997 }
7998 return MHD_YES;
7999 default:
8000 return MHD_NO;
8001 }
8002}
8003
8004
8052 unsigned int status_code,
8053 struct MHD_Response *response)
8054{
8055 struct MHD_Daemon *daemon;
8056 bool reply_icy;
8057
8058 if ((NULL == connection) || (NULL == response))
8059 return MHD_NO;
8060
8061 daemon = connection->daemon;
8062 if ((! connection->in_access_handler) && (! connection->suspended) &&
8063 MHD_D_IS_USING_THREADS_ (daemon))
8064 return MHD_NO;
8065
8066 reply_icy = (0 != (status_code & MHD_ICY_FLAG));
8067 status_code &= ~MHD_ICY_FLAG;
8068
8069#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
8070 if ( (! connection->suspended) &&
8071 MHD_D_IS_USING_THREADS_ (daemon) &&
8072 (! MHD_thread_handle_ID_is_current_thread_ (connection->tid)) )
8073 {
8074#ifdef HAVE_MESSAGES
8075 MHD_DLOG (daemon,
8076 _ ("Attempted to queue response on wrong thread!\n"));
8077#endif
8078 return MHD_NO;
8079 }
8080#endif
8081
8082 if (NULL != connection->rp.response)
8083 return MHD_NO; /* The response was already set */
8084
8085 if ( (MHD_CONNECTION_HEADERS_PROCESSED != connection->state) &&
8086 (MHD_CONNECTION_FULL_REQ_RECEIVED != connection->state) )
8087 return MHD_NO; /* Wrong connection state */
8088
8089 if (daemon->shutdown)
8090 return MHD_NO;
8091
8092#ifdef UPGRADE_SUPPORT
8093 if (NULL != response->upgrade_handler)
8094 {
8095 struct MHD_HTTP_Res_Header *conn_header;
8096 if (0 == (daemon->options & MHD_ALLOW_UPGRADE))
8097 {
8098#ifdef HAVE_MESSAGES
8099 MHD_DLOG (daemon,
8100 _ ("Attempted 'upgrade' connection on daemon without" \
8101 " MHD_ALLOW_UPGRADE option!\n"));
8102#endif
8103 return MHD_NO;
8104 }
8105 if (MHD_HTTP_SWITCHING_PROTOCOLS != status_code)
8106 {
8107#ifdef HAVE_MESSAGES
8108 MHD_DLOG (daemon,
8109 _ ("Application used invalid status code for" \
8110 " 'upgrade' response!\n"));
8111#endif
8112 return MHD_NO;
8113 }
8114 if (0 == (response->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
8115 {
8116#ifdef HAVE_MESSAGES
8117 MHD_DLOG (daemon,
8118 _ ("Application used invalid response" \
8119 " without \"Connection\" header!\n"));
8120#endif
8121 return MHD_NO;
8122 }
8123 conn_header = response->first_header;
8124 mhd_assert (NULL != conn_header);
8127 if (! MHD_str_has_s_token_caseless_ (conn_header->value,
8128 "upgrade"))
8129 {
8130#ifdef HAVE_MESSAGES
8131 MHD_DLOG (daemon,
8132 _ ("Application used invalid response" \
8133 " without \"upgrade\" token in" \
8134 " \"Connection\" header!\n"));
8135#endif
8136 return MHD_NO;
8137 }
8138 if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
8139 {
8140#ifdef HAVE_MESSAGES
8141 MHD_DLOG (daemon,
8142 _ ("Connection \"Upgrade\" can be used only " \
8143 "with HTTP/1.1 connections!\n"));
8144#endif
8145 return MHD_NO;
8146 }
8147 }
8148#endif /* UPGRADE_SUPPORT */
8149 if (MHD_HTTP_SWITCHING_PROTOCOLS == status_code)
8150 {
8151#ifdef UPGRADE_SUPPORT
8152 if (NULL == response->upgrade_handler)
8153 {
8154#ifdef HAVE_MESSAGES
8155 MHD_DLOG (daemon,
8156 _ ("Application used status code 101 \"Switching Protocols\" " \
8157 "with non-'upgrade' response!\n"));
8158#endif /* HAVE_MESSAGES */
8159 return MHD_NO;
8160 }
8161#else /* ! UPGRADE_SUPPORT */
8162#ifdef HAVE_MESSAGES
8163 MHD_DLOG (daemon,
8164 _ ("Application used status code 101 \"Switching Protocols\", " \
8165 "but this MHD was built without \"Upgrade\" support!\n"));
8166#endif /* HAVE_MESSAGES */
8167 return MHD_NO;
8168#endif /* ! UPGRADE_SUPPORT */
8169 }
8170 if ( (100 > status_code) ||
8171 (999 < status_code) )
8172 {
8173#ifdef HAVE_MESSAGES
8174 MHD_DLOG (daemon,
8175 _ ("Refused wrong status code (%u). " \
8176 "HTTP requires three digits status code!\n"),
8177 status_code);
8178#endif
8179 return MHD_NO;
8180 }
8181 if (200 > status_code)
8182 {
8183 if (MHD_HTTP_VER_1_0 == connection->rq.http_ver)
8184 {
8185#ifdef HAVE_MESSAGES
8186 MHD_DLOG (daemon,
8187 _ ("Wrong status code (%u) refused. " \
8188 "HTTP/1.0 clients do not support 1xx status codes!\n"),
8189 (status_code));
8190#endif
8191 return MHD_NO;
8192 }
8193 if (0 != (response->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
8195 {
8196#ifdef HAVE_MESSAGES
8197 MHD_DLOG (daemon,
8198 _ ("Wrong status code (%u) refused. " \
8199 "HTTP/1.0 reply mode does not support 1xx status codes!\n"),
8200 (status_code));
8201#endif
8202 return MHD_NO;
8203 }
8204 }
8205 if ( (MHD_HTTP_MTHD_CONNECT == connection->rq.http_mthd) &&
8206 (2 == status_code / 100) )
8207 {
8208#ifdef HAVE_MESSAGES
8209 MHD_DLOG (daemon,
8210 _ ("Successful (%u) response code cannot be used to answer " \
8211 "\"CONNECT\" request!\n"),
8212 (status_code));
8213#endif
8214 return MHD_NO;
8215 }
8216
8217 if ( (0 != (MHD_RF_HEAD_ONLY_RESPONSE & response->flags)) &&
8218 (RP_BODY_HEADERS_ONLY < is_reply_body_needed (connection, status_code)) )
8219 {
8220#ifdef HAVE_MESSAGES
8221 MHD_DLOG (daemon,
8222 _ ("HEAD-only response cannot be used when the request requires "
8223 "reply body to be sent!\n"));
8224#endif
8225 return MHD_NO;
8226 }
8227
8228#ifdef HAVE_MESSAGES
8229 if ( (0 != (MHD_RF_INSANITY_HEADER_CONTENT_LENGTH & response->flags)) &&
8230 (0 != (MHD_RAF_HAS_CONTENT_LENGTH & response->flags_auto)) )
8231 {
8232 MHD_DLOG (daemon,
8233 _ ("The response has application-defined \"Content-Length\" " \
8234 "header. The reply to the request will be not " \
8235 "HTTP-compliant and may result in hung connection or " \
8236 "other problems!\n"));
8237 }
8238#endif
8239
8240 MHD_increment_response_rc (response);
8241 connection->rp.response = response;
8242 connection->rp.responseCode = status_code;
8243 connection->rp.responseIcy = reply_icy;
8244#if defined(_MHD_HAVE_SENDFILE)
8245 if ( (response->fd == -1) ||
8246 (response->is_pipe) ||
8247 (0 != (connection->daemon->options & MHD_USE_TLS))
8248#if defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED) && \
8249 defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE)
8250 || (! daemon->sigpipe_blocked && ! connection->sk_spipe_suppress)
8251#endif /* MHD_SEND_SPIPE_SUPPRESS_NEEDED &&
8252 MHD_SEND_SPIPE_SUPPRESS_POSSIBLE */
8253 )
8254 connection->rp.resp_sender = MHD_resp_sender_std;
8255 else
8256 connection->rp.resp_sender = MHD_resp_sender_sendfile;
8257#endif /* _MHD_HAVE_SENDFILE */
8258 /* FIXME: if 'is_pipe' is set, TLS is off, and we have *splice*, we could use splice()
8259 to avoid two user-space copies... */
8260
8261 if ( (MHD_HTTP_MTHD_HEAD == connection->rq.http_mthd) ||
8262 (MHD_HTTP_OK > status_code) ||
8263 (MHD_HTTP_NO_CONTENT == status_code) ||
8264 (MHD_HTTP_NOT_MODIFIED == status_code) )
8265 {
8266 /* if this is a "HEAD" request, or a status code for
8267 which a body is not allowed, pretend that we
8268 have already sent the full message body. */
8269 /* TODO: remove the next assignment, use 'rp_props.send_reply_body' in
8270 * checks */
8271 connection->rp.rsp_write_position = response->total_size;
8272 }
8273 if (MHD_CONNECTION_HEADERS_PROCESSED == connection->state)
8274 {
8275 /* response was queued "early", refuse to read body / footers or
8276 further requests! */
8277 connection->discard_request = true;
8278 connection->state = MHD_CONNECTION_START_REPLY;
8279 connection->rq.remaining_upload_size = 0;
8280 }
8281 if (! connection->in_idle)
8282 (void) MHD_connection_handle_idle (connection);
8283 MHD_update_last_activity_ (connection);
8284 return MHD_YES;
8285}
8286
8287
8288/* end of connection.c */
#define ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG
Definition connection.c:167
#define REQUEST_CONTENTLENGTH_TOOLARGE
Definition connection.c:555
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:747
#define ERR_RSP_EMPTY_FOOTER_NAME
Definition connection.c:451
static enum MHD_Result build_header_response(struct MHD_Connection *connection)
static void connection_close_error(struct MHD_Connection *connection, const char *emsg)
static bool char_legal_in_field_name(char chr)
static bool process_request_target(struct MHD_Connection *c)
void MHD_connection_set_initial_state_(struct MHD_Connection *c)
#define MHD_CHUNK_HEADER_REASONABLE_LEN
Definition connection.c:75
#define MHD_lookup_header_s_token_ci(c, h, tkn)
MHD_ProcRecvDataStage
@ MHD_PROC_RECV_COOKIE
@ MHD_PROC_RECV_BODY_CHUNKED
@ MHD_PROC_RECV_HTTPVER
@ MHD_PROC_RECV_INIT
@ MHD_PROC_RECV_URI
@ MHD_PROC_RECV_METHOD
@ MHD_PROC_RECV_BODY_NORMAL
@ MHD_PROC_RECV_HEADERS
@ MHD_PROC_RECV_FOOTERS
#define REQUEST_CHUNK_TOO_LARGE
Definition connection.c:543
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:673
#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:531
#define REQ_HTTP_VER_IS_NOT_SUPPORTED
Definition connection.c:607
#define MHD_MAX_REASONABLE_HEADERS_SIZE_
#define RQ_LINE_TOO_MANY_WSP
Definition connection.c:198
static void call_connection_handler(struct MHD_Connection *connection)
static enum MHD_Result connection_add_header(void *cls, const char *key, size_t key_size, const char *value, size_t value_size, enum MHD_ValueKind kind)
static void process_request_body(struct MHD_Connection *connection)
#define ERR_RSP_INVALID_CHR_IN_FOOTER
Definition connection.c:399
static void setup_reply_properties(struct MHD_Connection *connection)
#define ERR_RSP_INVALID_CHAR_IN_FIELD_NAME
Definition connection.c:373
#define HTTP_100_CONTINUE
Definition connection.c:80
static void send_redirect_fixed_rq_target(struct MHD_Connection *c)
static void handle_recv_no_space(struct MHD_Connection *c, enum MHD_ProcRecvDataStage stage)
#define transmit_error_response_static(c, code, msg)
#define REQUEST_MALFORMED
Definition connection.c:512
static bool get_request_line(struct MHD_Connection *c)
MHD_HdrLineReadRes_
@ MHD_HDR_LINE_READING_NEED_MORE_DATA
@ MHD_HDR_LINE_READING_GOT_END_OF_HEADER
@ MHD_HDR_LINE_READING_GOT_HEADER
@ MHD_HDR_LINE_READING_DATA_ERROR
_MHD_static_inline void reset_rq_header_processing_state(struct MHD_Connection *c)
#define ERR_RSP_HEADER_WITHOUT_COLON
Definition connection.c:412
static void connection_shrink_read_buffer(struct MHD_Connection *connection)
#define ERR_RSP_WSP_IN_FOOTER_NAME
Definition connection.c:358
#define MHD_ALLOW_BARE_LF_AS_CRLF_(discp_lvl)
Definition connection.c:65
#define HTTP_VER_LEN
static enum replyBodyUse is_reply_body_needed(struct MHD_Connection *connection, unsigned int rcode)
void MHD_set_http_callbacks_(struct MHD_Connection *connection)
static void transmit_error_response_len(struct MHD_Connection *connection, unsigned int status_code, const char *message, size_t message_len, char *header_name, size_t header_name_len, char *header_value, size_t header_value_len)
#define REQ_HTTP_VER_IS_TOO_OLD
Definition connection.c:596
#define ERR_MSG_REQUEST_TOO_BIG
Definition connection.c:94
#define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
#define ERR_MSG_REQUEST_FOOTER_TOO_BIG
Definition connection.c:185
#define ERR_MSG_REQUEST_CHUNK_LINE_EXT_TOO_BIG
Definition connection.c:148
static bool connection_check_timedout(struct MHD_Connection *c)
static enum MHD_ConnKeepAlive keepalive_possible(struct MHD_Connection *connection)
static void handle_req_chunk_size_line_no_space(struct MHD_Connection *c, const char *chunk_size_line, size_t chunk_size_line_size)
#define REQUEST_LACKS_HOST
Definition connection.c:469
#define BARE_CR_IN_FOOTER
Definition connection.c:228
static bool try_grow_read_buffer(struct MHD_Connection *connection, bool required)
static bool parse_http_version(struct MHD_Connection *connection, const char *http_string, size_t len)
#define BARE_LF_IN_HEADER
Definition connection.c:243
#define ERR_RSP_EMPTY_HEADER_NAME
Definition connection.c:438
static bool need_100_continue(struct MHD_Connection *connection)
#define ERR_RSP_WSP_IN_HEADER_NAME
Definition connection.c:343
static bool get_req_headers(struct MHD_Connection *c, bool process_footers)
#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP
Definition connection.c:584
#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:482
enum MHD_Result MHD_connection_handle_idle(struct MHD_Connection *connection)
static void cleanup_connection(struct MHD_Connection *connection)
#define ERR_RSP_OBS_FOLD_FOOTER
Definition connection.c:298
replyBodyUse
@ RP_BODY_HEADERS_ONLY
@ RP_BODY_NONE
@ RP_BODY_SEND
static void parse_http_std_method(struct MHD_Connection *connection, const char *method, size_t len)
#define ERR_RSP_OBS_FOLD
Definition connection.c:285
static bool check_and_grow_read_buffer_space(struct MHD_Connection *c)
static enum MHD_Result try_ready_chunked_body(struct MHD_Connection *connection, bool *p_finished)
static bool get_date_str(char *date)
#define BARE_LF_IN_FOOTER
Definition connection.c:258
static void connection_reset(struct MHD_Connection *connection, bool reuse)
#define REQUEST_LENGTH_WITH_TR_ENCODING
Definition connection.c:497
#define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
#define RQ_TARGET_INVALID_CHAR
Definition connection.c:272
static bool get_request_line_inner(struct MHD_Connection *c)
static void handle_req_footers_no_space(struct MHD_Connection *c, const char *add_footer, size_t add_footer_size)
void MHD_connection_handle_read(struct MHD_Connection *connection, bool socket_error)
static void connection_switch_from_recv_to_send(struct MHD_Connection *connection)
#define ERR_RSP_WSP_BEFORE_FOOTER
Definition connection.c:328
static void parse_connection_headers(struct MHD_Connection *connection)
#define ERR_RSP_INVALID_CHR_IN_HEADER
Definition connection.c:386
void MHD_update_last_activity_(struct MHD_Connection *connection)
#define MHD_MAX_EMPTY_LINES_SKIP
#define MHD_MIN_REASONABLE_HEADERS_SIZE_
#define REQUEST_HAS_NUL_CHAR_IN_PATH
Definition connection.c:518
#define BARE_CR_IN_HEADER
Definition connection.c:213
#define ERR_MSG_REQUEST_HEADER_TOO_BIG
Definition connection.c:111
static bool add_user_headers(char *buf, size_t *ppos, size_t buf_size, struct MHD_Response *response, bool filter_transf_enc, bool filter_content_len, bool add_close, bool add_keep_alive)
#define REQUEST_CONTENTLENGTH_MALFORMED
Definition connection.c:568
static enum MHD_HdrLineReadRes_ get_req_header(struct MHD_Connection *c, bool process_footers, struct _MHD_str_w_len *hdr_name, struct _MHD_str_w_len *hdr_value)
static void check_connection_reply(struct MHD_Connection *connection)
static void handle_req_headers_no_space(struct MHD_Connection *c, const char *add_header, size_t add_header_size)
void MHD_connection_close_(struct MHD_Connection *connection, enum MHD_RequestTerminationCode termination_code)
static size_t connection_maximize_write_buffer(struct MHD_Connection *connection)
static bool has_unprocessed_upload_body_data_in_buffer(struct MHD_Connection *c)
void MHD_connection_mark_closed_(struct MHD_Connection *connection)
#define CONNECTION_CLOSE_ERROR(c, emsg)
#define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l)
static bool buffer_append(char *buf, size_t *ppos, size_t buf_size, const char *append, size_t append_size)
static enum MHD_Result check_write_done(struct MHD_Connection *connection, enum MHD_CONNECTION_STATE next_state)
#define ERR_RSP_WSP_BEFORE_HEADER
Definition connection.c:313
#define ERR_MSG_REQUEST_HEADER_WITH_COOKIES_TOO_BIG
Definition connection.c:129
_MHD_static_inline void switch_to_rq_headers_processing(struct MHD_Connection *c)
static bool get_date_header(char *header)
static bool MHD_lookup_header_token_ci(const struct MHD_Connection *connection, const char *header, size_t header_len, const char *token, size_t token_len)
#define ERR_RSP_FOOTER_WITHOUT_COLON
Definition connection.c:425
#define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
Methods for managing connections.
#define MHD_ERR_INVAL_
Definition connection.h:63
#define MHD_ERR_CONNRESET_
Definition connection.h:42
#define MHD_ERR_NOMEM_
Definition connection.h:53
#define MHD_connection_finish_forward_(conn)
Definition connection.h:174
#define MHD_ERR_TLS_
Definition connection.h:78
#define MHD_ERR_AGAIN_
Definition connection.h:37
#define MHD_ERR_OPNOTSUPP_
Definition connection.h:68
#define MHD_ERR_BADF_
Definition connection.h:58
#define MHD_ERR_PIPE_
Definition connection.h:73
#define MHD_ERR_NOTCONN_
Definition connection.h:48
bool MHD_tls_connection_shutdown(struct MHD_Connection *connection)
bool MHD_run_tls_handshake_(struct MHD_Connection *connection)
Methods for managing connections.
#define MHD_HTTP_HEADER_CONTENT_LENGTH
Definition microhttpd.h:596
#define MHD_HTTP_HEADER_CONNECTION
Definition microhttpd.h:590
#define MHD_HTTP_HEADER_TRANSFER_ENCODING
Definition microhttpd.h:654
#define MHD_HTTP_HEADER_COOKIE
Definition microhttpd.h:754
#define MHD_HTTP_HEADER_EXPECT
Definition microhttpd.h:608
#define MHD_HTTP_HEADER_LOCATION
Definition microhttpd.h:628
#define MHD_HTTP_HEADER_HOST
Definition microhttpd.h:614
#define MHD_HTTP_INTERNAL_SERVER_ERROR
Definition microhttpd.h:453
#define MHD_HTTP_OK
Definition microhttpd.h:350
#define MHD_HTTP_MOVED_PERMANENTLY
Definition microhttpd.h:374
#define MHD_HTTP_URI_TOO_LONG
Definition microhttpd.h:419
#define MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE
Definition microhttpd.h:447
#define MHD_HTTP_PROCESSING
Definition microhttpd.h:345
#define MHD_HTTP_NOT_IMPLEMENTED
Definition microhttpd.h:455
#define MHD_HTTP_SWITCHING_PROTOCOLS
Definition microhttpd.h:343
#define MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED
Definition microhttpd.h:463
#define MHD_HTTP_CONTENT_TOO_LARGE
Definition microhttpd.h:417
#define MHD_HTTP_NOT_MODIFIED
Definition microhttpd.h:380
#define MHD_HTTP_NO_CONTENT
Definition microhttpd.h:358
#define MHD_HTTP_BAD_REQUEST
Definition microhttpd.h:391
#define MHD_HTTP_METHOD_TRACE
#define MHD_HTTP_METHOD_OPTIONS
#define MHD_HTTP_METHOD_GET
#define MHD_HTTP_METHOD_HEAD
#define MHD_HTTP_METHOD_POST
#define MHD_HTTP_METHOD_PUT
#define MHD_HTTP_METHOD_CONNECT
#define MHD_HTTP_METHOD_DELETE
_MHD_EXTERN enum MHD_Result MHD_set_connection_value(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key, const char *value)
enum MHD_Result(* MHD_KeyValueIterator)(void *cls, enum MHD_ValueKind kind, const char *key, const char *value)
_MHD_EXTERN const char * MHD_lookup_connection_value(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key)
enum MHD_Result(* MHD_KeyValueIteratorN)(void *cls, enum MHD_ValueKind kind, const char *key, size_t key_size, const char *value, size_t value_size)
_MHD_EXTERN int MHD_get_connection_values_n(struct MHD_Connection *connection, enum MHD_ValueKind kind, MHD_KeyValueIteratorN iterator, void *iterator_cls)
Definition connection.c:880
_MHD_EXTERN int MHD_get_connection_values(struct MHD_Connection *connection, enum MHD_ValueKind kind, MHD_KeyValueIterator iterator, void *iterator_cls)
Definition connection.c:841
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:933
_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:803
_MHD_EXTERN enum MHD_Result MHD_set_connection_value_n(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key, size_t key_size, const char *value, size_t value_size)
Definition connection.c:993
MHD_ConnectionInfoType
_MHD_EXTERN enum MHD_Result MHD_lookup_connection_value_n(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key, size_t key_size, const char **value_ptr, size_t *value_size_ptr)
#define _MHD_FIXED_ENUM
Definition microhttpd.h:154
MHD_RequestTerminationCode
@ MHD_CONNECTION_INFO_CONNECTION_TIMEOUT
@ MHD_CONNECTION_INFO_SOCKET_CONTEXT
@ MHD_CONNECTION_INFO_GNUTLS_SESSION
@ MHD_CONNECTION_INFO_REQUEST_HEADER_SIZE
@ MHD_CONNECTION_INFO_CIPHER_ALGO
@ MHD_CONNECTION_INFO_CONNECTION_SUSPENDED
@ MHD_CONNECTION_INFO_CLIENT_ADDRESS
@ MHD_CONNECTION_INFO_DAEMON
@ MHD_CONNECTION_INFO_GNUTLS_CLIENT_CERT
@ MHD_CONNECTION_INFO_HTTP_STATUS
@ MHD_CONNECTION_INFO_CONNECTION_FD
@ MHD_CONNECTION_INFO_PROTOCOL
@ MHD_REQUEST_TERMINATED_TIMEOUT_REACHED
@ MHD_REQUEST_TERMINATED_COMPLETED_OK
@ MHD_REQUEST_TERMINATED_WITH_ERROR
@ MHD_REQUEST_TERMINATED_READ_ERROR
@ MHD_REQUEST_TERMINATED_CLIENT_ABORT
_MHD_EXTERN enum MHD_Result MHD_queue_response(struct MHD_Connection *connection, unsigned int status_code, struct MHD_Response *response)
_MHD_EXTERN void MHD_destroy_response(struct MHD_Response *response)
Definition response.c:2289
_MHD_EXTERN struct MHD_Response * MHD_create_response_from_buffer_static(size_t size, const void *buffer)
Definition response.c:1507
_MHD_EXTERN enum MHD_Result MHD_set_connection_option(struct MHD_Connection *connection, enum MHD_CONNECTION_OPTION option,...)
#define MHD_ICY_FLAG
Definition microhttpd.h:556
_MHD_EXTERN const union MHD_ConnectionInfo * MHD_get_connection_info(struct MHD_Connection *connection, enum MHD_ConnectionInfoType info_type,...)
#define MHD_HTTP_VERSION_1_0
#define MHD_HTTP_VERSION_1_1
enum MHD_Result MHD_parse_arguments_(struct MHD_Connection *connection, enum MHD_ValueKind kind, char *args, MHD_ArgumentIterator_ cb, void *cls)
Definition internal.c:169
MHD internal shared structures.
MHD_CONNECTION_STATE
Definition internal.h:613
@ MHD_CONNECTION_BODY_RECEIVED
Definition internal.h:663
@ MHD_CONNECTION_CHUNKED_BODY_SENT
Definition internal.h:725
@ MHD_CONNECTION_REQ_HEADERS_RECEIVING
Definition internal.h:636
@ MHD_CONNECTION_BODY_RECEIVING
Definition internal.h:656
@ MHD_CONNECTION_HEADERS_SENDING
Definition internal.h:694
@ MHD_CONNECTION_FOOTERS_SENDING
Definition internal.h:730
@ MHD_CONNECTION_FOOTERS_RECEIVED
Definition internal.h:675
@ MHD_CONNECTION_FULL_REPLY_SENT
Definition internal.h:736
@ MHD_CONNECTION_HEADERS_SENT
Definition internal.h:699
@ MHD_CONNECTION_HEADERS_PROCESSED
Definition internal.h:646
@ MHD_CONNECTION_INIT
Definition internal.h:618
@ MHD_CONNECTION_CLOSED
Definition internal.h:741
@ MHD_CONNECTION_REQ_LINE_RECEIVED
Definition internal.h:631
@ MHD_CONNECTION_NORMAL_BODY_UNREADY
Definition internal.h:705
@ MHD_CONNECTION_HEADERS_RECEIVED
Definition internal.h:641
@ MHD_CONNECTION_NORMAL_BODY_READY
Definition internal.h:710
@ MHD_CONNECTION_START_REPLY
Definition internal.h:688
@ MHD_CONNECTION_FOOTERS_RECEIVING
Definition internal.h:668
@ MHD_CONNECTION_CHUNKED_BODY_READY
Definition internal.h:720
@ MHD_CONNECTION_FULL_REQ_RECEIVED
Definition internal.h:681
@ MHD_CONNECTION_CHUNKED_BODY_UNREADY
Definition internal.h:715
@ MHD_CONNECTION_CONTINUE_SENDING
Definition internal.h:651
@ MHD_CONNECTION_REQ_LINE_RECEIVING
Definition internal.h:624
#define XDLL_insert(head, tail, element)
Definition internal.h:2718
MHD_EpollState
Definition internal.h:169
@ MHD_EPOLL_STATE_SUSPENDED
Definition internal.h:202
@ MHD_EPOLL_STATE_IN_EREADY_EDLL
Definition internal.h:192
@ MHD_EPOLL_STATE_READ_READY
Definition internal.h:181
@ MHD_EPOLL_STATE_IN_EPOLL_SET
Definition internal.h:197
@ MHD_EPOLL_STATE_WRITE_READY
Definition internal.h:187
@ MHD_TLS_CONN_TLS_CLOSING
Definition internal.h:766
@ MHD_TLS_CONN_WR_CLOSING
Definition internal.h:764
@ MHD_TLS_CONN_INVALID_STATE
Definition internal.h:769
@ MHD_TLS_CONN_WR_CLOSED
Definition internal.h:765
@ MHD_TLS_CONN_NO_TLS
Definition internal.h:760
@ MHD_TLS_CONN_INIT
Definition internal.h:761
@ MHD_TLS_CONN_TLS_CLOSED
Definition internal.h:767
@ MHD_TLS_CONN_TLS_FAILED
Definition internal.h:768
@ MHD_TLS_CONN_CONNECTED
Definition internal.h:763
@ MHD_TLS_CONN_HANDSHAKING
Definition internal.h:762
#define DLL_insert(head, tail, element)
Definition internal.h:2671
@ MHD_EVENT_LOOP_INFO_PROCESS_READ
Definition internal.h:235
@ MHD_EVENT_LOOP_INFO_PROCESS
Definition internal.h:229
@ MHD_EVENT_LOOP_INFO_READ
Definition internal.h:219
@ MHD_EVENT_LOOP_INFO_WRITE
Definition internal.h:224
@ MHD_EVENT_LOOP_INFO_CLEANUP
Definition internal.h:241
#define EDLL_insert(head, tail, element)
Definition internal.h:2765
struct MHD_IoVec MHD_iovec_
Definition internal.h:440
#define MHD_MIN(a, b)
Definition internal.h:130
#define MHD_IS_HTTP_VER_SUPPORTED(ver)
Definition internal.h:881
_MHD_static_inline struct MHD_Daemon * MHD_get_master(struct MHD_Daemon *const daemon)
Definition internal.h:2913
@ MHD_RAF_HAS_DATE_HDR
Definition internal.h:411
@ MHD_RAF_HAS_CONTENT_LENGTH
Definition internal.h:410
@ MHD_RAF_HAS_CONNECTION_CLOSE
Definition internal.h:408
@ MHD_RAF_HAS_TRANS_ENC_CHUNKED
Definition internal.h:409
@ MHD_RAF_HAS_CONNECTION_HDR
Definition internal.h:407
@ MHD_HTTP_VER_1_0
Definition internal.h:860
@ MHD_HTTP_VER_1_1
Definition internal.h:865
@ MHD_HTTP_VER_TOO_OLD
Definition internal.h:855
@ MHD_HTTP_VER_INVALID
Definition internal.h:845
@ MHD_HTTP_VER_UNKNOWN
Definition internal.h:850
@ MHD_HTTP_VER_1_2__1_9
Definition internal.h:870
@ MHD_HTTP_VER_FUTURE
Definition internal.h:875
MHD_ConnKeepAlive
Definition internal.h:818
@ MHD_CONN_USE_KEEPALIVE
Definition internal.h:832
@ MHD_CONN_MUST_UPGRADE
Definition internal.h:837
@ MHD_CONN_MUST_CLOSE
Definition internal.h:822
@ MHD_CONN_KEEPALIVE_UNKOWN
Definition internal.h:827
#define MHD_IS_HTTP_VER_1_1_COMPAT(ver)
Definition internal.h:890
#define MHD_BUF_INC_SIZE
Definition internal.h:142
@ MHD_HTTP_MTHD_GET
Definition internal.h:907
@ MHD_HTTP_MTHD_CONNECT
Definition internal.h:927
@ MHD_HTTP_MTHD_DELETE
Definition internal.h:923
@ MHD_HTTP_MTHD_OPTIONS
Definition internal.h:931
@ MHD_HTTP_MTHD_TRACE
Definition internal.h:935
@ MHD_HTTP_MTHD_HEAD
Definition internal.h:911
@ MHD_HTTP_MTHD_POST
Definition internal.h:915
@ MHD_HTTP_MTHD_OTHER
Definition internal.h:939
@ MHD_HTTP_MTHD_NO_METHOD
Definition internal.h:903
@ MHD_HTTP_MTHD_PUT
Definition internal.h:919
#define EDLL_remove(head, tail, element)
Definition internal.h:2785
#define PRIu64
Definition internal.h:53
#define XDLL_remove(head, tail, element)
Definition internal.h:2740
#define MHD_D_IS_USING_THREAD_PER_CONN_(d)
Definition internal.h:2591
#define DLL_remove(head, tail, element)
Definition internal.h:2693
#define MHD_D_IS_USING_THREADS_(d)
Definition internal.h:2587
#define MHD_D_IS_USING_EPOLL_(d)
Definition internal.h:2563
void * MHD_pool_reallocate(struct MemoryPool *pool, void *old, size_t old_size, size_t new_size)
Definition memorypool.c:533
void MHD_pool_destroy(struct MemoryPool *pool)
Definition memorypool.c:341
bool MHD_pool_is_resizable_inplace(struct MemoryPool *pool, void *block, size_t block_size)
Definition memorypool.c:440
void MHD_pool_deallocate(struct MemoryPool *pool, void *block, size_t block_size)
Definition memorypool.c:625
void * MHD_pool_try_alloc(struct MemoryPool *pool, size_t size, size_t *required_bytes)
Definition memorypool.c:481
size_t MHD_pool_get_free(struct MemoryPool *pool)
Definition memorypool.c:374
void * MHD_pool_reset(struct MemoryPool *pool, void *keep, size_t copy_bytes, size_t new_size)
Definition memorypool.c:729
void * MHD_pool_allocate(struct MemoryPool *pool, size_t size, bool from_end)
Definition memorypool.c:399
memory pool; mostly used for efficient (de)allocation for each connection and bounding memory use for...
macros for mhd_assert()
#define mhd_assert(ignore)
Definition mhd_assert.h:45
Header for platform missing functions.
Header for platform-independent inter-thread communication.
#define MHD_PANIC(msg)
Definition mhd_itc.h:45
limits values definitions
#define SSIZE_MAX
Definition mhd_limits.h:121
#define UINT64_MAX
Definition mhd_limits.h:93
#define SIZE_MAX
Definition mhd_limits.h:111
#define UINT_MAX
Definition mhd_limits.h:53
Header for platform-independent locks abstraction.
#define MHD_mutex_unlock_chk_(ignore)
Definition mhd_locks.h:198
#define MHD_mutex_lock_chk_(ignore)
Definition mhd_locks.h:196
#define NULL
uint64_t MHD_monotonic_msec_counter(void)
internal monotonic clock functions implementations
#define _(String)
Definition mhd_options.h:42
#define _MHD_EXTERN
Definition mhd_options.h:53
#define MHD_FUNC_
ssize_t MHD_send_hdr_and_body_(struct MHD_Connection *connection, const char *header, size_t header_size, bool never_push_hdr, const char *body, size_t body_size, bool complete_response)
Definition mhd_send.c:905
ssize_t MHD_send_iovec_(struct MHD_Connection *connection, struct MHD_iovec_track_ *const r_iov, bool push_data)
Definition mhd_send.c:1622
ssize_t MHD_send_data_(struct MHD_Connection *connection, const char *buffer, size_t buffer_size, bool push_data)
Definition mhd_send.c:753
Declarations of send() wrappers.
#define MHD_SCKT_ERR_IS_(err, code)
#define MHD_SCKT_ERR_IS_EAGAIN_(err)
#define MHD_SCKT_ERR_IS_LOW_RESOURCES_(err)
#define MHD_socket_last_strerr_()
#define MHD_SCKT_EOPNOTSUPP_
#define MHD_SCKT_EBADF_
#define MHD_socket_get_error_()
#define MHD_SCKT_ERR_IS_REMOTE_DISCNN_(err)
#define MHD_SCKT_EINVAL_
#define MHD_SCKT_ERR_IS_EINTR_(err)
#define MHD_SCKT_SEND_MAX_SIZE_
#define MHD_SCKT_ENOTCONN_
#define MHD_recv_(s, b, l)
#define MHD_SEND_SPIPE_SUPPRESS_NEEDED
int MHD_str_equal_caseless_(const char *str1, const char *str2)
Definition mhd_str.c:683
size_t MHD_uint8_to_str_pad(uint8_t val, uint8_t min_digits, char *buf, size_t buf_size)
Definition mhd_str.c:1629
size_t MHD_uint16_to_str(uint16_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1550
size_t MHD_str_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition mhd_str.c:1238
size_t MHD_uint64_to_str(uint64_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1591
size_t MHD_strx_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition mhd_str.c:1415
int MHD_str_equal_caseless_n_(const char *const str1, const char *const str2, size_t maxlen)
Definition mhd_str.c:717
bool MHD_str_has_token_caseless_(const char *str, const char *const token, size_t token_len)
Definition mhd_str.c:782
bool MHD_str_equal_caseless_bin_n_(const char *const str1, const char *const str2, size_t len)
Definition mhd_str.c:749
size_t MHD_uint32_to_strx(uint32_t val, char *buf, size_t buf_size)
Definition mhd_str.c:1516
Header for string manipulating helpers.
#define MHD_str_has_s_token_caseless_(str, tkn)
Definition mhd_str.h:154
#define MHD_STATICSTR_LEN_(macro)
#define MHD_thread_handle_ID_is_current_thread_(hndl_id)
#define MHD_SIZE_UNKNOWN
Definition microhttpd.h:183
MHD_Result
Definition microhttpd.h:163
@ MHD_YES
Definition microhttpd.h:172
@ MHD_NO
Definition microhttpd.h:167
#define MHD_CONTENT_READER_END_OF_STREAM
Definition microhttpd.h:186
_MHD_EXTERN size_t MHD_get_reason_phrase_len_for(unsigned int code)
#define MHD_INVALID_SOCKET
Definition microhttpd.h:207
_MHD_EXTERN const char * MHD_get_reason_phrase_for(unsigned int code)
#define MHD_CONTENT_READER_END_WITH_ERROR
Definition microhttpd.h:187
MHD_ValueKind
@ MHD_FOOTER_KIND
@ MHD_COOKIE_KIND
@ MHD_HEADER_KIND
@ MHD_GET_ARGUMENT_KIND
@ MHD_USE_TURBO
@ MHD_USE_SUPPRESS_DATE_NO_CLOCK
@ MHD_USE_TLS
@ MHD_ALLOW_UPGRADE
@ MHD_USE_ERROR_LOG
@ MHD_RF_SEND_KEEP_ALIVE_HEADER
@ MHD_RF_HEAD_ONLY_RESPONSE
@ MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
@ MHD_RF_HTTP_1_0_SERVER
@ MHD_RF_INSANITY_HEADER_CONTENT_LENGTH
MHD_CONNECTION_OPTION
@ MHD_CONNECTION_OPTION_TIMEOUT
bool MHD_add_response_entry_no_alloc_(struct MHD_Response *response, enum MHD_ValueKind kind, char *header, size_t header_len, char *content, size_t content_len)
Definition response.c:167
void MHD_increment_response_rc(struct MHD_Response *response)
Definition response.c:2335
Methods for managing response objects.
enum MHD_Result MHD_response_execute_upgrade_(struct MHD_Response *response, struct MHD_Connection *connection)
MHD_socket socket_fd
Definition internal.h:1489
size_t write_buffer_size
Definition internal.h:1447
size_t write_buffer_send_offset
Definition internal.h:1452
socklen_t addr_len
Definition internal.h:1469
enum MHD_ConnectionEventLoopInfo event_loop_info
Definition internal.h:1576
size_t write_buffer_append_offset
Definition internal.h:1458
char * write_buffer
Definition internal.h:1415
bool stop_with_error
Definition internal.h:1532
bool discard_request
Definition internal.h:1541
ReceiveCallback recv_cls
Definition internal.h:1581
volatile bool resuming
Definition internal.h:1627
void * socket_context
Definition internal.h:1395
uint64_t last_activity
Definition internal.h:1475
enum MHD_ConnKeepAlive keepalive
Definition internal.h:1402
struct MHD_Request rq
Definition internal.h:1371
union MHD_ConnectionInfo connection_info_dummy
Definition internal.h:1632
size_t continue_message_write_offset
Definition internal.h:1464
size_t read_buffer_offset
Definition internal.h:1442
struct MHD_Reply rp
Definition internal.h:1376
struct MemoryPool * pool
Definition internal.h:1386
enum MHD_CONNECTION_STATE state
Definition internal.h:1571
char * read_buffer
Definition internal.h:1409
struct MHD_Daemon * daemon
Definition internal.h:1366
bool in_access_handler
Definition internal.h:1622
bool sk_spipe_suppress
Definition internal.h:1505
uint64_t connection_timeout_ms
Definition internal.h:1482
struct sockaddr_storage * addr
Definition internal.h:1421
size_t read_buffer_size
Definition internal.h:1436
size_t pool_size
Definition internal.h:2142
MHD_AccessHandlerCallback default_handler
Definition internal.h:1873
LogCallback uri_log_callback
Definition internal.h:2063
struct MHD_Connection * normal_timeout_tail
Definition internal.h:2006
int client_discipline
Definition internal.h:2271
void * unescape_callback_cls
Definition internal.h:2078
struct MHD_Connection * connections_tail
Definition internal.h:1906
struct MHD_Connection * suspended_connections_tail
Definition internal.h:1916
struct MHD_Connection * manual_timeout_head
Definition internal.h:2014
struct MHD_Connection * normal_timeout_head
Definition internal.h:1999
MHD_RequestCompletedCallback notify_completed
Definition internal.h:2038
struct MHD_Connection * cleanup_head
Definition internal.h:1921
struct MHD_itc_ itc
Definition internal.h:2204
uint64_t connection_timeout_ms
Definition internal.h:2259
struct MHD_Connection * manual_timeout_tail
Definition internal.h:2021
volatile bool shutdown
Definition internal.h:2209
enum MHD_FLAG options
Definition internal.h:1886
struct MHD_Connection * cleanup_tail
Definition internal.h:1926
bool sigpipe_blocked
Definition internal.h:2299
UnescapeCallback unescape_callback
Definition internal.h:2073
void * notify_completed_cls
Definition internal.h:2043
struct MHD_Connection * suspended_connections_head
Definition internal.h:1911
int allow_bzero_in_url
Definition internal.h:2278
void * default_handler_cls
Definition internal.h:1878
struct MHD_Connection * connections_head
Definition internal.h:1901
size_t pool_increment
Definition internal.h:2147
void * uri_log_callback_cls
Definition internal.h:2068
enum MHD_ValueKind kind
Definition internal.h:396
const char * value
Definition internal.h:386
struct MHD_HTTP_Req_Header * next
Definition internal.h:366
const char * header
Definition internal.h:376
struct MHD_HTTP_Res_Header * next
Definition internal.h:323
enum MHD_ValueKind kind
Definition internal.h:353
uint64_t rsp_write_position
Definition internal.h:1301
bool responseIcy
Definition internal.h:1294
struct MHD_iovec_track_ resp_iov
Definition internal.h:1309
struct MHD_Response * response
Definition internal.h:1282
unsigned int responseCode
Definition internal.h:1288
struct MHD_Reply_Properties props
Definition internal.h:1318
unsigned int skipped_empty_lines
Definition internal.h:955
struct MHD_HTTP_Req_Header * headers_received
Definition internal.h:1117
union MHD_HeadersProcessing hdrs
Definition internal.h:1247
uint64_t current_chunk_size
Definition internal.h:1167
uint64_t current_chunk_offset
Definition internal.h:1173
bool some_payload_processed
Definition internal.h:1190
size_t skipped_broken_lines
Definition internal.h:1242
void * client_context
Definition internal.h:1198
const char * url
Definition internal.h:1096
size_t url_len
Definition internal.h:1101
const char * version
Definition internal.h:1075
size_t num_cr_sp_replaced
Definition internal.h:1237
enum MHD_HTTP_Version http_ver
Definition internal.h:1080
size_t req_target_len
Definition internal.h:1106
const char * method
Definition internal.h:1085
size_t header_size
Definition internal.h:1130
union MHD_StartOrSize field_lines
Definition internal.h:1140
struct MHD_HTTP_Req_Header * headers_received_tail
Definition internal.h:1122
enum MHD_HTTP_Method http_mthd
Definition internal.h:1090
bool client_aware
Definition internal.h:1205
bool have_chunked_upload
Definition internal.h:1158
const char * url_for_callback
Definition internal.h:1112
uint64_t remaining_upload_size
Definition internal.h:1146
size_t data_buffer_size
Definition internal.h:557
MHD_iovec_ * data_iov
Definition internal.h:588
const char * data
Definition internal.h:489
uint64_t data_start
Definition internal.h:541
MHD_ContentReaderCallback crc
Definition internal.h:501
enum MHD_ResponseAutoFlags flags_auto
Definition internal.h:578
unsigned int data_iovcnt
Definition internal.h:593
size_t data_size
Definition internal.h:552
enum MHD_ResponseFlags flags
Definition internal.h:573
void * crc_cls
Definition internal.h:495
struct MHD_HTTP_Res_Header * first_header
Definition internal.h:478
uint64_t total_size
Definition internal.h:535
MHD_iovec_ * iov
Definition internal.h:453
const char * str
unsigned int connection_timeout
struct MHD_Daemon * daemon
unsigned int http_status
struct sockaddr * client_addr
MHD_socket connect_fd
struct MHD_RequestLineProcessing rq_line
Definition internal.h:1036
struct MHD_HeaderProcessing hdr
Definition internal.h:1041
const char * start
Definition internal.h:1055