libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

commit 8aa7d23219052cde065b93adf04c5ded067a1fea
parent 08ea0cc894bfdd9aeddeb8bb113514c247d2c69e
Author: Evgeny Grin (Karlson2k) <k2k@narod.ru>
Date:   Wed,  1 May 2019 22:10:38 +0300

Partial revert of 1b610e5b13b7b96e7b3f372c8da1ec9d840f896a.
Implemented new functions for key and value with binary zero.
Significantly speedup search for key by using key size.

Diffstat:
Mdoc/examples/logging.c | 17+++++------------
Msrc/include/microhttpd.h | 42++++++++++++++++++++++++++++++++++--------
Msrc/microhttpd/connection.c | 51+++++++++++++++++++++++++++++++--------------------
Msrc/microhttpd/digestauth.c | 11++++++++---
Msrc/microhttpd/internal.c | 47++++++++++++++++++++++++++---------------------
Msrc/microhttpd/internal.h | 18+++++++++++++++---
Msrc/microhttpd/mhd_str.c | 29+++++++++++++++++++++++++++++
Msrc/microhttpd/mhd_str.h | 15+++++++++++++++
Msrc/microhttpd/response.c | 24++++++++++++++++++------
Msrc/testcurl/test_process_headers.c | 8+-------
Msrc/testcurl/test_urlparse.c | 9++-------
11 files changed, 184 insertions(+), 87 deletions(-)

diff --git a/doc/examples/logging.c b/doc/examples/logging.c @@ -15,16 +15,11 @@ static int -print_out_key (void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *value, - size_t value_size) +print_out_key (void *cls, enum MHD_ValueKind kind, const char *key, + const char *value) { - (void) cls; /* Unused. Silent compiler warning. */ - (void) kind; /* Unused. Silent compiler warning. */ - (void) value_size; - + (void)cls; /* Unused. Silent compiler warning. */ + (void)kind; /* Unused. Silent compiler warning. */ printf ("%s: %s\n", key, value); return MHD_YES; } @@ -43,9 +38,7 @@ answer_to_connection (void *cls, struct MHD_Connection *connection, (void)con_cls; /* Unused. Silent compiler warning. */ printf ("New %s request for %s using version %s\n", method, url, version); - MHD_get_connection_values (connection, - MHD_HEADER_KIND, - &print_out_key, + MHD_get_connection_values (connection, MHD_HEADER_KIND, print_out_key, NULL); return MHD_NO; diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h @@ -128,7 +128,7 @@ typedef intptr_t ssize_t; * Current version of the library. * 0x01093001 = 1.9.30-1. */ -#define MHD_VERSION 0x00096302 +#define MHD_VERSION 0x00096303 /** * MHD-internal return code for "YES". @@ -2041,6 +2041,29 @@ typedef void * @param kind kind of the header we are looking at * @param key key for the value, can be an empty string * @param value corresponding value, can be NULL + * @return #MHD_YES to continue iterating, + * #MHD_NO to abort the iteration + * @ingroup request + */ +typedef int +(*MHD_KeyValueIterator) (void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *value); + + +/** + * Iterator over key-value pairs with size parameters. + * This iterator can be used to iterate over all of + * the cookies, headers, or POST-data fields of a + * request, and also to iterate over the headers that + * have been added to a response. + * @note Available since #MHD_VERSION 0x00096303 + * + * @param cls closure + * @param kind kind of the header we are looking at + * @param key key for the value, can be an empty string + * @param value corresponding value, can be NULL * @param value_size number of bytes in @a value, NEW since #MHD_VERSION 0x00096301; * for C-strings, the length excludes the 0-terminator * @return #MHD_YES to continue iterating, @@ -2048,11 +2071,12 @@ typedef void * @ingroup request */ typedef int -(*MHD_KeyValueIterator) (void *cls, +(*MHD_KeyValueIteratorN) (void *cls, enum MHD_ValueKind kind, const char *key, + size_t key_size, const char *value, - size_t value_size); + size_t value_size); /** @@ -2531,6 +2555,7 @@ MHD_set_connection_value (struct MHD_Connection *connection, * value should be set * @param kind kind of the value * @param key key for the value + * @param key_size number of bytes in @a key (excluding 0-terminator for C-strings) * @param value the value itself * @param value_size number of bytes in @a value (excluding 0-terminator for C-strings) * @return #MHD_NO if the operation could not be @@ -2539,11 +2564,12 @@ MHD_set_connection_value (struct MHD_Connection *connection, * @ingroup request */ int -MHD_set_connection_value2 (struct MHD_Connection *connection, - enum MHD_ValueKind kind, - const char *key, - const char *value, - size_t value_size); +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); /** diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c @@ -706,8 +706,7 @@ MHD_get_connection_values (struct MHD_Connection *connection, (MHD_YES != iterator (iterator_cls, pos->kind, pos->header, - pos->value, - pos->value_size)) ) + pos->value)) ) return ret; } return ret; @@ -733,19 +732,21 @@ MHD_get_connection_values (struct MHD_Connection *connection, * value should be set * @param kind kind of the value * @param key key for the value + * @param key_size number of bytes in @a key (excluding 0-terminator for C-strings) * @param value the value itself - * @param value_size number of bytes in @a value + * @param value_size number of bytes in @a value (excluding 0-terminator for C-strings) * @return #MHD_NO if the operation could not be * performed due to insufficient memory; * #MHD_YES on success * @ingroup request */ int -MHD_set_connection_value2 (struct MHD_Connection *connection, - enum MHD_ValueKind kind, - const char *key, - const char *value, - size_t value_size) +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) { struct MHD_HTTP_Header *pos; @@ -755,6 +756,7 @@ MHD_set_connection_value2 (struct MHD_Connection *connection, if (NULL == pos) return MHD_NO; pos->header = (char *) key; + pos->header_size = key_size; pos->value = (char *) value; pos->value_size = value_size; pos->kind = kind; @@ -805,13 +807,16 @@ MHD_set_connection_value (struct MHD_Connection *connection, const char *key, const char *value) { - return MHD_set_connection_value2 (connection, - kind, - key, - value, - NULL != value - ? strlen (value) - : 0); + return MHD_set_connection_value_n (connection, + kind, + key, + NULL != key + ? strlen (key) + : 0, + value, + NULL != value + ? strlen (value) + : 0); } @@ -2105,6 +2110,7 @@ get_next_header_line (struct MHD_Connection *connection, * value should be set * @param kind kind of the value * @param key key for the value + * @param key_size number of bytes in @a key * @param value the value itself * @param value_size number of bytes in @a value * @return #MHD_NO on failure (out of memory), #MHD_YES for success @@ -2112,16 +2118,18 @@ get_next_header_line (struct MHD_Connection *connection, static int connection_add_header (struct MHD_Connection *connection, const char *key, + size_t key_size, const char *value, size_t value_size, enum MHD_ValueKind kind) { if (MHD_NO == - MHD_set_connection_value2 (connection, - kind, - key, - value, - value_size)) + MHD_set_connection_value_n (connection, + kind, + key, + key_size, + value, + value_size)) { #ifdef HAVE_MESSAGES MHD_DLOG (connection->daemon, @@ -2203,6 +2211,7 @@ parse_cookie_header (struct MHD_Connection *connection) if (MHD_NO == connection_add_header (connection, pos, + ekill - pos + 1, "", 0, MHD_COOKIE_KIND)) @@ -2243,6 +2252,7 @@ parse_cookie_header (struct MHD_Connection *connection) if (MHD_NO == connection_add_header (connection, pos, + ekill - pos + 1, equals, end - equals, MHD_COOKIE_KIND)) @@ -2774,6 +2784,7 @@ process_broken_line (struct MHD_Connection *connection, if (MHD_NO == connection_add_header (connection, last, + strlen (last), connection->colon, strlen (connection->colon), kind)) diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c @@ -731,6 +731,7 @@ calculate_nonce (uint32_t nonce_time, * * @param connection the connection * @param key the key + * @param key_size number of bytes in @a key * @param value the value, can be NULL * @param value_size number of bytes in @a value * @param kind type of the header @@ -740,6 +741,7 @@ calculate_nonce (uint32_t nonce_time, static int test_header (struct MHD_Connection *connection, const char *key, + size_t key_size, const char *value, size_t value_size, enum MHD_ValueKind kind) @@ -750,10 +752,13 @@ test_header (struct MHD_Connection *connection, { if (kind != pos->kind) continue; - if (value_size != pos->value_size) + if (key_size != pos->header_size) continue; - if (0 != strcmp (key, - pos->header)) + if (value_size != pos->value_size) + continue; + if (0 != memcmp (key, + pos->header, + key_size)) continue; if ( (NULL == value) && (NULL == pos->value) ) diff --git a/src/microhttpd/internal.c b/src/microhttpd/internal.c @@ -190,12 +190,13 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, struct MHD_Daemon *daemon = connection->daemon; char *equals; char *amper; - size_t len; *num_headers = 0; while ( (NULL != args) && ('\0' != args[0]) ) { + size_t key_len; + size_t value_len; equals = strchr (args, '='); amper = strchr (args, '&'); if (NULL == amper) @@ -205,11 +206,12 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, { /* last argument, without '=' */ MHD_unescape_plus (args); - daemon->unescape_callback (daemon->unescape_callback_cls, - connection, - args); + key_len = daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + args); if (MHD_YES != cb (connection, args, + key_len, NULL, 0, kind)) @@ -221,17 +223,18 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, equals[0] = '\0'; equals++; MHD_unescape_plus (args); - daemon->unescape_callback (daemon->unescape_callback_cls, - connection, - args); + key_len = daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + args); MHD_unescape_plus (equals); - len = daemon->unescape_callback (daemon->unescape_callback_cls, - connection, - equals); + value_len = daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + equals); if (MHD_YES != cb (connection, args, + key_len, equals, - len, + value_len, kind)) return MHD_NO; (*num_headers)++; @@ -245,11 +248,12 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, { /* got 'foo&bar' or 'foo&bar=val', add key 'foo' with NULL for value */ MHD_unescape_plus (args); - daemon->unescape_callback (daemon->unescape_callback_cls, - connection, - args); + key_len = daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + args); if (MHD_YES != cb (connection, args, + key_len, NULL, 0, kind)) @@ -264,17 +268,18 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, equals[0] = '\0'; equals++; MHD_unescape_plus (args); - daemon->unescape_callback (daemon->unescape_callback_cls, - connection, - args); + key_len = daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + args); MHD_unescape_plus (equals); - len = daemon->unescape_callback (daemon->unescape_callback_cls, - connection, - equals); + value_len = daemon->unescape_callback (daemon->unescape_callback_cls, + connection, + equals); if (MHD_YES != cb (connection, args, + key_len, equals, - len, + value_len, kind)) return MHD_NO; (*num_headers)++; diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h @@ -271,6 +271,11 @@ struct MHD_HTTP_Header char *header; /** + * Number of bytes in @a header. + */ + size_t header_size; + + /** * The value of the header. */ char *value; @@ -1886,6 +1891,7 @@ MHD_unescape_plus (char *arg); * * @param connection context of the iteration * @param key 0-terminated key string, never NULL + * @param key_size number of bytes in key * @param value 0-terminated binary data, may include binary zeros, may be NULL * @param value_size number of bytes in value * @param kind origin of the key-value pair @@ -1895,6 +1901,7 @@ MHD_unescape_plus (char *arg); typedef int (*MHD_ArgumentIterator_)(struct MHD_Connection *connection, const char *key, + size_t key_size, const char *value, size_t value_size, enum MHD_ValueKind kind); @@ -1923,14 +1930,17 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, /** - * Check whether response header contains particular @a token. + * Check whether response header contains particular token. * * Token could be surrounded by spaces and tabs and delimited by comma. * Case-insensitive match used for header names and tokens. + * * @param response the response to query * @param key header name + * @param key_len the length of @a key, not including optional + * terminating null-character. * @param token the token to find - * @param token_len the length of token, not including optional + * @param token_len the length of @a token, not including optional * terminating null-character. * @return true if token is found in specified header, * false otherwise @@ -1938,6 +1948,7 @@ MHD_parse_arguments_ (struct MHD_Connection *connection, bool MHD_check_response_header_token_ci (const struct MHD_Response *response, const char *key, + size_t key_len, const char *token, size_t token_len); @@ -1953,7 +1964,8 @@ MHD_check_response_header_token_ci (const struct MHD_Response *response, * false otherwise */ #define MHD_check_response_header_s_token_ci(r,k,tkn) \ - MHD_check_response_header_token_ci((r),(k),(tkn),MHD_STATICSTR_LEN_(tkn)) + MHD_check_response_header_token_ci((r),(k),MHD_STATICSTR_LEN_(k),\ + (tkn),MHD_STATICSTR_LEN_(tkn)) /** diff --git a/src/microhttpd/mhd_str.c b/src/microhttpd/mhd_str.c @@ -377,6 +377,35 @@ MHD_str_equal_caseless_n_ (const char * const str1, /** + * Check two string for equality, ignoring case of US-ASCII letters and + * checking exactly @a len characters. + * Compares exactly @a len characters, including binary zero characters. + * @param str1 first string to compare + * @param str2 second string to compare + * @param len number of characters to compare + * @return non-zero if two strings are equal, zero otherwise. + */ +bool +MHD_str_equal_caseless_bin_n_ (const char * const str1, + const char * const str2, + size_t len) +{ + size_t i; + + for (i = 0; i < len; ++i) + { + const char c1 = str1[i]; + const char c2 = str2[i]; + if ( (c1 != c2) && + (toasciilower (c1) != toasciilower (c2)) ) + return 0; + } + return !0; +} + + + +/** * Check whether @a str has case-insensitive @a token. * Token could be surrounded by spaces and tabs and delimited by comma. * Match succeed if substring between start, end (of string) or comma diff --git a/src/microhttpd/mhd_str.h b/src/microhttpd/mhd_str.h @@ -83,6 +83,21 @@ MHD_str_equal_caseless_n_ (const char * const str1, /** + * Check two string for equality, ignoring case of US-ASCII letters and + * checking exactly @a len characters. + * Compares exactly @a len characters, including binary zero characters. + * @param str1 first string to compare + * @param str2 second string to compare + * @param len number of characters to compare + * @return non-zero if two strings are equal, zero otherwise. + */ +bool +MHD_str_equal_caseless_bin_n_ (const char * const str1, + const char * const str2, + size_t len); + + +/** * Check whether @a str has case-insensitive @a token. * Token could be surrounded by spaces and tabs and delimited by comma. * Match succeed if substring between start, end of string or comma diff --git a/src/microhttpd/response.c b/src/microhttpd/response.c @@ -105,6 +105,7 @@ add_response_entry (struct MHD_Response *response, free (hdr); return MHD_NO; } + hdr->header_size = strlen (header); if (NULL == (hdr->value = strdup (content))) { free (hdr->header); @@ -254,8 +255,7 @@ MHD_get_response_headers (struct MHD_Response *response, (MHD_YES != iterator (iterator_cls, pos->kind, pos->header, - pos->value, - pos->value_size))) + pos->value))) break; } return numHeaders; @@ -275,14 +275,18 @@ MHD_get_response_header (struct MHD_Response *response, const char *key) { struct MHD_HTTP_Header *pos; + size_t key_size; if (NULL == key) return NULL; + + key_size = strlen (key); for (pos = response->first_header; NULL != pos; pos = pos->next) { - if (MHD_str_equal_caseless_ (pos->header, key)) + if ((pos->header_size == key_size) && + (MHD_str_equal_caseless_bin_n_ (pos->header, key, pos->header_size))) return pos->value; } return NULL; @@ -297,8 +301,10 @@ MHD_get_response_header (struct MHD_Response *response, * * @param response the response to query * @param key header name + * @param key_len the length of @a key, not including optional + * terminating null-character. * @param token the token to find - * @param token_len the length of token, not including optional + * @param token_len the length of @a token, not including optional * terminating null-character. * @return true if token is found in specified header, * false otherwise @@ -306,6 +312,7 @@ MHD_get_response_header (struct MHD_Response *response, bool MHD_check_response_header_token_ci (const struct MHD_Response *response, const char *key, + size_t key_len, const char *token, size_t token_len) { @@ -317,13 +324,18 @@ MHD_check_response_header_token_ci (const struct MHD_Response *response, ('\0' == token[0]) ) return false; + /* Token must not contain binary zero! */ + mhd_assert(strlen(token) == token_len); + for (pos = response->first_header; NULL != pos; pos = pos->next) { if ( (pos->kind == MHD_HEADER_KIND) && - MHD_str_equal_caseless_ (pos->header, - key) && + (key_len == pos->header_size) && + MHD_str_equal_caseless_bin_n_ (pos->header, + key, + key_len) && MHD_str_has_token_caseless_ (pos->value, token, token_len) ) diff --git a/src/testcurl/test_process_headers.c b/src/testcurl/test_process_headers.c @@ -65,13 +65,8 @@ copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) return size * nmemb; } - static int -kv_cb (void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *value, - size_t value_size) +kv_cb (void *cls, enum MHD_ValueKind kind, const char *key, const char *value) { if ((0 == strcmp (key, MHD_HTTP_HEADER_HOST)) && (0 == strncmp (value, "127.0.0.1", strlen("127.0.0.1"))) && (kind == MHD_HEADER_KIND)) @@ -82,7 +77,6 @@ kv_cb (void *cls, return MHD_YES; } - static int ahc_echo (void *cls, struct MHD_Connection *connection, diff --git a/src/testcurl/test_urlparse.c b/src/testcurl/test_urlparse.c @@ -67,18 +67,13 @@ copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx) return size * nmemb; } - static int test_values (void *cls, enum MHD_ValueKind kind, const char *key, - const char *value, - size_t value_size) + const char *value) { - (void) cls; - (void) kind; - (void) value_size; - + (void)cls;(void)kind; /* Unused. Silent compiler warning. */ if ( (0 == strcmp (key, "a")) && (0 == strcmp (value, "b")) ) matches += 1;