GNU libmicrohttpd  1.0.1
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 
364 #ifdef HAVE_MESSAGES
365 #define ERR_RSP_INVALID_CHR_IN_HEADER \
366  "<html>" \
367  "<head><title>Request broken</title></head>" \
368  "<body>HTTP request has invalid character in header.</body>" \
369  "</html>"
370 #else
371 #define ERR_RSP_INVALID_CHR_IN_HEADER ""
372 #endif
373 
377 #ifdef HAVE_MESSAGES
378 #define ERR_RSP_INVALID_CHR_IN_FOOTER \
379  "<html>" \
380  "<head><title>Request broken</title></head>" \
381  "<body>HTTP request has invalid character in footer.</body>" \
382  "</html>"
383 #else
384 #define ERR_RSP_INVALID_CHR_IN_FOOTER ""
385 #endif
386 
390 #ifdef HAVE_MESSAGES
391 #define ERR_RSP_HEADER_WITHOUT_COLON \
392  "<html>" \
393  "<head><title>Request broken</title></head>" \
394  "<body>HTTP request header line has no colon character.</body>" \
395  "</html>"
396 #else
397 #define ERR_RSP_HEADER_WITHOUT_COLON ""
398 #endif
399 
403 #ifdef HAVE_MESSAGES
404 #define ERR_RSP_FOOTER_WITHOUT_COLON \
405  "<html>" \
406  "<head><title>Request broken</title></head>" \
407  "<body>HTTP request footer line has no colon character.</body>" \
408  "</html>"
409 #else
410 #define ERR_RSP_FOOTER_WITHOUT_COLON ""
411 #endif
412 
416 #ifdef HAVE_MESSAGES
417 #define ERR_RSP_EMPTY_HEADER_NAME \
418  "<html>" \
419  "<head><title>Request broken</title></head>" \
420  "<body>HTTP request header has empty header name.</body>" \
421  "</html>"
422 #else
423 #define ERR_RSP_EMPTY_HEADER_NAME ""
424 #endif
425 
429 #ifdef HAVE_MESSAGES
430 #define ERR_RSP_EMPTY_FOOTER_NAME \
431  "<html>" \
432  "<head><title>Request broken</title></head>" \
433  "<body>HTTP request footer has empty footer name.</body>" \
434  "</html>"
435 #else
436 #define ERR_RSP_EMPTY_FOOTER_NAME ""
437 #endif
438 
446 #ifdef HAVE_MESSAGES
447 #define REQUEST_LACKS_HOST \
448  "<html>" \
449  "<head><title>&quot;Host:&quot; header required</title></head>" \
450  "<body>HTTP/1.1 request without <b>&quot;Host:&quot;</b>.</body>" \
451  "</html>"
452 
453 #else
454 #define REQUEST_LACKS_HOST ""
455 #endif
456 
460 #ifdef HAVE_MESSAGES
461 #define REQUEST_UNSUPPORTED_TR_ENCODING \
462  "<html>" \
463  "<head><title>Unsupported Transfer-Encoding</title></head>" \
464  "<body>The Transfer-Encoding used in request is not supported.</body>" \
465  "</html>"
466 #else
467 #define REQUEST_UNSUPPORTED_TR_ENCODING ""
468 #endif
469 
474 #ifdef HAVE_MESSAGES
475 #define REQUEST_LENGTH_WITH_TR_ENCODING \
476  "<html>" \
477  "<head><title>Malformed request</title></head>" \
478  "<body>Wrong combination of the request headers: both Transfer-Encoding " \
479  "and Content-Length headers are used at the same time.</body>" \
480  "</html>"
481 #else
482 #define REQUEST_LENGTH_WITH_TR_ENCODING ""
483 #endif
484 
492 #ifdef HAVE_MESSAGES
493 #define REQUEST_MALFORMED \
494  "<html><head><title>Request malformed</title></head>" \
495  "<body>HTTP request is syntactically incorrect.</body></html>"
496 #else
497 #define REQUEST_MALFORMED ""
498 #endif
499 
504 #ifdef HAVE_MESSAGES
505 #define REQUEST_CHUNKED_MALFORMED \
506  "<html><head><title>Request malformed</title></head>" \
507  "<body>HTTP chunked encoding is syntactically incorrect.</body></html>"
508 #else
509 #define REQUEST_CHUNKED_MALFORMED ""
510 #endif
511 
515 #ifdef HAVE_MESSAGES
516 #define REQUEST_CHUNK_TOO_LARGE \
517  "<html><head><title>Request content too large</title></head>" \
518  "<body>The chunk size used in HTTP chunked encoded " \
519  "request is too large.</body></html>"
520 #else
521 #define REQUEST_CHUNK_TOO_LARGE ""
522 #endif
523 
527 #ifdef HAVE_MESSAGES
528 #define REQUEST_CONTENTLENGTH_TOOLARGE \
529  "<html><head><title>Request content too large</title></head>" \
530  "<body>HTTP request has too large value for " \
531  "<b>Content-Length</b> header.</body></html>"
532 #else
533 #define REQUEST_CONTENTLENGTH_TOOLARGE ""
534 #endif
535 
540 #ifdef HAVE_MESSAGES
541 #define REQUEST_CONTENTLENGTH_MALFORMED \
542  "<html><head><title>Request malformed</title></head>" \
543  "<body>HTTP request has wrong value for " \
544  "<b>Content-Length</b> header.</body></html>"
545 #else
546 #define REQUEST_CONTENTLENGTH_MALFORMED ""
547 #endif
548 
555 #ifdef HAVE_MESSAGES
556 #define ERROR_MSG_DATA_NOT_HANDLED_BY_APP \
557  "<html><head><title>Internal server error</title></head>" \
558  "<body>Please ask the developer of this Web server to carefully " \
559  "read the GNU libmicrohttpd documentation about connection " \
560  "management and blocking.</body></html>"
561 #else
562 #define ERROR_MSG_DATA_NOT_HANDLED_BY_APP ""
563 #endif
564 
568 #ifdef HAVE_MESSAGES
569 #define REQ_HTTP_VER_IS_TOO_OLD \
570  "<html><head><title>Requested HTTP version is not supported</title></head>" \
571  "<body>Requested HTTP version is too old and not " \
572  "supported.</body></html>"
573 #else
574 #define REQ_HTTP_VER_IS_TOO_OLD ""
575 #endif
576 
580 #ifdef HAVE_MESSAGES
581 #define REQ_HTTP_VER_IS_NOT_SUPPORTED \
582  "<html><head><title>Requested HTTP version is not supported</title></head>" \
583  "<body>Requested HTTP version is not supported.</body></html>"
584 #else
585 #define REQ_HTTP_VER_IS_NOT_SUPPORTED ""
586 #endif
587 
588 
592 #define MHD_SENFILE_CHUNK_ (0x20000)
593 
597 #define MHD_SENFILE_CHUNK_THR_P_C_ (0x200000)
598 
599 #ifdef HAVE_MESSAGES
605 static const char *
606 str_conn_error_ (ssize_t mhd_err_code)
607 {
608  switch (mhd_err_code)
609  {
610  case MHD_ERR_AGAIN_:
611  return _ ("The operation would block, retry later");
612  case MHD_ERR_CONNRESET_:
613  return _ ("The connection was forcibly closed by remote peer");
614  case MHD_ERR_NOTCONN_:
615  return _ ("The socket is not connected");
616  case MHD_ERR_NOMEM_:
617  return _ ("Not enough system resources to serve the request");
618  case MHD_ERR_BADF_:
619  return _ ("Bad FD value");
620  case MHD_ERR_INVAL_:
621  return _ ("Argument value is invalid");
622  case MHD_ERR_OPNOTSUPP_:
623  return _ ("Argument value is not supported");
624  case MHD_ERR_PIPE_:
625  return _ ("The socket is no longer available for sending");
626  case MHD_ERR_TLS_:
627  return _ ("TLS encryption or decryption error");
628  default:
629  break; /* Mute compiler warning */
630  }
631  if (0 <= mhd_err_code)
632  return _ ("Not an error code");
633 
634  mhd_assert (0); /* Should never be reachable */
635  return _ ("Wrong error code value");
636 }
637 
638 
639 #endif /* HAVE_MESSAGES */
640 
650 void *
652  size_t size)
653 {
654  struct MHD_Connection *const c = connection; /* a short alias */
655  struct MemoryPool *const pool = c->pool; /* a short alias */
656  size_t need_to_be_freed = 0;
657  void *res;
658 
659  res = MHD_pool_try_alloc (pool,
660  size,
661  &need_to_be_freed);
662  if (NULL != res)
663  return res;
664 
666  c->write_buffer,
667  c->write_buffer_size))
668  {
670  need_to_be_freed)
671  {
672  char *buf;
673  const size_t new_buf_size = c->write_buffer_size - need_to_be_freed;
674  buf = MHD_pool_reallocate (pool,
675  c->write_buffer,
677  new_buf_size);
678  mhd_assert (c->write_buffer == buf);
679  mhd_assert (c->write_buffer_append_offset <= new_buf_size);
680  mhd_assert (c->write_buffer_send_offset <= new_buf_size);
681  c->write_buffer_size = new_buf_size;
682  c->write_buffer = buf;
683  }
684  else
685  return NULL;
686  }
687  else if (MHD_pool_is_resizable_inplace (pool,
688  c->read_buffer,
689  c->read_buffer_size))
690  {
691  if (c->read_buffer_size - c->read_buffer_offset >= need_to_be_freed)
692  {
693  char *buf;
694  const size_t new_buf_size = c->read_buffer_size - need_to_be_freed;
695  buf = MHD_pool_reallocate (pool,
696  c->read_buffer,
697  c->read_buffer_size,
698  new_buf_size);
699  mhd_assert (c->read_buffer == buf);
700  mhd_assert (c->read_buffer_offset <= new_buf_size);
701  c->read_buffer_size = new_buf_size;
702  c->read_buffer = buf;
703  }
704  else
705  return NULL;
706  }
707  else
708  return NULL;
709  res = MHD_pool_allocate (pool, size, true);
710  mhd_assert (NULL != res); /* It has been checked that pool has enough space */
711  return res;
712 }
713 
714 
724 static ssize_t
725 recv_param_adapter (struct MHD_Connection *connection,
726  void *other,
727  size_t i)
728 {
729  ssize_t ret;
730 
731  if ( (MHD_INVALID_SOCKET == connection->socket_fd) ||
732  (MHD_CONNECTION_CLOSED == connection->state) )
733  {
734  return MHD_ERR_NOTCONN_;
735  }
736  if (i > MHD_SCKT_SEND_MAX_SIZE_)
737  i = MHD_SCKT_SEND_MAX_SIZE_; /* return value limit */
738 
739  ret = MHD_recv_ (connection->socket_fd,
740  other,
741  i);
742  if (0 > ret)
743  {
744  const int err = MHD_socket_get_error_ ();
745  if (MHD_SCKT_ERR_IS_EAGAIN_ (err))
746  {
747 #ifdef EPOLL_SUPPORT
748  /* Got EAGAIN --- no longer read-ready */
749  connection->epoll_state &=
751 #endif /* EPOLL_SUPPORT */
752  return MHD_ERR_AGAIN_;
753  }
754  if (MHD_SCKT_ERR_IS_EINTR_ (err))
755  return MHD_ERR_AGAIN_;
757  return MHD_ERR_CONNRESET_;
759  return MHD_ERR_OPNOTSUPP_;
761  return MHD_ERR_NOTCONN_;
763  return MHD_ERR_INVAL_;
765  return MHD_ERR_NOMEM_;
767  return MHD_ERR_BADF_;
768  /* Treat any other error as a hard error. */
769  return MHD_ERR_NOTCONN_;
770  }
771 #ifdef EPOLL_SUPPORT
772  else if (i > (size_t) ret)
773  connection->epoll_state &=
775 #endif /* EPOLL_SUPPORT */
776  return ret;
777 }
778 
779 
792 _MHD_EXTERN int
794  enum MHD_ValueKind kind,
795  MHD_KeyValueIterator iterator,
796  void *iterator_cls)
797 {
798  int ret;
799  struct MHD_HTTP_Req_Header *pos;
800 
801  if (NULL == connection)
802  return -1;
803  ret = 0;
804  for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
805  if (0 != (pos->kind & kind))
806  {
807  ret++;
808  if ( (NULL != iterator) &&
809  (MHD_NO == iterator (iterator_cls,
810  pos->kind,
811  pos->header,
812  pos->value)) )
813  return ret;
814  }
815  return ret;
816 }
817 
818 
831 _MHD_EXTERN int
833  enum MHD_ValueKind kind,
834  MHD_KeyValueIteratorN iterator,
835  void *iterator_cls)
836 {
837  int ret;
838  struct MHD_HTTP_Req_Header *pos;
839 
840  if (NULL == connection)
841  return -1;
842  ret = 0;
843 
844  if (NULL == iterator)
845  for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
846  {
847  if (0 != (kind & pos->kind))
848  ret++;
849  }
850  else
851  for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
852  if (0 != (kind & pos->kind))
853  {
854  ret++;
855  if (MHD_NO == iterator (iterator_cls,
856  pos->kind,
857  pos->header,
858  pos->header_size,
859  pos->value,
860  pos->value_size))
861  return ret;
862  }
863  return ret;
864 }
865 
866 
884 static enum MHD_Result
886  enum MHD_ValueKind kind,
887  const char *key,
888  size_t key_size,
889  const char *value,
890  size_t value_size)
891 {
892  struct MHD_HTTP_Req_Header *pos;
893 
894  pos = MHD_connection_alloc_memory_ (connection,
895  sizeof (struct MHD_HTTP_Res_Header));
896  if (NULL == pos)
897  return MHD_NO;
898  pos->header = key;
899  pos->header_size = key_size;
900  pos->value = value;
901  pos->value_size = value_size;
902  pos->kind = kind;
903  pos->next = NULL;
904  /* append 'pos' to the linked list of headers */
905  if (NULL == connection->rq.headers_received_tail)
906  {
907  connection->rq.headers_received = pos;
908  connection->rq.headers_received_tail = pos;
909  }
910  else
911  {
912  connection->rq.headers_received_tail->next = pos;
913  connection->rq.headers_received_tail = pos;
914  }
915  return MHD_YES;
916 }
917 
918 
945 MHD_set_connection_value_n (struct MHD_Connection *connection,
946  enum MHD_ValueKind kind,
947  const char *key,
948  size_t key_size,
949  const char *value,
950  size_t value_size)
951 {
952  if ( (MHD_GET_ARGUMENT_KIND != kind) &&
953  ( ((key ? strlen (key) : 0) != key_size) ||
954  ((value ? strlen (value) : 0) != value_size) ) )
955  return MHD_NO; /* binary zero is allowed only in GET arguments */
956 
957  return MHD_set_connection_value_n_nocheck_ (connection,
958  kind,
959  key,
960  key_size,
961  value,
962  value_size);
963 }
964 
965 
992 MHD_set_connection_value (struct MHD_Connection *connection,
993  enum MHD_ValueKind kind,
994  const char *key,
995  const char *value)
996 {
997  return MHD_set_connection_value_n_nocheck_ (connection,
998  kind,
999  key,
1000  NULL != key
1001  ? strlen (key)
1002  : 0,
1003  value,
1004  NULL != value
1005  ? strlen (value)
1006  : 0);
1007 }
1008 
1009 
1020 _MHD_EXTERN const char *
1022  enum MHD_ValueKind kind,
1023  const char *key)
1024 {
1025  const char *value;
1026 
1027  value = NULL;
1028  (void) MHD_lookup_connection_value_n (connection,
1029  kind,
1030  key,
1031  (NULL == key) ? 0 : strlen (key),
1032  &value,
1033  NULL);
1034  return value;
1035 }
1036 
1037 
1058 MHD_lookup_connection_value_n (struct MHD_Connection *connection,
1059  enum MHD_ValueKind kind,
1060  const char *key,
1061  size_t key_size,
1062  const char **value_ptr,
1063  size_t *value_size_ptr)
1064 {
1065  struct MHD_HTTP_Req_Header *pos;
1066 
1067  if (NULL == connection)
1068  return MHD_NO;
1069 
1070  if (NULL == key)
1071  {
1072  for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1073  {
1074  if ( (0 != (kind & pos->kind)) &&
1075  (NULL == pos->header) )
1076  break;
1077  }
1078  }
1079  else
1080  {
1081  for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1082  {
1083  if ( (0 != (kind & pos->kind)) &&
1084  (key_size == pos->header_size) &&
1085  ( (key == pos->header) ||
1087  pos->header,
1088  key_size) ) ) )
1089  break;
1090  }
1091  }
1092 
1093  if (NULL == pos)
1094  return MHD_NO;
1095 
1096  if (NULL != value_ptr)
1097  *value_ptr = pos->value;
1098 
1099  if (NULL != value_size_ptr)
1100  *value_size_ptr = pos->value_size;
1101 
1102  return MHD_YES;
1103 }
1104 
1105 
1121 static bool
1122 MHD_lookup_header_token_ci (const struct MHD_Connection *connection,
1123  const char *header,
1124  size_t header_len,
1125  const char *token,
1126  size_t token_len)
1127 {
1128  struct MHD_HTTP_Req_Header *pos;
1129 
1130  if ((NULL == connection) || (NULL == header) || (0 == header[0]) ||
1131  (NULL == token) || (0 == token[0]))
1132  return false;
1133 
1134  for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
1135  {
1136  if ((0 != (pos->kind & MHD_HEADER_KIND)) &&
1137  (header_len == pos->header_size) &&
1138  ( (header == pos->header) ||
1140  pos->header,
1141  header_len)) ) &&
1142  (MHD_str_has_token_caseless_ (pos->value, token, token_len)))
1143  return true;
1144  }
1145  return false;
1146 }
1147 
1148 
1160 #define MHD_lookup_header_s_token_ci(c,h,tkn) \
1161  MHD_lookup_header_token_ci ((c),(h),MHD_STATICSTR_LEN_ (h), \
1162  (tkn),MHD_STATICSTR_LEN_ (tkn))
1163 
1164 
1172 static bool
1173 need_100_continue (struct MHD_Connection *connection)
1174 {
1175  const char *expect;
1176 
1177  if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
1178  return false;
1179 
1180  if (0 == connection->rq.remaining_upload_size)
1181  return false;
1182 
1183  if (MHD_NO ==
1184  MHD_lookup_connection_value_n (connection,
1187  MHD_STATICSTR_LEN_ ( \
1189  &expect,
1190  NULL))
1191  return false;
1192 
1193  if (MHD_str_equal_caseless_ (expect,
1194  "100-continue"))
1195  return true;
1196 
1197  return false;
1198 }
1199 
1200 
1207 void
1209 {
1210  const struct MHD_Daemon *daemon = connection->daemon;
1211 
1212  if (0 == (daemon->options & MHD_USE_TURBO))
1213  {
1214 #ifdef HTTPS_SUPPORT
1215  /* For TLS connection use shutdown of TLS layer
1216  * and do not shutdown TCP socket. This give more
1217  * chances to send TLS closure data to remote side.
1218  * Closure of TLS layer will be interpreted by
1219  * remote side as end of transmission. */
1220  if (0 != (daemon->options & MHD_USE_TLS))
1221  {
1222  if (! MHD_tls_connection_shutdown (connection))
1223  shutdown (connection->socket_fd,
1224  SHUT_WR);
1225  }
1226  else /* Combined with next 'shutdown()'. */
1227 #endif /* HTTPS_SUPPORT */
1228  shutdown (connection->socket_fd,
1229  SHUT_WR);
1230  }
1231  connection->state = MHD_CONNECTION_CLOSED;
1233 }
1234 
1235 
1245 void
1247  enum MHD_RequestTerminationCode termination_code)
1248 {
1249  struct MHD_Daemon *daemon = connection->daemon;
1250  struct MHD_Response *resp = connection->rp.response;
1251 
1252  mhd_assert (! connection->suspended);
1253 #ifdef MHD_USE_THREADS
1254  mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
1255  MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
1256 #endif /* MHD_USE_THREADS */
1257  if ( (NULL != daemon->notify_completed) &&
1258  (connection->rq.client_aware) )
1259  daemon->notify_completed (daemon->notify_completed_cls,
1260  connection,
1261  &connection->rq.client_context,
1262  termination_code);
1263  connection->rq.client_aware = false;
1264  if (NULL != resp)
1265  {
1266  connection->rp.response = NULL;
1267  MHD_destroy_response (resp);
1268  }
1269  if (NULL != connection->pool)
1270  {
1271  MHD_pool_destroy (connection->pool);
1272  connection->pool = NULL;
1273  }
1274 
1275  MHD_connection_mark_closed_ (connection);
1276 }
1277 
1278 
1279 #if defined(HTTPS_SUPPORT) && defined(UPGRADE_SUPPORT)
1290 void
1292 {
1293  struct MHD_Daemon *daemon = connection->daemon;
1294  struct MHD_UpgradeResponseHandle *urh = connection->urh;
1295 
1296 #ifdef MHD_USE_THREADS
1297  mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
1298  MHD_D_IS_USING_THREAD_PER_CONN_ (daemon) || \
1300 #endif /* MHD_USE_THREADS */
1301 
1302  if (0 == (daemon->options & MHD_USE_TLS))
1303  return; /* Nothing to do with non-TLS connection. */
1304 
1305  if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
1306  DLL_remove (daemon->urh_head,
1307  daemon->urh_tail,
1308  urh);
1309 #ifdef EPOLL_SUPPORT
1310  if (MHD_D_IS_USING_EPOLL_ (daemon) &&
1311  (0 != epoll_ctl (daemon->epoll_upgrade_fd,
1312  EPOLL_CTL_DEL,
1313  connection->socket_fd,
1314  NULL)) )
1315  {
1316  MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
1317  }
1318  if (urh->in_eready_list)
1319  {
1320  EDLL_remove (daemon->eready_urh_head,
1321  daemon->eready_urh_tail,
1322  urh);
1323  urh->in_eready_list = false;
1324  }
1325 #endif /* EPOLL_SUPPORT */
1326  if (MHD_INVALID_SOCKET != urh->mhd.socket)
1327  {
1328 #ifdef EPOLL_SUPPORT
1329  if (MHD_D_IS_USING_EPOLL_ (daemon) &&
1330  (0 != epoll_ctl (daemon->epoll_upgrade_fd,
1331  EPOLL_CTL_DEL,
1332  urh->mhd.socket,
1333  NULL)) )
1334  {
1335  MHD_PANIC (_ ("Failed to remove FD from epoll set.\n"));
1336  }
1337 #endif /* EPOLL_SUPPORT */
1338  /* Reflect remote disconnect to application by breaking
1339  * socketpair connection. */
1340  shutdown (urh->mhd.socket, SHUT_RDWR);
1341  }
1342  /* Socketpair sockets will remain open as they will be
1343  * used with MHD_UPGRADE_ACTION_CLOSE. They will be
1344  * closed by cleanup_upgraded_connection() during
1345  * connection's final cleanup.
1346  */
1347 }
1348 
1349 
1350 #endif /* HTTPS_SUPPORT && UPGRADE_SUPPORT*/
1351 
1352 
1360 static void
1362  const char *emsg)
1363 {
1364  connection->stop_with_error = true;
1365  connection->discard_request = true;
1366 #ifdef HAVE_MESSAGES
1367  if (NULL != emsg)
1368  MHD_DLOG (connection->daemon,
1369  "%s\n",
1370  emsg);
1371 #else /* ! HAVE_MESSAGES */
1372  (void) emsg; /* Mute compiler warning. */
1373 #endif /* ! HAVE_MESSAGES */
1374  MHD_connection_close_ (connection,
1376 }
1377 
1378 
1383 #ifdef HAVE_MESSAGES
1384 #define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, emsg)
1385 #else
1386 #define CONNECTION_CLOSE_ERROR(c, emsg) connection_close_error (c, NULL)
1387 #endif
1388 
1389 
1402 static enum MHD_Result
1403 try_ready_normal_body (struct MHD_Connection *connection)
1404 {
1405  ssize_t ret;
1406  struct MHD_Response *response;
1407 
1408  response = connection->rp.response;
1409  mhd_assert (connection->rp.props.send_reply_body);
1410 
1411  if ( (0 == response->total_size) ||
1412  /* TODO: replace the next check with assert */
1413  (connection->rp.rsp_write_position == response->total_size) )
1414  return MHD_YES; /* 0-byte response is always ready */
1415  if (NULL != response->data_iov)
1416  {
1417  size_t copy_size;
1418 
1419  if (NULL != connection->rp.resp_iov.iov)
1420  return MHD_YES;
1421  copy_size = response->data_iovcnt * sizeof(MHD_iovec_);
1422  connection->rp.resp_iov.iov = MHD_connection_alloc_memory_ (connection,
1423  copy_size);
1424  if (NULL == connection->rp.resp_iov.iov)
1425  {
1426  MHD_mutex_unlock_chk_ (&response->mutex);
1427  /* not enough memory */
1428  CONNECTION_CLOSE_ERROR (connection,
1429  _ ("Closing connection (out of memory)."));
1430  return MHD_NO;
1431  }
1432  memcpy (connection->rp.resp_iov.iov,
1433  response->data_iov,
1434  copy_size);
1435  connection->rp.resp_iov.cnt = response->data_iovcnt;
1436  connection->rp.resp_iov.sent = 0;
1437  return MHD_YES;
1438  }
1439  if (NULL == response->crc)
1440  return MHD_YES;
1441  if ( (response->data_start <=
1442  connection->rp.rsp_write_position) &&
1443  (response->data_size + response->data_start >
1444  connection->rp.rsp_write_position) )
1445  return MHD_YES; /* response already ready */
1446 #if defined(_MHD_HAVE_SENDFILE)
1447  if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
1448  {
1449  /* will use sendfile, no need to bother response crc */
1450  return MHD_YES;
1451  }
1452 #endif /* _MHD_HAVE_SENDFILE */
1453 
1454  ret = response->crc (response->crc_cls,
1455  connection->rp.rsp_write_position,
1456  (char *) response->data,
1457  (size_t) MHD_MIN ((uint64_t) response->data_buffer_size,
1458  response->total_size
1459  - connection->rp.rsp_write_position));
1460  if (0 > ret)
1461  {
1462  /* either error or http 1.0 transfer, close socket! */
1463  /* TODO: do not update total size, check whether response
1464  * was really with unknown size */
1465  response->total_size = connection->rp.rsp_write_position;
1466 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1467  MHD_mutex_unlock_chk_ (&response->mutex);
1468 #endif
1470  MHD_connection_close_ (connection,
1472  else
1473  CONNECTION_CLOSE_ERROR (connection,
1474  _ ("Closing connection (application reported " \
1475  "error generating data)."));
1476  return MHD_NO;
1477  }
1478  response->data_start = connection->rp.rsp_write_position;
1479  response->data_size = (size_t) ret;
1480  if (0 == ret)
1481  {
1483 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1484  MHD_mutex_unlock_chk_ (&response->mutex);
1485 #endif
1486  return MHD_NO;
1487  }
1488  return MHD_YES;
1489 }
1490 
1491 
1504 static enum MHD_Result
1505 try_ready_chunked_body (struct MHD_Connection *connection,
1506  bool *p_finished)
1507 {
1508  ssize_t ret;
1509  struct MHD_Response *response;
1510  static const size_t max_chunk = 0xFFFFFF;
1511  char chunk_hdr[6]; /* 6: max strlen of "FFFFFF" */
1512  /* "FFFFFF" + "\r\n" */
1513  static const size_t max_chunk_hdr_len = sizeof(chunk_hdr) + 2;
1514  /* "FFFFFF" + "\r\n" + "\r\n" (chunk termination) */
1515  static const size_t max_chunk_overhead = sizeof(chunk_hdr) + 2 + 2;
1516  size_t chunk_hdr_len;
1517  uint64_t left_to_send;
1518  size_t size_to_fill;
1519 
1520  response = connection->rp.response;
1521  mhd_assert (NULL != response->crc || NULL != response->data);
1522 
1523  mhd_assert (0 == connection->write_buffer_append_offset);
1524 
1525  /* The buffer must be reasonably large enough */
1526  if (128 > connection->write_buffer_size)
1527  {
1528  size_t size;
1529 
1530  size = connection->write_buffer_size + MHD_pool_get_free (connection->pool);
1531  if (128 > size)
1532  {
1533 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1534  MHD_mutex_unlock_chk_ (&response->mutex);
1535 #endif
1536  /* not enough memory */
1537  CONNECTION_CLOSE_ERROR (connection,
1538  _ ("Closing connection (out of memory)."));
1539  return MHD_NO;
1540  }
1541  /* Limit the buffer size to the largest usable size for chunks */
1542  if ( (max_chunk + max_chunk_overhead) < size)
1543  size = max_chunk + max_chunk_overhead;
1544  mhd_assert ((NULL == connection->write_buffer) || \
1545  MHD_pool_is_resizable_inplace (connection->pool, \
1546  connection->write_buffer, \
1547  connection->write_buffer_size));
1548  connection->write_buffer =
1549  MHD_pool_reallocate (connection->pool,
1550  connection->write_buffer,
1551  connection->write_buffer_size,
1552  size);
1553  mhd_assert (NULL != connection->write_buffer);
1554  connection->write_buffer_size = size;
1555  }
1556  mhd_assert (max_chunk_overhead < connection->write_buffer_size);
1557 
1558  if (MHD_SIZE_UNKNOWN == response->total_size)
1559  left_to_send = MHD_SIZE_UNKNOWN;
1560  else
1561  left_to_send = response->total_size
1562  - connection->rp.rsp_write_position;
1563 
1564  size_to_fill = connection->write_buffer_size - max_chunk_overhead;
1565  /* Limit size for the callback to the max usable size */
1566  if (max_chunk < size_to_fill)
1567  size_to_fill = max_chunk;
1568  if (left_to_send < size_to_fill)
1569  size_to_fill = (size_t) left_to_send;
1570 
1571  if (0 == left_to_send)
1572  /* nothing to send, don't bother calling crc */
1574  else if ( (response->data_start <=
1575  connection->rp.rsp_write_position) &&
1576  (response->data_start + response->data_size >
1577  connection->rp.rsp_write_position) )
1578  {
1579  /* difference between rsp_write_position and data_start is less
1580  than data_size which is size_t type, no need to check for overflow */
1581  const size_t data_write_offset
1582  = (size_t) (connection->rp.rsp_write_position
1583  - response->data_start);
1584  /* buffer already ready, use what is there for the chunk */
1585  mhd_assert (SSIZE_MAX >= (response->data_size - data_write_offset));
1586  mhd_assert (response->data_size >= data_write_offset);
1587  ret = (ssize_t) (response->data_size - data_write_offset);
1588  if ( ((size_t) ret) > size_to_fill)
1589  ret = (ssize_t) size_to_fill;
1590  memcpy (&connection->write_buffer[max_chunk_hdr_len],
1591  &response->data[data_write_offset],
1592  (size_t) ret);
1593  }
1594  else
1595  {
1596  if (NULL == response->crc)
1597  { /* There is no way to reach this code */
1598 #if defined(MHD_USE_THREADS)
1599  MHD_mutex_unlock_chk_ (&response->mutex);
1600 #endif
1601  CONNECTION_CLOSE_ERROR (connection,
1602  _ ("No callback for the chunked data."));
1603  return MHD_NO;
1604  }
1605  ret = response->crc (response->crc_cls,
1606  connection->rp.rsp_write_position,
1607  &connection->write_buffer[max_chunk_hdr_len],
1608  size_to_fill);
1609  }
1611  {
1612  /* error, close socket! */
1613  /* TODO: remove update of the response size */
1614  response->total_size = connection->rp.rsp_write_position;
1615 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1616  MHD_mutex_unlock_chk_ (&response->mutex);
1617 #endif
1618  CONNECTION_CLOSE_ERROR (connection,
1619  _ ("Closing connection (application error " \
1620  "generating response)."));
1621  return MHD_NO;
1622  }
1624  {
1625  *p_finished = true;
1626  /* TODO: remove update of the response size */
1627  response->total_size = connection->rp.rsp_write_position;
1628  return MHD_YES;
1629  }
1630  if (0 == ret)
1631  {
1633 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
1634  MHD_mutex_unlock_chk_ (&response->mutex);
1635 #endif
1636  return MHD_NO;
1637  }
1638  if (size_to_fill < (size_t) ret)
1639  {
1640 #if defined(MHD_USE_THREADS)
1641  MHD_mutex_unlock_chk_ (&response->mutex);
1642 #endif
1643  CONNECTION_CLOSE_ERROR (connection,
1644  _ ("Closing connection (application returned " \
1645  "more data than requested)."));
1646  return MHD_NO;
1647  }
1648  chunk_hdr_len = MHD_uint32_to_strx ((uint32_t) ret, chunk_hdr,
1649  sizeof(chunk_hdr));
1650  mhd_assert (chunk_hdr_len != 0);
1651  mhd_assert (chunk_hdr_len < sizeof(chunk_hdr));
1652  *p_finished = false;
1653  connection->write_buffer_send_offset =
1654  (max_chunk_hdr_len - (chunk_hdr_len + 2));
1655  memcpy (connection->write_buffer + connection->write_buffer_send_offset,
1656  chunk_hdr,
1657  chunk_hdr_len);
1658  connection->write_buffer[max_chunk_hdr_len - 2] = '\r';
1659  connection->write_buffer[max_chunk_hdr_len - 1] = '\n';
1660  connection->write_buffer[max_chunk_hdr_len + (size_t) ret] = '\r';
1661  connection->write_buffer[max_chunk_hdr_len + (size_t) ret + 1] = '\n';
1662  connection->rp.rsp_write_position += (size_t) ret;
1663  connection->write_buffer_append_offset = max_chunk_hdr_len + (size_t) ret + 2;
1664  return MHD_YES;
1665 }
1666 
1667 
1690 static enum MHD_ConnKeepAlive
1691 keepalive_possible (struct MHD_Connection *connection)
1692 {
1693  struct MHD_Connection *const c = connection;
1694  struct MHD_Response *const r = c->rp.response;
1696  mhd_assert (NULL != r);
1697  if (MHD_CONN_MUST_CLOSE == c->keepalive)
1698  return MHD_CONN_MUST_CLOSE;
1699 
1700 #ifdef UPGRADE_SUPPORT
1701  /* TODO: Move below the next check when MHD stops closing connections
1702  * when response is queued in first callback */
1703  if (NULL != r->upgrade_handler)
1704  {
1705  /* No "close" token is enforced by 'add_response_header_connection()' */
1707  /* Valid HTTP version is enforced by 'MHD_queue_response()' */
1709  mhd_assert (! c->stop_with_error);
1710  return MHD_CONN_MUST_UPGRADE;
1711  }
1712 #endif /* UPGRADE_SUPPORT */
1713 
1714  mhd_assert ( (! c->stop_with_error) || (c->discard_request));
1715  if ((c->read_closed) || (c->discard_request))
1716  return MHD_CONN_MUST_CLOSE;
1717 
1718  if (0 != (r->flags & MHD_RF_HTTP_1_0_COMPATIBLE_STRICT))
1719  return MHD_CONN_MUST_CLOSE;
1720  if (0 != (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE))
1721  return MHD_CONN_MUST_CLOSE;
1722 
1724  return MHD_CONN_MUST_CLOSE;
1725 
1728  "close"))
1729  return MHD_CONN_MUST_CLOSE;
1730 
1731  if ((MHD_HTTP_VER_1_0 == connection->rq.http_ver) ||
1732  (0 != (connection->rp.response->flags & MHD_RF_HTTP_1_0_SERVER)))
1733  {
1734  if (MHD_lookup_header_s_token_ci (connection,
1736  "Keep-Alive"))
1737  return MHD_CONN_USE_KEEPALIVE;
1738 
1739  return MHD_CONN_MUST_CLOSE;
1740  }
1741 
1743  return MHD_CONN_USE_KEEPALIVE;
1744 
1745  return MHD_CONN_MUST_CLOSE;
1746 }
1747 
1748 
1758 static bool
1759 get_date_str (char *date)
1760 {
1761  static const char *const days[] = {
1762  "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1763  };
1764  static const char *const mons[] = {
1765  "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1766  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1767  };
1768  static const size_t buf_len = 29;
1769  struct tm now;
1770  time_t t;
1771  const char *src;
1772 #if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
1773  ! defined(HAVE_GMTIME_R)
1774  struct tm *pNow;
1775 #endif
1776 
1777  if ((time_t) -1 == time (&t))
1778  return false;
1779 #if defined(HAVE_C11_GMTIME_S)
1780  if (NULL == gmtime_s (&t,
1781  &now))
1782  return false;
1783 #elif defined(HAVE_W32_GMTIME_S)
1784  if (0 != gmtime_s (&now,
1785  &t))
1786  return false;
1787 #elif defined(HAVE_GMTIME_R)
1788  if (NULL == gmtime_r (&t,
1789  &now))
1790  return false;
1791 #else
1792  pNow = gmtime (&t);
1793  if (NULL == pNow)
1794  return false;
1795  now = *pNow;
1796 #endif
1797 
1798  /* Day of the week */
1799  src = days[now.tm_wday % 7];
1800  date[0] = src[0];
1801  date[1] = src[1];
1802  date[2] = src[2];
1803  date[3] = ',';
1804  date[4] = ' ';
1805  /* Day of the month */
1806  if (2 != MHD_uint8_to_str_pad ((uint8_t) now.tm_mday, 2,
1807  date + 5, buf_len - 5))
1808  return false;
1809  date[7] = ' ';
1810  /* Month */
1811  src = mons[now.tm_mon % 12];
1812  date[8] = src[0];
1813  date[9] = src[1];
1814  date[10] = src[2];
1815  date[11] = ' ';
1816  /* Year */
1817  if (4 != MHD_uint16_to_str ((uint16_t) (1900 + now.tm_year), date + 12,
1818  buf_len - 12))
1819  return false;
1820  date[16] = ' ';
1821  /* Time */
1822  MHD_uint8_to_str_pad ((uint8_t) now.tm_hour, 2, date + 17, buf_len - 17);
1823  date[19] = ':';
1824  MHD_uint8_to_str_pad ((uint8_t) now.tm_min, 2, date + 20, buf_len - 20);
1825  date[22] = ':';
1826  MHD_uint8_to_str_pad ((uint8_t) now.tm_sec, 2, date + 23, buf_len - 23);
1827  date[25] = ' ';
1828  date[26] = 'G';
1829  date[27] = 'M';
1830  date[28] = 'T';
1831 
1832  return true;
1833 }
1834 
1835 
1843 static bool
1844 get_date_header (char *header)
1845 {
1846  if (! get_date_str (header + 6))
1847  {
1848  header[0] = 0;
1849  return false;
1850  }
1851  header[0] = 'D';
1852  header[1] = 'a';
1853  header[2] = 't';
1854  header[3] = 'e';
1855  header[4] = ':';
1856  header[5] = ' ';
1857  header[35] = '\r';
1858  header[36] = '\n';
1859  header[37] = 0;
1860  return true;
1861 }
1862 
1863 
1876 static bool
1878  bool required)
1879 {
1880  size_t new_size;
1881  size_t avail_size;
1882  const size_t def_grow_size = connection->daemon->pool_increment;
1883  void *rb;
1884 
1885  avail_size = MHD_pool_get_free (connection->pool);
1886  if (0 == avail_size)
1887  return false; /* No more space available */
1888  if (0 == connection->read_buffer_size)
1889  new_size = avail_size / 2; /* Use half of available buffer for reading */
1890  else
1891  {
1892  size_t grow_size;
1893 
1894  grow_size = avail_size / 8;
1895  if (def_grow_size > grow_size)
1896  { /* Shortage of space */
1897  const size_t left_free =
1898  connection->read_buffer_size - connection->read_buffer_offset;
1899  mhd_assert (connection->read_buffer_size >= \
1900  connection->read_buffer_offset);
1901  if ((def_grow_size <= grow_size + left_free)
1902  && (left_free < def_grow_size))
1903  grow_size = def_grow_size - left_free; /* Use precise 'def_grow_size' for new free space */
1904  else if (! required)
1905  return false; /* Grow is not mandatory, leave some space in pool */
1906  else
1907  {
1908  /* Shortage of space, but grow is mandatory */
1909  const size_t small_inc =
1910  ((MHD_BUF_INC_SIZE > def_grow_size) ?
1911  def_grow_size : MHD_BUF_INC_SIZE) / 8;
1912  if (small_inc < avail_size)
1913  grow_size = small_inc;
1914  else
1915  grow_size = avail_size;
1916  }
1917  }
1918  new_size = connection->read_buffer_size + grow_size;
1919  }
1920  /* Make sure that read buffer will not be moved */
1921  if ((NULL != connection->read_buffer) &&
1922  ! MHD_pool_is_resizable_inplace (connection->pool,
1923  connection->read_buffer,
1924  connection->read_buffer_size))
1925  {
1926  mhd_assert (0);
1927  return false;
1928  }
1929  /* we can actually grow the buffer, do it! */
1930  rb = MHD_pool_reallocate (connection->pool,
1931  connection->read_buffer,
1932  connection->read_buffer_size,
1933  new_size);
1934  if (NULL == rb)
1935  {
1936  /* This should NOT be possible: we just computed 'new_size' so that
1937  it should fit. If it happens, somehow our read buffer is not in
1938  the right position in the pool, say because someone called
1939  MHD_pool_allocate() without 'from_end' set to 'true'? Anyway,
1940  should be investigated! (Ideally provide all data from
1941  *pool and connection->read_buffer and new_size for debugging). */
1942  mhd_assert (0);
1943  return false;
1944  }
1945  mhd_assert (connection->read_buffer == rb);
1946  connection->read_buffer = rb;
1947  mhd_assert (NULL != connection->read_buffer);
1948  connection->read_buffer_size = new_size;
1949  return true;
1950 }
1951 
1952 
1957 static void
1959 {
1960  struct MHD_Connection *const c = connection;
1961  void *new_buf;
1962 
1963  if ((NULL == c->read_buffer) || (0 == c->read_buffer_size))
1964  {
1965  mhd_assert (0 == c->read_buffer_size);
1966  mhd_assert (0 == c->read_buffer_offset);
1967  return;
1968  }
1969 
1971  if (0 == c->read_buffer_offset)
1972  {
1974  c->read_buffer = NULL;
1975  c->read_buffer_size = 0;
1976  }
1977  else
1978  {
1980  c->read_buffer_size));
1981  new_buf = MHD_pool_reallocate (c->pool, c->read_buffer, c->read_buffer_size,
1982  c->read_buffer_offset);
1983  mhd_assert (c->read_buffer == new_buf);
1984  c->read_buffer = new_buf;
1986  }
1987 }
1988 
1989 
1996 static size_t
1998 {
1999  struct MHD_Connection *const c = connection;
2000  struct MemoryPool *const pool = connection->pool;
2001  void *new_buf;
2002  size_t new_size;
2003  size_t free_size;
2004 
2005  mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
2008 
2009  free_size = MHD_pool_get_free (pool);
2010  if (0 != free_size)
2011  {
2012  new_size = c->write_buffer_size + free_size;
2013  /* This function must not move the buffer position.
2014  * MHD_pool_reallocate () may return the new position only if buffer was
2015  * allocated 'from_end' or is not the last allocation,
2016  * which should not happen. */
2017  mhd_assert ((NULL == c->write_buffer) || \
2019  c->write_buffer_size));
2020  new_buf = MHD_pool_reallocate (pool,
2021  c->write_buffer,
2022  c->write_buffer_size,
2023  new_size);
2024  mhd_assert ((c->write_buffer == new_buf) || (NULL == c->write_buffer));
2025  c->write_buffer = new_buf;
2026  c->write_buffer_size = new_size;
2028  {
2029  /* All data have been sent, reset offsets to zero. */
2030  c->write_buffer_send_offset = 0;
2032  }
2033  }
2034 
2036 }
2037 
2038 
2039 #if 0 /* disable unused function */
2048 static void
2049 connection_shrink_write_buffer (struct MHD_Connection *connection)
2050 {
2051  struct MHD_Connection *const c = connection;
2052  struct MemoryPool *const pool = connection->pool;
2053  void *new_buf;
2054 
2055  mhd_assert ((NULL != c->write_buffer) || (0 == c->write_buffer_size));
2058 
2059  if ( (NULL == c->write_buffer) || (0 == c->write_buffer_size))
2060  {
2063  c->write_buffer = NULL;
2064  return;
2065  }
2067  return;
2068 
2069  new_buf = MHD_pool_reallocate (pool, c->write_buffer, c->write_buffer_size,
2071  mhd_assert ((c->write_buffer == new_buf) || \
2072  (0 == c->write_buffer_append_offset));
2074  if (0 == c->write_buffer_size)
2075  c->write_buffer = NULL;
2076  else
2077  c->write_buffer = new_buf;
2078 }
2079 
2080 
2081 #endif /* unused function */
2082 
2083 
2091 static void
2093 {
2094  /* Read buffer is not needed for this request, shrink it.*/
2095  connection_shrink_read_buffer (connection);
2096 }
2097 
2098 
2104 {
2111 
2118 
2124  RP_BODY_SEND = 2
2125 };
2126 
2127 
2139 static enum replyBodyUse
2140 is_reply_body_needed (struct MHD_Connection *connection,
2141  unsigned int rcode)
2142 {
2143  struct MHD_Connection *const c = connection;
2145  mhd_assert (100 <= rcode);
2146  mhd_assert (999 >= rcode);
2147 
2148  if (199 >= rcode)
2149  return RP_BODY_NONE;
2150 
2151  if (MHD_HTTP_NO_CONTENT == rcode)
2152  return RP_BODY_NONE;
2153 
2154 #if 0
2155  /* This check is not needed as upgrade handler is used only with code 101 */
2156 #ifdef UPGRADE_SUPPORT
2157  if (NULL != rp.response->upgrade_handler)
2158  return RP_BODY_NONE;
2159 #endif /* UPGRADE_SUPPORT */
2160 #endif
2161 
2162 #if 0
2163  /* CONNECT is not supported by MHD */
2164  /* Successful responses for connect requests are filtered by
2165  * MHD_queue_response() */
2166  if ( (MHD_HTTP_MTHD_CONNECT == c->rq.http_mthd) &&
2167  (2 == rcode / 100) )
2168  return false; /* Actually pass-through CONNECT is not supported by MHD */
2169 #endif
2170 
2171  /* Reply body headers could be used.
2172  * Check whether reply body itself must be used. */
2173 
2174  if (MHD_HTTP_MTHD_HEAD == c->rq.http_mthd)
2175  return RP_BODY_HEADERS_ONLY;
2176 
2177  if (MHD_HTTP_NOT_MODIFIED == rcode)
2178  return RP_BODY_HEADERS_ONLY;
2179 
2180  /* Reply body must be sent. The body may have zero length, but body size
2181  * must be indicated by headers ('Content-Length:' or
2182  * 'Transfer-Encoding: chunked'). */
2183  return RP_BODY_SEND;
2184 }
2185 
2186 
2195 static void
2197 {
2198  struct MHD_Connection *const c = connection;
2199  struct MHD_Response *const r = c->rp.response;
2200  enum replyBodyUse use_rp_body;
2201  bool use_chunked;
2202 
2203  mhd_assert (NULL != r);
2204 
2205  /* ** Adjust reply properties ** */
2206 
2207  c->keepalive = keepalive_possible (c);
2208  use_rp_body = is_reply_body_needed (c, c->rp.responseCode);
2209  c->rp.props.send_reply_body = (use_rp_body > RP_BODY_HEADERS_ONLY);
2210  c->rp.props.use_reply_body_headers = (use_rp_body >= RP_BODY_HEADERS_ONLY);
2211 
2212 #ifdef UPGRADE_SUPPORT
2213  mhd_assert ( (NULL == r->upgrade_handler) ||
2214  (RP_BODY_NONE == use_rp_body) );
2215 #endif /* UPGRADE_SUPPORT */
2216 
2218  {
2219  if ((MHD_SIZE_UNKNOWN == r->total_size) ||
2221  { /* Use chunked reply encoding if possible */
2222 
2223  /* Check whether chunked encoding is supported by the client */
2225  use_chunked = false;
2226  /* Check whether chunked encoding is allowed for the reply */
2227  else if (0 != (r->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
2229  use_chunked = false;
2230  else
2231  /* If chunked encoding is supported and allowed, and response size
2232  * is unknown, use chunked even for non-Keep-Alive connections.
2233  * See https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.3
2234  * Also use chunked if it is enforced by application and supported by
2235  * the client. */
2236  use_chunked = true;
2237  }
2238  else
2239  use_chunked = false;
2240 
2241  if ( (MHD_SIZE_UNKNOWN == r->total_size) &&
2242  (! use_chunked) )
2243  {
2244  /* End of the stream is indicated by closure */
2246  }
2247  }
2248  else
2249  use_chunked = false; /* chunked encoding cannot be used without body */
2250 
2251  c->rp.props.chunked = use_chunked;
2252 #ifdef _DEBUG
2253  c->rp.props.set = true;
2254 #endif /* _DEBUG */
2255 }
2256 
2257 
2262 static void
2264 {
2265  struct MHD_Connection *const c = connection;
2266  struct MHD_Response *const r = c->rp.response;
2268  mhd_assert (c->rp.props.set);
2269 #ifdef HAVE_MESSAGES
2270  if ( (! c->rp.props.use_reply_body_headers) &&
2271  (0 != r->total_size) )
2272  {
2273  MHD_DLOG (c->daemon,
2274  _ ("This reply with response code %u cannot use reply body. "
2275  "Non-empty response body is ignored and not used.\n"),
2276  (unsigned) (c->rp.responseCode));
2277  }
2278  if ( (! c->rp.props.use_reply_body_headers) &&
2279  (0 != (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH)) )
2280  {
2281  MHD_DLOG (c->daemon,
2282  _ ("This reply with response code %u cannot use reply body. "
2283  "Application defined \"Content-Length\" header violates"
2284  "HTTP specification.\n"),
2285  (unsigned) (c->rp.responseCode));
2286  }
2287 #else
2288  (void) c; /* Mute compiler warning */
2289  (void) r; /* Mute compiler warning */
2290 #endif
2291 }
2292 
2293 
2305 static bool
2306 buffer_append (char *buf,
2307  size_t *ppos,
2308  size_t buf_size,
2309  const char *append,
2310  size_t append_size)
2311 {
2312  mhd_assert (NULL != buf); /* Mute static analyzer */
2313  if (buf_size < *ppos + append_size)
2314  return false;
2315  memcpy (buf + *ppos, append, append_size);
2316  *ppos += append_size;
2317  return true;
2318 }
2319 
2320 
2331 #define buffer_append_s(buf,ppos,buf_size,str) \
2332  buffer_append (buf,ppos,buf_size,str, MHD_STATICSTR_LEN_ (str))
2333 
2334 
2354 static bool
2355 add_user_headers (char *buf,
2356  size_t *ppos,
2357  size_t buf_size,
2358  struct MHD_Response *response,
2359  bool filter_transf_enc,
2360  bool filter_content_len,
2361  bool add_close,
2362  bool add_keep_alive)
2363 {
2364  struct MHD_Response *const r = response;
2365  struct MHD_HTTP_Res_Header *hdr;
2366  size_t el_size;
2368  mhd_assert (! add_close || ! add_keep_alive);
2369 
2370  if (0 == (r->flags_auto & MHD_RAF_HAS_TRANS_ENC_CHUNKED))
2371  filter_transf_enc = false; /* No such header */
2372  if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2373  filter_content_len = false; /* No such header */
2374  if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2375  {
2376  add_close = false; /* No such header */
2377  add_keep_alive = false; /* No such header */
2378  }
2379  else if (0 != (r->flags_auto & MHD_RAF_HAS_CONNECTION_CLOSE))
2380  add_close = false; /* "close" token was already set */
2381 
2382  for (hdr = r->first_header; NULL != hdr; hdr = hdr->next)
2383  {
2384  size_t initial_pos = *ppos;
2385  if (MHD_HEADER_KIND != hdr->kind)
2386  continue;
2387  if (filter_transf_enc)
2388  { /* Need to filter-out "Transfer-Encoding" */
2390  hdr->header_size) &&
2392  hdr->header, hdr->header_size)) )
2393  {
2394  filter_transf_enc = false; /* There is the only one such header */
2395  continue; /* Skip "Transfer-Encoding" header */
2396  }
2397  }
2398  if (filter_content_len)
2399  { /* Need to filter-out "Content-Length" */
2401  hdr->header_size) &&
2403  hdr->header, hdr->header_size)) )
2404  {
2405  /* Reset filter flag if only one header is allowed */
2406  filter_transf_enc =
2408  continue; /* Skip "Content-Length" header */
2409  }
2410  }
2411 
2412  /* Add user header */
2413  el_size = hdr->header_size + 2 + hdr->value_size + 2;
2414  if (buf_size < *ppos + el_size)
2415  return false;
2416  memcpy (buf + *ppos, hdr->header, hdr->header_size);
2417  (*ppos) += hdr->header_size;
2418  buf[(*ppos)++] = ':';
2419  buf[(*ppos)++] = ' ';
2420  if (add_close || add_keep_alive)
2421  {
2422  /* "Connection:" header must be always the first one */
2425  hdr->header_size));
2426 
2427  if (add_close)
2428  {
2429  el_size += MHD_STATICSTR_LEN_ ("close, ");
2430  if (buf_size < initial_pos + el_size)
2431  return false;
2432  memcpy (buf + *ppos, "close, ",
2433  MHD_STATICSTR_LEN_ ("close, "));
2434  *ppos += MHD_STATICSTR_LEN_ ("close, ");
2435  }
2436  else
2437  {
2438  el_size += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2439  if (buf_size < initial_pos + el_size)
2440  return false;
2441  memcpy (buf + *ppos, "Keep-Alive, ",
2442  MHD_STATICSTR_LEN_ ("Keep-Alive, "));
2443  *ppos += MHD_STATICSTR_LEN_ ("Keep-Alive, ");
2444  }
2445  add_close = false;
2446  add_keep_alive = false;
2447  }
2448  if (0 != hdr->value_size)
2449  memcpy (buf + *ppos, hdr->value, hdr->value_size);
2450  *ppos += hdr->value_size;
2451  buf[(*ppos)++] = '\r';
2452  buf[(*ppos)++] = '\n';
2453  mhd_assert (initial_pos + el_size == (*ppos));
2454  }
2455  return true;
2456 }
2457 
2458 
2467 static enum MHD_Result
2468 build_header_response (struct MHD_Connection *connection)
2469 {
2470  struct MHD_Connection *const c = connection;
2471  struct MHD_Response *const r = c->rp.response;
2472  char *buf;
2473  size_t pos;
2474  size_t buf_size;
2475  size_t el_size;
2476  unsigned rcode;
2477  bool use_conn_close;
2478  bool use_conn_k_alive;
2480  mhd_assert (NULL != r);
2481 
2482  /* ** Adjust response properties ** */
2484 
2485  mhd_assert (c->rp.props.set);
2487  (MHD_CONN_USE_KEEPALIVE == c->keepalive) || \
2489 #ifdef UPGRADE_SUPPORT
2490  mhd_assert ((NULL == r->upgrade_handler) || \
2492 #else /* ! UPGRADE_SUPPORT */
2494 #endif /* ! UPGRADE_SUPPORT */
2496  mhd_assert ((! c->rp.props.send_reply_body) || \
2498 #ifdef UPGRADE_SUPPORT
2499  mhd_assert (NULL == r->upgrade_handler || \
2501 #endif /* UPGRADE_SUPPORT */
2502 
2504 
2505  rcode = (unsigned) c->rp.responseCode;
2506  if (MHD_CONN_MUST_CLOSE == c->keepalive)
2507  {
2508  /* The closure of connection must be always indicated by header
2509  * to avoid hung connections */
2510  use_conn_close = true;
2511  use_conn_k_alive = false;
2512  }
2513  else if (MHD_CONN_USE_KEEPALIVE == c->keepalive)
2514  {
2515  use_conn_close = false;
2516  /* Add "Connection: keep-alive" if request is HTTP/1.0 or
2517  * if reply is HTTP/1.0
2518  * For HTTP/1.1 add header only if explicitly requested by app
2519  * (by response flag), as "Keep-Alive" is default for HTTP/1.1. */
2520  if ((0 != (r->flags & MHD_RF_SEND_KEEP_ALIVE_HEADER)) ||
2521  (MHD_HTTP_VER_1_0 == c->rq.http_ver) ||
2522  (0 != (r->flags & MHD_RF_HTTP_1_0_SERVER)))
2523  use_conn_k_alive = true;
2524  else
2525  use_conn_k_alive = false;
2526  }
2527  else
2528  {
2529  use_conn_close = false;
2530  use_conn_k_alive = false;
2531  }
2532 
2533  /* ** Actually build the response header ** */
2534 
2535  /* Get all space available */
2537  buf = c->write_buffer;
2538  pos = c->write_buffer_append_offset;
2539  buf_size = c->write_buffer_size;
2540  if (0 == buf_size)
2541  return MHD_NO;
2542  mhd_assert (NULL != buf);
2543 
2544  /* * The status line * */
2545 
2546  /* The HTTP version */
2547  if (! c->rp.responseIcy)
2548  { /* HTTP reply */
2549  if (0 == (r->flags & MHD_RF_HTTP_1_0_SERVER))
2550  { /* HTTP/1.1 reply */
2551  /* Use HTTP/1.1 responses for HTTP/1.0 clients.
2552  * See https://datatracker.ietf.org/doc/html/rfc7230#section-2.6 */
2553  if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_1))
2554  return MHD_NO;
2555  }
2556  else
2557  { /* HTTP/1.0 reply */
2558  if (! buffer_append_s (buf, &pos, buf_size, MHD_HTTP_VERSION_1_0))
2559  return MHD_NO;
2560  }
2561  }
2562  else
2563  { /* ICY reply */
2564  if (! buffer_append_s (buf, &pos, buf_size, "ICY"))
2565  return MHD_NO;
2566  }
2567 
2568  /* The response code */
2569  if (buf_size < pos + 5) /* space + code + space */
2570  return MHD_NO;
2571  buf[pos++] = ' ';
2572  pos += MHD_uint16_to_str ((uint16_t) rcode, buf + pos,
2573  buf_size - pos);
2574  buf[pos++] = ' ';
2575 
2576  /* The reason phrase */
2577  el_size = MHD_get_reason_phrase_len_for (rcode);
2578  if (0 == el_size)
2579  {
2580  if (! buffer_append_s (buf, &pos, buf_size, "Non-Standard Status"))
2581  return MHD_NO;
2582  }
2583  else if (! buffer_append (buf, &pos, buf_size,
2584  MHD_get_reason_phrase_for (rcode),
2585  el_size))
2586  return MHD_NO;
2587 
2588  /* The linefeed */
2589  if (buf_size < pos + 2)
2590  return MHD_NO;
2591  buf[pos++] = '\r';
2592  buf[pos++] = '\n';
2593 
2594  /* * The headers * */
2595 
2596  /* Main automatic headers */
2597 
2598  /* The "Date:" header */
2599  if ( (0 == (r->flags_auto & MHD_RAF_HAS_DATE_HDR)) &&
2601  {
2602  /* Additional byte for unused zero-termination */
2603  if (buf_size < pos + 38)
2604  return MHD_NO;
2605  if (get_date_header (buf + pos))
2606  pos += 37;
2607  }
2608  /* The "Connection:" header */
2609  mhd_assert (! use_conn_close || ! use_conn_k_alive);
2610  mhd_assert (! use_conn_k_alive || ! use_conn_close);
2611  if (0 == (r->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
2612  {
2613  if (use_conn_close)
2614  {
2615  if (! buffer_append_s (buf, &pos, buf_size,
2616  MHD_HTTP_HEADER_CONNECTION ": close\r\n"))
2617  return MHD_NO;
2618  }
2619  else if (use_conn_k_alive)
2620  {
2621  if (! buffer_append_s (buf, &pos, buf_size,
2622  MHD_HTTP_HEADER_CONNECTION ": Keep-Alive\r\n"))
2623  return MHD_NO;
2624  }
2625  }
2626 
2627  /* User-defined headers */
2628 
2629  if (! add_user_headers (buf, &pos, buf_size, r,
2630  ! c->rp.props.chunked,
2631  (! c->rp.props.use_reply_body_headers) &&
2632  (0 ==
2634  use_conn_close,
2635  use_conn_k_alive))
2636  return MHD_NO;
2637 
2638  /* Other automatic headers */
2639 
2640  if ( (c->rp.props.use_reply_body_headers) &&
2641  (0 == (r->flags & MHD_RF_HEAD_ONLY_RESPONSE)) )
2642  {
2643  /* Body-specific headers */
2644 
2645  if (c->rp.props.chunked)
2646  { /* Chunked encoding is used */
2647  if (0 == (r->flags_auto & MHD_RAF_HAS_TRANS_ENC_CHUNKED))
2648  { /* No chunked encoding header set by user */
2649  if (! buffer_append_s (buf, &pos, buf_size,
2651  "chunked\r\n"))
2652  return MHD_NO;
2653  }
2654  }
2655  else /* Chunked encoding is not used */
2656  {
2657  if (MHD_SIZE_UNKNOWN != r->total_size)
2658  { /* The size is known */
2659  if (0 == (r->flags_auto & MHD_RAF_HAS_CONTENT_LENGTH))
2660  { /* The response does not have "Content-Length" header */
2661  if (! buffer_append_s (buf, &pos, buf_size,
2663  return MHD_NO;
2664  el_size = MHD_uint64_to_str (r->total_size, buf + pos,
2665  buf_size - pos);
2666  if (0 == el_size)
2667  return MHD_NO;
2668  pos += el_size;
2669 
2670  if (buf_size < pos + 2)
2671  return MHD_NO;
2672  buf[pos++] = '\r';
2673  buf[pos++] = '\n';
2674  }
2675  }
2676  }
2677  }
2678 
2679  /* * Header termination * */
2680  if (buf_size < pos + 2)
2681  return MHD_NO;
2682  buf[pos++] = '\r';
2683  buf[pos++] = '\n';
2684 
2685  c->write_buffer_append_offset = pos;
2686  return MHD_YES;
2687 }
2688 
2689 
2699 static enum MHD_Result
2701 {
2702  char *buf;
2703  size_t buf_size;
2704  size_t used_size;
2705  struct MHD_Connection *const c = connection;
2706  struct MHD_HTTP_Res_Header *pos;
2707 
2708  mhd_assert (connection->rp.props.chunked);
2709  /* TODO: allow combining of the final footer with the last chunk,
2710  * modify the next assert. */
2712  mhd_assert (NULL != c->rp.response);
2713 
2714  buf_size = connection_maximize_write_buffer (c);
2715  /* '5' is the minimal size of chunked footer ("0\r\n\r\n") */
2716  if (buf_size < 5)
2717  return MHD_NO;
2718  mhd_assert (NULL != c->write_buffer);
2720  mhd_assert (NULL != buf);
2721  used_size = 0;
2722  buf[used_size++] = '0';
2723  buf[used_size++] = '\r';
2724  buf[used_size++] = '\n';
2725 
2726  for (pos = c->rp.response->first_header; NULL != pos; pos = pos->next)
2727  {
2728  if (MHD_FOOTER_KIND == pos->kind)
2729  {
2730  size_t new_used_size; /* resulting size with this header */
2731  /* '4' is colon, space, linefeeds */
2732  new_used_size = used_size + pos->header_size + pos->value_size + 4;
2733  if (new_used_size > buf_size)
2734  return MHD_NO;
2735  memcpy (buf + used_size, pos->header, pos->header_size);
2736  used_size += pos->header_size;
2737  buf[used_size++] = ':';
2738  buf[used_size++] = ' ';
2739  memcpy (buf + used_size, pos->value, pos->value_size);
2740  used_size += pos->value_size;
2741  buf[used_size++] = '\r';
2742  buf[used_size++] = '\n';
2743  mhd_assert (used_size == new_used_size);
2744  }
2745  }
2746  if (used_size + 2 > buf_size)
2747  return MHD_NO;
2748  buf[used_size++] = '\r';
2749  buf[used_size++] = '\n';
2750 
2751  c->write_buffer_append_offset += used_size;
2753 
2754  return MHD_YES;
2755 }
2756 
2757 
2774 static void
2776  unsigned int status_code,
2777  const char *message,
2778  size_t message_len,
2779  char *header_name,
2780  size_t header_name_len,
2781  char *header_value,
2782  size_t header_value_len)
2783 {
2784  struct MHD_Response *response;
2785  enum MHD_Result iret;
2786 
2787  mhd_assert (! connection->stop_with_error); /* Do not send error twice */
2788  if (connection->stop_with_error)
2789  { /* Should not happen */
2790  if (MHD_CONNECTION_CLOSED > connection->state)
2791  connection->state = MHD_CONNECTION_CLOSED;
2792  free (header_name);
2793  free (header_value);
2794  return;
2795  }
2796  connection->stop_with_error = true;
2797  connection->discard_request = true;
2798 #ifdef HAVE_MESSAGES
2799  MHD_DLOG (connection->daemon,
2800  _ ("Error processing request (HTTP response code is %u ('%s')). " \
2801  "Closing connection.\n"),
2802  status_code,
2803  message);
2804 #endif
2805  if (MHD_CONNECTION_START_REPLY < connection->state)
2806  {
2807 #ifdef HAVE_MESSAGES
2808  MHD_DLOG (connection->daemon,
2809  _ ("Too late to send an error response, " \
2810  "response is being sent already.\n"),
2811  status_code,
2812  message);
2813 #endif
2814  CONNECTION_CLOSE_ERROR (connection,
2815  _ ("Too late for error response."));
2816  free (header_name);
2817  free (header_value);
2818  return;
2819  }
2820  /* TODO: remove when special error queue function is implemented */
2822  if (0 != connection->read_buffer_size)
2823  {
2824  /* Read buffer is not needed anymore, discard it
2825  * to free some space for error response. */
2826  MHD_pool_deallocate (connection->pool,
2827  connection->read_buffer,
2828  connection->read_buffer_size);
2829  connection->read_buffer = NULL;
2830  connection->read_buffer_size = 0;
2831  connection->read_buffer_offset = 0;
2832  }
2833  if (NULL != connection->rp.response)
2834  {
2835  MHD_destroy_response (connection->rp.response);
2836  connection->rp.response = NULL;
2837  }
2838  response = MHD_create_response_from_buffer_static (message_len,
2839  message);
2840  if (NULL == response)
2841  {
2842 #ifdef HAVE_MESSAGES
2843  MHD_DLOG (connection->daemon,
2844  _ ("Failed to create error response.\n"),
2845  status_code,
2846  message);
2847 #endif
2848  /* can't even send a reply, at least close the connection */
2849  connection->state = MHD_CONNECTION_CLOSED;
2850  free (header_name);
2851  free (header_value);
2852  return;
2853  }
2854  mhd_assert ((0 == header_name_len) || (NULL != header_name));
2855  mhd_assert ((NULL == header_name) || (0 != header_name_len));
2856  mhd_assert ((0 == header_value_len) || (NULL != header_value));
2857  mhd_assert ((NULL == header_value) || (0 != header_value_len));
2858  mhd_assert ((NULL == header_name) || (NULL != header_value));
2859  mhd_assert ((NULL != header_value) || (NULL == header_name));
2860  if (NULL != header_name)
2861  {
2862  iret = MHD_add_response_entry_no_alloc_ (response,
2864  header_name, header_name_len,
2865  header_value, header_value_len);
2866  if (MHD_NO == iret)
2867  {
2868  free (header_name);
2869  free (header_value);
2870  }
2871  }
2872  else
2873  iret = MHD_YES;
2874 
2875  if (MHD_NO != iret)
2876  {
2877  bool before = connection->in_access_handler;
2878 
2879  /* Fake the flag for the internal call */
2880  connection->in_access_handler = true;
2881  iret = MHD_queue_response (connection,
2882  status_code,
2883  response);
2884  connection->in_access_handler = before;
2885  }
2886  MHD_destroy_response (response);
2887  if (MHD_NO == iret)
2888  {
2889  /* can't even send a reply, at least close the connection */
2890  CONNECTION_CLOSE_ERROR (connection,
2891  _ ("Closing connection " \
2892  "(failed to queue error response)."));
2893  return;
2894  }
2895  mhd_assert (NULL != connection->rp.response);
2896  /* Do not reuse this connection. */
2897  connection->keepalive = MHD_CONN_MUST_CLOSE;
2898  if (MHD_NO == build_header_response (connection))
2899  {
2900  /* No memory. Release everything. */
2901  connection->rq.version = NULL;
2902  connection->rq.method = NULL;
2903  connection->rq.url = NULL;
2904  connection->rq.url_len = 0;
2905  connection->rq.headers_received = NULL;
2906  connection->rq.headers_received_tail = NULL;
2907  connection->write_buffer = NULL;
2908  connection->write_buffer_size = 0;
2909  connection->write_buffer_send_offset = 0;
2910  connection->write_buffer_append_offset = 0;
2911  connection->read_buffer
2912  = MHD_pool_reset (connection->pool,
2913  NULL,
2914  0,
2915  0);
2916  connection->read_buffer_size = 0;
2917 
2918  /* Retry with empty buffer */
2919  if (MHD_NO == build_header_response (connection))
2920  {
2921  CONNECTION_CLOSE_ERROR (connection,
2922  _ ("Closing connection " \
2923  "(failed to create error response header)."));
2924  return;
2925  }
2926  }
2927  connection->state = MHD_CONNECTION_HEADERS_SENDING;
2928 }
2929 
2930 
2934 #define transmit_error_response_static(c, code, msg) \
2935  transmit_error_response_len (c, code, \
2936  msg, MHD_STATICSTR_LEN_ (msg), \
2937  NULL, 0, NULL, 0)
2938 
2942 #define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l) \
2943  transmit_error_response_len (c, code, \
2944  m, MHD_STATICSTR_LEN_ (m), \
2945  hd_n, hd_n_l, \
2946  hd_v, hd_v_l)
2947 
2948 
2959 static bool
2961 {
2963  if (! c->rq.have_chunked_upload)
2964  return 0 != c->read_buffer_offset;
2965 
2966  /* Chunked upload */
2967  mhd_assert (0 != c->rq.remaining_upload_size); /* Must not be possible in MHD_CONNECTION_BODY_RECEIVING state */
2969  {
2970  /* 0 == c->rq.current_chunk_size: Waiting the chunk size (chunk header).
2971  0 != c->rq.current_chunk_size: Waiting for chunk-closing CRLF. */
2972  return false;
2973  }
2974  return 0 != c->read_buffer_offset; /* Chunk payload data in the read buffer */
2975 }
2976 
2977 
2983 {
2993 };
2994 
2995 
2996 #ifndef MHD_MAX_REASONABLE_HEADERS_SIZE_
3006 # define MHD_MAX_REASONABLE_HEADERS_SIZE_ (6 * 1024)
3007 #endif /* ! MHD_MAX_REASONABLE_HEADERS_SIZE_ */
3008 
3009 #ifndef MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
3020 # define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ 8000
3021 #endif /* ! MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ */
3022 
3023 #ifndef MHD_MIN_REASONABLE_HEADERS_SIZE_
3031 # define MHD_MIN_REASONABLE_HEADERS_SIZE_ 26
3032 #endif /* ! MHD_MIN_REASONABLE_HEADERS_SIZE_ */
3033 
3034 #ifndef MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
3042 # define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ 40
3043 #endif /* ! MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ */
3044 
3045 #ifndef MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
3053 # define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ 16
3054 #endif /* ! MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ */
3055 
3056 #ifndef MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
3062 # define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ 4
3063 #endif /* ! MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ */
3064 
3065 
3077 static unsigned int
3079  enum MHD_ProcRecvDataStage stage,
3080  const char *add_element,
3081  size_t add_element_size)
3082 {
3083  size_t method_size;
3084  size_t uri_size;
3085  size_t opt_headers_size;
3086  size_t host_field_line_size;
3087 
3088  mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVED < c->state);
3089  mhd_assert (MHD_PROC_RECV_HEADERS <= stage);
3090  mhd_assert ((0 == add_element_size) || (NULL != add_element));
3091 
3093  {
3095  opt_headers_size =
3096  (size_t) ((c->read_buffer + c->read_buffer_offset)
3097  - c->rq.field_lines.start);
3098  }
3099  else
3100  opt_headers_size = c->rq.field_lines.size;
3101 
3102  /* The read buffer is fully used by the request line, the field lines
3103  (headers) and internal information.
3104  The return status code works as a suggestion for the client to reduce
3105  one of the request elements. */
3106 
3107  if ((MHD_PROC_RECV_BODY_CHUNKED == stage) &&
3108  (MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_ < add_element_size))
3109  {
3110  /* Request could be re-tried easily with smaller chunk sizes */
3112  }
3113 
3114  host_field_line_size = 0;
3115  /* The "Host:" field line is mandatory.
3116  The total size of the field lines (headers) cannot be smaller than
3117  the size of the "Host:" field line. */
3118  if ((MHD_PROC_RECV_HEADERS == stage)
3119  && (0 != add_element_size))
3120  {
3121  static const size_t header_host_key_len =
3123  const bool is_host_header =
3124  (header_host_key_len + 1 <= add_element_size)
3125  && ( (0 == add_element[header_host_key_len])
3126  || (':' == add_element[header_host_key_len]) )
3128  add_element,
3129  header_host_key_len);
3130  if (is_host_header)
3131  {
3132  const bool is_parsed = ! (
3134  (add_element_size == c->read_buffer_offset) &&
3135  (c->read_buffer == add_element) );
3136  size_t actual_element_size;
3137 
3138  mhd_assert (! is_parsed || (0 == add_element[header_host_key_len]));
3139  /* The actual size should be larger due to CRLF or LF chars,
3140  however the exact termination sequence is not known here and
3141  as perfect precision is not required, to simplify the code
3142  assume the minimal length. */
3143  if (is_parsed)
3144  actual_element_size = add_element_size + 1; /* "1" for LF */
3145  else
3146  actual_element_size = add_element_size;
3147 
3148  host_field_line_size = actual_element_size;
3149  mhd_assert (opt_headers_size >= actual_element_size);
3150  opt_headers_size -= actual_element_size;
3151  }
3152  }
3153  if (0 == host_field_line_size)
3154  {
3155  static const size_t host_field_name_len =
3157  size_t host_field_name_value_len;
3161  host_field_name_len,
3162  NULL,
3163  &host_field_name_value_len))
3164  {
3165  /* Calculate the minimal size of the field line: no space between
3166  colon and the field value, line terminated by LR */
3167  host_field_line_size =
3168  host_field_name_len + host_field_name_value_len + 2; /* "2" for ':' and LF */
3169 
3170  /* The "Host:" field could be added by application */
3171  if (opt_headers_size >= host_field_line_size)
3172  {
3173  opt_headers_size -= host_field_line_size;
3174  /* Take into account typical space after colon and CR at the end of the line */
3175  if (opt_headers_size >= 2)
3176  opt_headers_size -= 2;
3177  }
3178  else
3179  host_field_line_size = 0; /* No "Host:" field line set by the client */
3180  }
3181  }
3182 
3183  uri_size = c->rq.req_target_len;
3184  if (MHD_HTTP_MTHD_OTHER != c->rq.http_mthd)
3185  method_size = 0; /* Do not recommend shorter request method */
3186  else
3187  {
3188  mhd_assert (NULL != c->rq.method);
3189  method_size = strlen (c->rq.method);
3190  }
3191 
3192  if ((size_t) MHD_MAX_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3193  {
3194  /* Typically the easiest way to reduce request header size is
3195  a removal of some optional headers. */
3196  if (opt_headers_size > (uri_size / 8))
3197  {
3198  if ((opt_headers_size / 2) > method_size)
3200  else
3201  return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3202  }
3203  else
3204  { /* Request target is MUCH larger than headers */
3205  if ((uri_size / 16) > method_size)
3206  return MHD_HTTP_URI_TOO_LONG;
3207  else
3208  return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3209  }
3210  }
3211  if ((size_t) MHD_MAX_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3212  {
3213  /* If request target size if larger than maximum reasonable size
3214  recommend client to reduce the request target size (length). */
3215  if ((uri_size / 16) > method_size)
3216  return MHD_HTTP_URI_TOO_LONG; /* Request target is MUCH larger than headers */
3217  else
3218  return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3219  }
3220 
3221  /* The read buffer is too small to handle reasonably large requests */
3222 
3223  if ((size_t) MHD_MIN_REASONABLE_HEADERS_SIZE_ < opt_headers_size)
3224  {
3225  /* Recommend application to retry with minimal headers */
3226  if ((opt_headers_size * 4) > uri_size)
3227  {
3228  if (opt_headers_size > method_size)
3230  else
3231  return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3232  }
3233  else
3234  { /* Request target is significantly larger than headers */
3235  if (uri_size > method_size * 4)
3236  return MHD_HTTP_URI_TOO_LONG;
3237  else
3238  return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3239  }
3240  }
3241  if ((size_t) MHD_MIN_REASONABLE_REQ_TARGET_SIZE_ < uri_size)
3242  {
3243  /* Recommend application to retry with a shorter request target */
3244  if (uri_size > method_size * 4)
3245  return MHD_HTTP_URI_TOO_LONG;
3246  else
3247  return MHD_HTTP_NOT_IMPLEMENTED; /* The length of the HTTP request method is unreasonably large */
3248  }
3249 
3250  if ((size_t) MHD_MIN_REASONABLE_REQ_METHOD_SIZE_ < method_size)
3251  {
3252  /* The request target (URI) and headers are (reasonably) very small.
3253  Some non-standard long request method is used. */
3254  /* The last resort response as it means "the method is not supported
3255  by the server for any URI". */
3256  return MHD_HTTP_NOT_IMPLEMENTED;
3257  }
3258 
3259  /* The almost impossible situation: all elements are small, but cannot
3260  fit the buffer. The application set the buffer size to
3261  critically low value? */
3262 
3263  if ((1 < opt_headers_size) || (1 < uri_size))
3264  {
3265  if (opt_headers_size >= uri_size)
3267  else
3268  return MHD_HTTP_URI_TOO_LONG;
3269  }
3270 
3271  /* Nothing to reduce in the request.
3272  Reply with some status. */
3273  if (0 != host_field_line_size)
3275 
3276  return MHD_HTTP_URI_TOO_LONG;
3277 }
3278 
3279 
3290 static void
3292  const char *add_header,
3293  size_t add_header_size)
3294 {
3295  unsigned int err_code;
3296 
3297  err_code = get_no_space_err_status_code (c,
3299  add_header,
3300  add_header_size);
3302  err_code,
3304 }
3305 
3306 
3307 #ifdef COOKIE_SUPPORT
3313 static void
3314 handle_req_cookie_no_space (struct MHD_Connection *c)
3315 {
3316  unsigned int err_code;
3317 
3318  err_code = get_no_space_err_status_code (c,
3320  NULL,
3321  0);
3323  err_code,
3325 }
3326 
3327 
3328 #endif /* COOKIE_SUPPORT */
3329 
3330 
3341 static void
3343  const char *chunk_size_line,
3344  size_t chunk_size_line_size)
3345 {
3346  unsigned int err_code;
3347 
3348  if (NULL != chunk_size_line)
3349  {
3350  const char *semicol;
3351  /* Check for chunk extension */
3352  semicol = memchr (chunk_size_line, ';', chunk_size_line_size);
3353  if (NULL != semicol)
3354  { /* Chunk extension present. It could be removed without any loss of the
3355  details of the request. */
3359  }
3360  }
3361  err_code = get_no_space_err_status_code (c,
3363  chunk_size_line,
3364  chunk_size_line_size);
3366  err_code,
3368 }
3369 
3370 
3381 static void
3383  const char *add_footer,
3384  size_t add_footer_size)
3385 {
3386  (void) add_footer; (void) add_footer_size; /* Unused */
3388 
3389  /* Footers should be optional */
3393 }
3394 
3395 
3406 static void
3408  enum MHD_ProcRecvDataStage stage)
3409 {
3410  mhd_assert (MHD_PROC_RECV_INIT <= stage);
3411  mhd_assert (MHD_PROC_RECV_FOOTERS >= stage);
3413  mhd_assert ((MHD_PROC_RECV_INIT != stage) || \
3414  (MHD_CONNECTION_INIT == c->state));
3415  mhd_assert ((MHD_PROC_RECV_METHOD != stage) || \
3417  mhd_assert ((MHD_PROC_RECV_URI != stage) || \
3419  mhd_assert ((MHD_PROC_RECV_HTTPVER != stage) || \
3421  mhd_assert ((MHD_PROC_RECV_HEADERS != stage) || \
3423  mhd_assert (MHD_PROC_RECV_COOKIE != stage); /* handle_req_cookie_no_space() must be called directly */
3424  mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3426  mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3428  mhd_assert ((MHD_PROC_RECV_FOOTERS != stage) || \
3430  mhd_assert ((MHD_PROC_RECV_BODY_NORMAL != stage) || \
3431  (! c->rq.have_chunked_upload));
3432  mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3433  (c->rq.have_chunked_upload));
3434  switch (stage)
3435  {
3436  case MHD_PROC_RECV_INIT:
3437  case MHD_PROC_RECV_METHOD:
3438  /* Some data has been received, but it is not clear yet whether
3439  * the received data is an valid HTTP request */
3441  _ ("No space left in the read buffer when " \
3442  "receiving the initial part of " \
3443  "the request line."));
3444  return;
3445  case MHD_PROC_RECV_URI:
3446  case MHD_PROC_RECV_HTTPVER:
3447  /* Some data has been received, but the request line is incomplete */
3450  /* A quick simple check whether the incomplete line looks
3451  * like an HTTP request */
3452  if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
3454  {
3458  return;
3459  }
3461  _ ("No space left in the read buffer when " \
3462  "receiving the URI in " \
3463  "the request line. " \
3464  "The request uses non-standard HTTP request " \
3465  "method token."));
3466  return;
3467  case MHD_PROC_RECV_HEADERS:
3469  return;
3472  mhd_assert ((MHD_PROC_RECV_BODY_CHUNKED != stage) || \
3473  ! c->rq.some_payload_processed);
3475  {
3476  /* The connection must not be in MHD_EVENT_LOOP_INFO_READ state
3477  when external polling is used and some data left unprocessed. */
3479  /* failed to grow the read buffer, and the
3480  client which is supposed to handle the
3481  received data in a *blocking* fashion
3482  (in this mode) did not handle the data as
3483  it was supposed to!
3484  => we would either have to do busy-waiting
3485  (on the client, which would likely fail),
3486  or if we do nothing, we would just timeout
3487  on the connection (if a timeout is even
3488  set!).
3489  Solution: we kill the connection with an error */
3493  }
3494  else
3495  {
3496  if (MHD_PROC_RECV_BODY_NORMAL == stage)
3497  {
3498  /* A header probably has been added to a suspended connection and
3499  it took precisely all the space in the buffer.
3500  Very low probability. */
3503  }
3504  else
3505  {
3508  { /* Receiving content of the chunk */
3509  /* A header probably has been added to a suspended connection and
3510  it took precisely all the space in the buffer.
3511  Very low probability. */
3513  }
3514  else
3515  {
3516  if (0 != c->rq.current_chunk_size)
3517  { /* Waiting for chunk-closing CRLF */
3518  /* Not really possible as some payload should be
3519  processed and the space used by payload should be available. */
3521  }
3522  else
3523  { /* Reading the line with the chunk size */
3525  c->read_buffer,
3526  c->read_buffer_offset);
3527  }
3528  }
3529  }
3530  }
3531  return;
3532  case MHD_PROC_RECV_FOOTERS:
3534  return;
3535  /* The next cases should not be possible */
3536  case MHD_PROC_RECV_COOKIE:
3537  default:
3538  break;
3539  }
3540  mhd_assert (0);
3541 }
3542 
3543 
3557 static bool
3559 {
3563  bool rbuff_grow_desired;
3567  bool rbuff_grow_required;
3568 
3570  mhd_assert (! c->discard_request);
3571 
3572  rbuff_grow_required = (c->read_buffer_offset == c->read_buffer_size);
3573  if (rbuff_grow_required)
3574  rbuff_grow_desired = true;
3575  else
3576  {
3577  rbuff_grow_desired = (c->read_buffer_offset + c->daemon->pool_increment >
3578  c->read_buffer_size);
3579 
3580  if ((rbuff_grow_desired) &&
3582  {
3583  if (! c->rq.have_chunked_upload)
3584  {
3586  /* Do not grow read buffer more than necessary to process the current
3587  request. */
3588  rbuff_grow_desired =
3590  }
3591  else
3592  {
3594  if (0 == c->rq.current_chunk_size)
3595  rbuff_grow_desired = /* Reading value of the next chunk size */
3597  c->read_buffer_size);
3598  else
3599  {
3600  const uint64_t cur_chunk_left =
3602  /* Do not grow read buffer more than necessary to process the current
3603  chunk with terminating CRLF. */
3605  rbuff_grow_desired =
3606  ((cur_chunk_left + 2) > (uint64_t) (c->read_buffer_size));
3607  }
3608  }
3609  }
3610  }
3611 
3612  if (! rbuff_grow_desired)
3613  return true; /* No need to increase the buffer */
3614 
3615  if (try_grow_read_buffer (c, rbuff_grow_required))
3616  return true; /* Buffer increase succeed */
3617 
3618  if (! rbuff_grow_required)
3619  return true; /* Can continue without buffer increase */
3620 
3621  /* Failed to increase the read buffer size, but need to read the data
3622  from the network.
3623  No more space left in the buffer, no more space to increase the buffer. */
3624 
3625  /* 'PROCESS_READ' event state flag must be set only if the last application
3626  callback has processed some data. If any data is processed then some
3627  space in the read buffer must be available. */
3629 
3630  if ((! MHD_D_IS_USING_THREADS_ (c->daemon))
3633  {
3634  /* The application is handling processing cycles.
3635  The data could be processed later. */
3637  return true;
3638  }
3639  else
3640  {
3641  enum MHD_ProcRecvDataStage stage;
3642 
3643  switch (c->state)
3644  {
3645  case MHD_CONNECTION_INIT:
3646  stage = MHD_PROC_RECV_INIT;
3647  break;
3650  stage = MHD_PROC_RECV_METHOD;
3651  else if (0 == c->rq.req_target_len)
3652  stage = MHD_PROC_RECV_URI;
3653  else
3654  stage = MHD_PROC_RECV_HTTPVER;
3655  break;
3657  stage = MHD_PROC_RECV_HEADERS;
3658  break;
3660  stage = c->rq.have_chunked_upload ?
3662  break;
3664  stage = MHD_PROC_RECV_FOOTERS;
3665  break;
3683  case MHD_CONNECTION_CLOSED:
3684 #ifdef UPGRADE_SUPPORT
3685  case MHD_CONNECTION_UPGRADE:
3686 #endif
3687  default:
3688  stage = MHD_PROC_RECV_BODY_NORMAL;
3689  mhd_assert (0);
3690  }
3691 
3692  handle_recv_no_space (c, stage);
3693  }
3694  return false;
3695 }
3696 
3697 
3706 static void
3708 {
3709  /* Do not update states of suspended connection */
3710  if (connection->suspended)
3711  return; /* States will be updated after resume. */
3712 #ifdef HTTPS_SUPPORT
3713  if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
3714  { /* HTTPS connection. */
3715  switch (connection->tls_state)
3716  {
3717  case MHD_TLS_CONN_INIT:
3719  return;
3722  if (0 == gnutls_record_get_direction (connection->tls_session))
3724  else
3726  return;
3728  break; /* Do normal processing */
3732  return;
3733  case MHD_TLS_CONN_TLS_CLOSING: /* Not implemented yet */
3734  case MHD_TLS_CONN_TLS_CLOSED: /* Not implemented yet */
3736  case MHD_TLS_CONN_NO_TLS: /* Not possible */
3737  default:
3738  MHD_PANIC (_ ("Invalid TLS state value.\n"));
3739  }
3740  }
3741 #endif /* HTTPS_SUPPORT */
3742  while (1)
3743  {
3744 #if DEBUG_STATES
3745  MHD_DLOG (connection->daemon,
3746  _ ("In function %s handling connection at state: %s\n"),
3747  MHD_FUNC_,
3748  MHD_state_to_string (connection->state));
3749 #endif
3750  switch (connection->state)
3751  {
3752  case MHD_CONNECTION_INIT:
3755  break;
3757  mhd_assert (0);
3758  break;
3761  break;
3764  mhd_assert (0);
3765  break;
3768  break;
3770  if ((connection->rq.some_payload_processed) &&
3772  {
3773  /* Some data was processed, the buffer must have some free space */
3774  mhd_assert (connection->read_buffer_offset < \
3775  connection->read_buffer_size);
3776  if (! connection->rq.have_chunked_upload)
3777  {
3778  /* Not a chunked upload. Do not read more than necessary to
3779  process the current request. */
3780  if (connection->rq.remaining_upload_size >=
3781  connection->read_buffer_offset)
3783  else
3785  }
3786  else
3787  {
3788  /* Chunked upload. The size of the current request is unknown.
3789  Continue reading as the space in the read buffer is available. */
3791  }
3792  }
3793  else
3795  break;
3797  mhd_assert (0);
3798  break;
3801  break;
3803  mhd_assert (0);
3804  break;
3807  break;
3809  mhd_assert (0);
3810  break;
3812  /* headers in buffer, keep writing */
3814  break;
3816  mhd_assert (0);
3817  break;
3820  break;
3823  break;
3826  break;
3829  break;
3831  mhd_assert (0);
3832  break;
3835  break;
3837  mhd_assert (0);
3838  break;
3839  case MHD_CONNECTION_CLOSED:
3841  return; /* do nothing, not even reading */
3842 #ifdef UPGRADE_SUPPORT
3843  case MHD_CONNECTION_UPGRADE:
3844  mhd_assert (0);
3845  break;
3846 #endif /* UPGRADE_SUPPORT */
3847  default:
3848  mhd_assert (0);
3849  }
3850 
3851  if (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info))
3852  {
3853  /* Check whether the space is available to receive data */
3854  if (! check_and_grow_read_buffer_space (connection))
3855  {
3856  mhd_assert (connection->discard_request);
3857  continue;
3858  }
3859  }
3860  break; /* Everything was processed. */
3861  }
3862 }
3863 
3864 
3877 static enum MHD_Result
3878 connection_add_header (void *cls,
3879  const char *key,
3880  size_t key_size,
3881  const char *value,
3882  size_t value_size,
3883  enum MHD_ValueKind kind)
3884 {
3885  struct MHD_Connection *connection = (struct MHD_Connection *) cls;
3886  if (MHD_NO ==
3887  MHD_set_connection_value_n (connection,
3888  kind,
3889  key,
3890  key_size,
3891  value,
3892  value_size))
3893  {
3894 #ifdef HAVE_MESSAGES
3895  MHD_DLOG (connection->daemon,
3896  _ ("Not enough memory in pool to allocate header record!\n"));
3897 #endif
3898  transmit_error_response_static (connection,
3901  return MHD_NO;
3902  }
3903  return MHD_YES;
3904 }
3905 
3906 
3907 #ifdef COOKIE_SUPPORT
3908 
3912 enum _MHD_ParseCookie
3913 {
3914  MHD_PARSE_COOKIE_OK = MHD_YES,
3915  MHD_PARSE_COOKIE_OK_LAX = 2,
3916  MHD_PARSE_COOKIE_MALFORMED = -1,
3917  MHD_PARSE_COOKIE_NO_MEMORY = MHD_NO
3918 };
3919 
3920 
3933 static enum _MHD_ParseCookie
3934 parse_cookies_string (char *str,
3935  const size_t str_len,
3936  struct MHD_Connection *connection)
3937 {
3938  size_t i;
3939  bool non_strict;
3940  /* Skip extra whitespaces and empty cookies */
3941  const bool allow_wsp_empty = (0 >= connection->daemon->client_discipline);
3942  /* Allow whitespaces around '=' character */
3943  const bool wsp_around_eq = (-3 >= connection->daemon->client_discipline);
3944  /* Allow whitespaces in quoted cookie value */
3945  const bool wsp_in_quoted = (-2 >= connection->daemon->client_discipline);
3946  /* Allow tab as space after semicolon between cookies */
3947  const bool tab_as_sp = (0 >= connection->daemon->client_discipline);
3948  /* Allow no space after semicolon between cookies */
3949  const bool allow_no_space = (0 >= connection->daemon->client_discipline);
3950 
3951  non_strict = false;
3952  i = 0;
3953  while (i < str_len)
3954  {
3955  size_t name_start;
3956  size_t name_len;
3957  size_t value_start;
3958  size_t value_len;
3959  bool val_quoted;
3960  /* Skip any whitespaces and empty cookies */
3961  while (' ' == str[i] || '\t' == str[i] || ';' == str[i])
3962  {
3963  if (! allow_wsp_empty)
3964  return MHD_PARSE_COOKIE_MALFORMED;
3965  non_strict = true;
3966  i++;
3967  if (i == str_len)
3968  return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
3969  }
3970  /* 'i' must point to the first char of cookie-name */
3971  name_start = i;
3972  /* Find the end of the cookie-name */
3973  do
3974  {
3975  const char l = str[i];
3976  if (('=' == l) || (' ' == l) || ('\t' == l) || ('"' == l) || (',' == l) ||
3977  (';' == l) || (0 == l))
3978  break;
3979  } while (str_len > ++i);
3980  name_len = i - name_start;
3981  /* Skip any whitespaces */
3982  while (str_len > i && (' ' == str[i] || '\t' == str[i]))
3983  {
3984  if (! wsp_around_eq)
3985  return MHD_PARSE_COOKIE_MALFORMED;
3986  non_strict = true;
3987  i++;
3988  }
3989  if ((str_len == i) || ('=' != str[i]) || (0 == name_len))
3990  return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie name */
3991  /* 'i' must point to the '=' char */
3992  mhd_assert ('=' == str[i]);
3993  i++;
3994  /* Skip any whitespaces */
3995  while (str_len > i && (' ' == str[i] || '\t' == str[i]))
3996  {
3997  if (! wsp_around_eq)
3998  return MHD_PARSE_COOKIE_MALFORMED;
3999  non_strict = true;
4000  i++;
4001  }
4002  /* 'i' must point to the first char of cookie-value */
4003  if (str_len == i)
4004  {
4005  value_start = 0;
4006  value_len = 0;
4007 #ifdef _DEBUG
4008  val_quoted = false; /* This assignment used in assert */
4009 #endif
4010  }
4011  else
4012  {
4013  bool valid_cookie;
4014  val_quoted = ('"' == str[i]);
4015  if (val_quoted)
4016  i++;
4017  value_start = i;
4018  /* Find the end of the cookie-value */
4019  while (str_len > i)
4020  {
4021  const char l = str[i];
4022  if ((';' == l) || ('"' == l) || (',' == l) || (';' == l) ||
4023  ('\\' == l) || (0 == l))
4024  break;
4025  if ((' ' == l) || ('\t' == l))
4026  {
4027  if (! val_quoted)
4028  break;
4029  if (! wsp_in_quoted)
4030  return MHD_PARSE_COOKIE_MALFORMED;
4031  non_strict = true;
4032  }
4033  i++;
4034  }
4035  value_len = i - value_start;
4036  if (val_quoted)
4037  {
4038  if ((str_len == i) || ('"' != str[i]))
4039  return MHD_PARSE_COOKIE_MALFORMED; /* Incomplete cookie value, no closing quote */
4040  i++;
4041  }
4042  /* Skip any whitespaces */
4043  if ((str_len > i) && ((' ' == str[i]) || ('\t' == str[i])))
4044  {
4045  do
4046  {
4047  i++;
4048  } while (str_len > i && (' ' == str[i] || '\t' == str[i]));
4049  /* Whitespace at the end? */
4050  if (str_len > i)
4051  {
4052  if (! allow_wsp_empty)
4053  return MHD_PARSE_COOKIE_MALFORMED;
4054  non_strict = true;
4055  }
4056  }
4057  if (str_len == i)
4058  valid_cookie = true;
4059  else if (';' == str[i])
4060  valid_cookie = true;
4061  else
4062  valid_cookie = false;
4063 
4064  if (! valid_cookie)
4065  return MHD_PARSE_COOKIE_MALFORMED; /* Garbage at the end of the cookie value */
4066  }
4067  mhd_assert (0 != name_len);
4068  str[name_start + name_len] = 0; /* Zero-terminate the name */
4069  if (0 != value_len)
4070  {
4071  mhd_assert (value_start + value_len <= str_len);
4072  str[value_start + value_len] = 0; /* Zero-terminate the value */
4073  if (MHD_NO ==
4076  str + name_start,
4077  name_len,
4078  str + value_start,
4079  value_len))
4080  return MHD_PARSE_COOKIE_NO_MEMORY;
4081  }
4082  else
4083  {
4084  if (MHD_NO ==
4087  str + name_start,
4088  name_len,
4089  "",
4090  0))
4091  return MHD_PARSE_COOKIE_NO_MEMORY;
4092  }
4093  if (str_len > i)
4094  {
4095  mhd_assert (0 == str[i] || ';' == str[i]);
4096  mhd_assert (! val_quoted || ';' == str[i]);
4097  mhd_assert (';' != str[i] || val_quoted || non_strict || 0 == value_len);
4098  i++;
4099  if (str_len == i)
4100  { /* No next cookie after semicolon */
4101  if (! allow_wsp_empty)
4102  return MHD_PARSE_COOKIE_MALFORMED;
4103  non_strict = true;
4104  }
4105  else if (' ' != str[i])
4106  {/* No space after semicolon */
4107  if (('\t' == str[i]) && tab_as_sp)
4108  i++;
4109  else if (! allow_no_space)
4110  return MHD_PARSE_COOKIE_MALFORMED;
4111  non_strict = true;
4112  }
4113  else
4114  {
4115  i++;
4116  if (str_len == i)
4117  {
4118  if (! allow_wsp_empty)
4119  return MHD_PARSE_COOKIE_MALFORMED;
4120  non_strict = true;
4121  }
4122  }
4123  }
4124  }
4125  return non_strict? MHD_PARSE_COOKIE_OK_LAX : MHD_PARSE_COOKIE_OK;
4126 }
4127 
4128 
4135 static enum _MHD_ParseCookie
4136 parse_cookie_header (struct MHD_Connection *connection)
4137 {
4138  const char *hdr;
4139  size_t hdr_len;
4140  char *cpy;
4141  size_t i;
4142  enum _MHD_ParseCookie parse_res;
4143  struct MHD_HTTP_Req_Header *const saved_tail =
4144  connection->rq.headers_received_tail;
4145  const bool allow_partially_correct_cookie =
4146  (1 >= connection->daemon->client_discipline);
4147 
4148  if (MHD_NO ==
4149  MHD_lookup_connection_value_n (connection,
4154  &hdr,
4155  &hdr_len))
4156  return MHD_PARSE_COOKIE_OK;
4157  if (0 == hdr_len)
4158  return MHD_PARSE_COOKIE_OK;
4159 
4160  cpy = MHD_connection_alloc_memory_ (connection,
4161  hdr_len + 1);
4162  if (NULL == cpy)
4163  parse_res = MHD_PARSE_COOKIE_NO_MEMORY;
4164  else
4165  {
4166  memcpy (cpy,
4167  hdr,
4168  hdr_len);
4169  cpy[hdr_len] = '\0';
4170 
4171  i = 0;
4172  /* Skip all initial whitespaces */
4173  while (i < hdr_len && (' ' == cpy[i] || '\t' == cpy[i]))
4174  i++;
4175 
4176  parse_res = parse_cookies_string (cpy + i, hdr_len - i, connection);
4177  }
4178 
4179  switch (parse_res)
4180  {
4181  case MHD_PARSE_COOKIE_OK:
4182  break;
4183  case MHD_PARSE_COOKIE_OK_LAX:
4184 #ifdef HAVE_MESSAGES
4185  if (saved_tail != connection->rq.headers_received_tail)
4186  MHD_DLOG (connection->daemon,
4187  _ ("The Cookie header has been parsed, but it is not fully "
4188  "compliant with the standard.\n"));
4189 #endif /* HAVE_MESSAGES */
4190  break;
4191  case MHD_PARSE_COOKIE_MALFORMED:
4192  if (saved_tail != connection->rq.headers_received_tail)
4193  {
4194  if (! allow_partially_correct_cookie)
4195  {
4196  /* Remove extracted values from partially broken cookie */
4197  /* Memory remains allocated until the end of the request processing */
4198  connection->rq.headers_received_tail = saved_tail;
4199  saved_tail->next = NULL;
4200 #ifdef HAVE_MESSAGES
4201  MHD_DLOG (connection->daemon,
4202  _ ("The Cookie header has been ignored as it contains "
4203  "malformed data.\n"));
4204 #endif /* HAVE_MESSAGES */
4205  }
4206 #ifdef HAVE_MESSAGES
4207  else
4208  MHD_DLOG (connection->daemon,
4209  _ ("The Cookie header has been only partially parsed as it "
4210  "contains malformed data.\n"));
4211 #endif /* HAVE_MESSAGES */
4212  }
4213 #ifdef HAVE_MESSAGES
4214  else
4215  MHD_DLOG (connection->daemon,
4216  _ ("The Cookie header has malformed data.\n"));
4217 #endif /* HAVE_MESSAGES */
4218  break;
4219  case MHD_PARSE_COOKIE_NO_MEMORY:
4220 #ifdef HAVE_MESSAGES
4221  MHD_DLOG (connection->daemon,
4222  _ ("Not enough memory in the connection pool to "
4223  "parse client cookies!\n"));
4224 #endif /* HAVE_MESSAGES */
4225  break;
4226  default:
4227  mhd_assert (0);
4228  break;
4229  }
4230 #ifndef HAVE_MESSAGES
4231  (void) saved_tail; /* Mute compiler warning */
4232 #endif /* ! HAVE_MESSAGES */
4233 
4234  return parse_res;
4235 }
4236 
4237 
4238 #endif /* COOKIE_SUPPORT */
4239 
4240 
4244 #define HTTP_VER_LEN (MHD_STATICSTR_LEN_ (MHD_HTTP_VERSION_1_1))
4245 
4255 static bool
4257  const char *http_string,
4258  size_t len)
4259 {
4260  const char *const h = http_string;
4261  mhd_assert (NULL != http_string);
4262 
4263  /* String must start with 'HTTP/d.d', case-sensetive match.
4264  * See https://www.rfc-editor.org/rfc/rfc9112#name-http-version */
4265  if ((HTTP_VER_LEN != len) ||
4266  ('H' != h[0]) || ('T' != h[1]) || ('T' != h[2]) || ('P' != h[3]) ||
4267  ('/' != h[4])
4268  || ('.' != h[6]) ||
4269  (('0' > h[5]) || ('9' < h[5])) ||
4270  (('0' > h[7]) || ('9' < h[7])))
4271  {
4272  connection->rq.http_ver = MHD_HTTP_VER_INVALID;
4273  transmit_error_response_static (connection,
4276  return false;
4277  }
4278  if (1 == h[5] - '0')
4279  {
4280  /* HTTP/1.x */
4281  if (1 == h[7] - '0')
4282  connection->rq.http_ver = MHD_HTTP_VER_1_1;
4283  else if (0 == h[7] - '0')
4284  connection->rq.http_ver = MHD_HTTP_VER_1_0;
4285  else
4286  connection->rq.http_ver = MHD_HTTP_VER_1_2__1_9;
4287 
4288  return true;
4289  }
4290 
4291  if (0 == h[5] - '0')
4292  {
4293  /* Too old major version */
4294  connection->rq.http_ver = MHD_HTTP_VER_TOO_OLD;
4295  transmit_error_response_static (connection,
4298  return false;
4299  }
4300 
4301  connection->rq.http_ver = MHD_HTTP_VER_FUTURE;
4302  transmit_error_response_static (connection,
4305  return false;
4306 }
4307 
4308 
4316 static void
4318  const char *method,
4319  size_t len)
4320 {
4321  const char *const m = method;
4322  mhd_assert (NULL != m);
4323  mhd_assert (0 != len);
4324 
4325  if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET) == len) &&
4326  (0 == memcmp (m, MHD_HTTP_METHOD_GET, len)))
4327  connection->rq.http_mthd = MHD_HTTP_MTHD_GET;
4328  else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_HEAD) == len) &&
4329  (0 == memcmp (m, MHD_HTTP_METHOD_HEAD, len)))
4330  connection->rq.http_mthd = MHD_HTTP_MTHD_HEAD;
4331  else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_POST) == len) &&
4332  (0 == memcmp (m, MHD_HTTP_METHOD_POST, len)))
4333  connection->rq.http_mthd = MHD_HTTP_MTHD_POST;
4334  else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_PUT) == len) &&
4335  (0 == memcmp (m, MHD_HTTP_METHOD_PUT, len)))
4336  connection->rq.http_mthd = MHD_HTTP_MTHD_PUT;
4337  else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_DELETE) == len) &&
4338  (0 == memcmp (m, MHD_HTTP_METHOD_DELETE, len)))
4339  connection->rq.http_mthd = MHD_HTTP_MTHD_DELETE;
4340  else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_CONNECT) == len) &&
4341  (0 == memcmp (m, MHD_HTTP_METHOD_CONNECT, len)))
4342  connection->rq.http_mthd = MHD_HTTP_MTHD_CONNECT;
4343  else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_OPTIONS) == len) &&
4344  (0 == memcmp (m, MHD_HTTP_METHOD_OPTIONS, len)))
4345  connection->rq.http_mthd = MHD_HTTP_MTHD_OPTIONS;
4346  else if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_TRACE) == len) &&
4347  (0 == memcmp (m, MHD_HTTP_METHOD_TRACE, len)))
4348  connection->rq.http_mthd = MHD_HTTP_MTHD_TRACE;
4349  else
4350  connection->rq.http_mthd = MHD_HTTP_MTHD_OTHER;
4351 }
4352 
4353 
4361 static void
4363 {
4364  struct MHD_Daemon *daemon = connection->daemon;
4365  size_t processed;
4366 
4367  if (NULL != connection->rp.response)
4368  return; /* already queued a response */
4369  processed = 0;
4370  connection->rq.client_aware = true;
4371  connection->in_access_handler = true;
4372  if (MHD_NO ==
4373  daemon->default_handler (daemon->default_handler_cls,
4374  connection,
4375  connection->rq.url,
4376  connection->rq.method,
4377  connection->rq.version,
4378  NULL,
4379  &processed,
4380  &connection->rq.client_context))
4381  {
4382  connection->in_access_handler = false;
4383  /* serious internal error, close connection */
4384  CONNECTION_CLOSE_ERROR (connection,
4385  _ ("Application reported internal error, " \
4386  "closing connection."));
4387  return;
4388  }
4389  connection->in_access_handler = false;
4390 }
4391 
4392 
4400 static void
4402 {
4403  struct MHD_Daemon *daemon = connection->daemon;
4404  size_t available;
4405  bool instant_retry;
4406  char *buffer_head;
4407  const int discp_lvl = daemon->client_discipline;
4408  /* Treat bare LF as the end of the line.
4409  RFC 9112, section 2.2-3
4410  Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
4411  Bare LF is processed as end of the line or rejected as broken request. */
4412  const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
4413  /* Allow "Bad WhiteSpace" in chunk extension.
4414  RFC 9112, Section 7.1.1, Paragraph 2 */
4415  const bool allow_bws = (2 < discp_lvl);
4416 
4417  mhd_assert (NULL == connection->rp.response);
4418 
4419  buffer_head = connection->read_buffer;
4420  available = connection->read_buffer_offset;
4421  do
4422  {
4423  size_t to_be_processed;
4424  size_t left_unprocessed;
4425  size_t processed_size;
4426 
4427  instant_retry = false;
4428  if (connection->rq.have_chunked_upload)
4429  {
4431  if ( (connection->rq.current_chunk_offset ==
4432  connection->rq.current_chunk_size) &&
4433  (0 != connection->rq.current_chunk_size) )
4434  {
4435  size_t i;
4436  mhd_assert (0 != available);
4437  /* skip new line at the *end* of a chunk */
4438  i = 0;
4439  if ( (2 <= available) &&
4440  ('\r' == buffer_head[0]) &&
4441  ('\n' == buffer_head[1]) )
4442  i += 2; /* skip CRLF */
4443  else if (bare_lf_as_crlf && ('\n' == buffer_head[0]))
4444  i++; /* skip bare LF */
4445  else if (2 > available)
4446  break; /* need more upload data */
4447  if (0 == i)
4448  {
4449  /* malformed encoding */
4450  transmit_error_response_static (connection,
4453  return;
4454  }
4455  available -= i;
4456  buffer_head += i;
4457  connection->rq.current_chunk_offset = 0;
4458  connection->rq.current_chunk_size = 0;
4459  if (0 == available)
4460  break;
4461  }
4462  if (0 != connection->rq.current_chunk_size)
4463  {
4464  uint64_t cur_chunk_left;
4465  mhd_assert (connection->rq.current_chunk_offset < \
4466  connection->rq.current_chunk_size);
4467  /* we are in the middle of a chunk, give
4468  as much as possible to the client (without
4469  crossing chunk boundaries) */
4470  cur_chunk_left
4471  = connection->rq.current_chunk_size
4472  - connection->rq.current_chunk_offset;
4473  if (cur_chunk_left > available)
4474  to_be_processed = available;
4475  else
4476  { /* cur_chunk_left <= (size_t)available */
4477  to_be_processed = (size_t) cur_chunk_left;
4478  if (available > to_be_processed)
4479  instant_retry = true;
4480  }
4481  }
4482  else
4483  { /* Need the parse the chunk size line */
4485  size_t num_dig;
4486  uint64_t chunk_size;
4487  bool broken;
4488  bool overflow;
4489 
4490  mhd_assert (0 != available);
4491 
4492  overflow = false;
4493  chunk_size = 0; /* Mute possible compiler warning.
4494  The real value will be set later. */
4495 
4496  num_dig = MHD_strx_to_uint64_n_ (buffer_head,
4497  available,
4498  &chunk_size);
4499  mhd_assert (num_dig <= available);
4500  if (num_dig == available)
4501  continue; /* Need line delimiter */
4502 
4503  broken = (0 == num_dig);
4504  if (broken)
4505  /* Check whether result is invalid due to uint64_t overflow */
4506  overflow = ((('0' <= buffer_head[0]) && ('9' >= buffer_head[0])) ||
4507  (('A' <= buffer_head[0]) && ('F' >= buffer_head[0])) ||
4508  (('a' <= buffer_head[0]) && ('f' >= buffer_head[0])));
4509  else
4510  {
4515  size_t chunk_size_line_len;
4516 
4517  chunk_size_line_len = 0;
4518  if ((';' == buffer_head[num_dig]) ||
4519  (allow_bws &&
4520  ((' ' == buffer_head[num_dig]) ||
4521  ('\t' == buffer_head[num_dig]))))
4522  { /* Chunk extension */
4523  size_t i;
4524 
4525  /* Skip bad whitespaces (if any) */
4526  for (i = num_dig; i < available; ++i)
4527  {
4528  if ((' ' != buffer_head[i]) && ('\t' != buffer_head[i]))
4529  break;
4530  }
4531  if (i == available)
4532  break; /* need more data */
4533  if (';' == buffer_head[i])
4534  {
4535  for (++i; i < available; ++i)
4536  {
4537  if ('\n' == buffer_head[i])
4538  break;
4539  }
4540  if (i == available)
4541  break; /* need more data */
4542  mhd_assert (i > num_dig);
4543  mhd_assert (1 <= i);
4544  /* Found LF position */
4545  if (bare_lf_as_crlf)
4546  chunk_size_line_len = i; /* Don't care about CR before LF */
4547  else if ('\r' == buffer_head[i - 1])
4548  chunk_size_line_len = i;
4549  }
4550  else
4551  { /* No ';' after "bad whitespace" */
4552  mhd_assert (allow_bws);
4553  mhd_assert (0 == chunk_size_line_len);
4554  }
4555  }
4556  else
4557  {
4558  mhd_assert (available >= num_dig);
4559  if ((2 <= (available - num_dig)) &&
4560  ('\r' == buffer_head[num_dig]) &&
4561  ('\n' == buffer_head[num_dig + 1]))
4562  chunk_size_line_len = num_dig + 2;
4563  else if (bare_lf_as_crlf &&
4564  ('\n' == buffer_head[num_dig]))
4565  chunk_size_line_len = num_dig + 1;
4566  else if (2 > (available - num_dig))
4567  break; /* need more data */
4568  }
4569 
4570  if (0 != chunk_size_line_len)
4571  { /* Valid termination of the chunk size line */
4572  mhd_assert (chunk_size_line_len <= available);
4573  /* Start reading payload data of the chunk */
4574  connection->rq.current_chunk_offset = 0;
4575  connection->rq.current_chunk_size = chunk_size;
4576 
4577  available -= chunk_size_line_len;
4578  buffer_head += chunk_size_line_len;
4579 
4580  if (0 == chunk_size)
4581  { /* The final (termination) chunk */
4582  connection->rq.remaining_upload_size = 0;
4583  break;
4584  }
4585  if (available > 0)
4586  instant_retry = true;
4587  continue;
4588  }
4589  /* Invalid chunk size line */
4590  }
4591 
4592  if (! overflow)
4593  transmit_error_response_static (connection,
4596  else
4597  transmit_error_response_static (connection,
4600  return;
4601  }
4602  }
4603  else
4604  {
4605  /* no chunked encoding, give all to the client */
4607  mhd_assert (0 != connection->rq.remaining_upload_size);
4608  if (connection->rq.remaining_upload_size < available)
4609  to_be_processed = (size_t) connection->rq.remaining_upload_size;
4610  else
4611  to_be_processed = available;
4612  }
4613  left_unprocessed = to_be_processed;
4614  connection->rq.client_aware = true;
4615  connection->in_access_handler = true;
4616  if (MHD_NO ==
4617  daemon->default_handler (daemon->default_handler_cls,
4618  connection,
4619  connection->rq.url,
4620  connection->rq.method,
4621  connection->rq.version,
4622  buffer_head,
4623  &left_unprocessed,
4624  &connection->rq.client_context))
4625  {
4626  connection->in_access_handler = false;
4627  /* serious internal error, close connection */
4628  CONNECTION_CLOSE_ERROR (connection,
4629  _ ("Application reported internal error, " \
4630  "closing connection."));
4631  return;
4632  }
4633  connection->in_access_handler = false;
4634 
4635  if (left_unprocessed > to_be_processed)
4636  MHD_PANIC (_ ("libmicrohttpd API violation.\n"));
4637 
4638  connection->rq.some_payload_processed =
4639  (left_unprocessed != to_be_processed);
4640 
4641  if (0 != left_unprocessed)
4642  {
4643  instant_retry = false; /* client did not process everything */
4644 #ifdef HAVE_MESSAGES
4645  if ((! connection->rq.some_payload_processed) &&
4646  (! connection->suspended))
4647  {
4648  /* client did not process any upload data, complain if
4649  the setup was incorrect, which may prevent us from
4650  handling the rest of the request */
4651  if (MHD_D_IS_USING_THREADS_ (daemon))
4652  MHD_DLOG (daemon,
4653  _ ("WARNING: Access Handler Callback has not processed " \
4654  "any upload data and connection is not suspended. " \
4655  "This may result in hung connection.\n"));
4656  }
4657 #endif /* HAVE_MESSAGES */
4658  }
4659  processed_size = to_be_processed - left_unprocessed;
4660  /* dh left "processed" bytes in buffer for next time... */
4661  buffer_head += processed_size;
4662  available -= processed_size;
4663  if (! connection->rq.have_chunked_upload)
4664  {
4666  connection->rq.remaining_upload_size -= processed_size;
4667  }
4668  else
4669  {
4671  connection->rq.current_chunk_offset += processed_size;
4672  }
4673  } while (instant_retry);
4674  /* TODO: zero out reused memory region */
4675  if ( (available > 0) &&
4676  (buffer_head != connection->read_buffer) )
4677  memmove (connection->read_buffer,
4678  buffer_head,
4679  available);
4680  else
4681  mhd_assert ((0 == available) || \
4682  (connection->read_buffer_offset == available));
4683  connection->read_buffer_offset = available;
4684 }
4685 
4686 
4695 static enum MHD_Result
4696 check_write_done (struct MHD_Connection *connection,
4697  enum MHD_CONNECTION_STATE next_state)
4698 {
4699  if ( (connection->write_buffer_append_offset !=
4700  connection->write_buffer_send_offset)
4701  /* || data_in_tls_buffers == true */
4702  )
4703  return MHD_NO;
4704  connection->write_buffer_append_offset = 0;
4705  connection->write_buffer_send_offset = 0;
4706  connection->state = next_state;
4707  return MHD_YES;
4708 }
4709 
4710 
4718 static void
4720 {
4721  const char *clen;
4722  const char *enc;
4723  size_t val_len;
4724 
4725 #ifdef COOKIE_SUPPORT
4726  if (MHD_PARSE_COOKIE_NO_MEMORY == parse_cookie_header (connection))
4727  {
4728  handle_req_cookie_no_space (connection);
4729  return;
4730  }
4731 #endif /* COOKIE_SUPPORT */
4732  if ( (-3 < connection->daemon->client_discipline) &&
4733  (MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver)) &&
4734  (MHD_NO ==
4735  MHD_lookup_connection_value_n (connection,
4740  NULL,
4741  NULL)) )
4742  {
4743 #ifdef HAVE_MESSAGES
4744  MHD_DLOG (connection->daemon,
4745  _ ("Received HTTP/1.1 request without `Host' header.\n"));
4746 #endif
4747  transmit_error_response_static (connection,
4750  return;
4751  }
4752 
4753  /* The presence of the request body is indicated by "Content-Length:" or
4754  "Transfer-Encoding:" request headers.
4755  Unless one of these two headers is used, the request has no request body.
4756  See RFC9112, Section 6, paragraph 4. */
4757  connection->rq.remaining_upload_size = 0;
4758  if (MHD_NO !=
4759  MHD_lookup_connection_value_n (connection,
4764  &enc,
4765  NULL))
4766  {
4767  if (! MHD_str_equal_caseless_ (enc,
4768  "chunked"))
4769  {
4770  transmit_error_response_static (connection,
4773  return;
4774  }
4775  else if (MHD_NO !=
4776  MHD_lookup_connection_value_n (connection,
4779  MHD_STATICSTR_LEN_ ( \
4781  NULL,
4782  NULL))
4783  {
4784  /* TODO: add individual settings */
4785  if (1 <= connection->daemon->client_discipline)
4786  {
4787  transmit_error_response_static (connection,
4790  return;
4791  }
4792  else
4793  {
4794  /* Must close connection after reply to prevent potential attack */
4795  connection->keepalive = MHD_CONN_MUST_CLOSE;
4796 #ifdef HAVE_MESSAGES
4797  MHD_DLOG (connection->daemon,
4798  _ ("The 'Content-Length' request header is ignored "
4799  "as chunked Transfer-Encoding is used "
4800  "for this request.\n"));
4801 #endif /* HAVE_MESSAGES */
4802  }
4803  }
4804  connection->rq.have_chunked_upload = true;
4806  }
4807  else if (MHD_NO !=
4808  MHD_lookup_connection_value_n (connection,
4813  &clen,
4814  &val_len))
4815  {
4816  size_t num_digits;
4817 
4818  num_digits = MHD_str_to_uint64_n_ (clen,
4819  val_len,
4820  &connection->rq.remaining_upload_size);
4821 
4822  if (((0 == num_digits) &&
4823  (0 != val_len) &&
4824  ('0' <= clen[0]) && ('9' >= clen[0]))
4825  || (MHD_SIZE_UNKNOWN == connection->rq.remaining_upload_size))
4826  {
4827  connection->rq.remaining_upload_size = 0;
4828 #ifdef HAVE_MESSAGES
4829  MHD_DLOG (connection->daemon,
4830  _ ("Too large value of 'Content-Length' header. " \
4831  "Closing connection.\n"));
4832 #endif
4833  transmit_error_response_static (connection,
4836  }
4837  else if ((val_len != num_digits) ||
4838  (0 == num_digits))
4839  {
4840  connection->rq.remaining_upload_size = 0;
4841 #ifdef HAVE_MESSAGES
4842  MHD_DLOG (connection->daemon,
4843  _ ("Failed to parse 'Content-Length' header. " \
4844  "Closing connection.\n"));
4845 #endif
4846  transmit_error_response_static (connection,
4849  }
4850  }
4851 }
4852 
4853 
4861 _MHD_static_inline void
4863 {
4864  memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
4865 }
4866 
4867 
4872 _MHD_static_inline void
4874 {
4875  c->rq.field_lines.start = c->read_buffer;
4876  memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
4878 }
4879 
4880 
4881 #ifndef MHD_MAX_EMPTY_LINES_SKIP
4886 #define MHD_MAX_EMPTY_LINES_SKIP 1024
4887 #endif /* ! MHD_MAX_EMPTY_LINES_SKIP */
4888 
4896 static bool
4898 {
4899  size_t p;
4900  const int discp_lvl = c->daemon->client_discipline;
4901  /* Allow to skip one or more empty lines before the request line.
4902  RFC 9112, section 2.2 */
4903  const bool skip_empty_lines = (1 >= discp_lvl);
4904  /* Allow to skip more then one empty line before the request line.
4905  RFC 9112, section 2.2 */
4906  const bool skip_several_empty_lines = (skip_empty_lines && (0 >= discp_lvl));
4907  /* Allow to skip number of unlimited empty lines before the request line.
4908  RFC 9112, section 2.2 */
4909  const bool skip_unlimited_empty_lines =
4910  (skip_empty_lines && (-3 >= discp_lvl));
4911  /* Treat bare LF as the end of the line.
4912  RFC 9112, section 2.2 */
4913  const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
4914  /* Treat tab as whitespace delimiter.
4915  RFC 9112, section 3 */
4916  const bool tab_as_wsp = (0 >= discp_lvl);
4917  /* Treat VT (vertical tab) and FF (form feed) as whitespace delimiters.
4918  RFC 9112, section 3 */
4919  const bool other_wsp_as_wsp = (-1 >= discp_lvl);
4920  /* Treat continuous whitespace block as a single space.
4921  RFC 9112, section 3 */
4922  const bool wsp_blocks = (-1 >= discp_lvl);
4923  /* Parse whitespace in URI, special parsing of the request line.
4924  RFC 9112, section 3.2 */
4925  const bool wsp_in_uri = (0 >= discp_lvl);
4926  /* Keep whitespace in URI, give app URI with whitespace instead of
4927  automatic redirect to fixed URI.
4928  Violates RFC 9112, section 3.2 */
4929  const bool wsp_in_uri_keep = (-2 >= discp_lvl);
4930  /* Keep bare CR character as is.
4931  Violates RFC 9112, section 2.2 */
4932  const bool bare_cr_keep = (wsp_in_uri_keep && (-3 >= discp_lvl));
4933  /* Treat bare CR as space; replace it with space before processing.
4934  RFC 9112, section 2.2 */
4935  const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
4936 
4939  mhd_assert (NULL == c->rq.method || \
4944  0 != c->rq.hdrs.rq_line.proc_pos);
4945 
4946  if (0 == c->read_buffer_offset)
4947  {
4949  return false; /* No data to process */
4950  }
4951  p = c->rq.hdrs.rq_line.proc_pos;
4952  mhd_assert (p <= c->read_buffer_offset);
4953 
4954  /* Skip empty lines, if any (and if allowed) */
4955  /* See RFC 9112, section 2.2 */
4956  if ((0 == p)
4957  && (skip_empty_lines))
4958  {
4959  /* Skip empty lines before the request line.
4960  See RFC 9112, section 2.2 */
4961  bool is_empty_line;
4963  mhd_assert (NULL == c->rq.method);
4964  mhd_assert (NULL == c->rq.url);
4965  mhd_assert (0 == c->rq.url_len);
4966  mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt);
4967  mhd_assert (0 == c->rq.req_target_len);
4968  mhd_assert (NULL == c->rq.version);
4969  do
4970  {
4971  is_empty_line = false;
4972  if ('\r' == c->read_buffer[0])
4973  {
4974  if (1 == c->read_buffer_offset)
4975  return false; /* Not enough data yet */
4976  if ('\n' == c->read_buffer[1])
4977  {
4978  is_empty_line = true;
4979  c->read_buffer += 2;
4980  c->read_buffer_size -= 2;
4981  c->read_buffer_offset -= 2;
4983  }
4984  }
4985  else if (('\n' == c->read_buffer[0]) &&
4986  (bare_lf_as_crlf))
4987  {
4988  is_empty_line = true;
4989  c->read_buffer += 1;
4990  c->read_buffer_size -= 1;
4991  c->read_buffer_offset -= 1;
4993  }
4994  if (is_empty_line)
4995  {
4996  if ((! skip_unlimited_empty_lines) &&
4997  (((unsigned int) ((skip_several_empty_lines) ?
4998  MHD_MAX_EMPTY_LINES_SKIP : 1)) <
5000  {
5002  _ ("Too many meaningless extra empty lines " \
5003  "received before the request"));
5004  return true; /* Process connection closure */
5005  }
5006  if (0 == c->read_buffer_offset)
5007  return false; /* No more data to process */
5008  }
5009  } while (is_empty_line);
5010  }
5011  /* All empty lines are skipped */
5012 
5014  /* Read and parse the request line */
5015  mhd_assert (1 <= c->read_buffer_offset);
5016 
5017  while (p < c->read_buffer_offset)
5018  {
5019  const char chr = c->read_buffer[p];
5020  bool end_of_line;
5021  /*
5022  The processing logic is different depending on the configured strictness:
5023 
5024  When whitespace BLOCKS are NOT ALLOWED, the end of the whitespace is
5025  processed BEFORE processing of the current character.
5026  When whitespace BLOCKS are ALLOWED, the end of the whitespace is
5027  processed AFTER processing of the current character.
5028 
5029  When space char in the URI is ALLOWED, the delimiter between the URI and
5030  the HTTP version string is processed only at the END of the line.
5031  When space in the URI is NOT ALLOWED, the delimiter between the URI and
5032  the HTTP version string is processed as soon as the FIRST whitespace is
5033  found after URI start.
5034  */
5035 
5036  end_of_line = false;
5037 
5038  mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5039  (c->rq.hdrs.rq_line.last_ws_end > \
5041  mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_start) || \
5042  (0 != c->rq.hdrs.rq_line.last_ws_end));
5043 
5044  /* Check for the end of the line */
5045  if ('\r' == chr)
5046  {
5047  if (p + 1 == c->read_buffer_offset)
5048  {
5049  c->rq.hdrs.rq_line.proc_pos = p;
5050  return false; /* Not enough data yet */
5051  }
5052  else if ('\n' == c->read_buffer[p + 1])
5053  end_of_line = true;
5054  else
5055  {
5056  /* Bare CR alone */
5057  /* Must be rejected or replaced with space char.
5058  See RFC 9112, section 2.2 */
5059  if (bare_cr_as_sp)
5060  {
5061  c->read_buffer[p] = ' ';
5062  c->rq.num_cr_sp_replaced++;
5063  continue; /* Re-start processing of the current character */
5064  }
5065  else if (! bare_cr_keep)
5066  {
5067  /* A quick simple check whether this line looks like an HTTP request */
5068  if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5070  {
5074  }
5075  else
5077  _ ("Bare CR characters are not allowed " \
5078  "in the request line.\n"));
5079  return true; /* Error in the request */
5080  }
5081  }
5082  }
5083  else if ('\n' == chr)
5084  {
5085  /* Bare LF may be recognised as a line delimiter.
5086  See RFC 9112, section 2.2 */
5087  if (bare_lf_as_crlf)
5088  end_of_line = true;
5089  else
5090  {
5091  /* While RFC does not enforce error for bare LF character,
5092  if this char is not treated as a line delimiter, it should be
5093  rejected to avoid any security weakness due to request smuggling. */
5094  /* A quick simple check whether this line looks like an HTTP request */
5095  if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5097  {
5101  }
5102  else
5104  _ ("Bare LF characters are not allowed " \
5105  "in the request line.\n"));
5106  return true; /* Error in the request */
5107  }
5108  }
5109 
5110  if (end_of_line)
5111  {
5112  /* Handle the end of the request line */
5113 
5114  if (NULL != c->rq.method)
5115  {
5116  if (wsp_in_uri)
5117  {
5118  /* The end of the URI and the start of the HTTP version string
5119  should be determined now. */
5120  mhd_assert (NULL == c->rq.version);
5121  mhd_assert (0 == c->rq.req_target_len);
5122  if (0 != c->rq.hdrs.rq_line.last_ws_end)
5123  {
5124  /* Determine the end and the length of the URI */
5125  if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5126  {
5127  c->read_buffer [c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5128  c->rq.req_target_len =
5130  - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5131  }
5132  else if ((c->rq.hdrs.rq_line.last_ws_start + 1 <
5133  c->rq.hdrs.rq_line.last_ws_end) &&
5134  (HTTP_VER_LEN == (p - c->rq.hdrs.rq_line.last_ws_end)))
5135  {
5136  /* Found only HTTP method and HTTP version and more than one
5137  whitespace between them. Assume zero-length URI. */
5138  mhd_assert (wsp_blocks);
5140  c->read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero terminate the URI */
5141  c->rq.hdrs.rq_line.rq_tgt =
5143  c->rq.req_target_len = 0;
5144  c->rq.hdrs.rq_line.num_ws_in_uri = 0;
5146  }
5147  /* Determine the start of the HTTP version string */
5148  if (NULL != c->rq.hdrs.rq_line.rq_tgt)
5149  {
5151  }
5152  }
5153  }
5154  else
5155  {
5156  /* The end of the URI and the start of the HTTP version string
5157  should be already known. */
5158  if ((NULL == c->rq.version)
5159  && (NULL != c->rq.hdrs.rq_line.rq_tgt)
5160  && (HTTP_VER_LEN == p - (size_t) (c->rq.hdrs.rq_line.rq_tgt
5161  - c->read_buffer))
5162  && (0 != c->read_buffer[(size_t)
5163  (c->rq.hdrs.rq_line.rq_tgt
5164  - c->read_buffer) - 1]))
5165  {
5166  /* Found only HTTP method and HTTP version and more than one
5167  whitespace between them. Assume zero-length URI. */
5168  size_t uri_pos;
5169  mhd_assert (wsp_blocks);
5170  mhd_assert (0 == c->rq.req_target_len);
5171  uri_pos = (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer) - 1;
5172  mhd_assert (uri_pos < p);
5173  c->rq.version = c->rq.hdrs.rq_line.rq_tgt;
5174  c->read_buffer[uri_pos] = 0; /* Zero terminate the URI */
5175  c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + uri_pos;
5176  c->rq.req_target_len = 0;
5177  c->rq.hdrs.rq_line.num_ws_in_uri = 0;
5179  }
5180  }
5181 
5182  if (NULL != c->rq.version)
5183  {
5184  mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
5185  if (! parse_http_version (c, c->rq.version,
5186  p
5187  - (size_t) (c->rq.version
5188  - c->read_buffer)))
5189  {
5190  mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state);
5191  return true; /* Unsupported / broken HTTP version */
5192  }
5193  c->read_buffer[p] = 0; /* Zero terminate the HTTP version strings */
5194  if ('\r' == chr)
5195  {
5196  p++; /* Consume CR */
5197  mhd_assert (p < c->read_buffer_offset); /* The next character has been already checked */
5198  }
5199  p++; /* Consume LF */
5200  c->read_buffer += p;
5201  c->read_buffer_size -= p;
5202  c->read_buffer_offset -= p;
5204  c->rq.req_target_len);
5205  mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
5206  (0 != c->rq.req_target_len));
5207  mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
5208  ((size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5209  - c->rq.hdrs.rq_line.rq_tgt) < \
5210  c->rq.req_target_len));
5211  mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
5212  (c->rq.hdrs.rq_line.rq_tgt_qmark >= \
5213  c->rq.hdrs.rq_line.rq_tgt));
5214  return true; /* The request line is successfully parsed */
5215  }
5216  }
5217  /* Error in the request line */
5218 
5219  /* A quick simple check whether this line looks like an HTTP request */
5220  if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5222  {
5226  }
5227  else
5229  _ ("The request line is malformed.\n"));
5230 
5231  return true;
5232  }
5233 
5234  /* Process possible end of the previously found whitespace delimiter */
5235  if ((! wsp_blocks) &&
5236  (p == c->rq.hdrs.rq_line.last_ws_end) &&
5237  (0 != c->rq.hdrs.rq_line.last_ws_end))
5238  {
5239  /* Previous character was a whitespace char and whitespace blocks
5240  are not allowed. */
5241  /* The current position is the next character after
5242  a whitespace delimiter */
5243  if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5244  {
5245  /* The current position is the start of the URI */
5246  mhd_assert (0 == c->rq.req_target_len);
5247  mhd_assert (NULL == c->rq.version);
5248  c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5249  /* Reset the whitespace marker */
5250  c->rq.hdrs.rq_line.last_ws_start = 0;
5251  c->rq.hdrs.rq_line.last_ws_end = 0;
5252  }
5253  else
5254  {
5255  /* It was a whitespace after the start of the URI */
5256  if (! wsp_in_uri)
5257  {
5258  mhd_assert ((0 != c->rq.req_target_len) || \
5259  (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5260  mhd_assert (NULL == c->rq.version); /* Too many whitespaces? This error is handled at whitespace start */
5261  c->rq.version = c->read_buffer + p;
5262  /* Reset the whitespace marker */
5263  c->rq.hdrs.rq_line.last_ws_start = 0;
5264  c->rq.hdrs.rq_line.last_ws_end = 0;
5265  }
5266  }
5267  }
5268 
5269  /* Process the current character.
5270  Is it not the end of the line. */
5271  if ((' ' == chr)
5272  || (('\t' == chr) && (tab_as_wsp))
5273  || ((other_wsp_as_wsp) && ((0xb == chr) || (0xc == chr))))
5274  {
5275  /* A whitespace character */
5276  if ((0 == c->rq.hdrs.rq_line.last_ws_end) ||
5277  (p != c->rq.hdrs.rq_line.last_ws_end) ||
5278  (! wsp_blocks))
5279  {
5280  /* Found first whitespace char of the new whitespace block */
5281  if (NULL == c->rq.method)
5282  {
5283  /* Found the end of the HTTP method string */
5285  mhd_assert (0 == c->rq.hdrs.rq_line.last_ws_end);
5286  mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt);
5287  mhd_assert (0 == c->rq.req_target_len);
5288  mhd_assert (NULL == c->rq.version);
5289  if (0 == p)
5290  {
5292  _ ("The request line starts with "
5293  "a whitespace.\n"));
5294  return true; /* Error in the request */
5295  }
5296  c->read_buffer[p] = 0; /* Zero-terminate the request method string */
5297  c->rq.method = c->read_buffer;
5298  parse_http_std_method (c, c->rq.method, p);
5299  }
5300  else
5301  {
5302  /* A whitespace after the start of the URI */
5303  if (! wsp_in_uri)
5304  {
5305  /* Whitespace in URI is not allowed to be parsed */
5306  if (NULL == c->rq.version)
5307  {
5308  mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
5309  /* This is a delimiter between URI and HTTP version string */
5310  c->read_buffer[p] = 0; /* Zero-terminate request URI string */
5311  mhd_assert (((size_t) (c->rq.hdrs.rq_line.rq_tgt \
5312  - c->read_buffer)) <= p);
5313  c->rq.req_target_len =
5314  p - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
5315  }
5316  else
5317  {
5318  /* This is a delimiter AFTER version string */
5319 
5320  /* A quick simple check whether this line looks like an HTTP request */
5321  if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
5323  {
5327  }
5328  else
5330  _ ("The request line has more than "
5331  "two whitespaces.\n"));
5332  return true; /* Error in the request */
5333  }
5334  }
5335  else
5336  {
5337  /* Whitespace in URI is allowed to be parsed */
5338  if (0 != c->rq.hdrs.rq_line.last_ws_end)
5339  {
5340  /* The whitespace after the start of the URI has been found already */
5344  }
5345  }
5346  }
5347  c->rq.hdrs.rq_line.last_ws_start = p;
5348  c->rq.hdrs.rq_line.last_ws_end = p + 1; /* Will be updated on the next char parsing */
5349  }
5350  else
5351  {
5352  /* Continuation of the whitespace block */
5353  mhd_assert (0 != c->rq.hdrs.rq_line.last_ws_end);
5354  mhd_assert (0 != p);
5355  c->rq.hdrs.rq_line.last_ws_end = p + 1;
5356  }
5357  }
5358  else
5359  {
5360  /* Non-whitespace char, not the end of the line */
5361  mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
5362  (c->rq.hdrs.rq_line.last_ws_end == p) || \
5363  wsp_in_uri);
5364 
5365  if ((p == c->rq.hdrs.rq_line.last_ws_end) &&
5366  (0 != c->rq.hdrs.rq_line.last_ws_end) &&
5367  (wsp_blocks))
5368  {
5369  /* The end of the whitespace block */
5370  if (NULL == c->rq.hdrs.rq_line.rq_tgt)
5371  {
5372  /* This is the first character of the URI */
5373  mhd_assert (0 == c->rq.req_target_len);
5374  mhd_assert (NULL == c->rq.version);
5375  c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
5376  /* Reset the whitespace marker */
5377  c->rq.hdrs.rq_line.last_ws_start = 0;
5378  c->rq.hdrs.rq_line.last_ws_end = 0;
5379  }
5380  else
5381  {
5382  if (! wsp_in_uri)
5383  {
5384  /* This is the first character of the HTTP version */
5385  mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
5386  mhd_assert ((0 != c->rq.req_target_len) || \
5387  (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
5388  mhd_assert (NULL == c->rq.version); /* Handled at whitespace start */
5389  c->rq.version = c->read_buffer + p;
5390  /* Reset the whitespace marker */
5391  c->rq.hdrs.rq_line.last_ws_start = 0;
5392  c->rq.hdrs.rq_line.last_ws_end = 0;
5393  }
5394  }
5395  }
5396 
5397  /* Handle other special characters */
5398  if ('?' == chr)
5399  {
5400  if ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) &&
5401  (NULL != c->rq.hdrs.rq_line.rq_tgt))
5402  {
5403  c->rq.hdrs.rq_line.rq_tgt_qmark = c->read_buffer + p;
5404  }
5405  }
5406  else if ((0xb == chr) || (0xc == chr))
5407  {
5408  /* VT or LF characters */
5409  mhd_assert (! other_wsp_as_wsp);
5410  if ((NULL != c->rq.hdrs.rq_line.rq_tgt) &&
5411  (NULL == c->rq.version) &&
5412  (wsp_in_uri))
5413  {
5415  }
5416  else
5417  {
5419  _ ("Invalid character is in the "
5420  "request line.\n"));
5421  return true; /* Error in the request */
5422  }
5423  }
5424  else if (0 == chr)
5425  {
5426  /* NUL character */
5428  _ ("The NUL character is in the "
5429  "request line.\n"));
5430  return true; /* Error in the request */
5431  }
5432  }
5433 
5434  p++;
5435  }
5436 
5437  c->rq.hdrs.rq_line.proc_pos = p;
5438  return false; /* Not enough data yet */
5439 }
5440 
5441 
5442 #ifndef MHD_MAX_FIXED_URI_LEN
5446 #define MHD_MAX_FIXED_URI_LEN (64 * 1024)
5447 #endif /* ! MHD_MAX_FIXED_URI_LEN */
5448 
5456 static void
5458 {
5459  char *b;
5460  size_t fixed_uri_len;
5461  size_t i;
5462  size_t o;
5463  char *hdr_name;
5464  size_t hdr_name_len;
5465 
5469  c->rq.req_target_len);
5470  fixed_uri_len = c->rq.req_target_len
5471  + 2 * c->rq.hdrs.rq_line.num_ws_in_uri;
5472  if ( (fixed_uri_len + 200 > c->daemon->pool_size) ||
5473  (fixed_uri_len > MHD_MAX_FIXED_URI_LEN) ||
5474  (NULL == (b = malloc (fixed_uri_len + 1))) )
5475  {
5477  _ ("The request has whitespace character is " \
5478  "in the URI and the URI is too large to " \
5479  "send automatic redirect to fixed URI.\n"));
5480  return;
5481  }
5482  i = 0;
5483  o = 0;
5484 
5485  do
5486  {
5487  const char chr = c->rq.hdrs.rq_line.rq_tgt[i++];
5488 
5489  mhd_assert ('\r' != chr); /* Replaced during request line parsing */
5490  mhd_assert ('\n' != chr); /* Rejected during request line parsing */
5491  mhd_assert (0 != chr); /* Rejected during request line parsing */
5492  switch (chr)
5493  {
5494  case ' ':
5495  b[o++] = '%';
5496  b[o++] = '2';
5497  b[o++] = '0';
5498  break;
5499  case '\t':
5500  b[o++] = '%';
5501  b[o++] = '0';
5502  b[o++] = '9';
5503  break;
5504  case 0x0B: /* VT (vertical tab) */
5505  b[o++] = '%';
5506  b[o++] = '0';
5507  b[o++] = 'B';
5508  break;
5509  case 0x0C: /* FF (form feed) */
5510  b[o++] = '%';
5511  b[o++] = '0';
5512  b[o++] = 'C';
5513  break;
5514  default:
5515  b[o++] = chr;
5516  break;
5517  }
5518  } while (i < c->rq.req_target_len);
5519  mhd_assert (fixed_uri_len == o);
5520  b[o] = 0; /* Zero-terminate the result */
5521 
5523  hdr_name = malloc (hdr_name_len + 1);
5524  if (NULL != hdr_name)
5525  {
5526  memcpy (hdr_name,
5528  hdr_name_len + 1);
5529  /* hdr_name and b are free()d within this call */
5533  hdr_name,
5534  hdr_name_len,
5535  b,
5536  o);
5537  return;
5538  }
5539  free (b);
5541  _ ("The request has whitespace character is in the " \
5542  "URI.\n"));
5543  return;
5544 }
5545 
5546 
5553 static bool
5555 {
5556 #ifdef _DEBUG
5557  size_t params_len;
5558 #endif /* _DEBUG */
5560  mhd_assert (NULL == c->rq.url);
5561  mhd_assert (0 == c->rq.url_len);
5562  mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
5563  mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
5565  mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
5566  (c->rq.req_target_len > \
5567  (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
5568  - c->rq.hdrs.rq_line.rq_tgt)));
5569 
5570  /* Log callback before the request-target is modified/decoded */
5571  if (NULL != c->daemon->uri_log_callback)
5572  {
5573  c->rq.client_aware = true;
5574  c->rq.client_context =
5576  c->rq.hdrs.rq_line.rq_tgt,
5577  c);
5578  }
5579 
5580  if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark)
5581  {
5582 #ifdef _DEBUG
5583  params_len =
5584  c->rq.req_target_len
5585  - (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark - c->rq.hdrs.rq_line.rq_tgt);
5586 #endif /* _DEBUG */
5587  c->rq.hdrs.rq_line.rq_tgt_qmark[0] = 0; /* Replace '?' with zero termination */
5588  if (MHD_NO == MHD_parse_arguments_ (c,
5590  c->rq.hdrs.rq_line.rq_tgt_qmark + 1,
5592  c))
5593  {
5595  return false;
5596  }
5597  }
5598 #ifdef _DEBUG
5599  else
5600  params_len = 0;
5601 #endif /* _DEBUG */
5602 
5603  mhd_assert (strlen (c->rq.hdrs.rq_line.rq_tgt) == \
5604  c->rq.req_target_len - params_len);
5605 
5606  /* Finally unescape URI itself */
5607  c->rq.url_len =
5609  c,
5610  c->rq.hdrs.rq_line.rq_tgt);
5611  c->rq.url = c->rq.hdrs.rq_line.rq_tgt;
5612 
5613  return true;
5614 }
5615 
5616 
5624 static bool
5626 {
5627  const int discp_lvl = c->daemon->client_discipline;
5628  /* Parse whitespace in URI, special parsing of the request line */
5629  const bool wsp_in_uri = (0 >= discp_lvl);
5630  /* Keep whitespace in URI, give app URI with whitespace instead of
5631  automatic redirect to fixed URI */
5632  const bool wsp_in_uri_keep = (-2 >= discp_lvl);
5633 
5634  if (! get_request_line_inner (c))
5635  {
5636  /* End of the request line has not been found yet */
5637  mhd_assert ((! wsp_in_uri) || NULL == c->rq.version);
5638  if ((NULL != c->rq.version) &&
5639  (HTTP_VER_LEN <
5640  (c->rq.hdrs.rq_line.proc_pos
5641  - (size_t) (c->rq.version - c->read_buffer))))
5642  {
5647  return true; /* Error in the request */
5648  }
5649  return false;
5650  }
5651  if (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state)
5652  return true; /* Error in the request */
5653 
5655  mhd_assert (NULL == c->rq.url);
5656  mhd_assert (0 == c->rq.url_len);
5657  mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
5658  if (0 != c->rq.hdrs.rq_line.num_ws_in_uri)
5659  {
5660  if (! wsp_in_uri)
5661  {
5665  return true; /* Error in the request */
5666  }
5667  if (! wsp_in_uri_keep)
5668  {
5670  return true; /* Error in the request */
5671  }
5672  }
5673  if (! process_request_target (c))
5674  return true; /* Error in processing */
5675 
5677  return true;
5678 }
5679 
5680 
5685 {
5703 
5704 
5716 static enum MHD_HdrLineReadRes_
5717 get_req_header (struct MHD_Connection *c,
5718  bool process_footers,
5719  struct _MHD_str_w_len *hdr_name,
5720  struct _MHD_str_w_len *hdr_value)
5721 {
5722  const int discp_lvl = c->daemon->client_discipline;
5723  /* Treat bare LF as the end of the line.
5724  RFC 9112, section 2.2-3
5725  Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
5726  Bare LF is processed as end of the line or rejected as broken request. */
5727  const bool bare_lf_as_crlf = MHD_ALLOW_BARE_LF_AS_CRLF_ (discp_lvl);
5728  /* Keep bare CR character as is.
5729  Violates RFC 9112, section 2.2-4 */
5730  const bool bare_cr_keep = (-3 >= discp_lvl);
5731  /* Treat bare CR as space; replace it with space before processing.
5732  RFC 9112, section 2.2-4 */
5733  const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
5734  /* Treat NUL as space; replace it with space before processing.
5735  RFC 9110, section 5.5-5 */
5736  const bool nul_as_sp = (-1 >= discp_lvl);
5737  /* Allow folded header lines.
5738  RFC 9112, section 5.2-4 */
5739  const bool allow_folded = (0 >= discp_lvl);
5740  /* Do not reject headers with the whitespace at the start of the first line.
5741  When allowed, the first line with whitespace character at the first
5742  position is ignored (as well as all possible line foldings of the first
5743  line).
5744  RFC 9112, section 2.2-8 */
5745  const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl);
5746  /* Allow whitespace in header (field) name.
5747  Violates RFC 9110, section 5.1-2 */
5748  const bool allow_wsp_in_name = (-2 >= discp_lvl);
5749  /* Allow zero-length header (field) name.
5750  Violates RFC 9110, section 5.1-2 */
5751  const bool allow_empty_name = (-2 >= discp_lvl);
5752  /* Allow whitespace before colon.
5753  Violates RFC 9112, section 5.1-2 */
5754  const bool allow_wsp_before_colon = (-3 >= discp_lvl);
5755  /* Do not abort the request when header line has no colon, just skip such
5756  bad lines.
5757  RFC 9112, section 5-1 */
5758  const bool allow_line_without_colon = (-2 >= discp_lvl);
5759 
5760  size_t p;
5762 #if ! defined (HAVE_MESSAGES) && ! defined(_DEBUG)
5763  (void) process_footers; /* Unused parameter */
5764 #endif /* !HAVE_MESSAGES && !_DEBUG */
5765 
5766  mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
5768  c->state);
5769 
5770  p = c->rq.hdrs.hdr.proc_pos;
5771 
5772  mhd_assert (p <= c->read_buffer_offset);
5773  while (p < c->read_buffer_offset)
5774  {
5775  const char chr = c->read_buffer[p];
5776  bool end_of_line;
5777 
5778  mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
5779  (c->rq.hdrs.hdr.name_len < p));
5780  mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p));
5781  mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
5782  (c->rq.hdrs.hdr.name_end_found));
5783  mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
5784  (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start));
5785  mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
5786  (0 != c->rq.hdrs.hdr.name_len));
5787  mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
5788  (0 == c->rq.hdrs.hdr.name_len) || \
5789  (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len));
5790  mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
5791  (0 == c->rq.hdrs.hdr.value_start) || \
5792  (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start));
5793 
5794  /* Check for the end of the line */
5795  if ('\r' == chr)
5796  {
5797  if (0 != p)
5798  {
5799  /* Line is not empty, need to check for possible line folding */
5800  if (p + 2 >= c->read_buffer_offset)
5801  break; /* Not enough data yet to check for folded line */
5802  }
5803  else
5804  {
5805  /* Line is empty, no need to check for possible line folding */
5806  if (p + 2 > c->read_buffer_offset)
5807  break; /* Not enough data yet to check for the end of the line */
5808  }
5809  if ('\n' == c->read_buffer[p + 1])
5810  end_of_line = true;
5811  else
5812  {
5813  /* Bare CR alone */
5814  /* Must be rejected or replaced with space char.
5815  See RFC 9112, section 2.2-4 */
5816  if (bare_cr_as_sp)
5817  {
5818  c->read_buffer[p] = ' ';
5819  c->rq.num_cr_sp_replaced++;
5820  continue; /* Re-start processing of the current character */
5821  }
5822  else if (! bare_cr_keep)
5823  {
5824  if (! process_footers)
5828  else
5832  return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
5833  }
5834  end_of_line = false;
5835  }
5836  }
5837  else if ('\n' == chr)
5838  {
5839  /* Bare LF may be recognised as a line delimiter.
5840  See RFC 9112, section 2.2-3 */
5841  if (bare_lf_as_crlf)
5842  {
5843  if (0 != p)
5844  {
5845  /* Line is not empty, need to check for possible line folding */
5846  if (p + 1 >= c->read_buffer_offset)
5847  break; /* Not enough data yet to check for folded line */
5848  }
5849  end_of_line = true;
5850  }
5851  else
5852  {
5853  if (! process_footers)
5857  else
5861  return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
5862  }
5863  }
5864  else
5865  end_of_line = false;
5866 
5867  if (end_of_line)
5868  {
5869  /* Handle the end of the line */
5873  const size_t line_len = p + (('\r' == chr) ? 2 : 1);
5874  char next_line_char;
5875  mhd_assert (line_len <= c->read_buffer_offset);
5876 
5877  if (0 == p)
5878  {
5879  /* Zero-length header line. This is the end of the request header
5880  section.
5881  RFC 9112, Section 2.1-1 */
5884  mhd_assert (0 == c->rq.hdrs.hdr.name_len);
5885  mhd_assert (0 == c->rq.hdrs.hdr.ws_start);
5886  mhd_assert (0 == c->rq.hdrs.hdr.value_start);
5887  /* Consume the line with CRLF (or bare LF) */
5888  c->read_buffer += line_len;
5889  c->read_buffer_offset -= line_len;
5890  c->read_buffer_size -= line_len;
5892  }
5893 
5894  mhd_assert (line_len < c->read_buffer_offset);
5895  mhd_assert (0 != line_len);
5896  mhd_assert ('\n' == c->read_buffer[line_len - 1]);
5897  next_line_char = c->read_buffer[line_len];
5898  if ((' ' == next_line_char) ||
5899  ('\t' == next_line_char))
5900  {
5901  /* Folded line */
5902  if (! allow_folded)
5903  {
5904  if (! process_footers)
5908  else
5912 
5913  return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
5914  }
5915  /* Replace CRLF (or bare LF) character(s) with space characters.
5916  See RFC 9112, Section 5.2-4 */
5917  c->read_buffer[p] = ' ';
5918  if ('\r' == chr)
5919  c->read_buffer[p + 1] = ' ';
5920  continue; /* Re-start processing of the current character */
5921  }
5922  else
5923  {
5924  /* It is not a folded line, it's the real end of the non-empty line */
5925  bool skip_line = false;
5926  mhd_assert (0 != p);
5927  if (c->rq.hdrs.hdr.starts_with_ws)
5928  {
5929  /* This is the first line and it starts with whitespace. This line
5930  must be discarded completely.
5931  See RFC 9112, Section 2.2-8 */
5932  mhd_assert (allow_wsp_at_start);
5933 #ifdef HAVE_MESSAGES
5934  MHD_DLOG (c->daemon,
5935  _ ("Whitespace-prefixed first header line " \
5936  "has been skipped.\n"));
5937 #endif /* HAVE_MESSAGES */
5938  skip_line = true;
5939  }
5940  else if (! c->rq.hdrs.hdr.name_end_found)
5941  {
5942  if (! allow_line_without_colon)
5943  {
5944  if (! process_footers)
5948  else
5952 
5953  return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
5954  }
5955  /* Skip broken line completely */
5956  c->rq.skipped_broken_lines++;
5957  skip_line = true;
5958  }
5959  if (skip_line)
5960  {
5961  /* Skip the entire line */
5962  c->read_buffer += line_len;
5963  c->read_buffer_offset -= line_len;
5964  c->read_buffer_size -= line_len;
5965  p = 0;
5966  /* Reset processing state */
5967  memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
5968  /* Start processing of the next line */
5969  continue;
5970  }
5971  else
5972  {
5973  /* This line should be valid header line */
5974  size_t value_len;
5975  mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name);
5976 
5977  hdr_name->str = c->read_buffer + 0; /* The name always starts at the first character */
5978  hdr_name->len = c->rq.hdrs.hdr.name_len;
5979  mhd_assert (0 == hdr_name->str[hdr_name->len]);
5980 
5981  if (0 == c->rq.hdrs.hdr.value_start)
5982  {
5983  c->rq.hdrs.hdr.value_start = p;
5984  c->read_buffer[p] = 0;
5985  value_len = 0;
5986  }
5987  else if (0 != c->rq.hdrs.hdr.ws_start)
5988  {
5989  mhd_assert (p > c->rq.hdrs.hdr.ws_start);
5991  c->read_buffer[c->rq.hdrs.hdr.ws_start] = 0;
5992  value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start;
5993  }
5994  else
5995  {
5996  mhd_assert (p > c->rq.hdrs.hdr.ws_start);
5997  c->read_buffer[p] = 0;
5998  value_len = p - c->rq.hdrs.hdr.value_start;
5999  }
6000  hdr_value->str = c->read_buffer + c->rq.hdrs.hdr.value_start;
6001  hdr_value->len = value_len;
6002  mhd_assert (0 == hdr_value->str[hdr_value->len]);
6003  /* Consume the entire line */
6004  c->read_buffer += line_len;
6005  c->read_buffer_offset -= line_len;
6006  c->read_buffer_size -= line_len;
6008  }
6009  }
6010  }
6011  else if ((' ' == chr) || ('\t' == chr))
6012  {
6013  if (0 == p)
6014  {
6015  if (! allow_wsp_at_start)
6016  {
6017  if (! process_footers)
6021  else
6025  return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6026  }
6027  c->rq.hdrs.hdr.starts_with_ws = true;
6028  }
6029  else if ((! c->rq.hdrs.hdr.name_end_found) &&
6030  (! c->rq.hdrs.hdr.starts_with_ws))
6031  {
6032  /* Whitespace in header name / between header name and colon */
6033  if (allow_wsp_in_name || allow_wsp_before_colon)
6034  {
6035  if (0 == c->rq.hdrs.hdr.ws_start)
6036  c->rq.hdrs.hdr.ws_start = p;
6037  }
6038  else
6039  {
6040  if (! process_footers)
6044  else
6048 
6049  return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6050  }
6051  }
6052  else
6053  {
6054  /* Whitespace before/inside/after header (field) value */
6055  if (0 == c->rq.hdrs.hdr.ws_start)
6056  c->rq.hdrs.hdr.ws_start = p;
6057  }
6058  }
6059  else if (0 == chr)
6060  {
6061  if (! nul_as_sp)
6062  {
6063  if (! process_footers)
6067  else
6071 
6072  return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6073  }
6074  c->read_buffer[p] = ' ';
6075  continue; /* Re-start processing of the current character */
6076  }
6077  else
6078  {
6079  /* Not a whitespace, not the end of the header line */
6080  mhd_assert ('\r' != chr);
6081  mhd_assert ('\n' != chr);
6082  mhd_assert ('\0' != chr);
6083  if ((! c->rq.hdrs.hdr.name_end_found) &&
6084  (! c->rq.hdrs.hdr.starts_with_ws))
6085  {
6086  /* Processing the header (field) name */
6087  if (':' == chr)
6088  {
6089  if (0 == c->rq.hdrs.hdr.ws_start)
6090  c->rq.hdrs.hdr.name_len = p;
6091  else
6092  {
6093  mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6094  if (! allow_wsp_before_colon)
6095  {
6096  if (! process_footers)
6100  else
6104  return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6105  }
6106  c->rq.hdrs.hdr.name_len = c->rq.hdrs.hdr.ws_start;
6107 #ifndef MHD_FAVOR_SMALL_CODE
6108  c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6109 #endif /* ! MHD_FAVOR_SMALL_CODE */
6110  }
6111  if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name)
6112  {
6113  if (! process_footers)
6117  else
6121  return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6122  }
6123  c->rq.hdrs.hdr.name_end_found = true;
6124  c->read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */
6125  }
6126  else
6127  {
6128  if (0 != c->rq.hdrs.hdr.ws_start)
6129  {
6130  /* End of the whitespace in header (field) name */
6131  mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
6132  if (! allow_wsp_in_name)
6133  {
6134  if (! process_footers)
6138  else
6142 
6143  return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
6144  }
6145 #ifndef MHD_FAVOR_SMALL_CODE
6146  c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6147 #endif /* ! MHD_FAVOR_SMALL_CODE */
6148  }
6149  }
6150  }
6151  else
6152  {
6153  /* Processing the header (field) value */
6154  if (0 == c->rq.hdrs.hdr.value_start)
6155  c->rq.hdrs.hdr.value_start = p;
6156 #ifndef MHD_FAVOR_SMALL_CODE
6157  c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6158 #endif /* ! MHD_FAVOR_SMALL_CODE */
6159  }
6160 #ifdef MHD_FAVOR_SMALL_CODE
6161  c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
6162 #endif /* MHD_FAVOR_SMALL_CODE */
6163  }
6164  p++;
6165  }
6166  c->rq.hdrs.hdr.proc_pos = p;
6167  return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */
6168 }
6169 
6170 
6181 static bool
6182 get_req_headers (struct MHD_Connection *c, bool process_footers)
6183 {
6184  do
6185  {
6186  struct _MHD_str_w_len hdr_name;
6187  struct _MHD_str_w_len hdr_value;
6188  enum MHD_HdrLineReadRes_ res;
6189 
6190  mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6192  c->state);
6193 
6194  #ifdef _DEBUG
6195  hdr_name.str = NULL;
6196  hdr_value.str = NULL;
6197 #endif /* _DEBUG */
6198  res = get_req_header (c, process_footers, &hdr_name, &hdr_value);
6200  {
6201  mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6203  c->state);
6204  mhd_assert (NULL != hdr_name.str);
6205  mhd_assert (NULL != hdr_value.str);
6206  /* Values must be zero-terminated and must not have binary zeros */
6207  mhd_assert (strlen (hdr_name.str) == hdr_name.len);
6208  mhd_assert (strlen (hdr_value.str) == hdr_value.len);
6209  /* Values must not have whitespaces at the start or at the end */
6210  mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != ' '));
6211  mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != '\t'));
6212  mhd_assert ((hdr_name.len == 0) || \
6213  (hdr_name.str[hdr_name.len - 1] != ' '));
6214  mhd_assert ((hdr_name.len == 0) || \
6215  (hdr_name.str[hdr_name.len - 1] != '\t'));
6216  mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != ' '));
6217  mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != '\t'));
6218  mhd_assert ((hdr_value.len == 0) || \
6219  (hdr_value.str[hdr_value.len - 1] != ' '));
6220  mhd_assert ((hdr_value.len == 0) || \
6221  (hdr_value.str[hdr_value.len - 1] != '\t'));
6222 
6223  if (MHD_NO ==
6225  (! process_footers) ?
6226  MHD_HEADER_KIND :
6228  hdr_name.str, hdr_name.len,
6229  hdr_value.str, hdr_value.len))
6230  {
6231  size_t add_element_size;
6232 
6233  mhd_assert (hdr_name.str < hdr_value.str);
6234 
6235 #ifdef HAVE_MESSAGES
6236  MHD_DLOG (c->daemon,
6237  _ ("Failed to allocate memory in the connection memory " \
6238  "pool to store %s.\n"),
6239  (! process_footers) ? _ ("header") : _ ("footer"));
6240 #endif /* HAVE_MESSAGES */
6241 
6242  add_element_size = hdr_value.len
6243  + (size_t) (hdr_value.str - hdr_name.str);
6244 
6245  if (! process_footers)
6246  handle_req_headers_no_space (c, hdr_name.str, add_element_size);
6247  else
6248  handle_req_footers_no_space (c, hdr_name.str, add_element_size);
6249 
6250  mhd_assert (MHD_CONNECTION_FULL_REQ_RECEIVED < c->state);
6251  return true;
6252  }
6253  /* Reset processing state */
6255  mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6257  c->state);
6258  /* Read the next header (field) line */
6259  continue;
6260  }
6261  else if (MHD_HDR_LINE_READING_NEED_MORE_DATA == res)
6262  {
6263  mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
6265  c->state);
6266  return false;
6267  }
6268  else if (MHD_HDR_LINE_READING_DATA_ERROR == res)
6269  {
6270  mhd_assert ((process_footers ? \
6275  return true;
6276  }
6278  break;
6279  } while (1);
6280 
6281 #ifdef HAVE_MESSAGES
6282  if (1 == c->rq.num_cr_sp_replaced)
6283  {
6284  MHD_DLOG (c->daemon,
6285  _ ("One bare CR character has been replaced with space " \
6286  "in %s.\n"),
6287  (! process_footers) ?
6288  _ ("the request line or in the request headers") :
6289  _ ("the request footers"));
6290  }
6291  else if (0 != c->rq.num_cr_sp_replaced)
6292  {
6293  MHD_DLOG (c->daemon,
6294  _ ("%" PRIu64 " bare CR characters have been replaced with " \
6295  "spaces in the request line and/or in the request %s.\n"),
6296  (uint64_t) c->rq.num_cr_sp_replaced,
6297  (! process_footers) ? _ ("headers") : _ ("footers"));
6298  }
6299  if (1 == c->rq.skipped_broken_lines)
6300  {
6301  MHD_DLOG (c->daemon,
6302  _ ("One %s line without colon has been skipped.\n"),
6303  (! process_footers) ? _ ("header") : _ ("footer"));
6304  }
6305  else if (0 != c->rq.skipped_broken_lines)
6306  {
6307  MHD_DLOG (c->daemon,
6308  _ ("%" PRIu64 " %s lines without colons has been skipped.\n"),
6309  (uint64_t) c->rq.skipped_broken_lines,
6310  (! process_footers) ? _ ("header") : _ ("footer"));
6311  }
6312 #endif /* HAVE_MESSAGES */
6313 
6314  mhd_assert (c->rq.method < c->read_buffer);
6315  if (! process_footers)
6316  {
6317  c->rq.header_size = (size_t) (c->read_buffer - c->rq.method);
6319  c->rq.field_lines.size =
6320  (size_t) ((c->read_buffer - c->rq.field_lines.start) - 1);
6321  if ('\r' == *(c->read_buffer - 2))
6322  c->rq.field_lines.size--;
6324 
6326  {
6327  /* Try to re-use some of the last bytes of the request header */
6328  /* Do this only if space in the read buffer is limited AND
6329  amount of read ahead data is small. */
6334  const char *last_elmnt_end;
6335  size_t shift_back_size;
6336  if (NULL != c->rq.headers_received_tail)
6337  last_elmnt_end =
6339  + c->rq.headers_received_tail->value_size;
6340  else
6341  last_elmnt_end = c->rq.version + HTTP_VER_LEN;
6342  mhd_assert ((last_elmnt_end + 1) < c->read_buffer);
6343  shift_back_size = (size_t) (c->read_buffer - (last_elmnt_end + 1));
6344  if (0 != c->read_buffer_offset)
6345  memmove (c->read_buffer - shift_back_size,
6346  c->read_buffer,
6347  c->read_buffer_offset);
6348  c->read_buffer -= shift_back_size;
6349  c->read_buffer_size += shift_back_size;
6350  }
6351  }
6352  else
6354 
6355  return true;
6356 }
6357 
6358 
6366 void
6368 {
6369  struct MHD_Daemon *daemon = connection->daemon;
6370 #if defined(MHD_USE_THREADS)
6371  mhd_assert (NULL == daemon->worker_pool);
6372 #endif /* MHD_USE_THREADS */
6373 
6374  if (0 == connection->connection_timeout_ms)
6375  return; /* Skip update of activity for connections
6376  without timeout timer. */
6377  if (connection->suspended)
6378  return; /* no activity on suspended connections */
6379 
6380  connection->last_activity = MHD_monotonic_msec_counter ();
6381  if (MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
6382  return; /* each connection has personal timeout */
6383 
6384  if (connection->connection_timeout_ms != daemon->connection_timeout_ms)
6385  return; /* custom timeout, no need to move it in "normal" DLL */
6386 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6388 #endif
6389  /* move connection to head of timeout list (by remove + add operation) */
6391  daemon->normal_timeout_tail,
6392  connection);
6394  daemon->normal_timeout_tail,
6395  connection);
6396 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6398 #endif
6399 }
6400 
6401 
6411 void
6413  bool socket_error)
6414 {
6415  ssize_t bytes_read;
6416 
6417  if ( (MHD_CONNECTION_CLOSED == connection->state) ||
6418  (connection->suspended) )
6419  return;
6420 #ifdef HTTPS_SUPPORT
6421  if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6422  { /* HTTPS connection. */
6423  if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6424  {
6425  if (! MHD_run_tls_handshake_ (connection))
6426  return;
6427  }
6428  }
6429 #endif /* HTTPS_SUPPORT */
6430 
6431  mhd_assert (NULL != connection->read_buffer);
6432  if (connection->read_buffer_size == connection->read_buffer_offset)
6433  return; /* No space for receiving data. */
6434 
6435  bytes_read = connection->recv_cls (connection,
6436  &connection->read_buffer
6437  [connection->read_buffer_offset],
6438  connection->read_buffer_size
6439  - connection->read_buffer_offset);
6440  if ((bytes_read < 0) || socket_error)
6441  {
6442  if ((MHD_ERR_AGAIN_ == bytes_read) && ! socket_error)
6443  return; /* No new data to process. */
6444  if ((bytes_read > 0) && connection->sk_nonblck)
6445  { /* Try to detect the socket error */
6446  int dummy;
6447  bytes_read = connection->recv_cls (connection, &dummy, sizeof (dummy));
6448  }
6449  if (MHD_ERR_CONNRESET_ == bytes_read)
6450  {
6451  if ( (MHD_CONNECTION_INIT < connection->state) &&
6452  (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6453  {
6454 #ifdef HAVE_MESSAGES
6455  MHD_DLOG (connection->daemon,
6456  _ ("Socket has been disconnected when reading request.\n"));
6457 #endif
6458  connection->discard_request = true;
6459  }
6460  MHD_connection_close_ (connection,
6462  return;
6463  }
6464 
6465 #ifdef HAVE_MESSAGES
6466  if (MHD_CONNECTION_INIT != connection->state)
6467  MHD_DLOG (connection->daemon,
6468  _ ("Connection socket is closed when reading " \
6469  "request due to the error: %s\n"),
6470  (bytes_read < 0) ? str_conn_error_ (bytes_read) :
6471  "detected connection closure");
6472 #endif
6473  CONNECTION_CLOSE_ERROR (connection,
6474  NULL);
6475  return;
6476  }
6477 
6478  if (0 == bytes_read)
6479  { /* Remote side closed connection. */
6480  connection->read_closed = true;
6481  if ( (MHD_CONNECTION_INIT < connection->state) &&
6482  (MHD_CONNECTION_FULL_REQ_RECEIVED > connection->state) )
6483  {
6484 #ifdef HAVE_MESSAGES
6485  MHD_DLOG (connection->daemon,
6486  _ ("Connection was closed by remote side with incomplete "
6487  "request.\n"));
6488 #endif
6489  connection->discard_request = true;
6490  MHD_connection_close_ (connection,
6492  }
6493  else if (MHD_CONNECTION_INIT == connection->state)
6494  /* This termination code cannot be reported to the application
6495  * because application has not been informed yet about this request */
6496  MHD_connection_close_ (connection,
6498  else
6499  MHD_connection_close_ (connection,
6501  return;
6502  }
6503  connection->read_buffer_offset += (size_t) bytes_read;
6504  MHD_update_last_activity_ (connection);
6505 #if DEBUG_STATES
6506  MHD_DLOG (connection->daemon,
6507  _ ("In function %s handling connection at state: %s\n"),
6508  MHD_FUNC_,
6509  MHD_state_to_string (connection->state));
6510 #endif
6511  /* TODO: check whether the next 'switch()' really needed */
6512  switch (connection->state)
6513  {
6514  case MHD_CONNECTION_INIT:
6520  /* nothing to do but default action */
6521  if (connection->read_closed)
6522  {
6523  /* TODO: check whether this really needed */
6524  MHD_connection_close_ (connection,
6526  }
6527  return;
6528  case MHD_CONNECTION_CLOSED:
6529  return;
6530 #ifdef UPGRADE_SUPPORT
6531  case MHD_CONNECTION_UPGRADE:
6532  mhd_assert (0);
6533  return;
6534 #endif /* UPGRADE_SUPPORT */
6536  /* shrink read buffer to how much is actually used */
6537  /* TODO: remove shrink as it handled in special function */
6538  if ((0 != connection->read_buffer_size) &&
6539  (connection->read_buffer_size != connection->read_buffer_offset))
6540  {
6541  mhd_assert (NULL != connection->read_buffer);
6542  connection->read_buffer =
6543  MHD_pool_reallocate (connection->pool,
6544  connection->read_buffer,
6545  connection->read_buffer_size,
6546  connection->read_buffer_offset);
6547  connection->read_buffer_size = connection->read_buffer_offset;
6548  }
6549  break;
6555  /* Milestone state, no data should be read */
6556  mhd_assert (0); /* Should not be possible */
6557  break;
6568  default:
6569  mhd_assert (0); /* Should not be possible */
6570  break;
6571  }
6572  return;
6573 }
6574 
6575 
6584 void
6586 {
6587  struct MHD_Response *response;
6588  ssize_t ret;
6589  if (connection->suspended)
6590  return;
6591 
6592 #ifdef HTTPS_SUPPORT
6593  if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
6594  { /* HTTPS connection. */
6595  if (MHD_TLS_CONN_CONNECTED > connection->tls_state)
6596  {
6597  if (! MHD_run_tls_handshake_ (connection))
6598  return;
6599  }
6600  }
6601 #endif /* HTTPS_SUPPORT */
6602 
6603 #if DEBUG_STATES
6604  MHD_DLOG (connection->daemon,
6605  _ ("In function %s handling connection at state: %s\n"),
6606  MHD_FUNC_,
6607  MHD_state_to_string (connection->state));
6608 #endif
6609  switch (connection->state)
6610  {
6611  case MHD_CONNECTION_INIT:
6617  mhd_assert (0);
6618  return;
6620  ret = MHD_send_data_ (connection,
6622  [connection->continue_message_write_offset],
6624  - connection->continue_message_write_offset,
6625  true);
6626  if (ret < 0)
6627  {
6628  if (MHD_ERR_AGAIN_ == ret)
6629  return;
6630 #ifdef HAVE_MESSAGES
6631  MHD_DLOG (connection->daemon,
6632  _ ("Failed to send data in request for %s.\n"),
6633  connection->rq.url);
6634 #endif
6635  CONNECTION_CLOSE_ERROR (connection,
6636  NULL);
6637  return;
6638  }
6639 #if _MHD_DEBUG_SEND_DATA
6640  fprintf (stderr,
6641  _ ("Sent 100 continue response: `%.*s'\n"),
6642  (int) ret,
6644 #endif
6645  connection->continue_message_write_offset += (size_t) ret;
6646  MHD_update_last_activity_ (connection);
6647  return;
6653  mhd_assert (0);
6654  return;
6656  mhd_assert (0);
6657  return;
6659  {
6660  struct MHD_Response *const resp = connection->rp.response;
6661  const size_t wb_ready = connection->write_buffer_append_offset
6662  - connection->write_buffer_send_offset;
6663  mhd_assert (connection->write_buffer_append_offset >= \
6664  connection->write_buffer_send_offset);
6665  mhd_assert (NULL != resp);
6666  mhd_assert ( (0 == resp->data_size) || \
6667  (0 == resp->data_start) || \
6668  (NULL != resp->crc) );
6669  mhd_assert ( (0 == connection->rp.rsp_write_position) || \
6670  (resp->total_size ==
6671  connection->rp.rsp_write_position) );
6672  mhd_assert ((MHD_CONN_MUST_UPGRADE != connection->keepalive) || \
6673  (! connection->rp.props.send_reply_body));
6674 
6675  if ( (connection->rp.props.send_reply_body) &&
6676  (NULL == resp->crc) &&
6677  (NULL == resp->data_iov) &&
6678  /* TODO: remove the next check as 'send_reply_body' is used */
6679  (0 == connection->rp.rsp_write_position) &&
6680  (! connection->rp.props.chunked) )
6681  {
6682  mhd_assert (resp->total_size >= resp->data_size);
6683  mhd_assert (0 == resp->data_start);
6684  /* Send response headers alongside the response body, if the body
6685  * data is available. */
6686  ret = MHD_send_hdr_and_body_ (connection,
6687  &connection->write_buffer
6688  [connection->write_buffer_send_offset],
6689  wb_ready,
6690  false,
6691  resp->data,
6692  resp->data_size,
6693  (resp->total_size == resp->data_size));
6694  }
6695  else
6696  {
6697  /* This is response for HEAD request or reply body is not allowed
6698  * for any other reason or reply body is dynamically generated. */
6699  /* Do not send the body data even if it's available. */
6700  ret = MHD_send_hdr_and_body_ (connection,
6701  &connection->write_buffer
6702  [connection->write_buffer_send_offset],
6703  wb_ready,
6704  false,
6705  NULL,
6706  0,
6707  ((0 == resp->total_size) ||
6708  (! connection->rp.props.send_reply_body)
6709  ));
6710  }
6711 
6712  if (ret < 0)
6713  {
6714  if (MHD_ERR_AGAIN_ == ret)
6715  return;
6716 #ifdef HAVE_MESSAGES
6717  MHD_DLOG (connection->daemon,
6718  _ ("Failed to send the response headers for the " \
6719  "request for `%s'. Error: %s\n"),
6720  connection->rq.url,
6721  str_conn_error_ (ret));
6722 #endif
6723  CONNECTION_CLOSE_ERROR (connection,
6724  NULL);
6725  return;
6726  }
6727  /* 'ret' is not negative, it's safe to cast it to 'size_t'. */
6728  if (((size_t) ret) > wb_ready)
6729  {
6730  /* The complete header and some response data have been sent,
6731  * update both offsets. */
6732  mhd_assert (0 == connection->rp.rsp_write_position);
6733  mhd_assert (! connection->rp.props.chunked);
6734  mhd_assert (connection->rp.props.send_reply_body);
6735  connection->write_buffer_send_offset += wb_ready;
6736  connection->rp.rsp_write_position = ((size_t) ret) - wb_ready;
6737  }
6738  else
6739  connection->write_buffer_send_offset += (size_t) ret;
6740  MHD_update_last_activity_ (connection);
6741  if (MHD_CONNECTION_HEADERS_SENDING != connection->state)
6742  return;
6743  check_write_done (connection,
6745  return;
6746  }
6748  return;
6750  response = connection->rp.response;
6751  if (connection->rp.rsp_write_position <
6752  connection->rp.response->total_size)
6753  {
6754  uint64_t data_write_offset;
6755 
6756 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6757  if (NULL != response->crc)
6758  MHD_mutex_lock_chk_ (&response->mutex);
6759 #endif
6760  if (MHD_NO == try_ready_normal_body (connection))
6761  {
6762  /* mutex was already unlocked by try_ready_normal_body */
6763  return;
6764  }
6765 #if defined(_MHD_HAVE_SENDFILE)
6766  if (MHD_resp_sender_sendfile == connection->rp.resp_sender)
6767  {
6768  mhd_assert (NULL == response->data_iov);
6769  ret = MHD_send_sendfile_ (connection);
6770  }
6771  else /* combined with the next 'if' */
6772 #endif /* _MHD_HAVE_SENDFILE */
6773  if (NULL != response->data_iov)
6774  {
6775  ret = MHD_send_iovec_ (connection,
6776  &connection->rp.resp_iov,
6777  true);
6778  }
6779  else
6780  {
6781  data_write_offset = connection->rp.rsp_write_position
6782  - response->data_start;
6783  if (data_write_offset > (uint64_t) SIZE_MAX)
6784  MHD_PANIC (_ ("Data offset exceeds limit.\n"));
6785  ret = MHD_send_data_ (connection,
6786  &response->data
6787  [(size_t) data_write_offset],
6788  response->data_size
6789  - (size_t) data_write_offset,
6790  true);
6791 #if _MHD_DEBUG_SEND_DATA
6792  if (ret > 0)
6793  fprintf (stderr,
6794  _ ("Sent %d-byte DATA response: `%.*s'\n"),
6795  (int) ret,
6796  (int) ret,
6797  &rp.response->data[connection->rp.rsp_write_position
6798  - rp.response->data_start]);
6799 #endif
6800  }
6801 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6802  if (NULL != response->crc)
6803  MHD_mutex_unlock_chk_ (&response->mutex);
6804 #endif
6805  if (ret < 0)
6806  {
6807  if (MHD_ERR_AGAIN_ == ret)
6808  return;
6809 #ifdef HAVE_MESSAGES
6810  MHD_DLOG (connection->daemon,
6811  _ ("Failed to send the response body for the " \
6812  "request for `%s'. Error: %s\n"),
6813  connection->rq.url,
6814  str_conn_error_ (ret));
6815 #endif
6816  CONNECTION_CLOSE_ERROR (connection,
6817  NULL);
6818  return;
6819  }
6820  connection->rp.rsp_write_position += (size_t) ret;
6821  MHD_update_last_activity_ (connection);
6822  }
6823  if (connection->rp.rsp_write_position ==
6824  connection->rp.response->total_size)
6825  connection->state = MHD_CONNECTION_FULL_REPLY_SENT;
6826  return;
6828  mhd_assert (0);
6829  return;
6831  ret = MHD_send_data_ (connection,
6832  &connection->write_buffer
6833  [connection->write_buffer_send_offset],
6834  connection->write_buffer_append_offset
6835  - connection->write_buffer_send_offset,
6836  true);
6837  if (ret < 0)
6838  {
6839  if (MHD_ERR_AGAIN_ == ret)
6840  return;
6841 #ifdef HAVE_MESSAGES
6842  MHD_DLOG (connection->daemon,
6843  _ ("Failed to send the chunked response body for the " \
6844  "request for `%s'. Error: %s\n"),
6845  connection->rq.url,
6846  str_conn_error_ (ret));
6847 #endif
6848  CONNECTION_CLOSE_ERROR (connection,
6849  NULL);
6850  return;
6851  }
6852  connection->write_buffer_send_offset += (size_t) ret;
6853  MHD_update_last_activity_ (connection);
6854  if (MHD_CONNECTION_CHUNKED_BODY_READY != connection->state)
6855  return;
6856  check_write_done (connection,
6857  (connection->rp.response->total_size ==
6858  connection->rp.rsp_write_position) ?
6861  return;
6864  mhd_assert (0);
6865  return;
6867  ret = MHD_send_data_ (connection,
6868  &connection->write_buffer
6869  [connection->write_buffer_send_offset],
6870  connection->write_buffer_append_offset
6871  - connection->write_buffer_send_offset,
6872  true);
6873  if (ret < 0)
6874  {
6875  if (MHD_ERR_AGAIN_ == ret)
6876  return;
6877 #ifdef HAVE_MESSAGES
6878  MHD_DLOG (connection->daemon,
6879  _ ("Failed to send the footers for the " \
6880  "request for `%s'. Error: %s\n"),
6881  connection->rq.url,
6882  str_conn_error_ (ret));
6883 #endif
6884  CONNECTION_CLOSE_ERROR (connection,
6885  NULL);
6886  return;
6887  }
6888  connection->write_buffer_send_offset += (size_t) ret;
6889  MHD_update_last_activity_ (connection);
6890  if (MHD_CONNECTION_FOOTERS_SENDING != connection->state)
6891  return;
6892  check_write_done (connection,
6894  return;
6896  mhd_assert (0);
6897  return;
6898  case MHD_CONNECTION_CLOSED:
6899  return;
6900 #ifdef UPGRADE_SUPPORT
6901  case MHD_CONNECTION_UPGRADE:
6902  mhd_assert (0);
6903  return;
6904 #endif /* UPGRADE_SUPPORT */
6905  default:
6906  mhd_assert (0);
6907  CONNECTION_CLOSE_ERROR (connection,
6908  _ ("Internal error.\n"));
6909  break;
6910  }
6911  return;
6912 }
6913 
6914 
6921 static bool
6923 {
6924  const uint64_t timeout = c->connection_timeout_ms;
6925  uint64_t now;
6926  uint64_t since_actv;
6927 
6928  if (c->suspended)
6929  return false;
6930  if (0 == timeout)
6931  return false;
6932  now = MHD_monotonic_msec_counter ();
6933  since_actv = now - c->last_activity;
6934  /* Keep the next lines in sync with #connection_get_wait() to avoid
6935  * undesired side-effects like busy-waiting. */
6936  if (timeout < since_actv)
6937  {
6938  if (UINT64_MAX / 2 < since_actv)
6939  {
6940  const uint64_t jump_back = c->last_activity - now;
6941  /* Very unlikely that it is more than quarter-million years pause.
6942  * More likely that system clock jumps back. */
6943  if (5000 >= jump_back)
6944  {
6945 #ifdef HAVE_MESSAGES
6946  MHD_DLOG (c->daemon,
6947  _ ("Detected system clock %u milliseconds jump back.\n"),
6948  (unsigned int) jump_back);
6949 #endif
6950  return false;
6951  }
6952 #ifdef HAVE_MESSAGES
6953  MHD_DLOG (c->daemon,
6954  _ ("Detected too large system clock %" PRIu64 " milliseconds "
6955  "jump back.\n"),
6956  jump_back);
6957 #endif
6958  }
6959  return true;
6960  }
6961  return false;
6962 }
6963 
6964 
6973 static void
6975 {
6976  struct MHD_Daemon *daemon = connection->daemon;
6977 #ifdef MHD_USE_THREADS
6978  mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
6979  MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
6980  mhd_assert (NULL == daemon->worker_pool);
6981 #endif /* MHD_USE_THREADS */
6982 
6983  if (connection->in_cleanup)
6984  return; /* Prevent double cleanup. */
6985  connection->in_cleanup = true;
6986  if (NULL != connection->rp.response)
6987  {
6988  MHD_destroy_response (connection->rp.response);
6989  connection->rp.response = NULL;
6990  }
6991 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
6993 #endif
6994  if (connection->suspended)
6995  {
6998  connection);
6999  connection->suspended = false;
7000  }
7001  else
7002  {
7003  if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
7004  {
7005  if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7007  daemon->normal_timeout_tail,
7008  connection);
7009  else
7011  daemon->manual_timeout_tail,
7012  connection);
7013  }
7014  DLL_remove (daemon->connections_head,
7015  daemon->connections_tail,
7016  connection);
7017  }
7018  DLL_insert (daemon->cleanup_head,
7019  daemon->cleanup_tail,
7020  connection);
7021  connection->resuming = false;
7022  connection->in_idle = false;
7023 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7025 #endif
7026  if (MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
7027  {
7028  /* if we were at the connection limit before and are in
7029  thread-per-connection mode, signal the main thread
7030  to resume accepting connections */
7031  if ( (MHD_ITC_IS_VALID_ (daemon->itc)) &&
7032  (! MHD_itc_activate_ (daemon->itc, "c")) )
7033  {
7034 #ifdef HAVE_MESSAGES
7035  MHD_DLOG (daemon,
7036  _ ("Failed to signal end of connection via inter-thread " \
7037  "communication channel.\n"));
7038 #endif
7039  }
7040  }
7041 }
7042 
7043 
7049 void
7051 {
7052  size_t read_buf_size;
7053 
7054 #ifdef HTTPS_SUPPORT
7055  mhd_assert ( (0 == (c->daemon->options & MHD_USE_TLS)) || \
7056  (MHD_TLS_CONN_INIT == c->tls_state) );
7057  mhd_assert ( (0 != (c->daemon->options & MHD_USE_TLS)) || \
7058  (MHD_TLS_CONN_NO_TLS == c->tls_state) );
7059 #endif /* HTTPS_SUPPORT */
7061 
7064 
7065  memset (&c->rq, 0, sizeof(c->rq));
7066  memset (&c->rp, 0, sizeof(c->rp));
7067 
7068  c->write_buffer = NULL;
7069  c->write_buffer_size = 0;
7070  c->write_buffer_send_offset = 0;
7072 
7074 
7075  c->read_buffer_offset = 0;
7076  read_buf_size = c->daemon->pool_size / 2;
7077  c->read_buffer
7078  = MHD_pool_allocate (c->pool,
7079  read_buf_size,
7080  false);
7081  c->read_buffer_size = read_buf_size;
7082 }
7083 
7084 
7091 static void
7092 connection_reset (struct MHD_Connection *connection,
7093  bool reuse)
7094 {
7095  struct MHD_Connection *const c = connection;
7096  struct MHD_Daemon *const d = connection->daemon;
7097 
7098  if (! reuse)
7099  {
7100  /* Next function will destroy response, notify client,
7101  * destroy memory pool, and set connection state to "CLOSED" */
7103  c->stop_with_error ?
7106  c->read_buffer = NULL;
7107  c->read_buffer_size = 0;
7108  c->read_buffer_offset = 0;
7109  c->write_buffer = NULL;
7110  c->write_buffer_size = 0;
7111  c->write_buffer_send_offset = 0;
7113  }
7114  else
7115  {
7116  /* Reset connection to process the next request */
7117  size_t new_read_buf_size;
7118  mhd_assert (! c->stop_with_error);
7119  mhd_assert (! c->discard_request);
7120 
7121  if ( (NULL != d->notify_completed) &&
7122  (c->rq.client_aware) )
7124  c,
7125  &c->rq.client_context,
7127  c->rq.client_aware = false;
7128 
7129  if (NULL != c->rp.response)
7131  c->rp.response = NULL;
7132 
7135  c->event_loop_info =
7136  (0 == c->read_buffer_offset) ?
7138 
7139  memset (&c->rq, 0, sizeof(c->rq));
7140 
7141  /* iov (if any) will be deallocated by MHD_pool_reset */
7142  memset (&c->rp, 0, sizeof(c->rp));
7143 
7144  c->write_buffer = NULL;
7145  c->write_buffer_size = 0;
7146  c->write_buffer_send_offset = 0;
7149 
7150  /* Reset the read buffer to the starting size,
7151  preserving the bytes we have already read. */
7152  new_read_buf_size = c->daemon->pool_size / 2;
7153  if (c->read_buffer_offset > new_read_buf_size)
7154  new_read_buf_size = c->read_buffer_offset;
7155 
7156  c->read_buffer
7157  = MHD_pool_reset (c->pool,
7158  c->read_buffer,
7159  c->read_buffer_offset,
7160  new_read_buf_size);
7161  c->read_buffer_size = new_read_buf_size;
7162  }
7163  c->rq.client_context = NULL;
7164 }
7165 
7166 
7179 enum MHD_Result
7180 MHD_connection_handle_idle (struct MHD_Connection *connection)
7181 {
7182  struct MHD_Daemon *daemon = connection->daemon;
7183  enum MHD_Result ret;
7184 #ifdef MHD_USE_THREADS
7185  mhd_assert ( (! MHD_D_IS_USING_THREADS_ (daemon)) || \
7186  MHD_thread_handle_ID_is_current_thread_ (connection->tid) );
7187 #endif /* MHD_USE_THREADS */
7188  /* 'daemon' is not used if epoll is not available and asserts are disabled */
7189  (void) daemon; /* Mute compiler warning */
7190 
7191  connection->in_idle = true;
7192  while (! connection->suspended)
7193  {
7194 #ifdef HTTPS_SUPPORT
7195  if (MHD_TLS_CONN_NO_TLS != connection->tls_state)
7196  { /* HTTPS connection. */
7197  if ((MHD_TLS_CONN_INIT <= connection->tls_state) &&
7198  (MHD_TLS_CONN_CONNECTED > connection->tls_state))
7199  break;
7200  }
7201 #endif /* HTTPS_SUPPORT */
7202 #if DEBUG_STATES
7203  MHD_DLOG (daemon,
7204  _ ("In function %s handling connection at state: %s\n"),
7205  MHD_FUNC_,
7206  MHD_state_to_string (connection->state));
7207 #endif
7208  switch (connection->state)
7209  {
7210  case MHD_CONNECTION_INIT:
7212  if (get_request_line (connection))
7213  {
7214  mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING < connection->state);
7215  mhd_assert ((MHD_IS_HTTP_VER_SUPPORTED (connection->rq.http_ver)) \
7216  || (connection->discard_request));
7217  continue;
7218  }
7220  break;
7222  switch_to_rq_headers_processing (connection);
7224  continue;
7226  if (get_req_headers (connection, false))
7227  {
7228  mhd_assert (MHD_CONNECTION_REQ_HEADERS_RECEIVING < connection->state);
7229  mhd_assert ((MHD_CONNECTION_HEADERS_RECEIVED == connection->state) || \
7230  (connection->discard_request));
7231  continue;
7232  }
7234  break;
7236  parse_connection_headers (connection);
7237  if (MHD_CONNECTION_HEADERS_RECEIVED != connection->state)
7238  continue;
7240  if (connection->suspended)
7241  break;
7242  continue;
7244  call_connection_handler (connection); /* first call */
7245  if (MHD_CONNECTION_HEADERS_PROCESSED != connection->state)
7246  continue;
7247  if (connection->suspended)
7248  continue;
7249 
7250  if ( (NULL == connection->rp.response) &&
7251  (need_100_continue (connection)) &&
7252  /* If the client is already sending the payload (body)
7253  there is no need to send "100 Continue" */
7254  (0 == connection->read_buffer_offset) )
7255  {
7256  connection->state = MHD_CONNECTION_CONTINUE_SENDING;
7257  break;
7258  }
7259  if ( (NULL != connection->rp.response) &&
7260  (0 != connection->rq.remaining_upload_size) )
7261  {
7262  /* we refused (no upload allowed!) */
7263  connection->rq.remaining_upload_size = 0;
7264  /* force close, in case client still tries to upload... */
7265  connection->discard_request = true;
7266  }
7267  connection->state = (0 == connection->rq.remaining_upload_size)
7270  if (connection->suspended)
7271  break;
7272  continue;
7274  if (connection->continue_message_write_offset ==
7276  {
7277  connection->state = MHD_CONNECTION_BODY_RECEIVING;
7278  continue;
7279  }
7280  break;
7282  mhd_assert (0 != connection->rq.remaining_upload_size);
7283  mhd_assert (! connection->discard_request);
7284  mhd_assert (NULL == connection->rp.response);
7285  if (0 != connection->read_buffer_offset)
7286  {
7287  process_request_body (connection); /* loop call */
7288  if (MHD_CONNECTION_BODY_RECEIVING != connection->state)
7289  continue;
7290  }
7291  /* Modify here when queueing of the response during data processing
7292  will be supported */
7293  mhd_assert (! connection->discard_request);
7294  mhd_assert (NULL == connection->rp.response);
7295  if (0 == connection->rq.remaining_upload_size)
7296  {
7297  connection->state = MHD_CONNECTION_BODY_RECEIVED;
7298  continue;
7299  }
7300  break;
7302  mhd_assert (! connection->discard_request);
7303  mhd_assert (NULL == connection->rp.response);
7304  if (0 == connection->rq.remaining_upload_size)
7305  {
7306  if (connection->rq.have_chunked_upload)
7307  {
7308  /* Reset counter variables reused for footers */
7309  connection->rq.num_cr_sp_replaced = 0;
7310  connection->rq.skipped_broken_lines = 0;
7311  reset_rq_header_processing_state (connection);
7313  }
7314  else
7316  continue;
7317  }
7318  break;
7320  if (get_req_headers (connection, true))
7321  {
7322  mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING < connection->state);
7323  mhd_assert ((MHD_CONNECTION_FOOTERS_RECEIVED == connection->state) || \
7324  (connection->discard_request));
7325  continue;
7326  }
7328  break;
7330  /* The header, the body, and the footers of the request has been received,
7331  * switch to the final processing of the request. */
7333  continue;
7335  call_connection_handler (connection); /* "final" call */
7336  if (connection->state != MHD_CONNECTION_FULL_REQ_RECEIVED)
7337  continue;
7338  if (NULL == connection->rp.response)
7339  break; /* try again next time */
7340  /* Response is ready, start reply */
7341  connection->state = MHD_CONNECTION_START_REPLY;
7342  continue;
7344  mhd_assert (NULL != connection->rp.response);
7346  if (MHD_NO == build_header_response (connection))
7347  {
7348  /* oops - close! */
7349  CONNECTION_CLOSE_ERROR (connection,
7350  _ ("Closing connection (failed to create "
7351  "response header).\n"));
7352  continue;
7353  }
7354  connection->state = MHD_CONNECTION_HEADERS_SENDING;
7355  break;
7356 
7358  /* no default action */
7359  break;
7361 #ifdef UPGRADE_SUPPORT
7362  if (NULL != connection->rp.response->upgrade_handler)
7363  {
7364  connection->state = MHD_CONNECTION_UPGRADE;
7365  /* This connection is "upgraded". Pass socket to application. */
7366  if (MHD_NO ==
7368  connection))
7369  {
7370  /* upgrade failed, fail hard */
7371  CONNECTION_CLOSE_ERROR (connection,
7372  NULL);
7373  continue;
7374  }
7375  /* Response is not required anymore for this connection. */
7376  if (1)
7377  {
7378  struct MHD_Response *const resp = connection->rp.response;
7379 
7380  connection->rp.response = NULL;
7381  MHD_destroy_response (resp);
7382  }
7383  continue;
7384  }
7385 #endif /* UPGRADE_SUPPORT */
7386 
7387  if (connection->rp.props.send_reply_body)
7388  {
7389  if (connection->rp.props.chunked)
7391  else
7393  }
7394  else
7395  connection->state = MHD_CONNECTION_FULL_REPLY_SENT;
7396  continue;
7398  mhd_assert (connection->rp.props.send_reply_body);
7399  mhd_assert (! connection->rp.props.chunked);
7400  /* nothing to do here */
7401  break;
7403  mhd_assert (connection->rp.props.send_reply_body);
7404  mhd_assert (! connection->rp.props.chunked);
7405 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7406  if (NULL != connection->rp.response->crc)
7407  MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7408 #endif
7409  if (0 == connection->rp.response->total_size)
7410  {
7411 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7412  if (NULL != connection->rp.response->crc)
7413  MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7414 #endif
7415  if (connection->rp.props.chunked)
7417  else
7418  connection->state = MHD_CONNECTION_FULL_REPLY_SENT;
7419  continue;
7420  }
7421  if (MHD_NO != try_ready_normal_body (connection))
7422  {
7423 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7424  if (NULL != connection->rp.response->crc)
7425  MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7426 #endif
7428  /* Buffering for flushable socket was already enabled*/
7429 
7430  break;
7431  }
7432  /* mutex was already unlocked by "try_ready_normal_body */
7433  /* not ready, no socket action */
7434  break;
7436  mhd_assert (connection->rp.props.send_reply_body);
7437  mhd_assert (connection->rp.props.chunked);
7438  /* nothing to do here */
7439  break;
7441  mhd_assert (connection->rp.props.send_reply_body);
7442  mhd_assert (connection->rp.props.chunked);
7443 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7444  if (NULL != connection->rp.response->crc)
7445  MHD_mutex_lock_chk_ (&connection->rp.response->mutex);
7446 #endif
7447  if ( (0 == connection->rp.response->total_size) ||
7448  (connection->rp.rsp_write_position ==
7449  connection->rp.response->total_size) )
7450  {
7451 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7452  if (NULL != connection->rp.response->crc)
7453  MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7454 #endif
7456  continue;
7457  }
7458  if (1)
7459  { /* pseudo-branch for local variables scope */
7460  bool finished;
7461  if (MHD_NO != try_ready_chunked_body (connection, &finished))
7462  {
7463 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7464  if (NULL != connection->rp.response->crc)
7465  MHD_mutex_unlock_chk_ (&connection->rp.response->mutex);
7466 #endif
7467  connection->state = finished ? MHD_CONNECTION_CHUNKED_BODY_SENT :
7469  continue;
7470  }
7471  /* mutex was already unlocked by try_ready_chunked_body */
7472  }
7473  break;
7475  mhd_assert (connection->rp.props.send_reply_body);
7476  mhd_assert (connection->rp.props.chunked);
7477  mhd_assert (connection->write_buffer_send_offset <= \
7478  connection->write_buffer_append_offset);
7479 
7481  {
7482  /* oops - close! */
7483  CONNECTION_CLOSE_ERROR (connection,
7484  _ ("Closing connection (failed to create " \
7485  "response footer)."));
7486  continue;
7487  }
7488  mhd_assert (connection->write_buffer_send_offset < \
7489  connection->write_buffer_append_offset);
7490  connection->state = MHD_CONNECTION_FOOTERS_SENDING;
7491  continue;
7493  mhd_assert (connection->rp.props.send_reply_body);
7494  mhd_assert (connection->rp.props.chunked);
7495  /* no default action */
7496  break;
7498  if (MHD_HTTP_PROCESSING == connection->rp.responseCode)
7499  {
7500  /* After this type of response, we allow sending another! */
7502  MHD_destroy_response (connection->rp.response);
7503  connection->rp.response = NULL;
7504  /* FIXME: maybe partially reset memory pool? */
7505  continue;
7506  }
7507  /* Reset connection after complete reply */
7508  connection_reset (connection,
7509  MHD_CONN_USE_KEEPALIVE == connection->keepalive &&
7510  ! connection->read_closed &&
7511  ! connection->discard_request);
7512  continue;
7513  case MHD_CONNECTION_CLOSED:
7514  cleanup_connection (connection);
7515  connection->in_idle = false;
7516  return MHD_NO;
7517 #ifdef UPGRADE_SUPPORT
7518  case MHD_CONNECTION_UPGRADE:
7519  connection->in_idle = false;
7520  return MHD_YES; /* keep open */
7521 #endif /* UPGRADE_SUPPORT */
7522  default:
7523  mhd_assert (0);
7524  break;
7525  }
7526  break;
7527  }
7528  if (connection_check_timedout (connection))
7529  {
7530  MHD_connection_close_ (connection,
7532  connection->in_idle = false;
7533  return MHD_YES;
7534  }
7536  ret = MHD_YES;
7537 #ifdef EPOLL_SUPPORT
7538  if ( (! connection->suspended) &&
7539  MHD_D_IS_USING_EPOLL_ (daemon) )
7540  {
7541  ret = MHD_connection_epoll_update_ (connection);
7542  }
7543 #endif /* EPOLL_SUPPORT */
7544  connection->in_idle = false;
7545  return ret;
7546 }
7547 
7548 
7549 #ifdef EPOLL_SUPPORT
7558 enum MHD_Result
7559 MHD_connection_epoll_update_ (struct MHD_Connection *connection)
7560 {
7561  struct MHD_Daemon *const daemon = connection->daemon;
7562 
7563  mhd_assert (MHD_D_IS_USING_EPOLL_ (daemon));
7564 
7565  if ((0 != (MHD_EVENT_LOOP_INFO_PROCESS & connection->event_loop_info)) &&
7566  (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EREADY_EDLL)))
7567  {
7568  /* Make sure that connection waiting for processing will be processed */
7569  EDLL_insert (daemon->eready_head,
7570  daemon->eready_tail,
7571  connection);
7572  connection->epoll_state |= MHD_EPOLL_STATE_IN_EREADY_EDLL;
7573  }
7574 
7575  if ( (0 == (connection->epoll_state & MHD_EPOLL_STATE_IN_EPOLL_SET)) &&
7576  (0 == (connection->epoll_state & MHD_EPOLL_STATE_SUSPENDED)) &&
7577  ( ( (MHD_EVENT_LOOP_INFO_WRITE == connection->event_loop_info) &&
7578  (0 == (connection->epoll_state & MHD_EPOLL_STATE_WRITE_READY))) ||
7579  ( (0 != (MHD_EVENT_LOOP_INFO_READ & connection->event_loop_info)) &&
7580  (0 == (connection->epoll_state & MHD_EPOLL_STATE_READ_READY)) ) ) )
7581  {
7582  /* add to epoll set */
7583  struct epoll_event event;
7584 
7585  event.events = EPOLLIN | EPOLLOUT | EPOLLPRI | EPOLLET;
7586  event.data.ptr = connection;
7587  if (0 != epoll_ctl (daemon->epoll_fd,
7588  EPOLL_CTL_ADD,
7589  connection->socket_fd,
7590  &event))
7591  {
7592 #ifdef HAVE_MESSAGES
7593  if (0 != (daemon->options & MHD_USE_ERROR_LOG))
7594  MHD_DLOG (daemon,
7595  _ ("Call to epoll_ctl failed: %s\n"),
7597 #endif
7598  connection->state = MHD_CONNECTION_CLOSED;
7599  cleanup_connection (connection);
7600  return MHD_NO;
7601  }
7602  connection->epoll_state |= MHD_EPOLL_STATE_IN_EPOLL_SET;
7603  }
7604  return MHD_YES;
7605 }
7606 
7607 
7608 #endif
7609 
7610 
7616 void
7618 {
7619  connection->recv_cls = &recv_param_adapter;
7620 }
7621 
7622 
7635 _MHD_EXTERN const union MHD_ConnectionInfo *
7637  enum MHD_ConnectionInfoType info_type,
7638  ...)
7639 {
7640  switch (info_type)
7641  {
7642 #ifdef HTTPS_SUPPORT
7644  if (NULL == connection->tls_session)
7645  return NULL;
7646  if (1)
7647  { /* Workaround to mute compiler warning */
7648  gnutls_cipher_algorithm_t res;
7649  res = gnutls_cipher_get (connection->tls_session);
7650  connection->connection_info_dummy.cipher_algorithm = (int) res;
7651  }
7652  return &connection->connection_info_dummy;
7654  if (NULL == connection->tls_session)
7655  return NULL;
7656  if (1)
7657  { /* Workaround to mute compiler warning */
7658  gnutls_protocol_t res;
7659  res = gnutls_protocol_get_version (connection->tls_session);
7660  connection->connection_info_dummy.protocol = (int) res;
7661  }
7662  return &connection->connection_info_dummy;
7664  if (NULL == connection->tls_session)
7665  return NULL;
7666  connection->connection_info_dummy.tls_session = connection->tls_session;
7667  return &connection->connection_info_dummy;
7668 #else /* ! HTTPS_SUPPORT */
7672 #endif /* ! HTTPS_SUPPORT */
7674  return NULL; /* Not implemented */
7676  if (0 < connection->addr_len)
7677  {
7678  mhd_assert (sizeof (connection->addr) == \
7679  sizeof (connection->connection_info_dummy.client_addr));
7680  memcpy (&connection->connection_info_dummy.client_addr,
7681  &connection->addr,
7682  sizeof(connection->addr));
7683  return &connection->connection_info_dummy;
7684  }
7685  return NULL;
7687  connection->connection_info_dummy.daemon =
7688  MHD_get_master (connection->daemon);
7689  return &connection->connection_info_dummy;
7691  connection->connection_info_dummy.connect_fd = connection->socket_fd;
7692  return &connection->connection_info_dummy;
7695  connection->socket_context;
7696  return &connection->connection_info_dummy;
7698  connection->connection_info_dummy.suspended =
7699  connection->suspended ? MHD_YES : MHD_NO;
7700  return &connection->connection_info_dummy;
7702 #if SIZEOF_UNSIGNED_INT <= (SIZEOF_UINT64_T - 2)
7703  if (UINT_MAX < connection->connection_timeout_ms / 1000)
7705  else
7706 #endif /* SIZEOF_UNSIGNED_INT <=(SIZEOF_UINT64_T - 2) */
7708  (unsigned int) (connection->connection_timeout_ms / 1000);
7709  return &connection->connection_info_dummy;
7711  if ( (MHD_CONNECTION_HEADERS_RECEIVED > connection->state) ||
7712  (MHD_CONNECTION_CLOSED == connection->state) )
7713  return NULL; /* invalid, too early! */
7714  connection->connection_info_dummy.header_size = connection->rq.header_size;
7715  return &connection->connection_info_dummy;
7717  if (NULL == connection->rp.response)
7718  return NULL;
7719  connection->connection_info_dummy.http_status = connection->rp.responseCode;
7720  return &connection->connection_info_dummy;
7721  default:
7722  return NULL;
7723  }
7724 }
7725 
7726 
7737 MHD_set_connection_option (struct MHD_Connection *connection,
7738  enum MHD_CONNECTION_OPTION option,
7739  ...)
7740 {
7741  va_list ap;
7742  struct MHD_Daemon *daemon;
7743  unsigned int ui_val;
7744 
7745  daemon = connection->daemon;
7746  switch (option)
7747  {
7749  if (0 == connection->connection_timeout_ms)
7750  connection->last_activity = MHD_monotonic_msec_counter ();
7751  va_start (ap, option);
7752  ui_val = va_arg (ap, unsigned int);
7753  va_end (ap);
7754 #if (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT
7755  if ((UINT64_MAX / 4000 - 1) < ui_val)
7756  {
7757 #ifdef HAVE_MESSAGES
7758  MHD_DLOG (connection->daemon,
7759  _ ("The specified connection timeout (%u) is too " \
7760  "large. Maximum allowed value (%" PRIu64 ") will be used " \
7761  "instead.\n"),
7762  ui_val,
7763  (UINT64_MAX / 4000 - 1));
7764 #endif
7765  ui_val = UINT64_MAX / 4000 - 1;
7766  }
7767 #endif /* (SIZEOF_UINT64_T - 2) <= SIZEOF_UNSIGNED_INT */
7768  if (! MHD_D_IS_USING_THREAD_PER_CONN_ (daemon))
7769  {
7770 #if defined(MHD_USE_THREADS)
7772 #endif
7773  if (! connection->suspended)
7774  {
7775  if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7777  daemon->normal_timeout_tail,
7778  connection);
7779  else
7781  daemon->manual_timeout_tail,
7782  connection);
7783  connection->connection_timeout_ms = ((uint64_t) ui_val) * 1000;
7784  if (connection->connection_timeout_ms == daemon->connection_timeout_ms)
7786  daemon->normal_timeout_tail,
7787  connection);
7788  else
7790  daemon->manual_timeout_tail,
7791  connection);
7792  }
7793 #if defined(MHD_USE_THREADS)
7795 #endif
7796  }
7797  return MHD_YES;
7798  default:
7799  return MHD_NO;
7800  }
7801 }
7802 
7803 
7850 MHD_queue_response (struct MHD_Connection *connection,
7851  unsigned int status_code,
7852  struct MHD_Response *response)
7853 {
7854  struct MHD_Daemon *daemon;
7855  bool reply_icy;
7856 
7857  if ((NULL == connection) || (NULL == response))
7858  return MHD_NO;
7859 
7860  daemon = connection->daemon;
7861  if ((! connection->in_access_handler) && (! connection->suspended) &&
7862  MHD_D_IS_USING_THREADS_ (daemon))
7863  return MHD_NO;
7864 
7865  reply_icy = (0 != (status_code & MHD_ICY_FLAG));
7866  status_code &= ~MHD_ICY_FLAG;
7867 
7868 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
7869  if ( (! connection->suspended) &&
7870  MHD_D_IS_USING_THREADS_ (daemon) &&
7871  (! MHD_thread_handle_ID_is_current_thread_ (connection->tid)) )
7872  {
7873 #ifdef HAVE_MESSAGES
7874  MHD_DLOG (daemon,
7875  _ ("Attempted to queue response on wrong thread!\n"));
7876 #endif
7877  return MHD_NO;
7878  }
7879 #endif
7880 
7881  if (NULL != connection->rp.response)
7882  return MHD_NO; /* The response was already set */
7883 
7884  if ( (MHD_CONNECTION_HEADERS_PROCESSED != connection->state) &&
7885  (MHD_CONNECTION_FULL_REQ_RECEIVED != connection->state) )
7886  return MHD_NO; /* Wrong connection state */
7887 
7888  if (daemon->shutdown)
7889  return MHD_NO;
7890 
7891 #ifdef UPGRADE_SUPPORT
7892  if (NULL != response->upgrade_handler)
7893  {
7894  struct MHD_HTTP_Res_Header *conn_header;
7895  if (0 == (daemon->options & MHD_ALLOW_UPGRADE))
7896  {
7897 #ifdef HAVE_MESSAGES
7898  MHD_DLOG (daemon,
7899  _ ("Attempted 'upgrade' connection on daemon without" \
7900  " MHD_ALLOW_UPGRADE option!\n"));
7901 #endif
7902  return MHD_NO;
7903  }
7904  if (MHD_HTTP_SWITCHING_PROTOCOLS != status_code)
7905  {
7906 #ifdef HAVE_MESSAGES
7907  MHD_DLOG (daemon,
7908  _ ("Application used invalid status code for" \
7909  " 'upgrade' response!\n"));
7910 #endif
7911  return MHD_NO;
7912  }
7913  if (0 == (response->flags_auto & MHD_RAF_HAS_CONNECTION_HDR))
7914  {
7915 #ifdef HAVE_MESSAGES
7916  MHD_DLOG (daemon,
7917  _ ("Application used invalid response" \
7918  " without \"Connection\" header!\n"));
7919 #endif
7920  return MHD_NO;
7921  }
7922  conn_header = response->first_header;
7923  mhd_assert (NULL != conn_header);
7924  mhd_assert (MHD_str_equal_caseless_ (conn_header->header,
7926  if (! MHD_str_has_s_token_caseless_ (conn_header->value,
7927  "upgrade"))
7928  {
7929 #ifdef HAVE_MESSAGES
7930  MHD_DLOG (daemon,
7931  _ ("Application used invalid response" \
7932  " without \"upgrade\" token in" \
7933  " \"Connection\" header!\n"));
7934 #endif
7935  return MHD_NO;
7936  }
7937  if (! MHD_IS_HTTP_VER_1_1_COMPAT (connection->rq.http_ver))
7938  {
7939 #ifdef HAVE_MESSAGES
7940  MHD_DLOG (daemon,
7941  _ ("Connection \"Upgrade\" can be used only " \
7942  "with HTTP/1.1 connections!\n"));
7943 #endif
7944  return MHD_NO;
7945  }
7946  }
7947 #endif /* UPGRADE_SUPPORT */
7948  if (MHD_HTTP_SWITCHING_PROTOCOLS == status_code)
7949  {
7950 #ifdef UPGRADE_SUPPORT
7951  if (NULL == response->upgrade_handler)
7952  {
7953 #ifdef HAVE_MESSAGES
7954  MHD_DLOG (daemon,
7955  _ ("Application used status code 101 \"Switching Protocols\" " \
7956  "with non-'upgrade' response!\n"));
7957 #endif /* HAVE_MESSAGES */
7958  return MHD_NO;
7959  }
7960 #else /* ! UPGRADE_SUPPORT */
7961 #ifdef HAVE_MESSAGES
7962  MHD_DLOG (daemon,
7963  _ ("Application used status code 101 \"Switching Protocols\", " \
7964  "but this MHD was built without \"Upgrade\" support!\n"));
7965 #endif /* HAVE_MESSAGES */
7966  return MHD_NO;
7967 #endif /* ! UPGRADE_SUPPORT */
7968  }
7969  if ( (100 > status_code) ||
7970  (999 < status_code) )
7971  {
7972 #ifdef HAVE_MESSAGES
7973  MHD_DLOG (daemon,
7974  _ ("Refused wrong status code (%u). " \
7975  "HTTP requires three digits status code!\n"),
7976  status_code);
7977 #endif
7978  return MHD_NO;
7979  }
7980  if (200 > status_code)
7981  {
7982  if (MHD_HTTP_VER_1_0 == connection->rq.http_ver)
7983  {
7984 #ifdef HAVE_MESSAGES
7985  MHD_DLOG (daemon,
7986  _ ("Wrong status code (%u) refused. " \
7987  "HTTP/1.0 clients do not support 1xx status codes!\n"),
7988  (status_code));
7989 #endif
7990  return MHD_NO;
7991  }
7992  if (0 != (response->flags & (MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
7994  {
7995 #ifdef HAVE_MESSAGES
7996  MHD_DLOG (daemon,
7997  _ ("Wrong status code (%u) refused. " \
7998  "HTTP/1.0 reply mode does not support 1xx status codes!\n"),
7999  (status_code));
8000 #endif
8001  return MHD_NO;
8002  }
8003  }
8004  if ( (MHD_HTTP_MTHD_CONNECT == connection->rq.http_mthd) &&
8005  (2 == status_code / 100) )
8006  {
8007 #ifdef HAVE_MESSAGES
8008  MHD_DLOG (daemon,
8009  _ ("Successful (%u) response code cannot be used to answer " \
8010  "\"CONNECT\" request!\n"),
8011  (status_code));
8012 #endif
8013  return MHD_NO;
8014  }
8015 
8016  if ( (0 != (MHD_RF_HEAD_ONLY_RESPONSE & response->flags)) &&
8017  (RP_BODY_HEADERS_ONLY < is_reply_body_needed (connection, status_code)) )
8018  {
8019 #ifdef HAVE_MESSAGES
8020  MHD_DLOG (daemon,
8021  _ ("HEAD-only response cannot be used when the request requires "
8022  "reply body to be sent!\n"));
8023 #endif
8024  return MHD_NO;
8025  }
8026 
8027 #ifdef HAVE_MESSAGES
8028  if ( (0 != (MHD_RF_INSANITY_HEADER_CONTENT_LENGTH & response->flags)) &&
8029  (0 != (MHD_RAF_HAS_CONTENT_LENGTH & response->flags_auto)) )
8030  {
8031  MHD_DLOG (daemon,
8032  _ ("The response has application-defined \"Content-Length\" " \
8033  "header. The reply to the request will be not " \
8034  "HTTP-compliant and may result in hung connection or " \
8035  "other problems!\n"));
8036  }
8037 #endif
8038 
8039  MHD_increment_response_rc (response);
8040  connection->rp.response = response;
8041  connection->rp.responseCode = status_code;
8042  connection->rp.responseIcy = reply_icy;
8043 #if defined(_MHD_HAVE_SENDFILE)
8044  if ( (response->fd == -1) ||
8045  (response->is_pipe) ||
8046  (0 != (connection->daemon->options & MHD_USE_TLS))
8047 #if defined(MHD_SEND_SPIPE_SUPPRESS_NEEDED) && \
8048  defined(MHD_SEND_SPIPE_SUPPRESS_POSSIBLE)
8049  || (! daemon->sigpipe_blocked && ! connection->sk_spipe_suppress)
8050 #endif /* MHD_SEND_SPIPE_SUPPRESS_NEEDED &&
8051  MHD_SEND_SPIPE_SUPPRESS_POSSIBLE */
8052  )
8053  connection->rp.resp_sender = MHD_resp_sender_std;
8054  else
8055  connection->rp.resp_sender = MHD_resp_sender_sendfile;
8056 #endif /* _MHD_HAVE_SENDFILE */
8057  /* FIXME: if 'is_pipe' is set, TLS is off, and we have *splice*, we could use splice()
8058  to avoid two user-space copies... */
8059 
8060  if ( (MHD_HTTP_MTHD_HEAD == connection->rq.http_mthd) ||
8061  (MHD_HTTP_OK > status_code) ||
8062  (MHD_HTTP_NO_CONTENT == status_code) ||
8063  (MHD_HTTP_NOT_MODIFIED == status_code) )
8064  {
8065  /* if this is a "HEAD" request, or a status code for
8066  which a body is not allowed, pretend that we
8067  have already sent the full message body. */
8068  /* TODO: remove the next assignment, use 'rp_props.send_reply_body' in
8069  * checks */
8070  connection->rp.rsp_write_position = response->total_size;
8071  }
8072  if (MHD_CONNECTION_HEADERS_PROCESSED == connection->state)
8073  {
8074  /* response was queued "early", refuse to read body / footers or
8075  further requests! */
8076  connection->discard_request = true;
8077  connection->state = MHD_CONNECTION_START_REPLY;
8078  connection->rq.remaining_upload_size = 0;
8079  }
8080  if (! connection->in_idle)
8081  (void) MHD_connection_handle_idle (connection);
8082  MHD_update_last_activity_ (connection);
8083  return MHD_YES;
8084 }
8085 
8086 
8087 /* end of connection.c */
#define ERR_MSG_REQUEST_CHUNK_LINE_TOO_BIG
Definition: connection.c:167
#define REQUEST_CONTENTLENGTH_TOOLARGE
Definition: connection.c:533
static enum MHD_Result build_connection_chunked_response_footer(struct MHD_Connection *connection)
Definition: connection.c:2700
static ssize_t recv_param_adapter(struct MHD_Connection *connection, void *other, size_t i)
Definition: connection.c:725
#define ERR_RSP_EMPTY_FOOTER_NAME
Definition: connection.c:436
static enum MHD_Result build_header_response(struct MHD_Connection *connection)
Definition: connection.c:2468
static void connection_close_error(struct MHD_Connection *connection, const char *emsg)
Definition: connection.c:1361
static bool process_request_target(struct MHD_Connection *c)
Definition: connection.c:5554
void MHD_connection_set_initial_state_(struct MHD_Connection *c)
Definition: connection.c:7050
#define MHD_CHUNK_HEADER_REASONABLE_LEN
Definition: connection.c:75
#define MHD_lookup_header_s_token_ci(c, h, tkn)
Definition: connection.c:1160
MHD_ProcRecvDataStage
Definition: connection.c:2983
@ MHD_PROC_RECV_COOKIE
Definition: connection.c:2989
@ MHD_PROC_RECV_BODY_CHUNKED
Definition: connection.c:2991
@ MHD_PROC_RECV_HTTPVER
Definition: connection.c:2987
@ MHD_PROC_RECV_INIT
Definition: connection.c:2984
@ MHD_PROC_RECV_URI
Definition: connection.c:2986
@ MHD_PROC_RECV_METHOD
Definition: connection.c:2985
@ MHD_PROC_RECV_BODY_NORMAL
Definition: connection.c:2990
@ MHD_PROC_RECV_HEADERS
Definition: connection.c:2988
@ MHD_PROC_RECV_FOOTERS
Definition: connection.c:2992
#define REQUEST_CHUNK_TOO_LARGE
Definition: connection.c:521
void MHD_connection_handle_write(struct MHD_Connection *connection)
Definition: connection.c:6585
static void MHD_connection_update_event_loop_info(struct MHD_Connection *connection)
Definition: connection.c:3707
#define buffer_append_s(buf, ppos, buf_size, str)
Definition: connection.c:2331
#define MHD_MAX_FIXED_URI_LEN
Definition: connection.c:5446
static enum MHD_Result try_ready_normal_body(struct MHD_Connection *connection)
Definition: connection.c:1403
#define REQUEST_CHUNKED_MALFORMED
Definition: connection.c:509
#define REQ_HTTP_VER_IS_NOT_SUPPORTED
Definition: connection.c:585
#define MHD_MAX_REASONABLE_HEADERS_SIZE_
Definition: connection.c:3006
#define RQ_LINE_TOO_MANY_WSP
Definition: connection.c:198
static void call_connection_handler(struct MHD_Connection *connection)
Definition: connection.c:4362
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)
Definition: connection.c:3878
static void process_request_body(struct MHD_Connection *connection)
Definition: connection.c:4401
#define ERR_RSP_INVALID_CHR_IN_FOOTER
Definition: connection.c:384
static void setup_reply_properties(struct MHD_Connection *connection)
Definition: connection.c:2196
#define HTTP_100_CONTINUE
Definition: connection.c:80
static void send_redirect_fixed_rq_target(struct MHD_Connection *c)
Definition: connection.c:5457
static void handle_recv_no_space(struct MHD_Connection *c, enum MHD_ProcRecvDataStage stage)
Definition: connection.c:3407
#define transmit_error_response_static(c, code, msg)
Definition: connection.c:2934
#define REQUEST_MALFORMED
Definition: connection.c:497
static bool get_request_line(struct MHD_Connection *c)
Definition: connection.c:5625
MHD_HdrLineReadRes_
Definition: connection.c:5685
@ MHD_HDR_LINE_READING_NEED_MORE_DATA
Definition: connection.c:5689
@ MHD_HDR_LINE_READING_GOT_END_OF_HEADER
Definition: connection.c:5701
@ MHD_HDR_LINE_READING_GOT_HEADER
Definition: connection.c:5693
@ MHD_HDR_LINE_READING_DATA_ERROR
Definition: connection.c:5697
_MHD_static_inline void reset_rq_header_processing_state(struct MHD_Connection *c)
Definition: connection.c:4862
#define ERR_RSP_HEADER_WITHOUT_COLON
Definition: connection.c:397
static void connection_shrink_read_buffer(struct MHD_Connection *connection)
Definition: connection.c:1958
#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
Definition: connection.c:4244
static enum replyBodyUse is_reply_body_needed(struct MHD_Connection *connection, unsigned int rcode)
Definition: connection.c:2140
void MHD_set_http_callbacks_(struct MHD_Connection *connection)
Definition: connection.c:7617
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)
Definition: connection.c:2775
#define REQ_HTTP_VER_IS_TOO_OLD
Definition: connection.c:574
void * MHD_connection_alloc_memory_(struct MHD_Connection *connection, size_t size)
Definition: connection.c:651
#define ERR_MSG_REQUEST_TOO_BIG
Definition: connection.c:94
#define MHD_MAX_REASONABLE_REQ_TARGET_SIZE_
Definition: connection.c:3020
#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)
Definition: connection.c:6922
static enum MHD_ConnKeepAlive keepalive_possible(struct MHD_Connection *connection)
Definition: connection.c:1691
static void handle_req_chunk_size_line_no_space(struct MHD_Connection *c, const char *chunk_size_line, size_t chunk_size_line_size)
Definition: connection.c:3342
#define REQUEST_LACKS_HOST
Definition: connection.c:454
#define BARE_CR_IN_FOOTER
Definition: connection.c:228
static bool try_grow_read_buffer(struct MHD_Connection *connection, bool required)
Definition: connection.c:1877
static bool parse_http_version(struct MHD_Connection *connection, const char *http_string, size_t len)
Definition: connection.c:4256
#define BARE_LF_IN_HEADER
Definition: connection.c:243
#define ERR_RSP_EMPTY_HEADER_NAME
Definition: connection.c:423
static bool need_100_continue(struct MHD_Connection *connection)
Definition: connection.c:1173
#define ERR_RSP_WSP_IN_HEADER_NAME
Definition: connection.c:343
static bool get_req_headers(struct MHD_Connection *c, bool process_footers)
Definition: connection.c:6182
#define ERROR_MSG_DATA_NOT_HANDLED_BY_APP
Definition: connection.c:562
#define MHD_MIN_REASONABLE_REQ_METHOD_SIZE_
Definition: connection.c:3053
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)
Definition: connection.c:3078
#define REQUEST_UNSUPPORTED_TR_ENCODING
Definition: connection.c:467
enum MHD_Result MHD_connection_handle_idle(struct MHD_Connection *connection)
Definition: connection.c:7180
static void cleanup_connection(struct MHD_Connection *connection)
Definition: connection.c:6974
#define ERR_RSP_OBS_FOLD_FOOTER
Definition: connection.c:298
replyBodyUse
Definition: connection.c:2104
@ RP_BODY_HEADERS_ONLY
Definition: connection.c:2117
@ RP_BODY_NONE
Definition: connection.c:2110
@ RP_BODY_SEND
Definition: connection.c:2124
static void parse_http_std_method(struct MHD_Connection *connection, const char *method, size_t len)
Definition: connection.c:4317
#define ERR_RSP_OBS_FOLD
Definition: connection.c:285
static bool check_and_grow_read_buffer_space(struct MHD_Connection *c)
Definition: connection.c:3558
static enum MHD_Result try_ready_chunked_body(struct MHD_Connection *connection, bool *p_finished)
Definition: connection.c:1505
static bool get_date_str(char *date)
Definition: connection.c:1759
#define BARE_LF_IN_FOOTER
Definition: connection.c:258
static void connection_reset(struct MHD_Connection *connection, bool reuse)
Definition: connection.c:7092
#define REQUEST_LENGTH_WITH_TR_ENCODING
Definition: connection.c:482
#define MHD_MIN_REASONABLE_REQ_TARGET_SIZE_
Definition: connection.c:3042
#define RQ_TARGET_INVALID_CHAR
Definition: connection.c:272
static bool get_request_line_inner(struct MHD_Connection *c)
Definition: connection.c:4897
static void handle_req_footers_no_space(struct MHD_Connection *c, const char *add_footer, size_t add_footer_size)
Definition: connection.c:3382
void MHD_connection_handle_read(struct MHD_Connection *connection, bool socket_error)
Definition: connection.c:6412
static void connection_switch_from_recv_to_send(struct MHD_Connection *connection)
Definition: connection.c:2092
#define ERR_RSP_WSP_BEFORE_FOOTER
Definition: connection.c:328
static void parse_connection_headers(struct MHD_Connection *connection)
Definition: connection.c:4719
#define ERR_RSP_INVALID_CHR_IN_HEADER
Definition: connection.c:371
void MHD_update_last_activity_(struct MHD_Connection *connection)
Definition: connection.c:6367
#define MHD_MAX_EMPTY_LINES_SKIP
Definition: connection.c:4886
#define MHD_MIN_REASONABLE_HEADERS_SIZE_
Definition: connection.c:3031
#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)
Definition: connection.c:2355
#define REQUEST_CONTENTLENGTH_MALFORMED
Definition: connection.c:546
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)
Definition: connection.c:5717
static void check_connection_reply(struct MHD_Connection *connection)
Definition: connection.c:2263
static void handle_req_headers_no_space(struct MHD_Connection *c, const char *add_header, size_t add_header_size)
Definition: connection.c:3291
void MHD_connection_close_(struct MHD_Connection *connection, enum MHD_RequestTerminationCode termination_code)
Definition: connection.c:1246
static size_t connection_maximize_write_buffer(struct MHD_Connection *connection)
Definition: connection.c:1997
static bool has_unprocessed_upload_body_data_in_buffer(struct MHD_Connection *c)
Definition: connection.c:2960
void MHD_connection_mark_closed_(struct MHD_Connection *connection)
Definition: connection.c:1208
#define CONNECTION_CLOSE_ERROR(c, emsg)
Definition: connection.c:1386
#define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l)
Definition: connection.c:2942
static bool buffer_append(char *buf, size_t *ppos, size_t buf_size, const char *append, size_t append_size)
Definition: connection.c:2306
static enum MHD_Result check_write_done(struct MHD_Connection *connection, enum MHD_CONNECTION_STATE next_state)
Definition: connection.c:4696
#define ERR_RSP_WSP_BEFORE_HEADER
Definition: connection.c:313
enum MHD_HdrLineReadRes_ _MHD_FIXED_ENUM
#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)
Definition: connection.c:4873
static bool get_date_header(char *header)
Definition: connection.c:1844
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)
Definition: connection.c:1122
#define ERR_RSP_FOOTER_WITHOUT_COLON
Definition: connection.c:410
#define MHD_MIN_REASONABLE_REQ_CHUNK_LINE_LENGTH_
Definition: connection.c:3062
Methods for managing connections.
#define MHD_ERR_TLS_
Definition: connection.h:78
#define MHD_ERR_OPNOTSUPP_
Definition: connection.h:68
#define MHD_ERR_PIPE_
Definition: connection.h:73
static int parse_cookie_header(struct MHD_Request *request)
void MHD_connection_finish_forward_(struct MHD_Connection *connection) MHD_NONNULL(1)
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:590
#define MHD_HTTP_HEADER_CONNECTION
Definition: microhttpd.h:584
#define MHD_HTTP_HEADER_TRANSFER_ENCODING
Definition: microhttpd.h:648
#define MHD_HTTP_HEADER_COOKIE
Definition: microhttpd.h:748
#define MHD_HTTP_HEADER_EXPECT
Definition: microhttpd.h:602
#define MHD_HTTP_HEADER_LOCATION
Definition: microhttpd.h:622
#define MHD_HTTP_HEADER_HOST
Definition: microhttpd.h:608
#define MHD_HTTP_INTERNAL_SERVER_ERROR
Definition: microhttpd.h:447
#define MHD_HTTP_OK
Definition: microhttpd.h:344
#define MHD_HTTP_MOVED_PERMANENTLY
Definition: microhttpd.h:368
#define MHD_HTTP_URI_TOO_LONG
Definition: microhttpd.h:413
#define MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE
Definition: microhttpd.h:441
#define MHD_HTTP_PROCESSING
Definition: microhttpd.h:339
#define MHD_HTTP_NOT_IMPLEMENTED
Definition: microhttpd.h:449
#define MHD_HTTP_SWITCHING_PROTOCOLS
Definition: microhttpd.h:337
#define MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED
Definition: microhttpd.h:457
#define MHD_HTTP_CONTENT_TOO_LARGE
Definition: microhttpd.h:411
#define MHD_HTTP_NOT_MODIFIED
Definition: microhttpd.h:374
#define MHD_HTTP_NO_CONTENT
Definition: microhttpd.h:352
#define MHD_HTTP_BAD_REQUEST
Definition: microhttpd.h:385
#define MHD_HTTP_METHOD_TRACE
Definition: microhttpd.h:1119
#define MHD_HTTP_METHOD_OPTIONS
Definition: microhttpd.h:1117
#define MHD_HTTP_METHOD_GET
Definition: microhttpd.h:1105
#define MHD_HTTP_METHOD_HEAD
Definition: microhttpd.h:1107
#define MHD_HTTP_METHOD_POST
Definition: microhttpd.h:1109
#define MHD_HTTP_METHOD_PUT
Definition: microhttpd.h:1111
#define MHD_HTTP_METHOD_CONNECT
Definition: microhttpd.h:1115
#define MHD_HTTP_METHOD_DELETE
Definition: microhttpd.h:1113
_MHD_EXTERN enum MHD_Result MHD_set_connection_value(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key, const char *value)
Definition: connection.c:992
enum MHD_Result(* MHD_KeyValueIterator)(void *cls, enum MHD_ValueKind kind, const char *key, const char *value)
Definition: microhttpd.h:2789
_MHD_EXTERN const char * MHD_lookup_connection_value(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key)
Definition: connection.c:1021
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)
Definition: microhttpd.h:2814
_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:832
_MHD_EXTERN int MHD_get_connection_values(struct MHD_Connection *connection, enum MHD_ValueKind kind, MHD_KeyValueIterator iterator, void *iterator_cls)
Definition: connection.c:793
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:885
_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:945
MHD_ConnectionInfoType
Definition: microhttpd.h:2469
_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)
Definition: connection.c:1058
MHD_RequestTerminationCode
Definition: microhttpd.h:2296
@ MHD_CONNECTION_INFO_CONNECTION_TIMEOUT
Definition: microhttpd.h:2541
@ MHD_CONNECTION_INFO_SOCKET_CONTEXT
Definition: microhttpd.h:2529
@ MHD_CONNECTION_INFO_GNUTLS_SESSION
Definition: microhttpd.h:2497
@ MHD_CONNECTION_INFO_REQUEST_HEADER_SIZE
Definition: microhttpd.h:2547
@ MHD_CONNECTION_INFO_CIPHER_ALGO
Definition: microhttpd.h:2475
@ MHD_CONNECTION_INFO_CONNECTION_SUSPENDED
Definition: microhttpd.h:2535
@ MHD_CONNECTION_INFO_CLIENT_ADDRESS
Definition: microhttpd.h:2491
@ MHD_CONNECTION_INFO_DAEMON
Definition: microhttpd.h:2511
@ MHD_CONNECTION_INFO_GNUTLS_CLIENT_CERT
Definition: microhttpd.h:2505
@ MHD_CONNECTION_INFO_HTTP_STATUS
Definition: microhttpd.h:2553
@ MHD_CONNECTION_INFO_CONNECTION_FD
Definition: microhttpd.h:2519
@ MHD_CONNECTION_INFO_PROTOCOL
Definition: microhttpd.h:2482
@ MHD_REQUEST_TERMINATED_TIMEOUT_REACHED
Definition: microhttpd.h:2319
@ MHD_REQUEST_TERMINATED_COMPLETED_OK
Definition: microhttpd.h:2302
@ MHD_REQUEST_TERMINATED_WITH_ERROR
Definition: microhttpd.h:2311
@ MHD_REQUEST_TERMINATED_READ_ERROR
Definition: microhttpd.h:2336
@ MHD_REQUEST_TERMINATED_CLIENT_ABORT
Definition: microhttpd.h:2343
_MHD_EXTERN enum MHD_Result MHD_queue_response(struct MHD_Connection *connection, unsigned int status_code, struct MHD_Response *response)
Definition: connection.c:7850
_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,...)
Definition: connection.c:7737
#define MHD_ICY_FLAG
Definition: microhttpd.h:550
_MHD_EXTERN const union MHD_ConnectionInfo * MHD_get_connection_info(struct MHD_Connection *connection, enum MHD_ConnectionInfoType info_type,...)
Definition: connection.c:7636
#define MHD_HTTP_VERSION_1_0
Definition: microhttpd.h:1090
#define MHD_HTTP_VERSION_1_1
Definition: microhttpd.h:1091
bool MHD_parse_arguments_(struct MHD_Request *request, enum MHD_ValueKind kind, char *args, MHD_ArgumentIterator_ cb, unsigned int *num_headers)
Definition: internal.c:190
#define MHD_ERR_INVAL_
Definition: internal.h:1889
#define MHD_ERR_CONNRESET_
Definition: internal.h:1868
#define XDLL_insert(head, tail, element)
Definition: internal.h:1786
#define MHD_ERR_NOMEM_
Definition: internal.h:1879
MHD_EpollState
Definition: internal.h:588
@ MHD_EPOLL_STATE_SUSPENDED
Definition: internal.h:621
@ MHD_EPOLL_STATE_IN_EREADY_EDLL
Definition: internal.h:611
@ MHD_EPOLL_STATE_READ_READY
Definition: internal.h:600
@ MHD_EPOLL_STATE_IN_EPOLL_SET
Definition: internal.h:616
@ MHD_EPOLL_STATE_WRITE_READY
Definition: internal.h:606
#define DLL_insert(head, tail, element)
Definition: internal.h:1743
#define EDLL_insert(head, tail, element)
Definition: internal.h:1829
#define MHD_PANIC(msg)
Definition: internal.h:69
#define MHD_MIN(a, b)
Definition: internal.h:110
MHD_ConnKeepAlive
Definition: internal.h:155
@ MHD_CONN_USE_KEEPALIVE
Definition: internal.h:169
@ MHD_CONN_MUST_CLOSE
Definition: internal.h:159
@ MHD_CONN_KEEPALIVE_UNKOWN
Definition: internal.h:164
#define MHD_ERR_AGAIN_
Definition: internal.h:1863
#define MHD_BUF_INC_SIZE
Definition: internal.h:120
#define EDLL_remove(head, tail, element)
Definition: internal.h:1847
#define XDLL_remove(head, tail, element)
Definition: internal.h:1806
#define DLL_remove(head, tail, element)
Definition: internal.h:1763
#define MHD_ERR_BADF_
Definition: internal.h:1884
#define MHD_ERR_NOTCONN_
Definition: internal.h:1874
void MHD_pool_destroy(struct MemoryPool *pool)
Definition: memorypool.c:157
void * MHD_pool_reallocate(struct MemoryPool *pool, void *old, size_t old_size, size_t new_size)
Definition: memorypool.c:248
size_t MHD_pool_get_free(struct MemoryPool *pool)
Definition: memorypool.c:185
void * MHD_pool_reset(struct MemoryPool *pool, void *keep, size_t copy_bytes, size_t new_size)
Definition: memorypool.c:314
void * MHD_pool_allocate(struct MemoryPool *pool, size_t size, int from_end)
Definition: memorypool.c:203
#define mhd_assert(CHK)
Definition: mhd_assert.h:39
#define UINT64_MAX
Definition: mhd_limits.h:81
#define SIZE_MAX
Definition: mhd_limits.h:99
#define UINT_MAX
Definition: mhd_limits.h:45
#define MHD_mutex_unlock_chk_(pmutex)
Definition: mhd_locks.h:180
#define MHD_mutex_lock_chk_(pmutex)
Definition: mhd_locks.h:154
#define MHD_SCKT_ERR_IS_(err, code)
Definition: mhd_sockets.h:611
#define MHD_SCKT_ERR_IS_EAGAIN_(err)
Definition: mhd_sockets.h:643
#define MHD_SCKT_ERR_IS_LOW_RESOURCES_(err)
Definition: mhd_sockets.h:656
#define MHD_socket_last_strerr_()
Definition: mhd_sockets.h:549
#define MHD_SCKT_EOPNOTSUPP_
Definition: mhd_sockets.h:484
#define MHD_SCKT_EBADF_
Definition: mhd_sockets.h:454
#define MHD_socket_get_error_()
Definition: mhd_sockets.h:523
#define MHD_SCKT_ERR_IS_REMOTE_DISCNN_(err)
Definition: mhd_sockets.h:688
#define MHD_SCKT_EINVAL_
Definition: mhd_sockets.h:464
#define MHD_SCKT_ERR_IS_EINTR_(err)
Definition: mhd_sockets.h:634
#define MHD_SCKT_SEND_MAX_SIZE_
Definition: mhd_sockets.h:222
#define MHD_SCKT_ENOTCONN_
Definition: mhd_sockets.h:429
#define MHD_recv_(s, b, l)
Definition: mhd_sockets.h:273
int MHD_str_equal_caseless_(const char *str1, const char *str2)
Definition: mhd_str.c:346
size_t MHD_str_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition: mhd_str.c:515
size_t MHD_strx_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition: mhd_str.c:692
int MHD_str_equal_caseless_n_(const char *const str1, const char *const str2, size_t maxlen)
Definition: mhd_str.c:378
bool MHD_str_has_token_caseless_(const char *str, const char *const token, size_t token_len)
Definition: mhd_str.c:412
#define MHD_str_has_s_token_caseless_(str, tkn)
Definition: mhd_str.h:115
#define MHD_STATICSTR_LEN_(macro)
Definition: mhd_str.h:45
#define NULL
Definition: reason_phrase.c:30
#define _(String)
Definition: mhd_options.h:42
#define _MHD_EXTERN
Definition: mhd_options.h:53
#define MHD_FUNC_
Definition: mhd_options.h:198
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.
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
_MHD_static_inline struct MHD_Daemon * MHD_get_master(struct MHD_Daemon *const daemon)
Definition: internal.h:2900
@ 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
@ 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
struct MHD_IoVec MHD_iovec_
Definition: internal.h:440
#define MHD_IS_HTTP_VER_SUPPORTED(ver)
Definition: internal.h:881
@ 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_CONN_MUST_UPGRADE
Definition: internal.h:837
#define MHD_IS_HTTP_VER_1_1_COMPAT(ver)
Definition: internal.h:890
@ 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 PRIu64
Definition: internal.h:53
#define MHD_D_IS_USING_THREAD_PER_CONN_(d)
Definition: internal.h:2578
#define MHD_D_IS_USING_THREADS_(d)
Definition: internal.h:2574
#define MHD_D_IS_USING_EPOLL_(d)
Definition: internal.h:2550
bool MHD_pool_is_resizable_inplace(struct MemoryPool *pool, void *block, size_t block_size)
Definition: memorypool.c:440
void * MHD_pool_try_alloc(struct MemoryPool *pool, size_t size, size_t *required_bytes)
Definition: memorypool.c:481
void MHD_pool_deallocate(struct MemoryPool *pool, void *block, size_t block_size)
Definition: memorypool.c:625
memory pool; mostly used for efficient (de)allocation for each connection and bounding memory use for...
macros for mhd_assert()
Header for platform missing functions.
Header for platform-independent inter-thread communication.
limits values definitions
#define SSIZE_MAX
Definition: mhd_limits.h:121
uint64_t MHD_monotonic_msec_counter(void)
internal monotonic clock functions implementations
#define MHD_SEND_SPIPE_SUPPRESS_NEEDED
Definition: mhd_sockets.h:949
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_uint64_to_str(uint64_t val, char *buf, size_t buf_size)
Definition: mhd_str.c:1591
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_thread_handle_ID_is_current_thread_(hndl_id)
Definition: mhd_threads.h:498
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
#define MHD_SIZE_UNKNOWN
Definition: microhttpd.h:178
MHD_Result
Definition: microhttpd.h:158
@ MHD_YES
Definition: microhttpd.h:167
@ MHD_NO
Definition: microhttpd.h:162
#define MHD_CONTENT_READER_END_OF_STREAM
Definition: microhttpd.h:181
_MHD_EXTERN size_t MHD_get_reason_phrase_len_for(unsigned int code)
#define MHD_INVALID_SOCKET
Definition: microhttpd.h:202
#define MHD_CONTENT_READER_END_WITH_ERROR
Definition: microhttpd.h:182
MHD_ValueKind
Definition: microhttpd.h:2245
@ MHD_FOOTER_KIND
Definition: microhttpd.h:2286
@ MHD_COOKIE_KIND
Definition: microhttpd.h:2266
@ MHD_HEADER_KIND
Definition: microhttpd.h:2260
@ MHD_GET_ARGUMENT_KIND
Definition: microhttpd.h:2281
_MHD_EXTERN const char * MHD_get_reason_phrase_for(unsigned int code)
@ MHD_USE_TURBO
Definition: microhttpd.h:1463
@ MHD_USE_SUPPRESS_DATE_NO_CLOCK
Definition: microhttpd.h:1365
@ MHD_USE_TLS
Definition: microhttpd.h:1265
@ MHD_ALLOW_UPGRADE
Definition: microhttpd.h:1501
@ MHD_USE_ERROR_LOG
Definition: microhttpd.h:1254
@ MHD_RF_SEND_KEEP_ALIVE_HEADER
Definition: microhttpd.h:3870
@ MHD_RF_HEAD_ONLY_RESPONSE
Definition: microhttpd.h:3887
@ MHD_RF_HTTP_1_0_COMPATIBLE_STRICT
Definition: microhttpd.h:3827
@ MHD_RF_HTTP_1_0_SERVER
Definition: microhttpd.h:3848
@ MHD_RF_INSANITY_HEADER_CONTENT_LENGTH
Definition: microhttpd.h:3862
MHD_CONNECTION_OPTION
Definition: microhttpd.h:6112
@ MHD_CONNECTION_OPTION_TIMEOUT
Definition: microhttpd.h:6123
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:752
size_t write_buffer_size
Definition: internal.h:1441
size_t write_buffer_send_offset
Definition: internal.h:1446
socklen_t addr_len
Definition: internal.h:733
enum MHD_ConnectionEventLoopInfo event_loop_info
Definition: internal.h:1570
size_t write_buffer_append_offset
Definition: internal.h:1452
char * write_buffer
Definition: internal.h:1409
bool stop_with_error
Definition: internal.h:1526
void * socket_context
Definition: internal.h:694
bool discard_request
Definition: internal.h:1535
bool suspended
Definition: internal.h:764
ReceiveCallback recv_cls
Definition: internal.h:706
bool sk_nonblck
Definition: internal.h:784
enum MHD_ConnKeepAlive keepalive
Definition: internal.h:1396
struct MHD_Request rq
Definition: internal.h:1365
union MHD_ConnectionInfo connection_info_dummy
Definition: internal.h:1626
size_t continue_message_write_offset
Definition: internal.h:1458
struct sockaddr_storage addr
Definition: internal.h:728
struct MemoryPool * pool
Definition: internal.h:685
size_t read_buffer_offset
Definition: internal.h:1436
struct MHD_Reply rp
Definition: internal.h:1370
bool read_closed
Definition: internal.h:792
time_t last_activity
Definition: internal.h:739
enum MHD_CONNECTION_STATE state
Definition: internal.h:1565
char * read_buffer
Definition: internal.h:1403
struct MHD_Daemon * daemon
Definition: internal.h:675
bool in_access_handler
Definition: internal.h:1616
bool sk_spipe_suppress
Definition: internal.h:1499
uint64_t connection_timeout_ms
Definition: internal.h:1476
size_t read_buffer_size
Definition: internal.h:1430
size_t pool_size
Definition: internal.h:2136
MHD_AccessHandlerCallback default_handler
Definition: internal.h:1867
LogCallback uri_log_callback
Definition: internal.h:2057
int client_discipline
Definition: internal.h:2265
void * unescape_callback_cls
Definition: internal.h:2072
MHD_mutex_ cleanup_connection_mutex
Definition: internal.h:1265
struct MHD_Connection * connections_head
Definition: internal.h:1155
MHD_RequestCompletedCallback notify_completed
Definition: internal.h:2032
struct MHD_itc_ itc
Definition: internal.h:1410
uint64_t connection_timeout_ms
Definition: internal.h:2253
struct MHD_Connection * manual_timeout_tail
Definition: internal.h:1150
volatile bool shutdown
Definition: internal.h:1526
enum MHD_FLAG options
Definition: internal.h:1880
bool sigpipe_blocked
Definition: internal.h:2286
UnescapeCallback unescape_callback
Definition: internal.h:2067
void * notify_completed_cls
Definition: internal.h:2037
struct MHD_Connection * cleanup_tail
Definition: internal.h:1182
struct MHD_Daemon * worker_pool
Definition: internal.h:1073
struct MHD_Connection * manual_timeout_head
Definition: internal.h:1143
void * default_handler_cls
Definition: internal.h:1872
struct MHD_Connection * suspended_connections_tail
Definition: internal.h:1172
struct MHD_Connection * cleanup_head
Definition: internal.h:1177
struct MHD_Connection * normal_timeout_head
Definition: internal.h:1128
struct MHD_Connection * normal_timeout_tail
Definition: internal.h:1135
size_t pool_increment
Definition: internal.h:2141
void * uri_log_callback_cls
Definition: internal.h:2062
struct MHD_Connection * suspended_connections_head
Definition: internal.h:1166
struct MHD_Connection * connections_tail
Definition: internal.h:1160
char * value
Definition: internal.h:352
struct MHD_HTTP_Header * next
Definition: internal.h:342
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:1295
bool responseIcy
Definition: internal.h:1288
struct MHD_iovec_track_ resp_iov
Definition: internal.h:1303
struct MHD_Response * response
Definition: internal.h:1276
unsigned int responseCode
Definition: internal.h:1282
struct MHD_Reply_Properties props
Definition: internal.h:1312
unsigned int skipped_empty_lines
Definition: internal.h:955
union MHD_HeadersProcessing hdrs
Definition: internal.h:1241
uint64_t current_chunk_size
Definition: internal.h:516
uint64_t current_chunk_offset
Definition: internal.h:522
struct MHD_HTTP_Header * headers_received
Definition: internal.h:388
struct MHD_HTTP_Header * headers_received_tail
Definition: internal.h:393
bool some_payload_processed
Definition: internal.h:1184
size_t skipped_broken_lines
Definition: internal.h:1236
enum MHD_Method method
Definition: internal.h:554
size_t url_len
Definition: internal.h:1101
const char * version
Definition: internal.h:1075
size_t num_cr_sp_replaced
Definition: internal.h:1231
enum MHD_HTTP_Version http_ver
Definition: internal.h:1080
void * client_context
Definition: internal.h:401
size_t req_target_len
Definition: internal.h:1106
size_t header_size
Definition: internal.h:502
const char * url
Definition: internal.h:413
union MHD_StartOrSize field_lines
Definition: internal.h:1134
enum MHD_HTTP_Method http_mthd
Definition: internal.h:1090
bool client_aware
Definition: internal.h:1199
bool have_chunked_upload
Definition: internal.h:580
uint64_t remaining_upload_size
Definition: internal.h:508
void * crc_cls
Definition: internal.h:1594
size_t data_buffer_size
Definition: internal.h:1664
MHD_iovec_ * data_iov
Definition: internal.h:588
enum MHD_HTTP_StatusCode status_code
Definition: internal.h:1669
uint64_t data_start
Definition: internal.h:1648
MHD_ContentReaderCallback crc
Definition: internal.h:1600
bool is_pipe
Definition: internal.h:583
enum MHD_ResponseAutoFlags flags_auto
Definition: internal.h:578
unsigned int data_iovcnt
Definition: internal.h:593
size_t data_size
Definition: internal.h:1659
enum MHD_ResponseFlags flags
Definition: internal.h:573
char * data
Definition: internal.h:1588
MHD_mutex_ mutex
Definition: internal.h:1637
uint64_t total_size
Definition: internal.h:1642
struct MHD_HTTP_Header * first_header
Definition: internal.h:1582
MHD_iovec_ * iov
Definition: internal.h:453
const char * str
Definition: mhd_str_types.h:50
unsigned int connection_timeout
Definition: microhttpd.h:2397
struct MHD_Daemon * daemon
Definition: microhttpd.h:2435
unsigned int http_status
Definition: microhttpd.h:2402
struct sockaddr * client_addr
Definition: microhttpd.h:2429
MHD_socket connect_fd
Definition: microhttpd.h:2407
struct MHD_RequestLineProcessing rq_line
Definition: internal.h:1036
struct MHD_HeaderProcessing hdr
Definition: internal.h:1041
const char * start
Definition: internal.h:1055