[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[libmicrohttpd] 02/29: Re-implemented parsing of the request line from s
From: |
gnunet |
Subject: |
[libmicrohttpd] 02/29: Re-implemented parsing of the request line from scratch. |
Date: |
Tue, 20 Jun 2023 22:24:15 +0200 |
This is an automated email from the git hooks/post-receive script.
karlson2k pushed a commit to branch master
in repository libmicrohttpd.
commit bd605be2029d04d5014adccccfb1a9fa25beb1c3
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
AuthorDate: Tue Nov 8 16:46:44 2022 +0300
Re-implemented parsing of the request line from scratch.
* New algorithm parse the request line in one pass thus multiple passes
over the same memory area are avoided (efficiency for large URI should
be improved)
* Strict implementation of RFC 9110 and 9112 requirements, unacceptable
characters are replaced or threaded as errors.
* Implemented various levels of strictness for requests interpretations:
three levels within RFC requirements (more strict and more secure; less
strict and more compatible with various clients; balanced (default)),
one more relaxed level with violation of RFC's SHOULD/SHOULD NOT,
one even more relaxed level with violation of MUST/MUST NOT,
one stricter level then required by RFC, but absolutely compatible with
clients following RFC's MUST/MUST NOT, and one more even stricter level
compatible with clients following both MUST/MUST NOT and
SHOULD/SHOULD NOT.
* Added more detailed responses for invalid requests with descriptions
of the found problems (as recommended by RFC).
* Limited number of empty lines skipped before the request (as
recommended by RFC).
* Implemented automatic redirection responses for requests targets
with forbidden characters (as recommended by RFC).
* In overall: increased flexibility, the security must be improved,
much better compliance with the standards.
---
src/microhttpd/connection.c | 1126 ++++++++++++++++++++++++++++++++++---------
src/microhttpd/internal.h | 84 +++-
2 files changed, 987 insertions(+), 223 deletions(-)
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index f3e139a2..63ec4aa6 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -75,6 +75,63 @@
#define REQUEST_TOO_BIG ""
#endif
+/**
+ * Response text used when the request line has more then two whitespaces.
+ */
+#ifdef HAVE_MESSAGES
+#define RQ_LINE_TOO_MANY_WSP \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>The request line has more then two whitespaces.</body>" \
+ "</html>"
+#else
+#define RQ_LINE_TOO_MANY_WSP ""
+#endif
+
+/**
+ * Response text used when the request HTTP header has bare CR character
+ * without LF character (and CR is not allowed to be treated as whitespace).
+ */
+#ifdef HAVE_MESSAGES
+#define BARE_CR_IN_HEADER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your HTTP header has bare CR character without " \
+ "following LF character.</body>" \
+ "</html>"
+#else
+#define BARE_CR_IN_HEADER ""
+#endif
+
+/**
+ * Response text used when the request HTTP header has bare LF character
+ * without CR character.
+ */
+#ifdef HAVE_MESSAGES
+#define BARE_LF_IN_HEADER \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your HTTP header has bare LF character without " \
+ "preceding CR character.</body>" \
+ "</html>"
+#else
+#define BARE_LF_IN_HEADER ""
+#endif
+
+/**
+ * Response text used when the request line has invalid characters in URI.
+ */
+#ifdef HAVE_MESSAGES
+#define RQ_TARGET_INVALID_CHAR \
+ "<html>" \
+ "<head><title>Request broken</title></head>" \
+ "<body>Your HTTP request has invalid characters in " \
+ "the request-target.</body>" \
+ "</html>"
+#else
+#define RQ_TARGET_INVALID_CHAR ""
+#endif
+
/**
* Response text used when the request (http header) does not
* contain a "Host:" header and still claims to be HTTP 1.1.
@@ -126,7 +183,7 @@
*/
#ifdef HAVE_MESSAGES
#define REQUEST_MALFORMED \
- "<html><head><title>Request malformed</title></head><body>Your HTTP request
was syntactically incorrect.</body></html>"
+ "<html><head><title>Request malformed</title></head><body>Your HTTP request
is syntactically incorrect.</body></html>"
#else
#define REQUEST_MALFORMED ""
#endif
@@ -1008,41 +1065,6 @@ connection_close_error (struct MHD_Connection
*connection,
#endif
-/**
- * A serious error occurred, check whether error response is already
- * queued and close the connection if response wasn't queued.
- *
- * @param connection connection to close with error
- * @param emsg error message (can be NULL)
- */
-static void
-connection_close_error_check (struct MHD_Connection *connection,
- const char *emsg)
-{
- if ( (NULL != connection->rp.response) &&
- (400 <= connection->rp.responseCode) &&
- (NULL == connection->rp.response->crc) && /* Static response only! */
- (connection->stop_with_error) &&
- (MHD_CONNECTION_HEADERS_SENDING == connection->state) )
- return; /* An error response was already queued */
-
- connection_close_error (connection, emsg);
-}
-
-
-/**
- * Macro to only include error message in call to
- * #connection_close_error_check() if we have HAVE_MESSAGES.
- */
-#ifdef HAVE_MESSAGES
-#define CONNECTION_CLOSE_ERROR_CHECK(c, emsg) \
- connection_close_error_check (c, emsg)
-#else
-#define CONNECTION_CLOSE_ERROR_CHECK(c, emsg) \
- connection_close_error_check (c, NULL)
-#endif
-
-
/**
* Prepare the response buffer of this connection for
* sending. Assumes that the response mutex is
@@ -2407,12 +2429,22 @@ build_connection_chunked_response_footer (struct
MHD_Connection *connection)
* @param status_code the response code to send (400, 413 or 414)
* @param message the error message to send
* @param message_len the length of the @a message
+ * @param header_name the name of the header, malloc()ed by the caller,
+ * free() by this function, optional, can be NULL
+ * @param header_name_len the length of the @a header_name
+ * @param header_value the value of the header, malloc()ed by the caller,
+ * free() by this function, optional, can be NULL
+ * @param header_value_len the length of the @a header_value
*/
static void
transmit_error_response_len (struct MHD_Connection *connection,
unsigned int status_code,
const char *message,
- size_t message_len)
+ size_t message_len,
+ char *header_name,
+ size_t header_name_len,
+ char *header_value,
+ size_t header_value_len)
{
struct MHD_Response *response;
enum MHD_Result iret;
@@ -2479,9 +2511,30 @@ transmit_error_response_len (struct MHD_Connection
*connection,
connection->state = MHD_CONNECTION_CLOSED;
return;
}
- iret = MHD_queue_response (connection,
- status_code,
- response);
+ mhd_assert ((0 == header_name_len) || (NULL != header_name));
+ mhd_assert ((NULL == header_name) || (0 != header_name_len));
+ mhd_assert ((0 == header_value_len) || (NULL != header_value));
+ mhd_assert ((NULL == header_value) || (0 != header_value_len));
+ mhd_assert ((NULL == header_name) || (NULL != header_value));
+ mhd_assert ((NULL != header_value) || (NULL == header_name));
+ if (NULL != header_name)
+ {
+ iret = MHD_add_response_entry_no_alloc_ (response, MHD_HEADER_KIND,
+ header_name, header_name_len,
+ header_value, header_value_len);
+ if (MHD_NO == iret)
+ {
+ free (header_name);
+ free (header_value);
+ }
+ }
+ else
+ iret = MHD_YES;
+
+ if (MHD_NO != iret)
+ iret = MHD_queue_response (connection,
+ status_code,
+ response);
MHD_destroy_response (response);
if (MHD_NO == iret)
{
@@ -2533,7 +2586,18 @@ transmit_error_response_len (struct MHD_Connection
*connection,
* Transmit static string as error response
*/
#define transmit_error_response_static(c, code, msg) \
- transmit_error_response_len (c, code, msg, MHD_STATICSTR_LEN_ (msg))
+ transmit_error_response_len (c, code, \
+ msg, MHD_STATICSTR_LEN_ (msg), \
+ NULL, 0, NULL, 0)
+
+/**
+ * Transmit static string as error response and add specified header
+ */
+#define transmit_error_response_header(c, code, m, hd_n, hd_n_l, hd_v, hd_v_l)
\
+ transmit_error_response_len (c, code, \
+ m, MHD_STATICSTR_LEN_ (m), \
+ hd_n, hd_n_l, \
+ hd_v, hd_v_l)
/**
* Update the 'event_loop_info' field of this connection based on the state
@@ -3183,10 +3247,10 @@ parse_cookie_header (struct MHD_Connection *connection)
* @param connection the connection
* @param http_string the pointer to HTTP version string
* @param len the length of @a http_string in bytes
- * @return #MHD_YES if HTTP version is correct and supported,
- * #MHD_NO if HTTP version is not correct or unsupported.
+ * @return true if HTTP version is correct and supported,
+ * false if HTTP version is not correct or unsupported.
*/
-static enum MHD_Result
+static bool
parse_http_version (struct MHD_Connection *connection,
const char *http_string,
size_t len)
@@ -3207,7 +3271,7 @@ parse_http_version (struct MHD_Connection *connection,
transmit_error_response_static (connection,
MHD_HTTP_BAD_REQUEST,
REQUEST_MALFORMED);
- return MHD_NO;
+ return false;
}
if (1 == h[5] - '0')
{
@@ -3219,7 +3283,7 @@ parse_http_version (struct MHD_Connection *connection,
else
connection->rq.http_ver = MHD_HTTP_VER_1_2__1_9;
- return MHD_YES;
+ return true;
}
if (0 == h[5] - '0')
@@ -3229,14 +3293,14 @@ parse_http_version (struct MHD_Connection *connection,
transmit_error_response_static (connection,
MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED,
REQ_HTTP_VER_IS_TOO_OLD);
- return MHD_NO;
+ return false;
}
connection->rq.http_ver = MHD_HTTP_VER_FUTURE;
transmit_error_response_static (connection,
MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED,
REQ_HTTP_VER_IS_NOT_SUPPORTED);
- return MHD_NO;
+ return false;
}
@@ -3246,19 +3310,15 @@ parse_http_version (struct MHD_Connection *connection,
* @param connection the connection
* @param method the pointer to HTTP request method string
* @param len the length of @a method in bytes
- * @return #MHD_YES if HTTP method is valid string,
- * #MHD_NO if HTTP method string is not valid.
*/
-static enum MHD_Result
+static void
parse_http_std_method (struct MHD_Connection *connection,
const char *method,
size_t len)
{
const char *const m = method; /**< short alias */
mhd_assert (NULL != m);
-
- if (0 == len)
- return MHD_NO;
+ mhd_assert (0 != len);
if ((MHD_STATICSTR_LEN_ (MHD_HTTP_METHOD_GET) == len) &&
(0 == memcmp (m, MHD_HTTP_METHOD_GET, len)))
@@ -3286,141 +3346,6 @@ parse_http_std_method (struct MHD_Connection
*connection,
connection->rq.http_mthd = MHD_HTTP_MTHD_TRACE;
else
connection->rq.http_mthd = MHD_HTTP_MTHD_OTHER;
-
- /* Any method string with non-zero length is valid */
- return MHD_YES;
-}
-
-
-/**
- * Parse the first line of the HTTP HEADER.
- *
- * @param connection the connection (updated)
- * @param line the first line, not 0-terminated
- * @param line_len length of the first @a line
- * @return #MHD_YES if the line is ok, #MHD_NO if it is malformed
- */
-static enum MHD_Result
-parse_initial_message_line (struct MHD_Connection *connection,
- char *line,
- size_t line_len)
-{
- struct MHD_Daemon *daemon = connection->daemon;
- const char *curi;
- char *uri;
- char *http_version;
- char *args;
-
- if (NULL == (uri = memchr (line,
- ' ',
- line_len)))
- return MHD_NO; /* serious error */
- uri[0] = '\0';
- connection->rq.method = line;
- if (MHD_NO == parse_http_std_method (connection, connection->rq.method,
- (size_t) (uri - line)))
- return MHD_NO;
- uri++;
- /* Skip any spaces. Not required by standard but allow
- to be more tolerant. */
- /* TODO: do not skip them in standard mode */
- while ( (' ' == uri[0]) &&
- ( (size_t) (uri - line) < line_len) )
- uri++;
- if ((size_t) (uri - line) == line_len)
- {
- /* No URI and no http version given */
- curi = "";
- uri = NULL;
- connection->rq.version = "";
- args = NULL;
- if (MHD_NO == parse_http_version (connection, connection->rq.version, 0))
- return MHD_NO;
- }
- else
- {
- size_t uri_len;
- curi = uri;
- /* Search from back to accept malformed URI with space */
- http_version = line + line_len - 1;
- /* Skip any trailing spaces */
- /* TODO: do not skip them in standard mode */
- while ( (' ' == http_version[0]) &&
- (http_version > uri) )
- http_version--;
- /* Find first space in reverse direction */
- while ( (' ' != http_version[0]) &&
- (http_version > uri) )
- http_version--;
- if (http_version > uri)
- {
- /* http_version points to character before HTTP version string */
- http_version[0] = '\0';
- connection->rq.version = http_version + 1;
- if (MHD_NO == parse_http_version (connection, connection->rq.version,
- line_len
- - (size_t)
- (connection->rq.version - line)))
- return MHD_NO;
- uri_len = (size_t) (http_version - uri);
- }
- else
- {
- connection->rq.version = "";
- if (MHD_NO == parse_http_version (connection, connection->rq.version, 0))
- return MHD_NO;
- uri_len = line_len - (size_t) (uri - line);
- }
- /* check for spaces in URI if we are "strict" */
- if ( (-2 < daemon->client_discipline) &&
- (NULL != memchr (uri,
- ' ',
- uri_len)) )
- {
- /* space exists in URI and we are supposed to be strict, reject */
- return MHD_NO;
- }
-
- args = memchr (uri,
- '?',
- uri_len);
- }
-
- /* log callback before we modify URI *or* args */
- if (NULL != daemon->uri_log_callback)
- {
- connection->rq.client_aware = true;
- connection->rq.client_context
- = daemon->uri_log_callback (daemon->uri_log_callback_cls,
- uri,
- connection);
- }
-
- if (NULL != args)
- {
- args[0] = '\0';
- args++;
- /* note that this call clobbers 'args' */
- MHD_parse_arguments_ (connection,
- MHD_GET_ARGUMENT_KIND,
- args,
- &connection_add_header,
- connection);
- }
-
- /* unescape URI *after* searching for arguments and log callback */
- if (NULL != uri)
- {
- connection->rq.url_len =
- daemon->unescape_callback (daemon->unescape_callback_cls,
- connection,
- uri);
- }
- else
- connection->rq.url_len = 0;
-
- connection->rq.url = curi;
- return MHD_YES;
}
@@ -4063,6 +3988,792 @@ parse_connection_headers (struct MHD_Connection
*connection)
}
+#ifndef MHD_MAX_EMPTY_LINES_SKIP
+/**
+ * The maximum number of ignored empty line before the request line
+ * at default "strictness" level.
+ */
+#define MHD_MAX_EMPTY_LINES_SKIP 1024
+#endif /* ! MHD_MAX_EMPTY_LINES_SKIP */
+
+/**
+ * Find and parse the request line.
+ * @param c the connection to process
+ * @return true if request line completely processed and state is changed,
+ * false if not enough data yet in the receive buffer
+ */
+static bool
+get_request_line_inner (struct MHD_Connection *c)
+{
+ size_t p; /**< The current processing position */
+ const int discp_lvl = c->daemon->client_discipline;
+ /* Allow to skip one or more empty lines before the request line.
+ RFC 9112, section 2.2 */
+ const bool skip_empty_lines = (1 >= discp_lvl);
+ /* Allow to skip more then one empty line before the request line.
+ RFC 9112, section 2.2 */
+ const bool skip_several_empty_lines = (skip_empty_lines && (0 >= discp_lvl));
+ /* Allow to skip number of unlimited empty lines before the request line.
+ RFC 9112, section 2.2 */
+ const bool skip_unlimited_empty_lines =
+ (skip_empty_lines && (-3 >= discp_lvl));
+ /* Treat bare LF as the end of the line.
+ RFC 9112, section 2.2 */
+ const bool bare_lf_as_crlf = (0 >= discp_lvl);
+ /* Treat tab as whitespace delimiter */
+ const bool tab_as_wsp = (0 >= discp_lvl);
+ /* Treat VT (vertical tab) and FF (form feed) as whitespace delimiters */
+ const bool other_wsp_as_wsp = (-1 >= discp_lvl);
+ /* Treat continuous whitespace block as a single space */
+ const bool wsp_blocks = (-1 >= discp_lvl);
+ /* Parse whitespace in URI, special parsing of the request line */
+ const bool wsp_in_uri = (0 >= discp_lvl);
+ /* Keep whitespace in URI, give app URI with whitespace instead of
+ automatic redirect to fixed URI */
+ const bool wsp_in_uri_keep = (-2 >= discp_lvl);
+ /* Keep bare CR character as is.
+ Violates RFC 9112, section 2.2 */
+ const bool bare_cr_keep = (wsp_in_uri_keep && (-3 >= discp_lvl));
+ /* Treat bare CR as space; replace it with space before processing.
+ RFC 9112, section 2.2 */
+ const bool bare_cr_as_sp = ((! bare_cr_keep) && (-1 >= discp_lvl));
+
+ mhd_assert (MHD_CONNECTION_INIT == c->state || \
+ MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+ mhd_assert (NULL == c->rq.method || \
+ MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+ mhd_assert (MHD_HTTP_MTHD_NO_METHOD == c->rq.http_mthd || \
+ MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+ mhd_assert (MHD_HTTP_MTHD_NO_METHOD == c->rq.http_mthd || \
+ 0 != c->rq.hdrs.rq_line.proc_pos);
+
+ if (0 == c->read_buffer_offset)
+ {
+ mhd_assert (MHD_CONNECTION_INIT == c->state);
+ return false; /* No data to process */
+ }
+ p = c->rq.hdrs.rq_line.proc_pos;
+ mhd_assert (p <= c->read_buffer_offset);
+
+ /* Skip empty lines, if any (and if allowed) */
+ /* See RFC 9112, section 2.2 */
+ if ((0 == p)
+ && (skip_empty_lines))
+ {
+ /* Skip empty lines before the request line.
+ See RFC 9112, section 2.2 */
+ bool is_empty_line;
+ mhd_assert (MHD_CONNECTION_INIT == c->state);
+ mhd_assert (NULL == c->rq.method);
+ mhd_assert (NULL == c->rq.url);
+ mhd_assert (0 == c->rq.url_len);
+ mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt);
+ mhd_assert (0 == c->rq.hdrs.rq_line.rq_tgt_len);
+ mhd_assert (NULL == c->rq.version);
+ do
+ {
+ is_empty_line = false;
+ if ('\r' == c->read_buffer[0])
+ {
+ if (1 == c->read_buffer_offset)
+ return false; /* Not enough data yet */
+ if ('\n' == c->read_buffer[1])
+ {
+ is_empty_line = true;
+ c->read_buffer += 2;
+ c->read_buffer_size -= 2;
+ c->read_buffer_offset -= 2;
+ c->rq.hdrs.rq_line.skipped_empty_lines++;
+ }
+ }
+ else if (('\n' == c->read_buffer[0]) &&
+ (bare_lf_as_crlf))
+ {
+ is_empty_line = true;
+ c->read_buffer += 1;
+ c->read_buffer_size -= 1;
+ c->read_buffer_offset -= 1;
+ c->rq.hdrs.rq_line.skipped_empty_lines++;
+ }
+ if (is_empty_line)
+ {
+ if ((! skip_unlimited_empty_lines) &&
+ (((skip_several_empty_lines) ? MHD_MAX_EMPTY_LINES_SKIP : 1) <
+ c->rq.hdrs.rq_line.skipped_empty_lines))
+ {
+ connection_close_error (c,
+ _ ("Too many meaningless extra empty lines "
\
+ "received before the request"));
+ return true; /* Process connection closure */
+ }
+ if (0 == c->read_buffer_offset)
+ return false; /* No more data to process */
+ }
+ } while (is_empty_line);
+ }
+ /* All empty lines are skipped */
+
+ c->state = MHD_CONNECTION_REQ_LINE_RECEIVING;
+ /* Read and parse the request line */
+ mhd_assert (1 <= c->read_buffer_offset);
+
+ while (p < c->read_buffer_offset)
+ {
+ const char chr = c->read_buffer[p];
+ bool end_of_line;
+ /*
+ The processing logic is different depending on the configured
strictness:
+
+ When whitespace BLOCKS are NOT ALLOWED, the end of the whitespace is
+ processed BEFORE processing of the current character.
+ When whitespace BLOCKS are ALLOWED, the end of the whitespace is
+ processed AFTER processing of the current character.
+
+ When space char in the URI is ALLOWED, the delimiter between the URI and
+ the HTTP version string is processed only at the END of the line.
+ When space in the URI is NOT ALLOWED, the delimiter between the URI and
+ the HTTP version string is processed as soon as the FIRST whitespace is
+ found after URI start.
+ */
+
+ end_of_line = false;
+
+ mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
+ (c->rq.hdrs.rq_line.last_ws_end > \
+ c->rq.hdrs.rq_line.last_ws_start));
+ mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_start) || \
+ (0 != c->rq.hdrs.rq_line.last_ws_end));
+
+ /* Check for the end of the line */
+ if ('\r' == chr)
+ {
+ if (p + 1 == c->read_buffer_offset)
+ {
+ c->rq.hdrs.rq_line.proc_pos = p;
+ return false; /* Not enough data yet */
+ }
+ else 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 */
+ 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)
+ {
+ /* A quick simple check whether this line looks like an HTTP request
*/
+ if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
+ (MHD_HTTP_MTHD_DELETE >= c->rq.http_mthd))
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ BARE_CR_IN_HEADER);
+ }
+ else
+ connection_close_error (c,
+ _ ("Bare CR characters are not allowed " \
+ "in the request line.\n"));
+ return true; /* Error in the request */
+ }
+ }
+ }
+ else if ('\n' == chr)
+ {
+ /* Bare LF may be recognised as a line delimiter.
+ See RFC 9112, section 2.2 */
+ if (bare_lf_as_crlf)
+ end_of_line = true;
+ else
+ {
+ /* While RFC does not enforce error for bare LF character,
+ if this char is not treated as a line delimiter, it should be
+ rejected to avoid any security weakness due to request smuggling. */
+ /* A quick simple check whether this line looks like an HTTP request */
+ if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
+ (MHD_HTTP_MTHD_DELETE >= c->rq.http_mthd))
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ BARE_LF_IN_HEADER);
+ }
+ else
+ connection_close_error (c,
+ _ ("Bare LF characters are not allowed " \
+ "in the request line.\n"));
+ return true; /* Error in the request */
+ }
+ }
+
+ if (end_of_line)
+ {
+ /* Handle the end of the request line */
+
+ if (NULL != c->rq.method)
+ {
+ if (wsp_in_uri)
+ {
+ /* The end of the URI and the start of the HTTP version string
+ should be determined now. */
+ mhd_assert (NULL == c->rq.version);
+ mhd_assert (0 == c->rq.hdrs.rq_line.rq_tgt_len);
+ if (0 != c->rq.hdrs.rq_line.last_ws_end)
+ {
+ /* Determine the end and the length of the URI */
+ if (NULL != c->rq.hdrs.rq_line.rq_tgt)
+ {
+ c->read_buffer [c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero
terminate the URI */
+ c->rq.hdrs.rq_line.rq_tgt_len =
+ c->rq.hdrs.rq_line.last_ws_start
+ - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
+ }
+ else if ((c->rq.hdrs.rq_line.last_ws_start + 1 <
+ c->rq.hdrs.rq_line.last_ws_end) &&
+ (HTTP_VER_LEN == (p - c->rq.hdrs.rq_line.last_ws_end)))
+ {
+ /* Found only HTTP method and HTTP version and more than one
+ whitespace between them. Assume zero-length URI. */
+ mhd_assert (wsp_blocks);
+ c->rq.hdrs.rq_line.last_ws_start++;
+ c->read_buffer[c->rq.hdrs.rq_line.last_ws_start] = 0; /* Zero
terminate the URI */
+ c->rq.hdrs.rq_line.rq_tgt =
+ c->read_buffer + c->rq.hdrs.rq_line.last_ws_start;
+ c->rq.hdrs.rq_line.rq_tgt_len = 0;
+ c->rq.hdrs.rq_line.num_ws_in_uri = 0;
+ c->rq.hdrs.rq_line.rq_tgt_qmark = NULL;
+ }
+ /* Determine the start of the HTTP version string */
+ if (NULL != c->rq.hdrs.rq_line.rq_tgt)
+ {
+ c->rq.version = c->read_buffer + c->rq.hdrs.rq_line.last_ws_end;
+ }
+ }
+ }
+ else
+ {
+ /* The end of the URI and the start of the HTTP version string
+ should be already known. */
+ if ((NULL == c->rq.version)
+ && (NULL != c->rq.hdrs.rq_line.rq_tgt)
+ && (HTTP_VER_LEN == p - (size_t) (c->rq.hdrs.rq_line.rq_tgt
+ - c->read_buffer))
+ && (0 != c->read_buffer[(size_t)
+ (c->rq.hdrs.rq_line.rq_tgt
+ - c->read_buffer) - 1]))
+ {
+ /* Found only HTTP method and HTTP version and more than one
+ whitespace between them. Assume zero-length URI. */
+ size_t uri_pos;
+ mhd_assert (wsp_blocks);
+ mhd_assert (0 == c->rq.hdrs.rq_line.rq_tgt_len);
+ uri_pos = (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer) -
1;
+ mhd_assert (uri_pos < p);
+ c->rq.version = c->rq.hdrs.rq_line.rq_tgt;
+ c->read_buffer[uri_pos] = 0; /* Zero terminate the URI */
+ c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + uri_pos;
+ c->rq.hdrs.rq_line.rq_tgt_len = 0;
+ c->rq.hdrs.rq_line.num_ws_in_uri = 0;
+ c->rq.hdrs.rq_line.rq_tgt_qmark = NULL;
+ }
+ }
+
+ if (NULL != c->rq.version)
+ {
+ mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
+ if (! parse_http_version (c, c->rq.version,
+ p
+ - (size_t) (c->rq.version
+ - c->read_buffer)))
+ {
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state);
+ return true; /* Unsupported / broken HTTP version */
+ }
+ c->read_buffer[p] = 0; /* Zero terminate the HTTP version strings */
+ if ('\r' == chr)
+ {
+ p++; /* Consume CR */
+ mhd_assert (p < c->read_buffer_offset); /* The next character has
been already checked */
+ }
+ p++; /* Consume LF */
+ c->read_buffer += p;
+ c->read_buffer_size -= p;
+ c->read_buffer_offset -= p;
+ mhd_assert (c->rq.hdrs.rq_line.num_ws_in_uri <= \
+ c->rq.hdrs.rq_line.rq_tgt_len);
+ mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
+ (0 != c->rq.hdrs.rq_line.rq_tgt_len));
+ mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
+ ((size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
+ - c->rq.hdrs.rq_line.rq_tgt) < \
+ c->rq.hdrs.rq_line.rq_tgt_len));
+ mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
+ (c->rq.hdrs.rq_line.rq_tgt_qmark >= \
+ c->rq.hdrs.rq_line.rq_tgt));
+ return true; /* The request line is successfully parsed */
+ }
+ }
+ /* Error in the request line */
+
+ /* A quick simple check whether this line looks like an HTTP request */
+ if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
+ (MHD_HTTP_MTHD_DELETE >= c->rq.http_mthd))
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ REQUEST_MALFORMED);
+ }
+ else
+ connection_close_error (c,
+ _ ("The request line is malformed.\n"));
+
+ return true;
+ }
+
+ /* Process possible end of the previously found whitespace delimiter */
+ if ((! wsp_blocks) &&
+ (p == c->rq.hdrs.rq_line.last_ws_end) &&
+ (0 != c->rq.hdrs.rq_line.last_ws_end))
+ {
+ /* Previous character was a whitespace char and whitespace blocks
+ are not allowed. */
+ /* The current position is the next character after
+ a whitespace delimiter */
+ if (NULL == c->rq.hdrs.rq_line.rq_tgt)
+ {
+ /* The current position is the start of the URI */
+ mhd_assert (0 == c->rq.hdrs.rq_line.rq_tgt_len);
+ mhd_assert (NULL == c->rq.version);
+ c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
+ /* Reset the whitespace marker */
+ c->rq.hdrs.rq_line.last_ws_start = 0;
+ c->rq.hdrs.rq_line.last_ws_end = 0;
+ }
+ else
+ {
+ /* It was a whitespace after the start of the URI */
+ if (! wsp_in_uri)
+ {
+ mhd_assert ((0 != c->rq.hdrs.rq_line.rq_tgt_len) || \
+ (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
+ mhd_assert (NULL == c->rq.version); /* Too many whitespaces? This
error is handled at whitespace start */
+ c->rq.version = c->read_buffer + p;
+ /* Reset the whitespace marker */
+ c->rq.hdrs.rq_line.last_ws_start = 0;
+ c->rq.hdrs.rq_line.last_ws_end = 0;
+ }
+ }
+ }
+
+ /* Process the current character.
+ Is it not the end of the line. */
+ if ((' ' == chr)
+ || (('\t' == chr) && (tab_as_wsp))
+ || ((other_wsp_as_wsp) && ((0xb == chr) || (0xc == chr))))
+ {
+ /* A whitespace character */
+ if ((0 == c->rq.hdrs.rq_line.last_ws_end) ||
+ (p != c->rq.hdrs.rq_line.last_ws_end) ||
+ (! wsp_blocks))
+ {
+ /* Found first whitespace char of the new whitespace block */
+ if (NULL == c->rq.method)
+ {
+ /* Found the end of the HTTP method string */
+ mhd_assert (0 == c->rq.hdrs.rq_line.last_ws_start);
+ mhd_assert (0 == c->rq.hdrs.rq_line.last_ws_end);
+ mhd_assert (NULL == c->rq.hdrs.rq_line.rq_tgt);
+ mhd_assert (0 == c->rq.hdrs.rq_line.rq_tgt_len);
+ mhd_assert (NULL == c->rq.version);
+ if (0 == p)
+ {
+ connection_close_error (c,
+ _ ("The request line starts with "
+ "a whitespace.\n"));
+ return true; /* Error in the request */
+ }
+ c->read_buffer[p] = 0; /* Zero-terminate the request method string */
+ c->rq.method = c->read_buffer;
+ parse_http_std_method (c, c->rq.method, p);
+ }
+ else
+ {
+ /* A whitespace after the start of the URI */
+ if (! wsp_in_uri)
+ {
+ /* Whitespace in URI is not allowed to be parsed */
+ if (NULL == c->rq.version)
+ {
+ mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
+ /* This is a delimiter between URI and HTTP version string */
+ c->read_buffer[p] = 0; /* Zero-terminate request URI string */
+ mhd_assert (((size_t) (c->rq.hdrs.rq_line.rq_tgt \
+ - c->read_buffer)) <= p);
+ c->rq.hdrs.rq_line.rq_tgt_len =
+ p - (size_t) (c->rq.hdrs.rq_line.rq_tgt - c->read_buffer);
+ }
+ else
+ {
+ /* This is a delimiter AFTER version string */
+
+ /* A quick simple check whether this line looks like an HTTP
request */
+ if ((MHD_HTTP_MTHD_GET <= c->rq.http_mthd) &&
+ (MHD_HTTP_MTHD_DELETE >= c->rq.http_mthd))
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ RQ_LINE_TOO_MANY_WSP);
+ }
+ else
+ connection_close_error (c,
+ _ ("The request line has more than "
+ "two whitespaces.\n"));
+ return true; /* Error in the request */
+ }
+ }
+ else
+ {
+ /* Whitespace in URI is allowed to be parsed */
+ if (0 != c->rq.hdrs.rq_line.last_ws_end)
+ {
+ /* The whitespace after the start of the URI has been found
already */
+ c->rq.hdrs.rq_line.num_ws_in_uri +=
+ c->rq.hdrs.rq_line.last_ws_end
+ - c->rq.hdrs.rq_line.last_ws_start;
+ }
+ }
+ }
+ c->rq.hdrs.rq_line.last_ws_start = p;
+ c->rq.hdrs.rq_line.last_ws_end = p + 1; /* Will be updated on the next
char parsing */
+ }
+ else
+ {
+ /* Continuation of the whitespace block */
+ mhd_assert (0 != c->rq.hdrs.rq_line.last_ws_end);
+ mhd_assert (0 != p);
+ c->rq.hdrs.rq_line.last_ws_end = p + 1;
+ }
+ }
+ else
+ {
+ /* Non-whitespace char, not the end of the line */
+ mhd_assert ((0 == c->rq.hdrs.rq_line.last_ws_end) || \
+ (c->rq.hdrs.rq_line.last_ws_end == p) || \
+ wsp_in_uri);
+
+ if ((p == c->rq.hdrs.rq_line.last_ws_end) &&
+ (0 != c->rq.hdrs.rq_line.last_ws_end) &&
+ (wsp_blocks))
+ {
+ /* The end of the whitespace block */
+ if (NULL == c->rq.hdrs.rq_line.rq_tgt)
+ {
+ /* This is the first character of the URI */
+ mhd_assert (0 == c->rq.hdrs.rq_line.rq_tgt_len);
+ mhd_assert (NULL == c->rq.version);
+ c->rq.hdrs.rq_line.rq_tgt = c->read_buffer + p;
+ /* Reset the whitespace marker */
+ c->rq.hdrs.rq_line.last_ws_start = 0;
+ c->rq.hdrs.rq_line.last_ws_end = 0;
+ }
+ else
+ {
+ if (! wsp_in_uri)
+ {
+ /* This is the first character of the HTTP version */
+ mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
+ mhd_assert ((0 != c->rq.hdrs.rq_line.rq_tgt_len) || \
+ (c->rq.hdrs.rq_line.rq_tgt + 1 == c->read_buffer + p));
+ mhd_assert (NULL == c->rq.version); /* Handled at whitespace start
*/
+ c->rq.version = c->read_buffer + p;
+ /* Reset the whitespace marker */
+ c->rq.hdrs.rq_line.last_ws_start = 0;
+ c->rq.hdrs.rq_line.last_ws_end = 0;
+ }
+ }
+ }
+
+ /* Handle other special characters */
+ if ('?' == chr)
+ {
+ if ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) &&
+ (NULL != c->rq.hdrs.rq_line.rq_tgt))
+ {
+ c->rq.hdrs.rq_line.rq_tgt_qmark = c->read_buffer + p;
+ }
+ }
+ else if ((0xb == chr) || (0xc == chr))
+ {
+ /* VT or LF characters */
+ mhd_assert (! other_wsp_as_wsp);
+ if ((NULL != c->rq.hdrs.rq_line.rq_tgt) &&
+ (NULL == c->rq.version) &&
+ (wsp_in_uri))
+ {
+ c->rq.hdrs.rq_line.num_ws_in_uri++;
+ }
+ else
+ {
+ connection_close_error (c,
+ _ ("Invalid character is in the "
+ "request line.\n"));
+ return true; /* Error in the request */
+ }
+ }
+ else if (0 == chr)
+ {
+ /* NUL character */
+ connection_close_error (c,
+ _ ("The NUL character is in the "
+ "request line.\n"));
+ return true; /* Error in the request */
+ }
+ }
+
+ p++;
+ }
+
+ c->rq.hdrs.rq_line.proc_pos = p;
+ return false; /* Not enough data yet */
+}
+
+
+#ifndef MHD_MAX_FIXED_URI_LEN
+/**
+ * The maximum size of the fixed URI for automatic redirection
+ */
+#define MHD_MAX_FIXED_URI_LEN (64 * 1024)
+#endif /* ! MHD_MAX_FIXED_URI_LEN */
+
+/**
+ * Send the automatic redirection to fixed URI when received URI with
+ * whitespaces.
+ * If URI is too large, close connection with error.
+ *
+ * @param c the connection to process
+ */
+static void
+send_redirect_fixed_rq_target (struct MHD_Connection *c)
+{
+ char *b;
+ size_t fixed_uri_len;
+ size_t i;
+ size_t o;
+ char *hdr_name;
+ size_t hdr_name_len;
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+ mhd_assert (0 != c->rq.hdrs.rq_line.num_ws_in_uri);
+ mhd_assert (c->rq.hdrs.rq_line.num_ws_in_uri <= \
+ c->rq.hdrs.rq_line.rq_tgt_len);
+ fixed_uri_len = c->rq.hdrs.rq_line.rq_tgt_len
+ + 2 * c->rq.hdrs.rq_line.num_ws_in_uri;
+ if ((fixed_uri_len + 200 > c->daemon->pool_size) ||
+ (fixed_uri_len > MHD_MAX_FIXED_URI_LEN) ||
+ (NULL == (b = malloc (fixed_uri_len + 1))))
+ {
+ connection_close_error (c,
+ _ ("The request has whitespace character is " \
+ "in the URI and the URI is too large to " \
+ "send automatic redirect to fixed URI.\n"));
+ return;
+ }
+ i = 0;
+ o = 0;
+
+ do
+ {
+ const char chr = c->rq.hdrs.rq_line.rq_tgt[i++];
+ mhd_assert ('\r' != chr); /* Replaced during request line parsing */
+ mhd_assert ('\n' != chr); /* Rejected during request line parsing */
+ mhd_assert (0 != chr); /* Rejected during request line parsing */
+ switch (chr)
+ {
+ case ' ':
+ b[o++] = '%';
+ b[o++] = '2';
+ b[o++] = '0';
+ break;
+ case '\t':
+ b[o++] = '%';
+ b[o++] = '0';
+ b[o++] = '9';
+ break;
+ case 0x0B: /* VT (vertical tab) */
+ b[o++] = '%';
+ b[o++] = '0';
+ b[o++] = 'B';
+ break;
+ case 0x0C: /* FF (form feed) */
+ b[o++] = '%';
+ b[o++] = '0';
+ b[o++] = 'C';
+ break;
+ default:
+ b[o++] = chr;
+ break;
+ }
+ } while (i < c->rq.hdrs.rq_line.rq_tgt_len);
+ mhd_assert (fixed_uri_len == o);
+ b[o] = 0; /* Zero-terminate the result */
+
+ hdr_name_len = MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_LOCATION);
+ hdr_name = malloc (hdr_name_len + 1);
+ if (NULL != hdr_name)
+ {
+ memcpy (hdr_name, MHD_HTTP_HEADER_LOCATION, hdr_name_len + 1);
+ transmit_error_response_header (c, MHD_HTTP_MOVED_PERMANENTLY,
+ RQ_TARGET_INVALID_CHAR,
+ hdr_name, hdr_name_len,
+ b, o);
+ return;
+ }
+ free (b);
+ connection_close_error (c,
+ _ ("The request has whitespace character is in the "
\
+ "URI.\n"));
+ return;
+}
+
+
+/**
+ * Process request-target string, form URI and URI parameters
+ * @param c the connection to process
+ * @return true if request-target successfully processed,
+ * false if error encountered
+ */
+static bool
+process_request_target (struct MHD_Connection *c)
+{
+#if _DEBUG
+ size_t params_len;
+#endif /* _DEBUG */
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+ mhd_assert (NULL == c->rq.url);
+ mhd_assert (0 == c->rq.url_len);
+ mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
+ mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
+ (c->rq.hdrs.rq_line.rq_tgt <= c->rq.hdrs.rq_line.rq_tgt_qmark));
+ mhd_assert ((NULL == c->rq.hdrs.rq_line.rq_tgt_qmark) || \
+ (c->rq.hdrs.rq_line.rq_tgt_len > \
+ (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark \
+ - c->rq.hdrs.rq_line.rq_tgt)));
+
+ /* Log callback before the request-target is modified/decoded */
+ if (NULL != c->daemon->uri_log_callback)
+ {
+ c->rq.client_aware = true;
+ c->rq.client_context =
+ c->daemon->uri_log_callback (c->daemon->uri_log_callback_cls,
+ c->rq.hdrs.rq_line.rq_tgt,
+ c);
+ }
+
+ if (NULL != c->rq.hdrs.rq_line.rq_tgt_qmark)
+ {
+#if _DEBUG
+ params_len =
+ c->rq.hdrs.rq_line.rq_tgt_len
+ - (size_t) (c->rq.hdrs.rq_line.rq_tgt_qmark - c->rq.hdrs.rq_line.rq_tgt);
+#endif /* _DEBUG */
+ c->rq.hdrs.rq_line.rq_tgt_qmark[0] = 0; /* Replace '?' with zero
termination */
+ if (MHD_NO == MHD_parse_arguments_ (c,
+ MHD_GET_ARGUMENT_KIND,
+ c->rq.hdrs.rq_line.rq_tgt_qmark + 1,
+ &connection_add_header,
+ c))
+ {
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING != c->state);
+ return false;
+ }
+ }
+#if _DEBUG
+ else
+ params_len = 0;
+#endif /* _DEBUG */
+
+ mhd_assert (strlen (c->rq.hdrs.rq_line.rq_tgt) == \
+ c->rq.hdrs.rq_line.rq_tgt_len - params_len);
+
+ /* Finally unescape URI itself */
+ c->rq.url_len =
+ c->daemon->unescape_callback (c->daemon->unescape_callback_cls,
+ c,
+ c->rq.hdrs.rq_line.rq_tgt);
+ c->rq.url = c->rq.hdrs.rq_line.rq_tgt;
+
+ return true;
+}
+
+
+/**
+ * Find and parse the request line.
+ * Advance to the next state when done, handle errors.
+ * @param c the connection to process
+ * @return true if request line completely processed and state is changed,
+ * false if not enough data yet in the receive buffer
+ */
+static bool
+get_request_line (struct MHD_Connection *c)
+{
+ const int discp_lvl = c->daemon->client_discipline;
+ /* Parse whitespace in URI, special parsing of the request line */
+ const bool wsp_in_uri = (0 >= discp_lvl);
+ /* Keep whitespace in URI, give app URI with whitespace instead of
+ automatic redirect to fixed URI */
+ const bool wsp_in_uri_keep = (-2 >= discp_lvl);
+
+ if (! get_request_line_inner (c))
+ return false;
+ if (MHD_CONNECTION_REQ_LINE_RECEIVING < c->state)
+ return true; /* Error in the request */
+
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING == c->state);
+ mhd_assert (NULL == c->rq.url);
+ mhd_assert (0 == c->rq.url_len);
+ mhd_assert (NULL != c->rq.hdrs.rq_line.rq_tgt);
+ if (0 != c->rq.hdrs.rq_line.num_ws_in_uri)
+ {
+ if (! wsp_in_uri)
+ {
+ transmit_error_response_static (c,
+ MHD_HTTP_BAD_REQUEST,
+ RQ_TARGET_INVALID_CHAR);
+ return true; /* Error in the request */
+ }
+ if (! wsp_in_uri_keep)
+ {
+ send_redirect_fixed_rq_target (c);
+ return true; /* Error in the request */
+ }
+ }
+#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"));
+ }
+ 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);
+ }
+#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_URL_RECEIVED;
+ return true;
+}
+
+
/**
* Update the 'last_activity' field of the connection to the current time
* and move the connection to the head of the 'normal_timeout' list if
@@ -4846,7 +5557,6 @@ MHD_connection_handle_idle (struct MHD_Connection
*connection)
{
struct MHD_Daemon *daemon = connection->daemon;
char *line;
- size_t line_len;
enum MHD_Result ret;
#ifdef MHD_USE_THREADS
mhd_assert ( (0 == (daemon->options & MHD_USE_INTERNAL_POLLING_THREAD)) || \
@@ -4876,40 +5586,14 @@ MHD_connection_handle_idle (struct MHD_Connection
*connection)
{
case MHD_CONNECTION_INIT:
case MHD_CONNECTION_REQ_LINE_RECEIVING:
- line = get_next_header_line (connection,
- &line_len);
- if (MHD_CONNECTION_REQ_LINE_RECEIVING < connection->state)
- continue;
- if (NULL != line)
- {
- /* Check for empty string, as we might want
- to tolerate 'spurious' empty lines */
- if (0 == line[0])
- {
- /* TODO: Add MHD option to not tolerate it */
- connection->state = MHD_CONNECTION_INIT;
- continue; /* Process the next line */
- }
- if (MHD_NO == parse_initial_message_line (connection,
- line,
- line_len))
- CONNECTION_CLOSE_ERROR_CHECK (connection,
- NULL);
- else
- {
- mhd_assert (MHD_IS_HTTP_VER_SUPPORTED (connection->rq.http_ver));
- connection->state = MHD_CONNECTION_URL_RECEIVED;
- }
- continue;
- }
- /* NULL means we didn't get a full line yet */
- if (connection->discard_request)
+ if (get_request_line (connection))
{
- mhd_assert (MHD_CONNECTION_INIT != connection->state);
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING < connection->state);
+ mhd_assert ((MHD_IS_HTTP_VER_SUPPORTED (connection->rq.http_ver)) \
+ || (connection->discard_request));
continue;
}
- if (0 < connection->read_buffer_offset)
- connection->state = MHD_CONNECTION_REQ_LINE_RECEIVING;
+ mhd_assert (MHD_CONNECTION_REQ_LINE_RECEIVING >= connection->state);
break;
case MHD_CONNECTION_URL_RECEIVED:
line = get_next_header_line (connection,
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h
index 9f5ed442..53f71a5f 100644
--- a/src/microhttpd/internal.h
+++ b/src/microhttpd/internal.h
@@ -1,7 +1,7 @@
/*
This file is part of libmicrohttpd
Copyright (C) 2007-2018 Daniel Pittman and Christian Grothoff
- Copyright (C) 2014-2021 Evgeny Grin (Karlson2k)
+ Copyright (C) 2014-2022 Evgeny Grin (Karlson2k)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -886,7 +886,7 @@ enum MHD_HTTP_Version
/**
* The HTTP method.
*
- * Only primary methods (specified in RFC7231) are defined here.
+ * Only primary methods (specified in RFC9110) are defined here.
*/
enum MHD_HTTP_Method
{
@@ -933,6 +933,76 @@ enum MHD_HTTP_Method
} _MHD_FIXED_ENUM;
+/**
+ * The request line processing data
+ */
+struct MHD_RequestLineProcessing
+{
+ /**
+ * The position of the next character to be processed
+ */
+ size_t proc_pos;
+ /**
+ * The number of empty lines skipped
+ */
+ unsigned int skipped_empty_lines;
+ /**
+ * The position of the start of the current/last found whitespace block,
+ * zero if not found yet.
+ */
+ size_t last_ws_start;
+ /**
+ * The position of the next character after the last known whitespace
+ * character in the current/last found whitespace block,
+ * zero if not found yet.
+ */
+ size_t last_ws_end;
+ /**
+ * The pointer to the request target.
+ * The request URI will be formed based on it.
+ */
+ char *rq_tgt;
+ /**
+ * The length of the @a rq_tgt, not including terminating zero.
+ */
+ size_t rq_tgt_len;
+ /**
+ * The pointer to the first question mark in the @a rq_tgt.
+ */
+ char *rq_tgt_qmark;
+ /**
+ * The number of whitespace characters in the request URI
+ */
+ size_t num_ws_in_uri;
+};
+
+/**
+ * The request header processing data
+ */
+struct MHD_HeaderProcessing
+{
+ /**
+ * The position of the last processed character
+ */
+ size_t proc_pos;
+};
+
+/**
+ * The union of request line and header processing data
+ */
+union MHD_HeadersProcessing
+{
+ /**
+ * The request line processing data
+ */
+ struct MHD_RequestLineProcessing rq_line;
+
+ /**
+ * The request header processing data
+ */
+ struct MHD_HeaderProcessing hdr;
+};
+
/**
* Request-specific values.
*
@@ -1074,6 +1144,16 @@ struct MHD_Request
*/
bool dauth_tried;
#endif /* DAUTH_SUPPORT */
+ /**
+ * Number of bare CR characters that were replaced with space characters
+ * in the request line or in the headers (field lines).
+ */
+ size_t num_cr_sp_replaced;
+
+ /**
+ * The data of the request line / request headers processing
+ */
+ union MHD_HeadersProcessing hdrs;
/**
* Last incomplete header line during parsing of headers.
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [libmicrohttpd] branch master updated (8dc93b08 -> f02888d4), gnunet, 2023/06/20
- [libmicrohttpd] 03/29: microhttpd.h: fixed typos in comments, gnunet, 2023/06/20
- [libmicrohttpd] 04/29: Renamed connection state for clarity, gnunet, 2023/06/20
- [libmicrohttpd] 02/29: Re-implemented parsing of the request line from scratch.,
gnunet <=
- [libmicrohttpd] 08/29: tests: minor improvemnts, gnunet, 2023/06/20
- [libmicrohttpd] 07/29: Removed request line and headers processing old functions and variables, gnunet, 2023/06/20
- [libmicrohttpd] 09/29: daemon: added debug member to avoid accept4() usage, gnunet, 2023/06/20
- [libmicrohttpd] 01/29: Refactoring: check whether memory block is resizable, gnunet, 2023/06/20
- [libmicrohttpd] 06/29: Re-implemented parsing of the request headers and footers from scratch., gnunet, 2023/06/20
- [libmicrohttpd] 12/29: Added back testing with socat as a fallback option, gnunet, 2023/06/20
- [libmicrohttpd] 05/29: Request body processing: removed impossible code paths, gnunet, 2023/06/20
- [libmicrohttpd] 16/29: Added tests with single and double folded header, gnunet, 2023/06/20
- [libmicrohttpd] 21/29: process_request_body(): minor readability improvementы, gnunet, 2023/06/20
- [libmicrohttpd] 11/29: testzzuf: re-worked zzuf testing, gnunet, 2023/06/20