summaryrefslogtreecommitdiff
path: root/src/microhttpd/connection.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microhttpd/connection.c')
-rw-r--r--src/microhttpd/connection.c1027
1 files changed, 886 insertions, 141 deletions
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index 756aba4f..2020b76b 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -64,13 +64,14 @@
/**
* Response text used when the request (http header) is too big to
* be processed.
- *
- * Intentionally empty here to keep our memory footprint
- * minimal.
*/
#ifdef HAVE_MESSAGES
#define REQUEST_TOO_BIG \
- "<html><head><title>Request too big</title></head><body>Your HTTP header was too big for the memory constraints of this webserver.</body></html>"
+ "<html>" \
+ "<head><title>Request too big</title></head>" \
+ "<body>Your HTTP header is too big for the memory constraints " \
+ "of this webserver.</body>" \
+ "</html>"
#else
#define REQUEST_TOO_BIG ""
#endif
@@ -104,6 +105,21 @@
#endif
/**
+ * Response text used when the request HTTP footer has bare CR character
+ * without LF character (and CR is not allowed to be treated as whitespace).
+ */
+#ifdef HAVE_MESSAGES
+#define BARE_CR_IN_FOOTER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your HTTP footer has bare CR character without " \
+ "following LF character.</body>" \
+ "</html>"
+#else
+#define BARE_CR_IN_FOOTER ""
+#endif
+
+/**
* Response text used when the request HTTP header has bare LF character
* without CR character.
*/
@@ -119,6 +135,21 @@
#endif
/**
+ * Response text used when the request HTTP footer has bare LF character
+ * without CR character.
+ */
+#ifdef HAVE_MESSAGES
+#define BARE_LF_IN_FOOTER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your HTTP footer has bare LF character without " \
+ "preceding CR character.</body>" \
+ "</html>"
+#else
+#define BARE_LF_IN_FOOTER ""
+#endif
+
+/**
* Response text used when the request line has invalid characters in URI.
*/
#ifdef HAVE_MESSAGES
@@ -133,6 +164,170 @@
#endif
/**
+ * Response text used when line folding is used in request headers.
+ */
+#ifdef HAVE_MESSAGES
+#define ERR_RSP_OBS_FOLD \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Obsolete line folding is used in your HTTP request header.</body>" \
+ "</html>"
+#else
+#define ERR_RSP_OBS_FOLD ""
+#endif
+
+/**
+ * Response text used when line folding is used in request footers.
+ */
+#ifdef HAVE_MESSAGES
+#define ERR_RSP_OBS_FOLD_FOOTER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Obsolete line folding is used in your HTTP request footer.</body>" \
+ "</html>"
+#else
+#define ERR_RSP_OBS_FOLD_FOOTER ""
+#endif
+
+/**
+ * Response text used when the request has whitespace at the start
+ * of the first header line.
+ */
+#ifdef HAVE_MESSAGES
+#define ERR_RSP_WSP_BEFORE_HEADER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your HTTP request has whitespace between the request line and " \
+ "the first header.</body>" \
+ "</html>"
+#else
+#define ERR_RSP_WSP_BEFORE_HEADER ""
+#endif
+
+/**
+ * Response text used when the request has whitespace at the start
+ * of the first footer line.
+ */
+#ifdef HAVE_MESSAGES
+#define ERR_RSP_WSP_BEFORE_FOOTER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your first HTTP footer line has whitespace at the first " \
+ "position.</body>" \
+ "</html>"
+#else
+#define ERR_RSP_WSP_BEFORE_FOOTER ""
+#endif
+
+/**
+ * Response text used when the whitespace found before colon (inside header
+ * name or between header name and colon).
+ */
+#ifdef HAVE_MESSAGES
+#define ERR_RSP_WSP_IN_HEADER_NAME \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your HTTP request has whitespace before the first colon " \
+ "in header line.</body>" \
+ "</html>"
+#else
+#define ERR_RSP_WSP_IN_HEADER_NAME ""
+#endif
+
+/**
+ * Response text used when the whitespace found before colon (inside header
+ * name or between header name and colon).
+ */
+#ifdef HAVE_MESSAGES
+#define ERR_RSP_WSP_IN_FOOTER_NAME \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your HTTP request has whitespace before the first colon " \
+ "in footer line.</body>" \
+ "</html>"
+#else
+#define ERR_RSP_WSP_IN_FOOTER_NAME ""
+#endif
+
+/**
+ * Response text used when request header has invalid character.
+ */
+#ifdef HAVE_MESSAGES
+#define ERR_RSP_INVALID_CHR_IN_HEADER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your HTTP request has invalid character in header.</body>" \
+ "</html>"
+#else
+#define ERR_RSP_INVALID_CHR_IN_HEADER ""
+#endif
+
+/**
+ * Response text used when request header has invalid character.
+ */
+#ifdef HAVE_MESSAGES
+#define ERR_RSP_INVALID_CHR_IN_FOOTER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your HTTP request has invalid character in footer.</body>" \
+ "</html>"
+#else
+#define ERR_RSP_INVALID_CHR_IN_HEADER ""
+#endif
+
+/**
+ * Response text used when request header has no colon character.
+ */
+#ifdef HAVE_MESSAGES
+#define ERR_RSP_HEADER_WITHOUT_COLON \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your HTTP request header line has no colon character.</body>" \
+ "</html>"
+#else
+#define ERR_RSP_INVALID_CHR_IN_HEADER ""
+#endif
+
+/**
+ * Response text used when request footer has no colon character.
+ */
+#ifdef HAVE_MESSAGES
+#define ERR_RSP_FOOTER_WITHOUT_COLON \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your HTTP request footer line has no colon character.</body>" \
+ "</html>"
+#else
+#define ERR_RSP_FOOTER_WITHOUT_COLON ""
+#endif
+
+/**
+ * Response text used when request header has zero-length header (filed) name.
+ */
+#ifdef HAVE_MESSAGES
+#define ERR_RSP_EMPTY_HEADER_NAME \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your HTTP request header has empty header name.</body>" \
+ "</html>"
+#else
+#define ERR_RSP_EMPTY_HEADER_NAME ""
+#endif
+
+/**
+ * Response text used when request header has zero-length header (filed) name.
+ */
+#ifdef HAVE_MESSAGES
+#define ERR_RSP_EMPTY_FOOTER_NAME \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your HTTP request footer has empty header name.</body>" \
+ "</html>"
+#else
+#define ERR_RSP_EMPTY_FOOTER_NAME ""
+#endif
+
+/**
* Response text used when the request (http header) does not
* contain a "Host:" header and still claims to be HTTP 1.1.
*
@@ -2656,7 +2851,7 @@ MHD_connection_update_event_loop_info (struct MHD_Connection *connection)
case MHD_CONNECTION_INIT:
case MHD_CONNECTION_REQ_LINE_RECEIVING:
case MHD_CONNECTION_REQ_LINE_RECEIVED:
- case MHD_CONNECTION_HEADER_PART_RECEIVED:
+ case MHD_CONNECTION_REQ_HEADERS_RECEIVING:
/* while reading headers, we always grow the
read buffer if needed, no size-check required */
if ( (connection->read_buffer_offset == connection->read_buffer_size) &&
@@ -2723,7 +2918,7 @@ MHD_connection_update_event_loop_info (struct MHD_Connection *connection)
connection->event_loop_info = MHD_EVENT_LOOP_INFO_READ;
break;
case MHD_CONNECTION_BODY_RECEIVED:
- case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+ case MHD_CONNECTION_FOOTERS_RECEIVING:
/* while reading footers, we always grow the
read buffer if needed, no size-check required */
if (connection->read_closed)
@@ -3971,6 +4166,20 @@ parse_connection_headers (struct MHD_Connection *connection)
}
+/**
+ * Reset request header processing state.
+ *
+ * This function resets the processing state before processing the next header
+ * (or footer) line.
+ * @param c the connection to process
+ */
+_MHD_static_inline void
+reset_rq_header_processing_state (struct MHD_Connection *c)
+{
+ memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
+}
+
+
#ifndef MHD_MAX_EMPTY_LINES_SKIP
/**
* The maximum number of ignored empty line before the request line
@@ -3982,7 +4191,8 @@ parse_connection_headers (struct MHD_Connection *connection)
/**
* Find and parse the request line.
* @param c the connection to process
- * @return true if request line completely processed and state is changed,
+ * @return true if request line completely processed (or unrecoverable error
+ * found) and state is changed,
* false if not enough data yet in the receive buffer
*/
static bool
@@ -4733,26 +4943,641 @@ get_request_line (struct MHD_Connection *c)
return true; /* Error in the request */
}
}
+ if (! process_request_target (c))
+ return true; /* Error in processing */
+
+ c->state = MHD_CONNECTION_REQ_LINE_RECEIVED;
+ return true;
+}
+
+
+/**
+ * Results of header line reading
+ */
+enum MHD_HdrLineReadRes_
+{
+ /**
+ * Not enough data yet
+ */
+ MHD_HDR_LINE_READING_NEED_MORE_DATA = 0,
+ /**
+ * New header line has been read
+ */
+ MHD_HDR_LINE_READING_GOT_HEADER,
+ /**
+ * Error in header data, error response has been queued
+ */
+ MHD_HDR_LINE_READING_DATA_ERROR,
+ /**
+ * Found the end of the request header (end of field lines)
+ */
+ MHD_HDR_LINE_READING_GOT_END_OF_HEADER
+} _MHD_FIXED_ENUM;
+
+
+/**
+ * Find the end of the request header line and make basic header parsing.
+ * Handle errors and header folding.
+ * @param c the connection to process
+ * @param process_footers if true then footers are processed,
+ * if false then headers are processed
+ * @param[out] hdr_name the name of the parsed header (field)
+ * @param[out] hdr_name the value of the parsed header (field)
+ * @return true if request header line completely processed,
+ * false if not enough data yet in the receive buffer
+ */
+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)
+{
+ const int discp_lvl = c->daemon->client_discipline;
+ /* Treat bare LF as the end of the line.
+ RFC 9112, section 2.2-3
+ Note: MHD never replaces bare LF with space (RFC 9110, section 5.5-5).
+ Bare LF is processed as end of the line or rejected as broken request. */
+ const bool bare_lf_as_crlf = (0 >= discp_lvl);
+ /* Keep bare CR character as is.
+ Violates RFC 9112, section 2.2-4 */
+ const bool bare_cr_keep = (-3 >= discp_lvl);
+ /* Treat bare CR as space; replace it with space before processing.
+ RFC 9112, section 2.2-4 */
+ const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
+ /* Treat NUL as space; replace it with space before processing.
+ RFC 9110, section 5.5-5 */
+ const bool nul_as_sp = (-1 >= discp_lvl);
+ /* Allow folded header lines.
+ RFC 9112, section 5.2-4 */
+ const bool allow_folded = (0 >= discp_lvl);
+ /* Do not reject headers with the whitespace at the start of the first line.
+ When allowed, the first line with whitespace character at the first
+ position is ignored (as well as all possible line foldings of the first
+ line).
+ RFC 9112, section 2.2-8 */
+ const bool allow_wsp_at_start = allow_folded && (-1 >= discp_lvl);
+ /* Allow whitespace in header (field) name.
+ Violates RFC 9110, section 5.1-2 */
+ const bool allow_wsp_in_name = (-2 >= discp_lvl);
+ /* Allow zero-length header (field) name.
+ Violates RFC 9110, section 5.1-2 */
+ const bool allow_empty_name = (-2 >= discp_lvl);
+ /* Allow whitespace before colon.
+ Violates RFC 9112, section 5.1-2 */
+ const bool allow_wsp_before_colon = (-3 >= discp_lvl);
+ /* Do not abort the request when header line has no colon, just skip such
+ bad lines.
+ RFC 9112, section 5-1 */
+ const bool allow_line_without_colon = (-2 >= discp_lvl);
+
+ size_t p; /**< The position of the currently processed character */
+
+#if ! defined (HAVE_MESSAGES) && ! defined(_DEBUG)
+ (void) process_footers; /* Unused parameter */
+#endif /* !HAVE_MESSAGES && !_DEBUG */
+
+ mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+ MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+ c->state);
+
+ p = c->rq.hdrs.hdr.proc_pos;
+
+ mhd_assert (p <= c->read_buffer_offset);
+ while (p < c->read_buffer_offset)
+ {
+ const char chr = c->read_buffer[p];
+ bool end_of_line;
+
+ mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
+ (c->rq.hdrs.hdr.name_len < p));
+ mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || (0 != p));
+ mhd_assert ((0 == c->rq.hdrs.hdr.name_len) || \
+ (c->rq.hdrs.hdr.name_end_found));
+ mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
+ (c->rq.hdrs.hdr.name_len < c->rq.hdrs.hdr.value_start));
+ mhd_assert ((0 == c->rq.hdrs.hdr.value_start) || \
+ (0 != c->rq.hdrs.hdr.name_len));
+ mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
+ (0 == c->rq.hdrs.hdr.name_len) || \
+ (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.name_len));
+ mhd_assert ((0 == c->rq.hdrs.hdr.ws_start) || \
+ (0 == c->rq.hdrs.hdr.value_start) || \
+ (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start));
+
+ /* Check for the end of the line */
+ if ('\r' == chr)
+ {
+ if (0 != p)
+ {
+ /* Line is not empty, need to check for possible line folding */
+ if (p + 2 >= c->read_buffer_offset)
+ break; /* Not enough data yet to check for folded line */
+ }
+ else
+ {
+ /* Line is empty, no need to check for possible line folding */
+ if (p + 2 > c->read_buffer_offset)
+ break; /* Not enough data yet to check for the end of the line */
+ }
+ if ('\n' == c->read_buffer[p + 1])
+ end_of_line = true;
+ else
+ {
+ /* Bare CR alone */
+ /* Must be rejected or replaced with space char.
+ See RFC 9112, section 2.2-4 */
+ if (bare_cr_as_sp)
+ {
+ c->read_buffer[p] = ' ';
+ c->rq.num_cr_sp_replaced++;
+ continue; /* Re-start processing of the current character */
+ }
+ else if (! bare_cr_keep)
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ (! process_footers) ?
+ BARE_CR_IN_HEADER :
+ BARE_CR_IN_FOOTER);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ end_of_line = false;
+ }
+ }
+ else if ('\n' == chr)
+ {
+ /* Bare LF may be recognised as a line delimiter.
+ See RFC 9112, section 2.2-3 */
+ if (bare_lf_as_crlf)
+ {
+ if (0 != p)
+ {
+ /* Line is not empty, need to check for possible line folding */
+ if (p + 1 >= c->read_buffer_offset)
+ break; /* Not enough data yet to check for folded line */
+ }
+ end_of_line = true;
+ }
+ else
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ (! process_footers) ?
+ BARE_LF_IN_HEADER : BARE_LF_IN_FOOTER);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ }
+ else
+ end_of_line = false;
+
+ if (end_of_line)
+ {
+ /* Handle the end of the line */
+ /**
+ * The full length of the line, including CRLF (or bare LF).
+ */
+ const size_t line_len = p + (('\r' == chr) ? 2 : 1);
+ char next_line_char;
+ mhd_assert (line_len <= c->read_buffer_offset);
+
+ if (0 == p)
+ {
+ /* Zero-length header line. This is the end of the request header
+ section.
+ RFC 9112, Section 2.1-1 */
+ mhd_assert (! c->rq.hdrs.hdr.starts_with_ws);
+ mhd_assert (! c->rq.hdrs.hdr.name_end_found);
+ mhd_assert (0 == c->rq.hdrs.hdr.name_len);
+ mhd_assert (0 == c->rq.hdrs.hdr.ws_start);
+ mhd_assert (0 == c->rq.hdrs.hdr.value_start);
+ /* Consume the line with CRLF (or bare LF) */
+ c->read_buffer += line_len;
+ c->read_buffer_offset -= line_len;
+ c->read_buffer_size -= line_len;
+ return MHD_HDR_LINE_READING_GOT_END_OF_HEADER;
+ }
+
+ mhd_assert (line_len < c->read_buffer_offset);
+ mhd_assert (0 != line_len);
+ mhd_assert ('\n' == c->read_buffer[line_len - 1]);
+ next_line_char = c->read_buffer[line_len];
+ if ((' ' == next_line_char) ||
+ ('\t' == next_line_char))
+ {
+ /* Folded line */
+ if (! allow_folded)
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ (! process_footers) ?
+ ERR_RSP_OBS_FOLD :
+ ERR_RSP_OBS_FOLD_FOOTER);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ /* Replace CRLF (or bare LF) character(s) with space characters.
+ See RFC 9112, Section 5.2-4 */
+ c->read_buffer[p] = ' ';
+ if ('\r' == chr)
+ c->read_buffer[p + 1] = ' ';
+ continue; /* Re-start processing of the current character */
+ }
+ else
+ {
+ /* It is not a folded line, it's the real end of the non-empty line */
+ bool skip_line = false;
+ mhd_assert (0 != p);
+ if (c->rq.hdrs.hdr.starts_with_ws)
+ {
+ /* This is the first line and it starts with whitespace. This line
+ must be discarded completely.
+ See RFC 9112, Section 2.2-8 */
+ mhd_assert (allow_wsp_at_start);
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (c->daemon,
+ _ ("Whitespace-prefixed first header line " \
+ "has been skipped.\n"));
+#endif /* HAVE_MESSAGES */
+ skip_line = true;
+ }
+ else if (! c->rq.hdrs.hdr.name_end_found)
+ {
+ if (! allow_line_without_colon)
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ (! process_footers) ?
+ ERR_RSP_HEADER_WITHOUT_COLON :
+ ERR_RSP_FOOTER_WITHOUT_COLON);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ /* Skip broken line completely */
+ c->rq.skipped_broken_lines++;
+ skip_line = true;
+ }
+ if (skip_line)
+ {
+ /* Skip the entire line */
+ c->read_buffer += line_len;
+ c->read_buffer_offset -= line_len;
+ c->read_buffer_size -= line_len;
+ p = 0;
+ /* Reset processing state */
+ memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
+ /* Start processing of the next line */
+ continue;
+ }
+ else
+ {
+ /* This line should be valid header line */
+ size_t value_len;
+ mhd_assert ((0 != c->rq.hdrs.hdr.name_len) || allow_empty_name);
+
+ hdr_name->str = c->read_buffer + 0; /* The name always starts at the first character */
+ hdr_name->len = c->rq.hdrs.hdr.name_len;
+ mhd_assert (0 == hdr_name->str[hdr_name->len]);
+
+ if (0 == c->rq.hdrs.hdr.value_start)
+ {
+ c->rq.hdrs.hdr.value_start = p;
+ c->read_buffer[p] = 0;
+ value_len = 0;
+ }
+ else if (0 != c->rq.hdrs.hdr.ws_start)
+ {
+ mhd_assert (p > c->rq.hdrs.hdr.ws_start);
+ mhd_assert (c->rq.hdrs.hdr.ws_start > c->rq.hdrs.hdr.value_start);
+ c->read_buffer[c->rq.hdrs.hdr.ws_start] = 0;
+ value_len = c->rq.hdrs.hdr.ws_start - c->rq.hdrs.hdr.value_start;
+ }
+ else
+ {
+ mhd_assert (p > c->rq.hdrs.hdr.ws_start);
+ c->read_buffer[p] = 0;
+ value_len = p - c->rq.hdrs.hdr.value_start;
+ }
+ hdr_value->str = c->read_buffer + c->rq.hdrs.hdr.value_start;
+ hdr_value->len = value_len;
+ mhd_assert (0 == hdr_value->str[hdr_value->len]);
+ /* Consume the entire line */
+ c->read_buffer += line_len;
+ c->read_buffer_offset -= line_len;
+ c->read_buffer_size -= line_len;
+ return MHD_HDR_LINE_READING_GOT_HEADER;
+ }
+ }
+ }
+ else if ((' ' == chr) || ('\t' == chr))
+ {
+ if (0 == p)
+ {
+ if (! allow_wsp_at_start)
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ (! process_footers) ?
+ ERR_RSP_WSP_BEFORE_HEADER :
+ ERR_RSP_WSP_BEFORE_FOOTER);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ c->rq.hdrs.hdr.starts_with_ws = true;
+ }
+ else if ((! c->rq.hdrs.hdr.name_end_found) &&
+ (! c->rq.hdrs.hdr.starts_with_ws))
+ {
+ /* Whitespace in header name / between header name and colon */
+ if (allow_wsp_in_name || allow_wsp_before_colon)
+ {
+ if (0 == c->rq.hdrs.hdr.ws_start)
+ c->rq.hdrs.hdr.ws_start = p;
+ }
+ else
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ (! process_footers) ?
+ ERR_RSP_WSP_IN_HEADER_NAME :
+ ERR_RSP_WSP_IN_FOOTER_NAME);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ }
+ else
+ {
+ /* Whitespace before/inside/after header (field) value */
+ if (0 == c->rq.hdrs.hdr.ws_start)
+ c->rq.hdrs.hdr.ws_start = p;
+ }
+ }
+ else if (0 == chr)
+ {
+ if (! nul_as_sp)
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ (! process_footers) ?
+ ERR_RSP_INVALID_CHR_IN_HEADER :
+ ERR_RSP_INVALID_CHR_IN_FOOTER);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ c->read_buffer[p] = ' ';
+ continue; /* Re-start processing of the current character */
+ }
+ else
+ {
+ /* Not a whitespace, not the end of the header line */
+ mhd_assert ('\r' != chr);
+ mhd_assert ('\n' != chr);
+ mhd_assert ('\0' != chr);
+ if ((! c->rq.hdrs.hdr.name_end_found) &&
+ (! c->rq.hdrs.hdr.starts_with_ws))
+ {
+ /* Processing the header (field) name */
+ if (':' == chr)
+ {
+ if (0 == c->rq.hdrs.hdr.ws_start)
+ c->rq.hdrs.hdr.name_len = p;
+ else
+ {
+ mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
+ if (! allow_wsp_before_colon)
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ (! process_footers) ?
+ ERR_RSP_WSP_IN_HEADER_NAME :
+ ERR_RSP_WSP_IN_FOOTER_NAME);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ c->rq.hdrs.hdr.name_len = c->rq.hdrs.hdr.ws_start;
+#ifndef MHD_FAVOR_SMALL_CODE
+ c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+ }
+ if ((0 == c->rq.hdrs.hdr.name_len) && ! allow_empty_name)
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ (! process_footers) ?
+ ERR_RSP_EMPTY_HEADER_NAME :
+ ERR_RSP_EMPTY_FOOTER_NAME);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+ c->rq.hdrs.hdr.name_end_found = true;
+ c->read_buffer[c->rq.hdrs.hdr.name_len] = 0; /* Zero-terminate the name */
+ }
+ else
+ {
+ if (0 != c->rq.hdrs.hdr.ws_start)
+ {
+ /* End of the whitespace in header (field) name */
+ mhd_assert (allow_wsp_in_name || allow_wsp_before_colon);
+ if (! allow_wsp_in_name)
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ (! process_footers) ?
+ ERR_RSP_WSP_IN_HEADER_NAME :
+ ERR_RSP_WSP_IN_FOOTER_NAME);
+ return MHD_HDR_LINE_READING_DATA_ERROR; /* Error in the request */
+ }
+#ifndef MHD_FAVOR_SMALL_CODE
+ c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+ }
+ }
+ }
+ else
+ {
+ /* Processing the header (field) value */
+ if (0 == c->rq.hdrs.hdr.value_start)
+ c->rq.hdrs.hdr.value_start = p;
+#ifndef MHD_FAVOR_SMALL_CODE
+ c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
+#endif /* ! MHD_FAVOR_SMALL_CODE */
+ }
+#ifdef MHD_FAVOR_SMALL_CODE
+ c->rq.hdrs.hdr.ws_start = 0; /* Not on whitespace anymore */
+#endif /* MHD_FAVOR_SMALL_CODE */
+ }
+ p++;
+ }
+ c->rq.hdrs.hdr.proc_pos = p;
+ return MHD_HDR_LINE_READING_NEED_MORE_DATA; /* Not enough data yet */
+}
+
+
+/**
+ * Find the end of the request headers and make basic header parsing.
+ * Advance to the next state when done, handle errors.
+ * @param c the connection to process
+ * @param process_footers if true then footers are processed,
+ * if false then headers are processed
+ * @return true if request headers reading finished (either successfully
+ * or with error),
+ * false if not enough data yet in the receive buffer
+ */
+static bool
+get_req_headers (struct MHD_Connection *c, bool process_footers)
+{
+ do
+ {
+ struct _MHD_str_w_len hdr_name;
+ struct _MHD_str_w_len hdr_value;
+ enum MHD_HdrLineReadRes_ res;
+
+ mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+ MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+ c->state);
+
+ #ifdef _DEBUG
+ hdr_name.str = NULL;
+ hdr_value.str = NULL;
+#endif /* _DEBUG */
+ res = get_req_header (c, process_footers, &hdr_name, &hdr_value);
+ if (MHD_HDR_LINE_READING_GOT_HEADER == res)
+ {
+ mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+ MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+ c->state);
+ mhd_assert (NULL != hdr_name.str);
+ mhd_assert (NULL != hdr_value.str);
+ /* Values must be zero-terminated and must not have binary zeros */
+ mhd_assert (strlen (hdr_name.str) == hdr_name.len);
+ mhd_assert (strlen (hdr_value.str) == hdr_value.len);
+ /* Values must not have whitespaces at the start or at the end */
+ mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != ' '));
+ mhd_assert ((hdr_name.len == 0) || (hdr_name.str[0] != '\t'));
+ mhd_assert ((hdr_name.len == 0) || \
+ (hdr_name.str[hdr_name.len - 1] != ' '));
+ mhd_assert ((hdr_name.len == 0) || \
+ (hdr_name.str[hdr_name.len - 1] != '\t'));
+ mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != ' '));
+ mhd_assert ((hdr_value.len == 0) || (hdr_value.str[0] != '\t'));
+ mhd_assert ((hdr_value.len == 0) || \
+ (hdr_value.str[hdr_value.len - 1] != ' '));
+ mhd_assert ((hdr_value.len == 0) || \
+ (hdr_value.str[hdr_value.len - 1] != '\t'));
+
+ if (MHD_NO ==
+ MHD_set_connection_value_n_nocheck_ (c,
+ (! process_footers) ?
+ MHD_HEADER_KIND :
+ MHD_FOOTER_KIND,
+ hdr_name.str, hdr_name.len,
+ hdr_value.str, hdr_value.len))
+ {
+ /**
+ * If 'true' then "headers too large" is used for the error response,
+ * if 'false' then "URI too large is used for the error response.
+ */
+ bool headers_large_err;
+#ifdef HAVE_MESSAGES
+ MHD_DLOG (c->daemon,
+ _ ("Failed to allocate memory in the connection memory " \
+ "pool to store %s.\n"),
+ (! process_footers) ? _ ("header") : _ ("footer"));
+#endif /* HAVE_MESSAGES */
+
+ if (! process_footers)
+ {
+ size_t http_headers_size;
+ size_t url_size;
+ const struct MHD_HTTP_Req_Header *hdr;
+
+ http_headers_size = hdr_name.len + hdr_value.len;
+ url_size = c->rq.url_len;
+ for (hdr = c->rq.headers_received; NULL != hdr; hdr = hdr->next)
+ {
+ if (MHD_HEADER_KIND == hdr->kind)
+ http_headers_size += hdr->header_size + hdr->value_size + 2;
+ else if (MHD_GET_ARGUMENT_KIND == hdr->kind)
+ url_size += hdr->header_size + hdr->value_size + 2;
+ }
+ /* The comparison is not precise as linefeeds (for headers) and
+ unescaping (for GET parameters) are not taken into account,
+ but precision is not required here. */
+ headers_large_err =
+ (http_headers_size >= url_size);
+ }
+ else
+ headers_large_err = true;
+
+ transmit_error_response_static (c,
+ headers_large_err ?
+ MHD_HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE
+ : MHD_HTTP_URI_TOO_LONG,
+ REQUEST_TOO_BIG);
+ mhd_assert (MHD_CONNECTION_FULL_REQ_RECEIVED < c->state);
+ return true;
+ }
+ /* Reset processing state */
+ reset_rq_header_processing_state (c);
+ mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+ MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+ c->state);
+ /* Read the next header (field) line */
+ continue;
+ }
+ else if (MHD_HDR_LINE_READING_NEED_MORE_DATA == res)
+ {
+ mhd_assert ((process_footers ? MHD_CONNECTION_FOOTERS_RECEIVING : \
+ MHD_CONNECTION_REQ_HEADERS_RECEIVING) == \
+ c->state);
+ return false;
+ }
+ else if (MHD_HDR_LINE_READING_DATA_ERROR == res)
+ {
+ mhd_assert ((process_footers ? \
+ MHD_CONNECTION_FOOTERS_RECEIVING : \
+ MHD_CONNECTION_REQ_HEADERS_RECEIVING) < c->state);
+ mhd_assert (c->stop_with_error);
+ mhd_assert (c->discard_request);
+ return true;
+ }
+ mhd_assert (MHD_HDR_LINE_READING_GOT_END_OF_HEADER == res);
+ break;
+ } while (1);
+
#ifdef HAVE_MESSAGES
if (1 == c->rq.num_cr_sp_replaced)
{
MHD_DLOG (c->daemon,
_ ("One bare CR character has been replaced with space " \
- "in the request line.\n"));
+ "in %s.\n"),
+ (! process_footers) ?
+ _ ("the request line or in the request headers") :
+ _ ("the request footers"));
}
else if (0 != c->rq.num_cr_sp_replaced)
{
MHD_DLOG (c->daemon,
_ ("%" PRIu64 " bare CR characters have been replaced with " \
- "spaces in the request line.\n"),
- (uint64_t) c->rq.num_cr_sp_replaced);
+ "spaces in the request line and/or in the request %s.\n"),
+ (uint64_t) c->rq.num_cr_sp_replaced,
+ (! process_footers) ? _ ("headers") : _ ("footers"));
+ }
+ if (1 == c->rq.skipped_broken_lines)
+ {
+ MHD_DLOG (c->daemon,
+ _ ("One %s line without colon has been skipped.\n"),
+ (! process_footers) ? _ ("header") : _ ("footer"));
+ }
+ else if (0 != c->rq.skipped_broken_lines)
+ {
+ MHD_DLOG (c->daemon,
+ _ ("%" PRIu64 " %s lines without colons has been skipped.\n"),
+ (uint64_t) c->rq.skipped_broken_lines,
+ (! process_footers) ? _ ("header") : _ ("footer"));
}
#endif /* HAVE_MESSAGES */
- if (! process_request_target (c))
- return true; /* Error in processing */
- memset (&c->rq.hdrs.hdr, 0, sizeof(c->rq.hdrs.hdr));
- c->state = MHD_CONNECTION_REQ_LINE_RECEIVED;
+ mhd_assert (c->rq.method < c->read_buffer);
+ if (! process_footers)
+ {
+ c->rq.header_size = (size_t) (c->read_buffer - c->rq.method);
+ c->state = MHD_CONNECTION_HEADERS_RECEIVED;
+ }
+ else
+ c->state = MHD_CONNECTION_FOOTERS_RECEIVED;
return true;
}
@@ -4920,15 +5745,9 @@ MHD_connection_handle_read (struct MHD_Connection *connection,
{
case MHD_CONNECTION_INIT:
case MHD_CONNECTION_REQ_LINE_RECEIVING:
- case MHD_CONNECTION_REQ_LINE_RECEIVED:
- case MHD_CONNECTION_HEADER_PART_RECEIVED:
- case MHD_CONNECTION_HEADERS_RECEIVED:
- case MHD_CONNECTION_HEADERS_PROCESSED:
- case MHD_CONNECTION_CONTINUE_SENDING:
+ case MHD_CONNECTION_REQ_HEADERS_RECEIVING:
case MHD_CONNECTION_BODY_RECEIVING:
- case MHD_CONNECTION_BODY_RECEIVED:
- case MHD_CONNECTION_FOOTER_PART_RECEIVED:
- case MHD_CONNECTION_FOOTERS_RECEIVED:
+ case MHD_CONNECTION_FOOTERS_RECEIVING:
case MHD_CONNECTION_FULL_REQ_RECEIVED:
/* nothing to do but default action */
if (connection->read_closed)
@@ -4960,6 +5779,15 @@ MHD_connection_handle_read (struct MHD_Connection *connection,
connection->read_buffer_size = connection->read_buffer_offset;
}
break;
+ case MHD_CONNECTION_REQ_LINE_RECEIVED:
+ case MHD_CONNECTION_HEADERS_RECEIVED:
+ case MHD_CONNECTION_HEADERS_PROCESSED:
+ case MHD_CONNECTION_BODY_RECEIVED:
+ case MHD_CONNECTION_FOOTERS_RECEIVED:
+ /* Milestone state, no data should be read */
+ mhd_assert (0); /* Should not be possible */
+ break;
+ case MHD_CONNECTION_CONTINUE_SENDING:
case MHD_CONNECTION_HEADERS_SENDING:
case MHD_CONNECTION_HEADERS_SENT:
case MHD_CONNECTION_NORMAL_BODY_UNREADY:
@@ -4971,6 +5799,7 @@ MHD_connection_handle_read (struct MHD_Connection *connection,
case MHD_CONNECTION_FULL_REPLY_SENT:
default:
mhd_assert (0); /* Should not be possible */
+ break;
}
return;
}
@@ -5014,11 +5843,10 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
case MHD_CONNECTION_INIT:
case MHD_CONNECTION_REQ_LINE_RECEIVING:
case MHD_CONNECTION_REQ_LINE_RECEIVED:
- case MHD_CONNECTION_HEADER_PART_RECEIVED:
+ case MHD_CONNECTION_REQ_HEADERS_RECEIVING:
case MHD_CONNECTION_HEADERS_RECEIVED:
- mhd_assert (0);
- return;
case MHD_CONNECTION_HEADERS_PROCESSED:
+ mhd_assert (0);
return;
case MHD_CONNECTION_CONTINUE_SENDING:
ret = MHD_send_data_ (connection,
@@ -5051,9 +5879,11 @@ MHD_connection_handle_write (struct MHD_Connection *connection)
return;
case MHD_CONNECTION_BODY_RECEIVING:
case MHD_CONNECTION_BODY_RECEIVED:
- case MHD_CONNECTION_FOOTER_PART_RECEIVED:
+ case MHD_CONNECTION_FOOTERS_RECEIVING:
case MHD_CONNECTION_FOOTERS_RECEIVED:
case MHD_CONNECTION_FULL_REQ_RECEIVED:
+ mhd_assert (0);
+ return;
case MHD_CONNECTION_START_REPLY:
mhd_assert (0);
return;
@@ -5579,67 +6409,19 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING >= connection->state);
break;
case MHD_CONNECTION_REQ_LINE_RECEIVED:
- line = get_next_header_line (connection,
- NULL);
- if (MHD_CONNECTION_REQ_LINE_RECEIVED != connection->state)
- continue;
- if (NULL == line)
- {
- if (connection->read_closed)
- {
- CONNECTION_CLOSE_ERROR (connection,
- NULL);
- continue;
- }
- break;
- }
- if (0 == line[0])
- {
- connection->state = MHD_CONNECTION_HEADERS_RECEIVED;
- connection->rq.header_size = (size_t) (connection->read_buffer
- - connection->rq.method);
- continue;
- }
- if (MHD_NO == process_header_line (connection,
- line))
- {
- transmit_error_response_static (connection,
- MHD_HTTP_BAD_REQUEST,
- REQUEST_MALFORMED);
- break;
- }
- connection->state = MHD_CONNECTION_HEADER_PART_RECEIVED;
+ reset_rq_header_processing_state (connection);
+ connection->state = MHD_CONNECTION_REQ_HEADERS_RECEIVING;
continue;
- case MHD_CONNECTION_HEADER_PART_RECEIVED:
- line = get_next_header_line (connection,
- NULL);
- if (MHD_CONNECTION_HEADER_PART_RECEIVED != connection->state)
- continue;
- if (NULL == line)
+ case MHD_CONNECTION_REQ_HEADERS_RECEIVING:
+ if (get_req_headers (connection, false))
{
- if (connection->state != MHD_CONNECTION_HEADER_PART_RECEIVED)
- continue;
- if (connection->read_closed)
- {
- CONNECTION_CLOSE_ERROR (connection,
- NULL);
- continue;
- }
- break;
- }
- if (MHD_NO ==
- process_broken_line (connection,
- line,
- MHD_HEADER_KIND))
- continue;
- if (0 == line[0])
- {
- connection->state = MHD_CONNECTION_HEADERS_RECEIVED;
- connection->rq.header_size = (size_t) (connection->read_buffer
- - connection->rq.method);
+ mhd_assert (MHD_CONNECTION_REQ_HEADERS_RECEIVING < connection->state);
+ mhd_assert ((MHD_CONNECTION_HEADERS_RECEIVED == connection->state) || \
+ (connection->discard_request));
continue;
}
- continue;
+ mhd_assert (MHD_CONNECTION_REQ_HEADERS_RECEIVING == connection->state);
+ break;
case MHD_CONNECTION_HEADERS_RECEIVED:
parse_connection_headers (connection);
if (MHD_CONNECTION_HEADERS_RECEIVED != connection->state)
@@ -5696,81 +6478,44 @@ MHD_connection_handle_idle (struct MHD_Connection *connection)
if (MHD_CONNECTION_BODY_RECEIVING != connection->state)
continue;
}
- /* Modify here when response queue during data processing
+ /* Modify here when queueing of the response during data processing
will be supported */
mhd_assert (! connection->discard_request);
mhd_assert (NULL == connection->rp.response);
if (0 == connection->rq.remaining_upload_size)
{
- if (connection->rq.have_chunked_upload)
- connection->state = MHD_CONNECTION_BODY_RECEIVED;
- else
- connection->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
+ connection->state = MHD_CONNECTION_BODY_RECEIVED;
continue;
}
break;
case MHD_CONNECTION_BODY_RECEIVED:
- line = get_next_header_line (connection,
- NULL);
- if (connection->state != MHD_CONNECTION_BODY_RECEIVED)
- continue;
- if (NULL == line)
+ mhd_assert (! connection->discard_request);
+ mhd_assert (NULL == connection->rp.response);
+ if (0 == connection->rq.remaining_upload_size)
{
- if (connection->read_closed)
+ if (connection->rq.have_chunked_upload)
{
- CONNECTION_CLOSE_ERROR (connection,
- NULL);
- continue;
+ /* Reset counter variables reused for footers */
+ connection->rq.num_cr_sp_replaced = 0;
+ connection->rq.skipped_broken_lines = 0;
+ reset_rq_header_processing_state (connection);
+ connection->state = MHD_CONNECTION_FOOTERS_RECEIVING;
}
- if (0 < connection->read_buffer_offset)
- connection->state = MHD_CONNECTION_FOOTER_PART_RECEIVED;
- break;
- }
- if (0 == line[0])
- {
- connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
- if (connection->suspended)
- break;
- continue;
- }
- if (MHD_NO == process_header_line (connection,
- line))
- {
- transmit_error_response_static (connection,
- MHD_HTTP_BAD_REQUEST,
- REQUEST_MALFORMED);
- break;
- }
- connection->state = MHD_CONNECTION_FOOTER_PART_RECEIVED;
- continue;
- case MHD_CONNECTION_FOOTER_PART_RECEIVED:
- line = get_next_header_line (connection,
- NULL);
- if (connection->state != MHD_CONNECTION_FOOTER_PART_RECEIVED)
+ else
+ connection->state = MHD_CONNECTION_FULL_REQ_RECEIVED;
continue;
- if (NULL == line)
- {
- if (connection->read_closed)
- {
- CONNECTION_CLOSE_ERROR (connection,
- NULL);
- continue;
- }
- break;
}
- if (MHD_NO ==
- process_broken_line (connection,
- line,
- MHD_FOOTER_KIND))
- continue;
- if (0 == line[0])
+ break;
+ case MHD_CONNECTION_FOOTERS_RECEIVING:
+ if (get_req_headers (connection, true))
{
- connection->state = MHD_CONNECTION_FOOTERS_RECEIVED;
- if (connection->suspended)
- break;
+ mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING < connection->state);
+ mhd_assert ((MHD_CONNECTION_FOOTERS_RECEIVED == connection->state) || \
+ (connection->discard_request));
continue;
}
- continue;
+ mhd_assert (MHD_CONNECTION_FOOTERS_RECEIVING == connection->state);
+ break;
case MHD_CONNECTION_FOOTERS_RECEIVED:
/* The header, the body, and the footers of the request has been received,
* switch to the final processing of the request. */