From 439431ab3017158cf418bcf5706b606b6eb24321 Mon Sep 17 00:00:00 2001 From: StunMan Date: Fri, 8 Mar 2013 01:47:49 +0100 Subject: [PATCH 01/59] client ssl connection added. This requires a SSL_CTX and must be set by the user --- evhtp.c | 37 +++++++++++++++++++++++++++++++++++++ evhtp.h | 5 +++++ 2 files changed, 42 insertions(+) diff --git a/evhtp.c b/evhtp.c index 088609e..4b2092a 100644 --- a/evhtp.c +++ b/evhtp.c @@ -3621,6 +3621,43 @@ evhtp_connection_new(evbase_t * evbase, const char * addr, uint16_t port) { return conn; } +#ifndef DISABLE_SSL +evhtp_connection_t * +evhtp_connection_ssl_new(evbase_t * evbase, const char * addr, uint16_t port, evhtp_ssl_ctx_t* ctx) { + evhtp_connection_t * conn; + struct sockaddr_in sin; + + if (evbase == NULL) { + return NULL; + } + + if (!(conn = _evhtp_connection_new(NULL, -1, evhtp_type_client))) { + return NULL; + } + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr(addr); + sin.sin_port = htons(port); + + conn->ssl = SSL_new(ctx); + conn->evbase = evbase; + conn->bev = bufferevent_openssl_socket_new(evbase, -1, conn->ssl, BUFFEREVENT_SSL_CONNECTING, BEV_OPT_CLOSE_ON_FREE); + + bufferevent_enable(conn->bev, EV_READ); + + bufferevent_setcb(conn->bev, NULL, NULL, + _evhtp_connection_eventcb, conn); + + bufferevent_socket_connect(conn->bev, + (struct sockaddr *)&sin, sizeof(sin)); + + + return conn; +} +#endif + + + evhtp_request_t * evhtp_request_new(evhtp_callback_cb cb, void * arg) { evhtp_request_t * r; diff --git a/evhtp.h b/evhtp.h index 75185f0..9e49f3a 100644 --- a/evhtp.h +++ b/evhtp.h @@ -1062,6 +1062,11 @@ void evhtp_set_max_keepalive_requests(evhtp_t * htp, uint64_t num); */ evhtp_connection_t * evhtp_connection_new(evbase_t * evbase, const char * addr, uint16_t port); +#ifndef DISABLE_SSL +evhtp_connection_t * evhtp_connection_ssl_new(evbase_t * evbase, const char * addr, uint16_t port, evhtp_ssl_ctx_t* ctx); +#endif + + /** * @brief allocate a new request */ From cf66c7c81a52312f18584c972ba74e25f29de82b Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Sun, 23 Mar 2014 09:40:36 -0400 Subject: [PATCH 02/59] Only export public symbols. --- CMakeLists.txt | 15 ++++++-- evhtp-internal.h | 21 +++++++++++ evhtp.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++ evthr/evthr.c | 21 +++++++++++ examples/test.c | 2 +- htparse/htparse.c | 23 +++++++++++++ 6 files changed, 167 insertions(+), 3 deletions(-) create mode 100644 evhtp-internal.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d0a967..848a150 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules) INCLUDE (CheckFunctionExists) INCLUDE (CheckIncludeFiles) INCLUDE (CheckTypeSize) +INCLUDE (CheckCCompilerFlag) CHECK_FUNCTION_EXISTS(alloca C_ALLOCA) CHECK_FUNCTION_EXISTS(memcmp HAVE_MEMCMP) @@ -34,6 +35,13 @@ CHECK_TYPE_SIZE("int" SIZEOF_INT) CHECK_TYPE_SIZE("long" SIZEOF_LONG) CHECK_TYPE_SIZE("short" SIZEOF_SHORT) +check_c_compiler_flag(-fvisibility=hidden EVHTP_HAS_VISIBILITY_HIDDEN) + +if (EVHTP_HAS_VISIBILITY_HIDDEN) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") + add_definitions(-DEVHTP_HAS_VISIBILITY_HIDDEN) +endif() + if (NOT HAVE_SYS_TREE) CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/compat/sys/tree.h.in ${CMAKE_CURRENT_BINARY_DIR}/compat/sys/tree.h) endif(NOT HAVE_SYS_TREE) @@ -243,13 +251,16 @@ add_executable(test EXCLUDE_FROM_ALL examples/test.c) add_executable(test_basic EXCLUDE_FROM_ALL examples/test_basic.c) add_executable(test_vhost EXCLUDE_FROM_ALL examples/test_vhost.c) add_executable(test_client EXCLUDE_FROM_ALL examples/test_client.c) -add_executable(test_proxy EXCLUDE_FROM_ALL examples/test_proxy.c) + +if (NOT EVHTP_DISABLE_EVTHR) + add_executable(test_proxy EXCLUDE_FROM_ALL examples/test_proxy.c) + target_link_libraries(test_proxy libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) +endif() target_link_libraries(test libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_basic libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_vhost libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_client libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) -target_link_libraries(test_proxy libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) add_dependencies(examples test test_basic test_vhost test_client test_proxy) diff --git a/evhtp-internal.h b/evhtp-internal.h new file mode 100644 index 0000000..07798c4 --- /dev/null +++ b/evhtp-internal.h @@ -0,0 +1,21 @@ +#ifndef __EVHTP_INTERNAL_H__ +#define __EVHTP_INTERNAL_H__ + +#ifdef EVHTP_HAS_VISIBILITY_HIDDEN +#define __visible __attribute__((visibility("default"))) +#define EXPORT_SYMBOL(x) typeof(x)(x)__visible +#else +#define EXPORT_SYMBOL(n) +#endif + +#ifndef TAILQ_FOREACH_SAFE +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST((head)); \ + (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) +#endif + + + +#endif + diff --git a/evhtp.c b/evhtp.c index d51ce5e..070e756 100644 --- a/evhtp.c +++ b/evhtp.c @@ -22,6 +22,7 @@ #include +#include "evhtp-internal.h" #include "evhtp.h" static int _evhtp_request_parser_start(htparser * p); @@ -3788,3 +3789,90 @@ evhtp_request_status(evhtp_request_t * r) { return htparser_get_status(r->conn->parser); } +EXPORT_SYMBOL(evhtp_new); +EXPORT_SYMBOL(evhtp_free); +EXPORT_SYMBOL(evhtp_set_timeouts); +EXPORT_SYMBOL(evhtp_set_bev_flags); + +#ifndef EVHTP_DISABLE_SSL +EXPORT_SYMBOL(evhtp_ssl_init); +#endif + +EXPORT_SYMBOL(evhtp_disable_100_continue); +EXPORT_SYMBOL(evhtp_use_callback_locks); +EXPORT_SYMBOL(evhtp_set_gencb); +EXPORT_SYMBOL(evhtp_set_pre_accept_cb); +EXPORT_SYMBOL(evhtp_set_post_accept_cb); +EXPORT_SYMBOL(evhtp_set_cb); + +#ifndef EVHTP_DISABLE_REGEX +EXPORT_SYMBOL(evhtp_set_regex_cb); +#endif + +EXPORT_SYMBOL(evhtp_set_glob_cb); +EXPORT_SYMBOL(evhtp_set_hook); +EXPORT_SYMBOL(evhtp_unset_hook); +EXPORT_SYMBOL(evhtp_unset_all_hooks); +EXPORT_SYMBOL(evhtp_bind_socket); +EXPORT_SYMBOL(evhtp_unbind_socket); +EXPORT_SYMBOL(evhtp_bind_sockaddr); + +#ifndef EVHTP_DISABLE_EVTHR +EXPORT_SYMBOL(evhtp_use_threads); +#ifndef EVHTP_DISABLE_SSL +EXPORT_SYMBOL(evhtp_ssl_use_threads); +#endif +#endif + +EXPORT_SYMBOL(evhtp_send_reply); +EXPORT_SYMBOL(evhtp_send_reply_start); +EXPORT_SYMBOL(evhtp_send_reply_body); +EXPORT_SYMBOL(evhtp_send_reply_end); +EXPORT_SYMBOL(evhtp_response_needs_body); +EXPORT_SYMBOL(evhtp_send_reply_chunk_start); +EXPORT_SYMBOL(evhtp_send_reply_chunk); +EXPORT_SYMBOL(evhtp_send_reply_chunk_end); +EXPORT_SYMBOL(evhtp_callback_new); +EXPORT_SYMBOL(evhtp_callback_free); +EXPORT_SYMBOL(evhtp_callbacks_add_callback); +EXPORT_SYMBOL(evhtp_add_vhost); +EXPORT_SYMBOL(evhtp_add_alias); +EXPORT_SYMBOL(evhtp_kv_new); +EXPORT_SYMBOL(evhtp_kvs_new); +EXPORT_SYMBOL(evhtp_kv_free); +EXPORT_SYMBOL(evhtp_kvs_free); +EXPORT_SYMBOL(evhtp_kv_rm_and_free); +EXPORT_SYMBOL(evhtp_kv_find); +EXPORT_SYMBOL(evhtp_kvs_find_kv); +EXPORT_SYMBOL(evhtp_kvs_add_kv); +EXPORT_SYMBOL(evhtp_kvs_add_kvs); +EXPORT_SYMBOL(evhtp_kvs_for_each); +EXPORT_SYMBOL(evhtp_parse_query); +EXPORT_SYMBOL(evhtp_unescape_string); +EXPORT_SYMBOL(evhtp_header_new); +EXPORT_SYMBOL(evhtp_header_key_add); +EXPORT_SYMBOL(evhtp_header_val_add); +EXPORT_SYMBOL(evhtp_headers_add_header); +EXPORT_SYMBOL(evhtp_header_find); +EXPORT_SYMBOL(evhtp_request_get_method); +EXPORT_SYMBOL(evhtp_connection_pause); +EXPORT_SYMBOL(evhtp_connection_resume); +EXPORT_SYMBOL(evhtp_request_pause); +EXPORT_SYMBOL(evhtp_request_resume); +EXPORT_SYMBOL(evhtp_request_get_connection); +EXPORT_SYMBOL(evhtp_connection_set_bev); +EXPORT_SYMBOL(evhtp_request_set_bev); +EXPORT_SYMBOL(evhtp_connection_get_bev); +EXPORT_SYMBOL(evhtp_connection_set_timeouts); +EXPORT_SYMBOL(evhtp_request_get_bev); +EXPORT_SYMBOL(evhtp_connection_take_ownership); +EXPORT_SYMBOL(evhtp_connection_free); +EXPORT_SYMBOL(evhtp_request_free); +EXPORT_SYMBOL(evhtp_set_max_body_size); +EXPORT_SYMBOL(evhtp_connection_set_max_body_size); +EXPORT_SYMBOL(evhtp_request_set_max_body_size); +EXPORT_SYMBOL(evhtp_set_max_keepalive_requests); +EXPORT_SYMBOL(evhtp_connection_new); +EXPORT_SYMBOL(evhtp_request_new); +EXPORT_SYMBOL(evhtp_make_request); +EXPORT_SYMBOL(evhtp_request_status); diff --git a/evthr/evthr.c b/evthr/evthr.c index 22303ec..c50ee70 100644 --- a/evthr/evthr.c +++ b/evthr/evthr.c @@ -19,6 +19,7 @@ #include #include +#include "evhtp-internal.h" #include "evthr.h" #if (__GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC__MINOR__ > 4)) && (!defined(__STRICT_ANSI__) || __STRICT_ANSI__ == 0) @@ -501,3 +502,23 @@ evthr_pool_start(evthr_pool_t * pool) { return 0; } +EXPORT_SYMBOL(evthr_new); +EXPORT_SYMBOL(evthr_get_base); +EXPORT_SYMBOL(evthr_set_aux); +EXPORT_SYMBOL(evthr_get_aux); +EXPORT_SYMBOL(evthr_start); +EXPORT_SYMBOL(evthr_stop); +EXPORT_SYMBOL(evthr_defer); +EXPORT_SYMBOL(evthr_free); +EXPORT_SYMBOL(evthr_inc_backlog); +EXPORT_SYMBOL(evthr_dec_backlog); +EXPORT_SYMBOL(evthr_get_backlog); +EXPORT_SYMBOL(evthr_set_max_backlog); +EXPORT_SYMBOL(evthr_set_backlog); +EXPORT_SYMBOL(evthr_pool_new); +EXPORT_SYMBOL(evthr_pool_start); +EXPORT_SYMBOL(evthr_pool_stop); +EXPORT_SYMBOL(evthr_pool_defer); +EXPORT_SYMBOL(evthr_pool_free); +EXPORT_SYMBOL(evthr_pool_set_max_backlog); +EXPORT_SYMBOL(evthr_pool_set_backlog); diff --git a/examples/test.c b/examples/test.c index 4c66740..e1532d3 100644 --- a/examples/test.c +++ b/examples/test.c @@ -106,7 +106,6 @@ test_pause_cb(evhtp_request_t * request, void * arg) { evhtp_send_reply(request, EVHTP_RES_OK); } -#ifndef EVHTP_DISABLE_REGEX static void _owned_readcb(evbev_t * bev, void * arg) { /* echo the input back to the client */ @@ -129,6 +128,7 @@ test_ownership(evhtp_request_t * request, void * arg) { _owned_eventcb, NULL); } +#ifndef EVHTP_DISABLE_REGEX static void test_regex(evhtp_request_t * req, void * arg) { evbuffer_add_printf(req->buffer_out, diff --git a/htparse/htparse.c b/htparse/htparse.c index 352dc90..9bb600b 100644 --- a/htparse/htparse.c +++ b/htparse/htparse.c @@ -8,6 +8,7 @@ #include #include "htparse.h" +#include "evhtp-internal.h" #ifdef PARSER_DEBUG #define __QUOTE(x) # x @@ -1901,3 +1902,25 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) return i; } /* htparser_run */ + +EXPORT_SYMBOL(htparser_run); +EXPORT_SYMBOL(htparser_should_keep_alive); +EXPORT_SYMBOL(htparser_get_scheme); +EXPORT_SYMBOL(htparser_get_method); +EXPORT_SYMBOL(htparser_get_methodstr); +EXPORT_SYMBOL(htparser_get_methodstr_m); +EXPORT_SYMBOL(htparser_set_major); +EXPORT_SYMBOL(htparser_set_minor); +EXPORT_SYMBOL(htparser_get_major); +EXPORT_SYMBOL(htparser_get_minor); +EXPORT_SYMBOL(htparser_get_multipart); +EXPORT_SYMBOL(htparser_get_status); +EXPORT_SYMBOL(htparser_get_content_length); +EXPORT_SYMBOL(htparser_get_content_pending); +EXPORT_SYMBOL(htparser_get_total_bytes_read); +EXPORT_SYMBOL(htparser_get_error); +EXPORT_SYMBOL(htparser_get_strerror); +EXPORT_SYMBOL(htparser_get_userdata); +EXPORT_SYMBOL(htparser_set_userdata); +EXPORT_SYMBOL(htparser_init); +EXPORT_SYMBOL(htparser_new); From df2fbd6ec2f5a0df5b6d60773a3c38b023487a2c Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Sun, 23 Mar 2014 09:51:33 -0400 Subject: [PATCH 03/59] Export evhtp_connection_set_ratelimit --- evhtp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/evhtp.c b/evhtp.c index 070e756..902944c 100644 --- a/evhtp.c +++ b/evhtp.c @@ -101,7 +101,7 @@ static void _evhtp_path_free(evhtp_path_t * path); (var) = (tvar)) #endif -const char * +static const char * status_code_to_str(evhtp_res code) { switch (code) { case EVHTP_RES_200: @@ -3876,3 +3876,4 @@ EXPORT_SYMBOL(evhtp_connection_new); EXPORT_SYMBOL(evhtp_request_new); EXPORT_SYMBOL(evhtp_make_request); EXPORT_SYMBOL(evhtp_request_status); +EXPORT_SYMBOL(evhtp_connection_set_ratelimit); From 8a6873e508de5d5d8e21bf5f492ae731b51f449e Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Sun, 23 Mar 2014 10:56:45 -0400 Subject: [PATCH 04/59] Uncomplexify evthr - huge performance boost. --- evthr/evthr.c | 84 +++++++++++++++++---------------------------------- 1 file changed, 28 insertions(+), 56 deletions(-) diff --git a/evthr/evthr.c b/evthr/evthr.c index c50ee70..768c9d4 100644 --- a/evthr/evthr.c +++ b/evthr/evthr.c @@ -55,7 +55,6 @@ struct evthr { ev_t * event; evbase_t * evbase; pthread_mutex_t lock; - pthread_mutex_t stat_lock; pthread_mutex_t rlock; pthread_t * thr; evthr_init_cb init_cb; @@ -65,13 +64,6 @@ struct evthr { TAILQ_ENTRY(evthr) next; }; -#ifndef TAILQ_FOREACH_SAFE -#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ - for ((var) = TAILQ_FIRST((head)); \ - (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \ - (var) = (tvar)) -#endif - inline void evthr_inc_backlog(evthr_t * evthr) { __sync_fetch_and_add(&evthr->cur_backlog, 1); @@ -105,67 +97,48 @@ evthr_set_backlog(evthr_t * evthr, int num) { return setsockopt(evthr->wdr, SOL_SOCKET, SO_RCVBUF, &rnum, sizeof(int)); } +static inline int +_evthr_read(evthr_t * thr, evthr_cmd_t * cmd, evutil_socket_t sock) { + if (recv(sock, cmd, sizeof(evthr_cmd_t), 0) != sizeof(evthr_cmd_t)) { + return 0; + } + + return 1; +} + static void _evthr_read_cmd(evutil_socket_t sock, short __unused__ which, void * args) { evthr_t * thread; evthr_cmd_t cmd; - ssize_t recvd; + int stopped; if (!(thread = (evthr_t *)args)) { return; } - if (pthread_mutex_trylock(&thread->lock) != 0) { - return; - } - pthread_mutex_lock(&thread->rlock); - if ((recvd = recv(sock, &cmd, sizeof(evthr_cmd_t), 0)) <= 0) { - pthread_mutex_unlock(&thread->rlock); - if (errno == EAGAIN) { - goto end; - } else { - goto error; - } - } + stopped = 0; - if (recvd < (ssize_t)sizeof(evthr_cmd_t)) { - pthread_mutex_unlock(&thread->rlock); - goto error; - } + while (_evthr_read(thread, &cmd, sock) == 1) { + if (cmd.stop == 1) { + stopped = 1; + break; + } - pthread_mutex_unlock(&thread->rlock); + if (cmd.cb != NULL) { + (cmd.cb)(thread, cmd.args, thread->arg); + } - if (recvd != sizeof(evthr_cmd_t)) { - goto error; + evthr_dec_backlog(thread); } - if (cmd.stop == 1) { - goto stop; - } + pthread_mutex_unlock(&thread->rlock); - if (cmd.cb != NULL) { - cmd.cb(thread, cmd.args, thread->arg); - goto done; - } else { - goto done; + if (stopped == 1) { + event_base_loopbreak(thread->evbase); } -stop: - event_base_loopbreak(thread->evbase); -done: - evthr_dec_backlog(thread); -end: - pthread_mutex_unlock(&thread->lock); - return; -error: - pthread_mutex_lock(&thread->stat_lock); - thread->cur_backlog = -1; - thread->err = 1; - pthread_mutex_unlock(&thread->stat_lock); - pthread_mutex_unlock(&thread->lock); - event_base_loopbreak(thread->evbase); return; } /* _evthr_read_cmd */ @@ -188,9 +161,11 @@ _evthr_loop(void * args) { event_add(thread->event, NULL); pthread_mutex_lock(&thread->lock); + if (thread->init_cb != NULL) { thread->init_cb(thread, thread->arg); } + pthread_mutex_unlock(&thread->lock); event_base_loop(thread->evbase, 0); @@ -302,11 +277,6 @@ evthr_new(evthr_init_cb init_cb, void * args) { return NULL; } - if (pthread_mutex_init(&thread->stat_lock, NULL)) { - evthr_free(thread); - return NULL; - } - if (pthread_mutex_init(&thread->rlock, NULL)) { evthr_free(thread); return NULL; @@ -423,11 +393,13 @@ evthr_pool_defer(evthr_pool_t * pool, evthr_cb cb, void * arg) { min_thr = thr; } else if (thr_backlog == 0) { min_thr = thr; - break; } else if (thr_backlog < min_backlog) { min_thr = thr; } + if (evthr_get_backlog(min_thr) == 0) { + break; + } } return evthr_defer(min_thr, cb, arg); From 76b4a967554b325074cdfcfe33f555e916bec18e Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Sun, 23 Mar 2014 12:43:11 -0400 Subject: [PATCH 05/59] Do backlogs matter for evthr? I am thinking not. --- evthr/evthr.c | 47 ++++++++++++++++++++++++++++------------------- examples/test.c | 2 +- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/evthr/evthr.c b/evthr/evthr.c index 768c9d4..86c2099 100644 --- a/evthr/evthr.c +++ b/evthr/evthr.c @@ -23,21 +23,19 @@ #include "evthr.h" #if (__GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC__MINOR__ > 4)) && (!defined(__STRICT_ANSI__) || __STRICT_ANSI__ == 0) -#define __unused__ __attribute__((unused)) +#define __unused__ __attribute__((unused)) #else #define __unused__ #endif -#define _EVTHR_MAGIC 0x03fb - typedef struct evthr_cmd evthr_cmd_t; typedef struct evthr_pool_slist evthr_pool_slist_t; struct evthr_cmd { - uint8_t stop : 1; + uint8_t stop; void * args; evthr_cb cb; -} __attribute__ ((packed)); +}; TAILQ_HEAD(evthr_pool_slist, evthr); @@ -182,19 +180,20 @@ evthr_defer(evthr_t * thread, evthr_cb cb, void * arg) { int cur_backlog; evthr_cmd_t cmd; - cur_backlog = evthr_get_backlog(thread); if (thread->max_backlog) { + cur_backlog = evthr_get_backlog(thread); + + + if (cur_backlog == -1) { + return EVTHR_RES_FATAL; + } + if (cur_backlog + 1 > thread->max_backlog) { return EVTHR_RES_BACKLOG; } } - if (cur_backlog == -1) { - return EVTHR_RES_FATAL; - } - - /* cmd.magic = _EVTHR_MAGIC; */ cmd.cb = cb; cmd.args = arg; cmd.stop = 0; @@ -378,31 +377,41 @@ evthr_pool_defer(evthr_pool_t * pool, evthr_cb cb, void * arg) { return EVTHR_RES_NOCB; } +#if 0 /* find the thread with the smallest backlog */ TAILQ_FOREACH(thr, &pool->threads, next) { - int thr_backlog = 0; + int thr_backlog; int min_backlog = 0; thr_backlog = evthr_get_backlog(thr); + if (thr_backlog == 0) { + min_thr = thr; + break; + } + if (min_thr) { min_backlog = evthr_get_backlog(min_thr); + + if (min_backlog == 0) { + break; + } } if (min_thr == NULL) { min_thr = thr; - } else if (thr_backlog == 0) { - min_thr = thr; } else if (thr_backlog < min_backlog) { min_thr = thr; } - - if (evthr_get_backlog(min_thr) == 0) { - break; - } } +#endif + thr = TAILQ_FIRST(&pool->threads); + + TAILQ_REMOVE(&pool->threads, thr, next); + TAILQ_INSERT_TAIL(&pool->threads, thr, next); + - return evthr_defer(min_thr, cb, arg); + return evthr_defer(thr, cb, arg); } /* evthr_pool_defer */ evthr_pool_t * diff --git a/examples/test.c b/examples/test.c index e1532d3..858f623 100644 --- a/examples/test.c +++ b/examples/test.c @@ -613,7 +613,7 @@ main(int argc, char ** argv) { } #endif - if (evhtp_bind_socket(htp, bind_addr, bind_port, 128) < 0) { + if (evhtp_bind_socket(htp, bind_addr, bind_port, 2046) < 0) { fprintf(stderr, "Could not bind socket: %s\n", strerror(errno)); exit(-1); } From cb4e28003468fda6a65815bc40bd05337dea6542 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Tue, 25 Mar 2014 15:22:02 -0400 Subject: [PATCH 06/59] Remove all the stupid backlog stuff. --- evhtp.c | 8 -------- evthr/evthr.c | 46 ---------------------------------------------- 2 files changed, 54 deletions(-) diff --git a/evhtp.c b/evhtp.c index 902944c..98b27f3 100644 --- a/evhtp.c +++ b/evhtp.c @@ -1792,8 +1792,6 @@ _evhtp_run_in_thread(evthr_t * thr, void * arg, void * shared) { connection->evbase = evthr_get_base(thr); connection->thread = thr; - evthr_inc_backlog(connection->thread); - if (_evhtp_connection_accept(connection->evbase, connection) < 0) { evhtp_connection_free(connection); return; @@ -3493,12 +3491,6 @@ evhtp_connection_free(evhtp_connection_t * connection) { #endif } -#ifndef EVHTP_DISABLE_EVTHR - if (connection->thread && connection->type == evhtp_type_server) { - evthr_dec_backlog(connection->thread); - } -#endif - if (connection->ratelimit_cfg != NULL) { ev_token_bucket_cfg_free(connection->ratelimit_cfg); } diff --git a/evthr/evthr.c b/evthr/evthr.c index 86c2099..55e3f9c 100644 --- a/evthr/evthr.c +++ b/evthr/evthr.c @@ -127,8 +127,6 @@ _evthr_read_cmd(evutil_socket_t sock, short __unused__ which, void * args) { if (cmd.cb != NULL) { (cmd.cb)(thread, cmd.args, thread->arg); } - - evthr_dec_backlog(thread); } pthread_mutex_unlock(&thread->rlock); @@ -181,29 +179,13 @@ evthr_defer(evthr_t * thread, evthr_cb cb, void * arg) { evthr_cmd_t cmd; - if (thread->max_backlog) { - cur_backlog = evthr_get_backlog(thread); - - - if (cur_backlog == -1) { - return EVTHR_RES_FATAL; - } - - if (cur_backlog + 1 > thread->max_backlog) { - return EVTHR_RES_BACKLOG; - } - } - cmd.cb = cb; cmd.args = arg; cmd.stop = 0; pthread_mutex_lock(&thread->rlock); - evthr_inc_backlog(thread); - if (send(thread->wdr, &cmd, sizeof(cmd), 0) <= 0) { - evthr_dec_backlog(thread); pthread_mutex_unlock(&thread->rlock); return EVTHR_RES_RETRY; } @@ -377,34 +359,6 @@ evthr_pool_defer(evthr_pool_t * pool, evthr_cb cb, void * arg) { return EVTHR_RES_NOCB; } -#if 0 - /* find the thread with the smallest backlog */ - TAILQ_FOREACH(thr, &pool->threads, next) { - int thr_backlog; - int min_backlog = 0; - - thr_backlog = evthr_get_backlog(thr); - - if (thr_backlog == 0) { - min_thr = thr; - break; - } - - if (min_thr) { - min_backlog = evthr_get_backlog(min_thr); - - if (min_backlog == 0) { - break; - } - } - - if (min_thr == NULL) { - min_thr = thr; - } else if (thr_backlog < min_backlog) { - min_thr = thr; - } - } -#endif thr = TAILQ_FIRST(&pool->threads); TAILQ_REMOVE(&pool->threads, thr, next); From 6cd89466fd6bd76c5b8624be65af5893afe3e40c Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Wed, 26 Mar 2014 14:03:41 -0400 Subject: [PATCH 07/59] Proposed changes for request pause/resume (pipelined) - When a request is paused, both EV_READ AND EV_WRITE are disabled. - When a user wants to resume the request, they must call request_resume AFTER send_reply (to make sure there is data in the bufferevent's output) - The resume function will then check to see if there is data waiting to be sent. If true, ONLY the EV_WRITE flag is set on the bufferevent - Once all data has been written, the EV_READ flag is re-enabled, and everything should work as usual. REMEMBER: resume needs to be called AFTER evhtp_send_reply()! --- evhtp.c | 66 ++++++++++++++++++++++++++++++++++++----------- evhtp.h | 1 + htparse/htparse.c | 21 +++++---------- 3 files changed, 58 insertions(+), 30 deletions(-) diff --git a/evhtp.c b/evhtp.c index 98b27f3..6fae12c 100644 --- a/evhtp.c +++ b/evhtp.c @@ -1283,6 +1283,10 @@ static int _evhtp_request_parser_fini(htparser * p) { evhtp_connection_t * c = htparser_get_userdata(p); + if (c->paused) { + return -1; + } + /* check to see if we should use the body of the request as the query * arguments. */ @@ -1315,8 +1319,12 @@ _evhtp_request_parser_fini(htparser * p) { (c->request->cb)(c->request, c->request->cbarg); } + if (c->paused == 1) { + return -1; + } + return 0; -} +} /* _evhtp_request_parser_fini */ static int _evhtp_create_headers(evhtp_header_t * header, void * arg) { @@ -1447,8 +1455,6 @@ _evhtp_connection_resumecb(int fd, short events, void * arg) { c->paused = 0; - bufferevent_enable(c->bev, EV_READ); - if (c->request) { c->request->status = EVHTP_RES_OK; } @@ -1458,7 +1464,20 @@ _evhtp_connection_resumecb(int fd, short events, void * arg) { return; } - _evhtp_connection_readcb(c->bev, c); + /* XXX this is a hack to show a potential fix for issues/86, the main indea + * is that you call resume AFTER you have sent the reply (not BEFORE). + * + * When it has been decided this is a proper fix, the pause bit should be + * changed to a state-type flag. + */ + + if (evbuffer_get_length(bufferevent_get_output(c->bev))) { + bufferevent_enable(c->bev, EV_WRITE); + c->wait_4_write = 1; + } else { + bufferevent_enable(c->bev, EV_READ | EV_WRITE); + _evhtp_connection_readcb(c->bev, c); + } } static void @@ -1470,6 +1489,10 @@ _evhtp_connection_readcb(evbev_t * bev, void * arg) { avail = evbuffer_get_length(bufferevent_get_input(bev)); + if (avail == 0) { + return; + } + if (c->request) { c->request->status = EVHTP_RES_OK; } @@ -1478,13 +1501,9 @@ _evhtp_connection_readcb(evbev_t * bev, void * arg) { return; } - buf = evbuffer_pullup(bufferevent_get_input(bev), avail); + buf = evbuffer_pullup(bufferevent_get_input(bev), avail); - bufferevent_disable(bev, EV_WRITE); - { - nread = htparser_run(c->parser, &request_psets, (const char *)buf, avail); - } - bufferevent_enable(bev, EV_WRITE); + nread = htparser_run(c->parser, &request_psets, (const char *)buf, avail); if (c->owner != 1) { /* @@ -1532,10 +1551,23 @@ _evhtp_connection_writecb(evbev_t * bev, void * arg) { return; } + if (c->wait_4_write == 1) { + c->wait_4_write = 0; + + bufferevent_enable(bev, EV_READ); + + if (evbuffer_get_length(bufferevent_get_input(bev))) { + _evhtp_connection_readcb(bev, arg); + } + + return; + } + if (c->request->finished == 0 || evbuffer_get_length(bufferevent_get_output(bev))) { return; } + /* * if there is a set maximum number of keepalive requests configured, check * to make sure we are not over it. If we have gone over the max we set the @@ -1978,6 +2010,9 @@ evhtp_request_get_method(evhtp_request_t * r) { */ void evhtp_connection_pause(evhtp_connection_t * c) { + c->paused = 1; + bufferevent_disable(c->bev, EV_READ | EV_WRITE); + return; if ((bufferevent_get_enabled(c->bev) & EV_READ)) { c->paused = 1; bufferevent_disable(c->bev, EV_READ); @@ -1991,11 +2026,10 @@ evhtp_connection_pause(evhtp_connection_t * c) { */ void evhtp_connection_resume(evhtp_connection_t * c) { - if (!(bufferevent_get_enabled(c->bev) & EV_READ)) { - /* bufferevent_enable(c->bev, EV_READ); */ - c->paused = 0; - event_active(c->resume_ev, EV_WRITE, 1); - } + c->paused = 0; + + event_active(c->resume_ev, EV_WRITE, 1); + return; } /** @@ -2585,8 +2619,10 @@ void evhtp_send_reply_end(evhtp_request_t * request) { request->finished = 1; +#if 0 _evhtp_connection_writecb(evhtp_request_get_bev(request), evhtp_request_get_connection(request)); +#endif } void diff --git a/evhtp.h b/evhtp.h index 9c11c76..bdb34d6 100644 --- a/evhtp.h +++ b/evhtp.h @@ -439,6 +439,7 @@ struct evhtp_connection_s { uint64_t num_requests; evhtp_type type; /**< server or client */ char paused; + char wait_4_write; char free_connection; struct ev_token_bucket_cfg * ratelimit_cfg; /**< connection-specific ratelimiting configuration. */ diff --git a/htparse/htparse.c b/htparse/htparse.c index 9bb600b..d72fad2 100644 --- a/htparse/htparse.c +++ b/htparse/htparse.c @@ -1864,30 +1864,22 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) const char * pe = (const char *)(data + len); size_t to_read = _MIN_READ(pe - pp, p->content_len); - htparse_log_debug("[%p] s_body_read %zu", p, to_read); - if (to_read > 0) { res = hook_body_run(p, hooks, pp, to_read); i += to_read - 1; p->content_len -= to_read; + } - htparse_log_debug("[%p] s_body_read content_len is now %zu", p, p->content_len); - - if (p->content_len == 0) { - res = hook_on_msg_complete_run(p, hooks); - - p->state = s_start; - } - } else { + if (p->content_len == 0) { res = hook_on_msg_complete_run(p, hooks); p->state = s_start; } - } - if (res) { - p->error = htparse_error_user; - return i + 1; + if (res) { + p->error = htparse_error_user; + return i + 1; + } } break; @@ -1902,7 +1894,6 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) return i; } /* htparser_run */ - EXPORT_SYMBOL(htparser_run); EXPORT_SYMBOL(htparser_should_keep_alive); EXPORT_SYMBOL(htparser_get_scheme); From 3d51c7691ecf05d79c44693069d01b9d255268f9 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Thu, 3 Apr 2014 11:58:05 -0400 Subject: [PATCH 08/59] Remove dead code from evthr --- evthr/evthr.c | 74 ++------------------------------------------------- 1 file changed, 2 insertions(+), 72 deletions(-) diff --git a/evthr/evthr.c b/evthr/evthr.c index 55e3f9c..32f53a5 100644 --- a/evthr/evthr.c +++ b/evthr/evthr.c @@ -22,12 +22,6 @@ #include "evhtp-internal.h" #include "evthr.h" -#if (__GNUC__ > 2 || ( __GNUC__ == 2 && __GNUC__MINOR__ > 4)) && (!defined(__STRICT_ANSI__) || __STRICT_ANSI__ == 0) -#define __unused__ __attribute__((unused)) -#else -#define __unused__ -#endif - typedef struct evthr_cmd evthr_cmd_t; typedef struct evthr_pool_slist evthr_pool_slist_t; @@ -45,8 +39,6 @@ struct evthr_pool { }; struct evthr { - int cur_backlog; - int max_backlog; int rdr; int wdr; char err; @@ -62,39 +54,6 @@ struct evthr { TAILQ_ENTRY(evthr) next; }; -inline void -evthr_inc_backlog(evthr_t * evthr) { - __sync_fetch_and_add(&evthr->cur_backlog, 1); -} - -inline void -evthr_dec_backlog(evthr_t * evthr) { - __sync_fetch_and_sub(&evthr->cur_backlog, 1); -} - -inline int -evthr_get_backlog(evthr_t * evthr) { - return __sync_add_and_fetch(&evthr->cur_backlog, 0); -} - -inline void -evthr_set_max_backlog(evthr_t * evthr, int max) { - evthr->max_backlog = max; -} - -inline int -evthr_set_backlog(evthr_t * evthr, int num) { - int rnum; - - if (evthr->wdr < 0) { - return -1; - } - - rnum = num * sizeof(evthr_cmd_t); - - return setsockopt(evthr->wdr, SOL_SOCKET, SO_RCVBUF, &rnum, sizeof(int)); -} - static inline int _evthr_read(evthr_t * thr, evthr_cmd_t * cmd, evutil_socket_t sock) { if (recv(sock, cmd, sizeof(evthr_cmd_t), 0) != sizeof(evthr_cmd_t)) { @@ -105,7 +64,7 @@ _evthr_read(evthr_t * thr, evthr_cmd_t * cmd, evutil_socket_t sock) { } static void -_evthr_read_cmd(evutil_socket_t sock, short __unused__ which, void * args) { +_evthr_read_cmd(evutil_socket_t sock, short which, void * args) { evthr_t * thread; evthr_cmd_t cmd; int stopped; @@ -175,7 +134,6 @@ _evthr_loop(void * args) { evthr_res evthr_defer(evthr_t * thread, evthr_cb cb, void * arg) { - int cur_backlog; evthr_cmd_t cmd; @@ -348,8 +306,7 @@ evthr_pool_stop(evthr_pool_t * pool) { evthr_res evthr_pool_defer(evthr_pool_t * pool, evthr_cb cb, void * arg) { - evthr_t * min_thr = NULL; - evthr_t * thr = NULL; + evthr_t * thr = NULL; if (pool == NULL) { return EVTHR_RES_FATAL; @@ -398,26 +355,6 @@ evthr_pool_new(int nthreads, evthr_init_cb init_cb, void * shared) { return pool; } -int -evthr_pool_set_backlog(evthr_pool_t * pool, int num) { - evthr_t * thr; - - TAILQ_FOREACH(thr, &pool->threads, next) { - evthr_set_backlog(thr, num); - } - - return 0; -} - -void -evthr_pool_set_max_backlog(evthr_pool_t * pool, int max) { - evthr_t * thr; - - TAILQ_FOREACH(thr, &pool->threads, next) { - evthr_set_max_backlog(thr, max); - } -} - int evthr_pool_start(evthr_pool_t * pool) { evthr_t * evthr = NULL; @@ -445,15 +382,8 @@ EXPORT_SYMBOL(evthr_start); EXPORT_SYMBOL(evthr_stop); EXPORT_SYMBOL(evthr_defer); EXPORT_SYMBOL(evthr_free); -EXPORT_SYMBOL(evthr_inc_backlog); -EXPORT_SYMBOL(evthr_dec_backlog); -EXPORT_SYMBOL(evthr_get_backlog); -EXPORT_SYMBOL(evthr_set_max_backlog); -EXPORT_SYMBOL(evthr_set_backlog); EXPORT_SYMBOL(evthr_pool_new); EXPORT_SYMBOL(evthr_pool_start); EXPORT_SYMBOL(evthr_pool_stop); EXPORT_SYMBOL(evthr_pool_defer); EXPORT_SYMBOL(evthr_pool_free); -EXPORT_SYMBOL(evthr_pool_set_max_backlog); -EXPORT_SYMBOL(evthr_pool_set_backlog); From 72488a80954e230cfd980c0d6ec3a95e9f5f5ffa Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Thu, 3 Apr 2014 11:58:38 -0400 Subject: [PATCH 09/59] Remove dead declarations in evthr.h --- evthr/evthr.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/evthr/evthr.h b/evthr/evthr.h index d151fdc..c5ae91b 100644 --- a/evthr/evthr.h +++ b/evthr/evthr.h @@ -43,19 +43,12 @@ int evthr_start(evthr_t * evthr); evthr_res evthr_stop(evthr_t * evthr); evthr_res evthr_defer(evthr_t * evthr, evthr_cb cb, void * arg); void evthr_free(evthr_t * evthr); -void evthr_inc_backlog(evthr_t * evthr); -void evthr_dec_backlog(evthr_t * evthr); -int evthr_get_backlog(evthr_t * evthr); -void evthr_set_max_backlog(evthr_t * evthr, int max); -int evthr_set_backlog(evthr_t *, int); evthr_pool_t * evthr_pool_new(int nthreads, evthr_init_cb init_cb, void * shared); int evthr_pool_start(evthr_pool_t * pool); evthr_res evthr_pool_stop(evthr_pool_t * pool); evthr_res evthr_pool_defer(evthr_pool_t * pool, evthr_cb cb, void * arg); void evthr_pool_free(evthr_pool_t * pool); -void evthr_pool_set_max_backlog(evthr_pool_t * evthr, int max); -int evthr_pool_set_backlog(evthr_pool_t *, int); #ifdef __cplusplus } From 86ba10b5b46cd01cc6c0544c332a4b22f130d9ba Mon Sep 17 00:00:00 2001 From: TJ Koblentz Date: Wed, 9 Apr 2014 11:45:11 +0400 Subject: [PATCH 10/59] Be more consistent and slightly more lenient when handling GET params Fixes an inconsistency when passed length of buffer without NUL terminating character (ie. "this_key_is_present=&but_this_one_is_not="). Also handles the "key" without = case by ignoring the key. Signed-off-by: Mark Ellzey --- evhtp.c | 8 +++++++- examples/test_query.c | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 examples/test_query.c diff --git a/evhtp.c b/evhtp.c index 6fae12c..ca16af2 100644 --- a/evhtp.c +++ b/evhtp.c @@ -2473,6 +2473,12 @@ evhtp_parse_query(const char * query, size_t len) { key_buf[key_idx] = '\0'; state = s_query_key_hex_1; break; + case ';': + case '&': + /* no = for key, so ignore it and look for next key */ + memset(key_buf, 0, len); + key_idx = 0; + break; default: key_buf[key_idx++] = ch; key_buf[key_idx] = '\0'; @@ -2575,7 +2581,7 @@ evhtp_parse_query(const char * query, size_t len) { } /* switch */ } - if (key_idx && val_idx) { + if (key_idx && (val_idx || state == s_query_val)) { evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, val_buf, 1, 1)); } diff --git a/examples/test_query.c b/examples/test_query.c new file mode 100644 index 0000000..ccfb96c --- /dev/null +++ b/examples/test_query.c @@ -0,0 +1,20 @@ +#include +#include +#include + +int +main(int argc, char ** argv) { + const char query_raw[] = "notp&ifp=&othernotp;thenp="; + evhtp_query_t *query = evhtp_parse_query(query_raw, sizeof(query_raw) - 1); + + const char *notp = evhtp_kv_find(query, "notp"); + assert(NULL == notp); + const char *ifp = evhtp_kv_find(query, "ifp"); + assert(NULL != ifp); + const char *othernotp = evhtp_kv_find(query, "othernotp"); + assert(NULL == othernotp); + const char *thenp = evhtp_kv_find(query, "thenp"); + assert(NULL != thenp); + + return 0; +} From bc897d2ff033b855e9c935674467e4b0b908ba8d Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Wed, 9 Apr 2014 01:57:16 -0400 Subject: [PATCH 11/59] Empty query args processed with a val of NULL, extended the test_query code --- CMakeLists.txt | 4 +- evhtp.c | 20 ++++++-- examples/test_query.c | 103 +++++++++++++++++++++++++++++++++++++----- 3 files changed, 109 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 848a150..ae82852 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -251,6 +251,7 @@ add_executable(test EXCLUDE_FROM_ALL examples/test.c) add_executable(test_basic EXCLUDE_FROM_ALL examples/test_basic.c) add_executable(test_vhost EXCLUDE_FROM_ALL examples/test_vhost.c) add_executable(test_client EXCLUDE_FROM_ALL examples/test_client.c) +add_executable(test_query EXCLUDE_FROM_ALL examples/test_query.c) if (NOT EVHTP_DISABLE_EVTHR) add_executable(test_proxy EXCLUDE_FROM_ALL examples/test_proxy.c) @@ -261,8 +262,9 @@ target_link_libraries(test libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_basic libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_vhost libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) target_link_libraries(test_client libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) +target_link_libraries(test_query libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) -add_dependencies(examples test test_basic test_vhost test_client test_proxy) +add_dependencies(examples test test_basic test_vhost test_client test_proxy test_query) if (NOT LIB_INSTALL_DIR) set (LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib") diff --git a/evhtp.c b/evhtp.c index ca16af2..6e7c425 100644 --- a/evhtp.c +++ b/evhtp.c @@ -2475,15 +2475,24 @@ evhtp_parse_query(const char * query, size_t len) { break; case ';': case '&': - /* no = for key, so ignore it and look for next key */ + /* in this state, we have an empty value, so we just + * insert the key with a value of NULL. Then set the + * state back to parsing s_query_key. + */ + evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, NULL, 1, 1)); + memset(key_buf, 0, len); - key_idx = 0; + memset(val_buf, 0, len); + + key_idx = 0; + val_idx = 0; + state = s_query_key; break; default: key_buf[key_idx++] = ch; key_buf[key_idx] = '\0'; break; - } + } /* switch */ break; case s_query_key_hex_1: if (!evhtp_is_hex_query_char(ch)) { @@ -2581,8 +2590,9 @@ evhtp_parse_query(const char * query, size_t len) { } /* switch */ } - if (key_idx && (val_idx || state == s_query_val)) { - evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, val_buf, 1, 1)); + if (key_idx > 0) { + evhtp_kvs_add_kv(query_args, + evhtp_kv_new(key_buf, val_idx ? val_buf : NULL, 1, 1)); } free(key_buf); diff --git a/examples/test_query.c b/examples/test_query.c index ccfb96c..a57a8db 100644 --- a/examples/test_query.c +++ b/examples/test_query.c @@ -1,20 +1,99 @@ #include #include -#include + +#include "evhtp.h" + +struct expected { + char * key; + char * val; +}; + +static int +test_cmp(evhtp_query_t * query, evhtp_kv_t * kvobj, const char * valstr, struct expected * exp) { + if (!query || !kvobj) { + return -1; + } + + if (exp->val == NULL) { + if (kvobj->val || valstr) { + return -1; + } + + return 0; + } + + if (strcmp(kvobj->val, exp->val)) { + return -1; + } + + if (strcmp(valstr, exp->val)) { + return -1; + } + + return 0; +} + +static int +query_test(const char * raw_query, struct expected expected_data[]) { + evhtp_query_t * query; + struct expected * check; + int idx = 0; + int num_errors = 0; + + if (!(query = evhtp_parse_query(raw_query, strlen(raw_query)))) { + return -1; + } + + while (1) { + evhtp_kv_t * kvobj = NULL; + const char * valstr = NULL; + + check = &expected_data[idx++]; + + if (check == NULL || check->key == NULL) { + break; + } + + kvobj = evhtp_kvs_find_kv(query, check->key); + valstr = evhtp_kv_find(query, check->key); + + if (test_cmp(query, kvobj, valstr, check) == -1) { + num_errors += 1; + } + } + + return num_errors; +} + +static const char * t1_str = "notp&ifp=&othernotp;thenp=;key=val"; +static const char * t2_str = "foo=bar;baz=raz&a=1"; + +static struct expected t1_exp[] = { + { "notp", NULL }, + { "ifp", "" }, + { "othernotp", NULL }, + { "thenp", "" }, + { "key", "val" }, + { NULL, NULL } +}; + + +static struct expected t2_exp[] = { + { "foo", "bar" }, + { "baz", "raz" }, + { "a", "1" }, + { NULL, NULL } +}; + +static void +test(const char * qstr, struct expected exp[]) { + printf("%-50s %s\n", qstr, query_test(qstr, exp) ? "ERROR" : "OK"); +} int main(int argc, char ** argv) { - const char query_raw[] = "notp&ifp=&othernotp;thenp="; - evhtp_query_t *query = evhtp_parse_query(query_raw, sizeof(query_raw) - 1); - - const char *notp = evhtp_kv_find(query, "notp"); - assert(NULL == notp); - const char *ifp = evhtp_kv_find(query, "ifp"); - assert(NULL != ifp); - const char *othernotp = evhtp_kv_find(query, "othernotp"); - assert(NULL == othernotp); - const char *thenp = evhtp_kv_find(query, "thenp"); - assert(NULL != thenp); + test(t1_str, t1_exp); + test(t2_str, t2_exp); return 0; } From 0e839f0a8c9ab52d6b9250eecbadf0059398cd63 Mon Sep 17 00:00:00 2001 From: zhangjing Date: Mon, 28 Apr 2014 16:13:03 +0800 Subject: [PATCH 12/59] add connection connected status for client connection Signed-off-by: Mark Ellzey --- evhtp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/evhtp.c b/evhtp.c index 6e7c425..651df28 100644 --- a/evhtp.c +++ b/evhtp.c @@ -1615,6 +1615,7 @@ _evhtp_connection_eventcb(evbev_t * bev, short events, void * arg) { if ((events & BEV_EVENT_CONNECTED)) { if (c->type == evhtp_type_client) { + c->connected = 1; bufferevent_setcb(bev, _evhtp_connection_readcb, _evhtp_connection_writecb, @@ -1774,6 +1775,7 @@ _evhtp_connection_new(evhtp_t * htp, evutil_socket_t sock, evhtp_type type) { connection->error = 0; connection->owner = 1; connection->paused = 0; + connection->connected = 0; connection->sock = sock; connection->htp = htp; connection->type = type; From 22c0facd3df838996fb92f2187da1e2924dae935 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Mon, 28 Apr 2014 11:23:26 -0400 Subject: [PATCH 13/59] Added on_event hook / cleanup --- evhtp.c | 42 ++++++++++++------- evhtp.h | 126 +++++++++++++++++++++++++++++++------------------------- 2 files changed, 96 insertions(+), 72 deletions(-) diff --git a/evhtp.c b/evhtp.c index 651df28..f35a8a3 100644 --- a/evhtp.c +++ b/evhtp.c @@ -1283,7 +1283,7 @@ static int _evhtp_request_parser_fini(htparser * p) { evhtp_connection_t * c = htparser_get_userdata(p); - if (c->paused) { + if (c->paused == 1) { return -1; } @@ -1579,7 +1579,7 @@ _evhtp_connection_writecb(evbev_t * bev, void * arg) { } } - if (c->request->keepalive) { + if (c->request->keepalive == 1) { _evhtp_request_free(c->request); c->request = NULL; @@ -1613,6 +1613,10 @@ static void _evhtp_connection_eventcb(evbev_t * bev, short events, void * arg) { evhtp_connection_t * c = arg; + if (c->hooks && c->hooks->on_event) { + (*c->hooks->on_event)(c, events, c->hooks->on_event_arg); + } + if ((events & BEV_EVENT_CONNECTED)) { if (c->type == evhtp_type_client) { c->connected = 1; @@ -1650,7 +1654,8 @@ _evhtp_connection_eventcb(evbev_t * bev, short events, void * arg) { } } - c->error = 1; + c->error = 1; + c->connected = 0; if (c->request && c->request->hooks && c->request->hooks->on_error) { (*c->request->hooks->on_error)(c->request, events, @@ -1772,14 +1777,14 @@ _evhtp_connection_new(evhtp_t * htp, evutil_socket_t sock, evhtp_type type) { return NULL; } - connection->error = 0; - connection->owner = 1; - connection->paused = 0; + connection->error = 0; + connection->owner = 1; + connection->paused = 0; connection->connected = 0; - connection->sock = sock; - connection->htp = htp; - connection->type = type; - connection->parser = htparser_new(); + connection->sock = sock; + connection->htp = htp; + connection->type = type; + connection->parser = htparser_new(); htparser_init(connection->parser, ptype); htparser_set_userdata(connection->parser, connection); @@ -2013,12 +2018,9 @@ evhtp_request_get_method(evhtp_request_t * r) { void evhtp_connection_pause(evhtp_connection_t * c) { c->paused = 1; + bufferevent_disable(c->bev, EV_READ | EV_WRITE); return; - if ((bufferevent_get_enabled(c->bev) & EV_READ)) { - c->paused = 1; - bufferevent_disable(c->bev, EV_READ); - } } /** @@ -2746,7 +2748,7 @@ evhtp_send_reply_chunk(evhtp_request_t * request, evbuf_t * buf) { if (evbuffer_get_length(buf) == 0) { return; } - if (request->chunked) { + if (request->chunked == 1) { evbuffer_add_printf(output, "%x\r\n", (unsigned)evbuffer_get_length(buf)); } @@ -2759,7 +2761,7 @@ evhtp_send_reply_chunk(evhtp_request_t * request, evbuf_t * buf) { void evhtp_send_reply_chunk_end(evhtp_request_t * request) { - if (request->chunked) { + if (request->chunked == 1) { evbuffer_add(bufferevent_get_output(evhtp_request_get_bev(request)), "0\r\n\r\n", 5); } @@ -3030,6 +3032,10 @@ evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void (*hooks)->on_write = (evhtp_hook_write_cb)cb; (*hooks)->on_write_arg = arg; break; + case evhtp_hook_on_event: + (*hooks)->on_event = (evhtp_hook_event_cb)cb; + (*hooks)->on_event_arg = arg; + break; default: return -1; } /* switch */ @@ -3098,6 +3104,10 @@ evhtp_unset_all_hooks(evhtp_hooks_t ** hooks) { return -1; } + if (evhtp_unset_hook(hooks, evhtp_hook_on_event)) { + return -1; + } + return res; } /* evhtp_unset_all_hooks */ diff --git a/evhtp.h b/evhtp.h index bdb34d6..f21877b 100644 --- a/evhtp.h +++ b/evhtp.h @@ -108,7 +108,8 @@ enum evhtp_hook_type { evhtp_hook_on_headers_start, evhtp_hook_on_error, /**< type which defines to hook whenever an error occurs */ evhtp_hook_on_hostname, - evhtp_hook_on_write + evhtp_hook_on_write, + evhtp_hook_on_event }; enum evhtp_callback_type { @@ -139,6 +140,7 @@ typedef enum evhtp_type evhtp_type; typedef void (*evhtp_thread_init_cb)(evhtp_t * htp, evthr_t * thr, void * arg); typedef void (*evhtp_callback_cb)(evhtp_request_t * req, void * arg); typedef void (*evhtp_hook_err_cb)(evhtp_request_t * req, evhtp_error_flags errtype, void * arg); +typedef void (*evhtp_hook_event_cb)(evhtp_connection_t * conn, short events, void * arg); /* Generic hook for passing ISO tests */ typedef evhtp_res (*evhtp_hook)(); @@ -161,6 +163,7 @@ typedef evhtp_res (*evhtp_hook_write_cb)(evhtp_connection_t * conn, void * arg); typedef int (*evhtp_kvs_iterator)(evhtp_kv_t * kv, void * arg); typedef int (*evhtp_headers_iterator)(evhtp_header_t * header, void * arg); +#ifndef EVHTP_DISABLE_SSL typedef int (*evhtp_ssl_verify_cb)(int pre_verify, evhtp_x509_store_ctx_t * ctx); typedef int (*evhtp_ssl_chk_issued_cb)(evhtp_x509_store_ctx_t * ctx, evhtp_x509_t * x, evhtp_x509_t * issuer); @@ -168,6 +171,7 @@ typedef int (*evhtp_ssl_scache_add)(evhtp_connection_t * connection, unsigned ch typedef void (*evhtp_ssl_scache_del)(evhtp_t * htp, unsigned char * sid, int sid_len); typedef evhtp_ssl_sess_t * (*evhtp_ssl_scache_get)(evhtp_connection_t * connection, unsigned char * sid, int sid_len); typedef void * (*evhtp_ssl_scache_init)(evhtp_t *); +#endif #define EVHTP_VERSION "1.2.9" #define EVHTP_VERSION_MAJOR 1 @@ -353,10 +357,10 @@ TAILQ_HEAD(evhtp_kvs_s, evhtp_kv_s); struct evhtp_uri_s { evhtp_authority_t * authority; evhtp_path_t * path; - unsigned char * fragment; /**< data after '#' in uri */ - unsigned char * query_raw; /**< the unparsed query arguments */ - evhtp_query_t * query; /**< list of k/v for query arguments */ - htp_scheme scheme; /**< set if a scheme is found */ + unsigned char * fragment; /**< data after '#' in uri */ + unsigned char * query_raw; /**< the unparsed query arguments */ + evhtp_query_t * query; /**< list of k/v for query arguments */ + htp_scheme scheme; /**< set if a scheme is found */ }; @@ -364,10 +368,10 @@ struct evhtp_uri_s { * @brief structure which represents authority information in a URI */ struct evhtp_authority_s { - char * username; /**< the username in URI (scheme://USER:.. */ - char * password; /**< the password in URI (scheme://...:PASS.. */ - char * hostname; /**< hostname if present in URI */ - uint16_t port; /**< port if present in URI */ + char * username; /**< the username in URI (scheme://USER:.. */ + char * password; /**< the password in URI (scheme://...:PASS.. */ + char * hostname; /**< hostname if present in URI */ + uint16_t port; /**< port if present in URI */ }; @@ -375,17 +379,17 @@ struct evhtp_authority_s { * @brief structure which represents a URI path and or file */ struct evhtp_path_s { - char * full; /**< the full path+file (/a/b/c.html) */ - char * path; /**< the path (/a/b/) */ - char * file; /**< the filename if present (c.html) */ + char * full; /**< the full path+file (/a/b/c.html) */ + char * path; /**< the path (/a/b/) */ + char * file; /**< the filename if present (c.html) */ char * match_start; char * match_end; - unsigned int matched_soff; /**< offset of where the uri starts - * mainly used for regex matching - */ - unsigned int matched_eoff; /**< offset of where the uri ends - * mainly used for regex matching - */ + unsigned int matched_soff; /**< offset of where the uri starts + * mainly used for regex matching + */ + unsigned int matched_eoff; /**< offset of where the uri ends + * mainly used for regex matching + */ }; @@ -393,24 +397,24 @@ struct evhtp_path_s { * @brief a structure containing all information for a http request. */ struct evhtp_request_s { - evhtp_t * htp; /**< the parent evhtp_t structure */ - evhtp_connection_t * conn; /**< the associated connection */ - evhtp_hooks_t * hooks; /**< request specific hooks */ - evhtp_uri_t * uri; /**< request URI information */ - evbuf_t * buffer_in; /**< buffer containing data from client */ - evbuf_t * buffer_out; /**< buffer containing data to client */ - evhtp_headers_t * headers_in; /**< headers from client */ - evhtp_headers_t * headers_out; /**< headers to client */ - evhtp_proto proto; /**< HTTP protocol used */ - htp_method method; /**< HTTP method used */ - evhtp_res status; /**< The HTTP response code or other error conditions */ - int keepalive; /**< set to 1 if the connection is keep-alive */ - int finished; /**< set to 1 if the request is fully processed */ - int chunked; /**< set to 1 if the request is chunked */ - - evhtp_callback_cb cb; /**< the function to call when fully processed */ - void * cbarg; /**< argument which is passed to the cb function */ - int error; + evhtp_t * htp; /**< the parent evhtp_t structure */ + evhtp_connection_t * conn; /**< the associated connection */ + evhtp_hooks_t * hooks; /**< request specific hooks */ + evhtp_uri_t * uri; /**< request URI information */ + evbuf_t * buffer_in; /**< buffer containing data from client */ + evbuf_t * buffer_out; /**< buffer containing data to client */ + evhtp_headers_t * headers_in; /**< headers from client */ + evhtp_headers_t * headers_out; /**< headers to client */ + evhtp_proto proto; /**< HTTP protocol used */ + htp_method method; /**< HTTP method used */ + evhtp_res status; /**< The HTTP response code or other error conditions */ + int8_t keepalive : 1; /**< set to 1 if the connection is keep-alive */ + int8_t finished : 1; /**< set to 1 if the request is fully processed */ + int8_t chunked : 1; /**< set to 1 if the request is chunked */ + int8_t error : 1; + + evhtp_callback_cb cb; /**< the function to call when fully processed */ + void * cbarg; /**< argument which is passed to the cb function */ TAILQ_ENTRY(evhtp_request_s) next; }; @@ -418,32 +422,35 @@ struct evhtp_request_s { #define evhtp_request_content_len(r) htparser_get_content_length(r->conn->parser) struct evhtp_connection_s { - evhtp_t * htp; - evbase_t * evbase; - evbev_t * bev; - evthr_t * thread; - evhtp_ssl_t * ssl; + evhtp_t * htp; + evbase_t * evbase; + evbev_t * bev; + evthr_t * thread; +#ifndef EVHTP_DISABLE_SSL + evhtp_ssl_t * ssl; +#endif evhtp_hooks_t * hooks; htparser * parser; event_t * resume_ev; struct sockaddr * saddr; - struct timeval recv_timeo; /**< conn read timeouts (overrides global) */ - struct timeval send_timeo; /**< conn write timeouts (overrides global) */ + struct timeval recv_timeo; /**< conn read timeouts (overrides global) */ + struct timeval send_timeo; /**< conn write timeouts (overrides global) */ evutil_socket_t sock; - uint8_t error; - uint8_t owner; /**< set to 1 if this structure owns the bufferevent */ - uint8_t vhost_via_sni; /**< set to 1 if the vhost was found via SSL SNI */ - evhtp_request_t * request; /**< the request currently being processed */ + evhtp_request_t * request; /**< the request currently being processed */ uint64_t max_body_size; uint64_t body_bytes_read; uint64_t num_requests; - evhtp_type type; /**< server or client */ - char paused; - char wait_4_write; - char free_connection; - struct ev_token_bucket_cfg * ratelimit_cfg; /**< connection-specific ratelimiting configuration. */ - - TAILQ_HEAD(, evhtp_request_s) pending; /**< client pending data */ + evhtp_type type; /**< server or client */ + uint8_t error : 1; + uint8_t owner : 1; /**< set to 1 if this structure owns the bufferevent */ + uint8_t vhost_via_sni : 1; /**< set to 1 if the vhost was found via SSL SNI */ + int8_t paused : 1; + int8_t connected : 1; /**< upstream connection status, for client */ + int8_t wait_4_write : 1; + int8_t free_connection : 1; + struct ev_token_bucket_cfg * ratelimit_cfg; /**< connection-specific ratelimiting configuration. */ + + TAILQ_HEAD(, evhtp_request_s) pending; /**< client pending data */ }; struct evhtp_hooks_s { @@ -460,6 +467,7 @@ struct evhtp_hooks_s { evhtp_hook_chunks_fini_cb on_chunks_fini; evhtp_hook_hostname_cb on_hostname; evhtp_hook_write_cb on_write; + evhtp_hook_event_cb on_event; void * on_headers_start_arg; void * on_header_arg; @@ -474,8 +482,10 @@ struct evhtp_hooks_s { void * on_chunks_fini_arg; void * on_hostname_arg; void * on_write_arg; + void * on_event_arg; }; +#ifndef EVHTP_DISABLE_SSL struct evhtp_ssl_cfg_s { char * pemfile; char * privfile; @@ -500,6 +510,7 @@ struct evhtp_ssl_cfg_s { evhtp_ssl_scache_del scache_del; void * args; }; +#endif /** * @brief creates a new evhtp_t instance @@ -524,8 +535,11 @@ void evhtp_free(evhtp_t * evhtp); */ void evhtp_set_timeouts(evhtp_t * htp, const struct timeval * r, const struct timeval * w); void evhtp_set_bev_flags(evhtp_t * htp, int flags); -int evhtp_ssl_use_threads(void); -int evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * ssl_cfg); + +#ifndef EVHTP_DISABLE_SSL +int evhtp_ssl_use_threads(void); +int evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * ssl_cfg); +#endif /** From 246e33d36fb9d86a19e66ac932b9229d1d6409c7 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Mon, 28 Apr 2014 11:45:16 -0400 Subject: [PATCH 14/59] Fixed bug with calling user-defined event callback :) Don't do: (*cb)(stuff), do (cb)(stuff) <- mark is an idiot --- evhtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evhtp.c b/evhtp.c index f35a8a3..3b24ae7 100644 --- a/evhtp.c +++ b/evhtp.c @@ -1614,7 +1614,7 @@ _evhtp_connection_eventcb(evbev_t * bev, short events, void * arg) { evhtp_connection_t * c = arg; if (c->hooks && c->hooks->on_event) { - (*c->hooks->on_event)(c, events, c->hooks->on_event_arg); + (c->hooks->on_event)(c, events, c->hooks->on_event_arg); } if ((events & BEV_EVENT_CONNECTED)) { From 6e85a9b804a136efb98c56c8aa5309f8466d1f0a Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Mon, 26 May 2014 09:53:07 -0400 Subject: [PATCH 15/59] Add a pkg-config .pc file --- CMakeLists.txt | 4 ++++ evhtp.pc.in | 11 +++++++++++ 2 files changed, 15 insertions(+) create mode 100644 evhtp.pc.in diff --git a/CMakeLists.txt b/CMakeLists.txt index ae82852..ab45ce5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -295,3 +295,7 @@ IF (WIN32) install (FILES compat/sys/queue.h DESTINATION ${INCLUDE_INSTALL_DIR}/sys) install (FILES oniguruma/onigposix.h DESTINATION ${INCLUDE_INSTALL_DIR}) ENDIF (WIN32) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/evhtp.pc.in + ${CMAKE_CURRENT_BINARY_DIR}/evhtp.pc @ONLY) diff --git a/evhtp.pc.in b/evhtp.pc.in new file mode 100644 index 0000000..8ca3523 --- /dev/null +++ b/evhtp.pc.in @@ -0,0 +1,11 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +libdir=@LIB_INSTALL_DIR@ +includedir=@INCLUDE_INSTALL_DIR@ + +Name: libevhtp +Description: A more flexible replacement for libevent's httpd API +Version: @PROJECT_VERSION@ +Libs: -L${libdir} -levhtp +Libs.private: @LIBEVHTP_EXTERNAL_LIBS@ +Cflags: -I${includedir} + From 736bc801a89eeab1cd82d9a766e1a9c8029417f7 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Wed, 2 Jul 2014 13:08:18 -0400 Subject: [PATCH 16/59] TestBigEndian, and add big endian versions of _str*cmp macros --- CMakeLists.txt | 8 ++++++++ htparse/htparse.c | 41 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ab45ce5..74618b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,8 @@ INCLUDE (CheckFunctionExists) INCLUDE (CheckIncludeFiles) INCLUDE (CheckTypeSize) INCLUDE (CheckCCompilerFlag) +INCLUDE (TestBigEndian) + CHECK_FUNCTION_EXISTS(alloca C_ALLOCA) CHECK_FUNCTION_EXISTS(memcmp HAVE_MEMCMP) @@ -35,6 +37,8 @@ CHECK_TYPE_SIZE("int" SIZEOF_INT) CHECK_TYPE_SIZE("long" SIZEOF_LONG) CHECK_TYPE_SIZE("short" SIZEOF_SHORT) +TEST_BIG_ENDIAN(HOST_BIG_ENDIAN) + check_c_compiler_flag(-fvisibility=hidden EVHTP_HAS_VISIBILITY_HIDDEN) if (EVHTP_HAS_VISIBILITY_HIDDEN) @@ -62,6 +66,10 @@ if (NOT HAVE_SYS_UN) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNO_SYS_UN") endif(NOT HAVE_SYS_UN) +if (HOST_BIG_ENDIAN) + add_definitions(-DHOST_BIG_ENDIAN) +endif() + # -DEVHTP_DISABLE_SSL:STRING=ON OPTION(EVHTP_DISABLE_SSL "Disable ssl support" OFF) diff --git a/htparse/htparse.c b/htparse/htparse.c index d72fad2..9970f64 100644 --- a/htparse/htparse.c +++ b/htparse/htparse.c @@ -49,10 +49,10 @@ enum eval_hdr_val { }; enum parser_flags { - parser_flag_chunked = 1 << 0, - parser_flag_connection_keep_alive = 1 << 1, - parser_flag_connection_close = 1 << 2, - parser_flag_trailing = 1 << 3, + parser_flag_chunked = 1 << 0, + parser_flag_connection_keep_alive = 1 << 1, + parser_flag_connection_close = 1 << 2, + parser_flag_trailing = 1 << 3, }; enum parser_state { @@ -198,6 +198,8 @@ static const char * method_strmap[] = { #define _MIN_READ(a, b) ((a) < (b) ? (a) : (b)) +#ifndef HOST_BIG_ENDIAN +/* Little-endian cmp macros */ #define _str3_cmp(m, c0, c1, c2, c3) \ *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) @@ -227,6 +229,37 @@ static const char * method_strmap[] = { *(uint32_t *)m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0) \ && ((uint32_t *)m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4) \ && m[8] == c8 +#else +/* Big endian cmp macros */ +#define _str3_cmp(m, c0, c1, c2, c3) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 + +#define _str3Ocmp(m, c0, c1, c2, c3) \ + m[0] == c0 && m[2] == c2 && m[3] == c3 + +#define _str4cmp(m, c0, c1, c2, c3) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 + +#define _str5cmp(m, c0, c1, c2, c3, c4) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4 + +#define _str6cmp(m, c0, c1, c2, c3, c4, c5) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \ + && m[4] == c4 && m[5] == c5 + +#define _str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \ + && m[4] == c4 && m[5] == c5 && m[6] == c6 + +#define _str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \ + && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 + +#define _str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8) \ + m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 \ + && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8 + +#endif #define __HTPARSE_GENHOOK(__n) \ static inline int hook_ ## __n ## _run(htparser * p, htparse_hooks * hooks) { \ From 1aceedb98643390f85c9dbc3a3e34968b3e7a6c9 Mon Sep 17 00:00:00 2001 From: TJ Koblentz Date: Wed, 2 Jul 2014 11:07:20 -0700 Subject: [PATCH 17/59] ignore build directory (submodule doesnt become dirty on build) --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 4d45a10..95d0775 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ /test_client /test_proxy + +/build/* From 787eeb9c7d6d7a6a9212889240f7cd789c976248 Mon Sep 17 00:00:00 2001 From: TJ Koblentz Date: Mon, 7 Jul 2014 11:48:26 -0700 Subject: [PATCH 18/59] missing SSL guard + test compilation problems (-Wunused-but-set-variable) --- evhtp.c | 2 ++ examples/test.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/evhtp.c b/evhtp.c index 3b24ae7..8e4c911 100644 --- a/evhtp.c +++ b/evhtp.c @@ -1629,6 +1629,7 @@ _evhtp_connection_eventcb(evbev_t * bev, short events, void * arg) { return; } +#ifndef EVHTP_DISABLE_SSL if (c->ssl && !(events & BEV_EVENT_EOF)) { /* XXX need to do better error handling for SSL specific errors */ c->error = 1; @@ -1637,6 +1638,7 @@ _evhtp_connection_eventcb(evbev_t * bev, short events, void * arg) { c->request->error = 1; } } +#endif if (events == (BEV_EVENT_EOF | BEV_EVENT_READING)) { if (errno == EAGAIN) { diff --git a/examples/test.c b/examples/test.c index 858f623..00a0d54 100644 --- a/examples/test.c +++ b/examples/test.c @@ -498,6 +498,8 @@ sigint(int sig, short why, void * data) { event_base_loopexit(data, NULL); } +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-but-set-variable" int main(int argc, char ** argv) { struct event * ev_sigint; @@ -631,4 +633,4 @@ main(int argc, char ** argv) { return 0; } /* main */ - +#pragma GCC diagnostic pop From e282b6fb9a7af0b00003af2b9931707a304b331d Mon Sep 17 00:00:00 2001 From: TJ Koblentz Date: Mon, 7 Jul 2014 12:03:36 -0700 Subject: [PATCH 19/59] Fix some empty GET param cases (with tests) --- evhtp.c | 70 ++++++++++++---- evhtp.h | 23 ++++- examples/test_query.c | 189 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 237 insertions(+), 45 deletions(-) diff --git a/evhtp.c b/evhtp.c index 3b24ae7..3115be5 100644 --- a/evhtp.c +++ b/evhtp.c @@ -2402,7 +2402,7 @@ evhtp_unescape_string(unsigned char ** out, unsigned char * str, size_t str_len) } /* evhtp_unescape_string */ evhtp_query_t * -evhtp_parse_query(const char * query, size_t len) { +evhtp_parse_query_wflags(const char * query, size_t len, int flags) { evhtp_query_t * query_args; query_parser_state state = s_query_start; char * key_buf = NULL; @@ -2475,12 +2475,24 @@ evhtp_parse_query(const char * query, size_t len) { case '%': key_buf[key_idx++] = ch; key_buf[key_idx] = '\0'; - state = s_query_key_hex_1; + + if (!(flags & EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX)) { + state = s_query_key_hex_1; + } break; case ';': + if (!(flags & EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP)) { + key_buf[key_idx++] = ch; + key_buf[key_idx] = '\0'; + break; + } case '&': - /* in this state, we have an empty value, so we just - * insert the key with a value of NULL. Then set the + /* in this state, we have a NULL value */ + if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS)) { + goto error; + } + + /* insert the key with value of NULL and set the * state back to parsing s_query_key. */ evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, NULL, 1, 1)); @@ -2488,9 +2500,9 @@ evhtp_parse_query(const char * query, size_t len) { memset(key_buf, 0, len); memset(val_buf, 0, len); - key_idx = 0; - val_idx = 0; - state = s_query_key; + key_idx = 0; + val_idx = 0; + state = s_query_key; break; default: key_buf[key_idx++] = ch; @@ -2506,9 +2518,9 @@ evhtp_parse_query(const char * query, size_t len) { goto error; } - key_buf[key_idx - 1] = '%'; - key_buf[key_idx++] = ch; - key_buf[key_idx] = '\0'; + key_buf[key_idx++] = '%'; + key_buf[key_idx++] = ch; + key_buf[key_idx] = '\0'; state = s_query_key; break; } @@ -2531,6 +2543,11 @@ evhtp_parse_query(const char * query, size_t len) { case s_query_val: switch (ch) { case ';': + if (!(flags & EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP)) { + val_buf[val_idx++] = ch; + val_buf[val_idx] = '\0'; + break; + } case '&': evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, val_buf, 1, 1)); @@ -2547,7 +2564,9 @@ evhtp_parse_query(const char * query, size_t len) { val_buf[val_idx++] = ch; val_buf[val_idx] = '\0'; - state = s_query_val_hex_1; + if (!(flags & EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX)) { + state = s_query_val_hex_1; + } break; default: val_buf[val_idx++] = ch; @@ -2564,10 +2583,9 @@ evhtp_parse_query(const char * query, size_t len) { goto error; } - - val_buf[val_idx - 1] = '%'; - val_buf[val_idx++] = ch; - val_buf[val_idx] = '\0'; + val_buf[val_idx++] = '%'; + val_buf[val_idx++] = ch; + val_buf[val_idx] = '\0'; state = s_query_val; break; @@ -2594,9 +2612,20 @@ evhtp_parse_query(const char * query, size_t len) { } /* switch */ } - if (key_idx > 0) { - evhtp_kvs_add_kv(query_args, - evhtp_kv_new(key_buf, val_idx ? val_buf : NULL, 1, 1)); + if (key_idx) { + if (val_idx) { + evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, val_buf, 1, 1)); + } else if (state >= s_query_val) { + if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS)) { + goto error; + } + evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, "", 1, 1)); + } else { + if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS)) { + goto error; + } + evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, NULL, 1, 1)); + } } free(key_buf); @@ -2610,6 +2639,11 @@ evhtp_parse_query(const char * query, size_t len) { return NULL; } /* evhtp_parse_query */ +evhtp_query_t * +evhtp_parse_query(const char * query, size_t len) { + return evhtp_parse_query_wflags(query, len, EVHTP_PARSE_QUERY_FLAG_STRICT); +} + void evhtp_send_reply_start(evhtp_request_t * request, evhtp_res code) { evhtp_connection_t * c; diff --git a/evhtp.h b/evhtp.h index f21877b..20eecd5 100644 --- a/evhtp.h +++ b/evhtp.h @@ -857,10 +857,31 @@ void evhtp_kvs_add_kvs(evhtp_kvs_t * dst, evhtp_kvs_t * src); int evhtp_kvs_for_each(evhtp_kvs_t * kvs, evhtp_kvs_iterator cb, void * arg); +#define EVHTP_PARSE_QUERY_FLAG_STRICT 0 +#define EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX (1 << 0) +#define EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS (1 << 1) +#define EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS (1 << 2) +#define EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP (1 << 3) +#define EVHTP_PARSE_QUERY_FLAG_LENIENT (unsigned)(-1) + /** * @brief Parses the query portion of the uri into a set of key/values * - * Parses query arguments like "?herp=derp&foo=bar;blah=baz" + * Parses query arguments like "?herp=&foo=bar;blah=baz&a=%3" + * + * @param query data containing the uri query arguments + * @param len size of the data + * @param flags parse query flags to alter 'strictness' (see EVHTP_PARSE_QUERY_FLAG_*) + * + * @return evhtp_query_t * on success, NULL on error + */ +evhtp_query_t * evhtp_parse_query_wflags(const char * query, size_t len, int flags); + +/** + * @brief Parses the query portion of the uri into a set of key/values in a + * strict manner + * + * Parses query arguments like "?herp=derp&foo=bar&blah=baz" * * @param query data containing the uri query arguments * @param len size of the data diff --git a/examples/test_query.c b/examples/test_query.c index a57a8db..2f73230 100644 --- a/examples/test_query.c +++ b/examples/test_query.c @@ -1,11 +1,16 @@ -#include #include +#include +#include #include "evhtp.h" -struct expected { - char * key; - char * val; +struct test { + const char * raw_query; + int exp_error; + struct expected { + char * key; + char * val; + } exp[10]; /* avoid flexible array member: limit expectations per raw_query */ }; static int @@ -23,6 +28,9 @@ test_cmp(evhtp_query_t * query, evhtp_kv_t * kvobj, const char * valstr, struct } if (strcmp(kvobj->val, exp->val)) { + printf("\n"); + printf(" expected: '%s'\n", exp->val); + printf(" actual: '%s'\n", kvobj->val); return -1; } @@ -33,22 +41,51 @@ test_cmp(evhtp_query_t * query, evhtp_kv_t * kvobj, const char * valstr, struct return 0; } +/* evhtp_kvs_iterator */ +int +kvs_print(evhtp_kv_t * kvobj, void * arg) { + int *key_idx = arg; + + if (*key_idx) { + printf(", "); + } + + printf("\"%s\": %s%s%s", kvobj->key, + kvobj->val ? "\"" : "", + kvobj->val, + kvobj->val ? "\"" : ""); + + *key_idx += 1; + + return 0; +} + static int -query_test(const char * raw_query, struct expected expected_data[]) { +query_test(const char * raw_query, int exp_error, struct expected exp[], int flags) { evhtp_query_t * query; struct expected * check; + int key_idx = 0; int idx = 0; int num_errors = 0; - if (!(query = evhtp_parse_query(raw_query, strlen(raw_query)))) { - return -1; + /* print whether error is expected or not */ + printf("%-7s ", exp_error ? "(error)" : ""); + query = evhtp_parse_query_wflags(raw_query, strlen(raw_query), flags); + if (!query) { + printf(""); + return exp_error == 0; } + printf("{"); + evhtp_kvs_for_each(query, kvs_print, &key_idx); + /* TODO check for keys in query but not in exp */ + printf("}"); + while (1) { evhtp_kv_t * kvobj = NULL; const char * valstr = NULL; - check = &expected_data[idx++]; + check = &exp[idx++]; if (check == NULL || check->key == NULL) { break; @@ -59,41 +96,141 @@ query_test(const char * raw_query, struct expected expected_data[]) { if (test_cmp(query, kvobj, valstr, check) == -1) { num_errors += 1; + printf(" "); } } + if (exp_error) { + return -1; + } + return num_errors; } -static const char * t1_str = "notp&ifp=&othernotp;thenp=;key=val"; -static const char * t2_str = "foo=bar;baz=raz&a=1"; +struct test base_tests[] = { + { "a=b;key&c=val", 0, { + { "a", "b;key" }, + { "c", "val" }, + { NULL, NULL }, + }}, + { "a=b;key=val", 0, { + { "a", "b;key=val" }, + { NULL , NULL }, + }}, + { "a;b=val", 0, { + { "a;b", "val" }, + { NULL, NULL }, + }}, + { "end_empty_string=", 1 }, + { "end_null", 1 }, + { "hexa=some%20&hexb=bla%0", 0, { + { "hexa", "some%20" }, + { "hexb", "bla%0" }, + { NULL, NULL }, + }}, + { "hexa=some%20;hexb=bla", 0, { + { "hexa", "some%20;hexb=bla" }, + { NULL, NULL }, + }}, + { "hexa%z=some", 0, { + { "hexa\%%z", "some" }, + { NULL, NULL }, + }}, + { "aaa=some\%az", 1 }, +}; -static struct expected t1_exp[] = { - { "notp", NULL }, - { "ifp", "" }, - { "othernotp", NULL }, - { "thenp", "" }, - { "key", "val" }, - { NULL, NULL } +struct test ignore_hex_tests[] = { + { "hexa=some%20&hexb=bla%0&hexc=%", 0, { + { "hexa", "some%20" }, + { "hexb", "bla%0" }, + { "hexc", "%" }, + { NULL, NULL }, + }}, + { "hexa%z=some", 0, { + { "hexa%z", "some" }, + { NULL, NULL }, + }}, + { "aaa=some%zz", 0, { + { "aaa", "some%zz" }, + { NULL, NULL }, + }}, }; +struct test allow_empty_tests[] = { + { "end_empty_string=", 0, { + { "end_empty_string", "" }, + { NULL, NULL }, + }}, +}; -static struct expected t2_exp[] = { - { "foo", "bar" }, - { "baz", "raz" }, - { "a", "1" }, - { NULL, NULL } +struct test allow_null_tests[] = { + { "end_null", 0, { + { "end_null", NULL }, + { NULL, NULL }, + }}, +}; + +struct test treat_semicolon_as_sep_tests[] = { + { "a=b;key=val", 0, { + { "a", "b" }, + { "key", "val" }, + { NULL, NULL }, + }}, + { "a;b=val", 1 }, +}; + +struct test lenient_tests[] = { + { "a=b;key&c=val", 0, { + { "a", "b" }, + { "key", NULL }, + { "c", "val" }, + { NULL, NULL }, + }}, + { "a=b;key=val", 0, { + { "a", "b" }, + { "key", "val" }, + { NULL, NULL }, + }}, + { "end_empty_string=", 0, { + { "end_empty_string", "" }, + { NULL, NULL }, + }}, + { "end_null", 0, { + { "end_null", NULL }, + { NULL, NULL }, + }}, + { "hexa=some\%a;hexb=bl%0&hexc=\%az", 0, { + { "hexa", "some\%a" }, + { "hexb", "bl%0" }, + { "hexc", "\%az" }, + { NULL, NULL }, + }}, }; static void -test(const char * qstr, struct expected exp[]) { - printf("%-50s %s\n", qstr, query_test(qstr, exp) ? "ERROR" : "OK"); +test(const char * raw_query, int exp_error, struct expected exp[], int flags) { + printf(" %-30s ", raw_query); + printf("\r %s\n", query_test(raw_query, exp_error, exp, flags) ? "ERROR" : "OK"); } +#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0])) + int main(int argc, char ** argv) { - test(t1_str, t1_exp); - test(t2_str, t2_exp); + int i; + +#define PARSE_QUERY_TEST(tests, flags) do { \ + printf("- " #tests "\n"); \ + for (i = 0; i < ARRAY_SIZE(tests); i++) \ + test((tests)[i].raw_query, (tests)[i].exp_error, (tests)[i].exp, flags); \ +} while (0) + + PARSE_QUERY_TEST(base_tests, EVHTP_PARSE_QUERY_FLAG_STRICT); + PARSE_QUERY_TEST(ignore_hex_tests, EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX); + PARSE_QUERY_TEST(allow_empty_tests, EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS); + PARSE_QUERY_TEST(allow_null_tests, EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS); + PARSE_QUERY_TEST(treat_semicolon_as_sep_tests, EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP); + PARSE_QUERY_TEST(lenient_tests, EVHTP_PARSE_QUERY_FLAG_LENIENT); return 0; } From 89f11cd62f6fb5a2191658f5cf87e653d71e4a18 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Mon, 29 Sep 2014 21:15:33 -0400 Subject: [PATCH 20/59] Formatting cleanup --- htparse/htparse.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/htparse/htparse.c b/htparse/htparse.c index 9970f64..b4411d3 100644 --- a/htparse/htparse.c +++ b/htparse/htparse.c @@ -49,10 +49,10 @@ enum eval_hdr_val { }; enum parser_flags { - parser_flag_chunked = 1 << 0, - parser_flag_connection_keep_alive = 1 << 1, - parser_flag_connection_close = 1 << 2, - parser_flag_trailing = 1 << 3, + parser_flag_chunked = 1 << 0, + parser_flag_connection_keep_alive = 1 << 1, + parser_flag_connection_close = 1 << 2, + parser_flag_trailing = 1 << 3, }; enum parser_state { From 61c7f4f33a799dea62b37d5f5abdc844cd1a6ea6 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Tue, 30 Sep 2014 01:38:39 -0400 Subject: [PATCH 21/59] Cleanup and fixes --- CMakeLists.txt | 33 ++--- CMakeModules/AddOptions.cmake | 102 ++++++++++++++++ CMakeModules/UseCompVer.cmake | 108 +++++++++++++++++ CMakeModules/UseDebugSymbols.cmake | 139 +++++++++++++++++++++ evhtp-internal.h | 2 +- evhtp.c | 96 ++++++++++----- evhtp.h | 9 +- examples/test.c | 2 +- examples/test_query.c | 188 +++++++++++++++-------------- 9 files changed, 532 insertions(+), 147 deletions(-) create mode 100644 CMakeModules/AddOptions.cmake create mode 100644 CMakeModules/UseCompVer.cmake create mode 100644 CMakeModules/UseDebugSymbols.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 74618b4..2d7a7de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,6 +5,8 @@ set(PROJECT_MAJOR_VERSION 1) set(PROJECT_MINOR_VERSION 2) set(PROJECT_PATCH_VERSION 9) + +add_definitions(-D_FORTIFY_SOURCE=2) set (PROJECT_VERSION ${PROJECT_MAJOR_VERSION}.${PROJECT_MINOR_VERSION}.${PROJECT_PATCH_VERSION}) set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules) @@ -13,9 +15,9 @@ INCLUDE (CheckIncludeFiles) INCLUDE (CheckTypeSize) INCLUDE (CheckCCompilerFlag) INCLUDE (TestBigEndian) +INCLUDE (UseDebugSymbols) -CHECK_FUNCTION_EXISTS(alloca C_ALLOCA) CHECK_FUNCTION_EXISTS(memcmp HAVE_MEMCMP) CHECK_FUNCTION_EXISTS(strndup HAVE_STRNDUP) CHECK_FUNCTION_EXISTS(strnlen HAVE_STRNLEN) @@ -97,6 +99,7 @@ message("Build Type: ${CMAKE_BUILD_TYPE}") message("Std CFLAGS: ${CMAKE_C_FLAGS}") message("Dbg CFLAGS: ${CMAKE_C_FLAGS_DEBUG}") message("Rel CFLAGS: ${CMAKE_C_FLAGS_RELEASE}") +message("RelDbg CFLAGS: ${CMAKE_C_FLAGS_RELWITHDEBINFO}") find_package(LibEvent REQUIRED) @@ -216,10 +219,6 @@ if (NOT EVHTP_DISABLE_EVTHR) set (LIBEVHTP_SOURCES ${LIBEVHTP_SOURCES} evthr/evthr.c) endif(NOT EVHTP_DISABLE_EVTHR) -if (NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNDEBUG") -endif (NOT ${CMAKE_BUILD_TYPE} STREQUAL "Debug") - IF (WIN32) ADD_DEFINITIONS(-DWIN32) ADD_DEFINITIONS(-march=i486) @@ -250,10 +249,8 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/evhtp-config.h.in ${CMAKE_CURRENT_BINARY_DIR}/evhtp-config.h) -add_library(libevhtp ${EVHTP_LIBTYPE} ${LIBEVHTP_SOURCES} ${ONIG_SOURCES}) - -set_target_properties(libevhtp PROPERTIES OUTPUT_NAME "evhtp") -target_link_libraries(libevhtp ${LIBEVHTP_EXTERNAL_LIBS}) +add_library(evhtp ${EVHTP_LIBTYPE} ${LIBEVHTP_SOURCES} ${ONIG_SOURCES}) +target_link_libraries(evhtp ${LIBEVHTP_EXTERNAL_LIBS}) add_executable(test EXCLUDE_FROM_ALL examples/test.c) add_executable(test_basic EXCLUDE_FROM_ALL examples/test_basic.c) @@ -261,16 +258,18 @@ add_executable(test_vhost EXCLUDE_FROM_ALL examples/test_vhost.c) add_executable(test_client EXCLUDE_FROM_ALL examples/test_client.c) add_executable(test_query EXCLUDE_FROM_ALL examples/test_query.c) +strip_debug_symbols(test_query) + if (NOT EVHTP_DISABLE_EVTHR) add_executable(test_proxy EXCLUDE_FROM_ALL examples/test_proxy.c) - target_link_libraries(test_proxy libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) + target_link_libraries(test_proxy evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) endif() -target_link_libraries(test libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) -target_link_libraries(test_basic libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) -target_link_libraries(test_vhost libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) -target_link_libraries(test_client libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) -target_link_libraries(test_query libevhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) +target_link_libraries(test evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) +target_link_libraries(test_basic evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) +target_link_libraries(test_vhost evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) +target_link_libraries(test_client evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) +target_link_libraries(test_query evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS}) add_dependencies(examples test test_basic test_vhost test_client test_proxy test_query) @@ -282,7 +281,7 @@ if(NOT INCLUDE_INSTALL_DIR) set(INCLUDE_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/include) endif() -install (TARGETS libevhtp DESTINATION ${LIB_INSTALL_DIR}) +install (TARGETS evhtp DESTINATION ${LIB_INSTALL_DIR}) install (FILES evhtp.h DESTINATION ${INCLUDE_INSTALL_DIR}) install (FILES htparse/htparse.h DESTINATION ${INCLUDE_INSTALL_DIR}) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/evhtp-config.h DESTINATION ${INCLUDE_INSTALL_DIR}) @@ -307,3 +306,5 @@ ENDIF (WIN32) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/evhtp.pc.in ${CMAKE_CURRENT_BINARY_DIR}/evhtp.pc @ONLY) + +message("CFLAGS: ${CMAKE_C_FLAGS}") diff --git a/CMakeModules/AddOptions.cmake b/CMakeModules/AddOptions.cmake new file mode 100644 index 0000000..3255d90 --- /dev/null +++ b/CMakeModules/AddOptions.cmake @@ -0,0 +1,102 @@ +# - Add options without repeating them on the command line +# +# Synopsis: +# +# add_options (lang build opts) +# +# where: +# +# lang Name of the language whose compiler should receive the +# options, e.g. CXX. If a comma-separated list is received +# then the option is added for all those languages. Use the +# special value ALL_LANGUAGES for these languages: CXX, C +# and Fortran +# +# build Kind of build to which this options should apply, +# such as DEBUG and RELEASE. This can also be a comma- +# separated list. Use the special value ALL_BUILDS to apply +# to all builds. +# +# opts List of options to add. Each should be quoted. +# +# Example: +# +# add_options (CXX RELEASE "-O3" "-DNDEBUG" "-Wall") + +function (add_options langs builds) + # special handling of empty language specification + if ("${langs}" STREQUAL "ALL_LANGUAGES") + set (langs CXX C Fortran) + endif ("${langs}" STREQUAL "ALL_LANGUAGES") + foreach (lang IN LISTS langs) + # prepend underscore if necessary + foreach (build IN LISTS builds) + if (NOT ("${build}" STREQUAL "ALL_BUILDS")) + set (_bld "_${build}") + string (TOUPPER "${_bld}" _bld) + else (NOT ("${build}" STREQUAL "ALL_BUILDS")) + set (_bld "") + endif (NOT ("${build}" STREQUAL "ALL_BUILDS")) + # if we want everything in the "global" flag, then simply + # ignore the build type here and go add everything to that one + if (CMAKE_NOT_USING_CONFIG_FLAGS) + set (_bld "") + endif () + foreach (_opt IN LISTS ARGN) + set (_var "CMAKE_${lang}_FLAGS${_bld}") + #message (STATUS "Adding \"${_opt}\" to \${${_var}}") + # remove it first + string (REPLACE "${_opt}" "" _without "${${_var}}") + string (STRIP "${_without}" _without) + # we need to strip this one as well, so they are comparable + string (STRIP "${${_var}}" _stripped) + # if it wasn't there, then add it at the end + if ("${_without}" STREQUAL "${_stripped}") + # don't add any extra spaces if no options yet are set + if (NOT ${_stripped} STREQUAL "") + set (${_var} "${_stripped} ${_opt}") + else (NOT ${_stripped} STREQUAL "") + set (${_var} "${_opt}") + endif (NOT ${_stripped} STREQUAL "") + set (${_var} "${${_var}}" PARENT_SCOPE) + endif ("${_without}" STREQUAL "${_stripped}") + endforeach (_opt) + endforeach (build) + endforeach (lang) +endfunction (add_options lang build) + +# set varname to flag unless user has specified something that matches regex +function (set_default_option lang varname flag regex) + # lang is either C, CXX or Fortran + if ("${lang}" STREQUAL "Fortran") + set (letter "F") + else () + set (letter "${lang}") + endif () + string (TOUPPER "${CMAKE_BUILD_TYPE}" _build) + if ((NOT ("$ENV{${letter}FLAGS}" MATCHES "${regex}")) + AND (NOT ("${CMAKE_${lang}_FLAGS}" MATCHES "${regex}")) + AND (NOT ("${CMAKE_${lang}_FLAGS_${_build}}" MATCHES "${regex}"))) + set (${varname} ${flag} PARENT_SCOPE) + else () + set (${varname} PARENT_SCOPE) + endif () +endfunction (set_default_option) + +# clear default options as a proxy for not using any default options +# at all. there is one *huge* problem with this: CMake runs the platform +# initialization before executing any line at all in the project and +# there seems to be no way to disable that behaviour, so we cannot really +# distinguish between a platform default and something that the user has +# passed on the command line. the best thing we can do is to all user- +# defined setting if they are something other than the platform default. +macro (no_default_options) + foreach (lang IN ITEMS C CXX Fortran) + foreach (build IN ITEMS DEBUG RELEASE MINSIZEREL RELWITHDEBINFO) + if ("${CMAKE_${lang}_FLAGS_${build}}" STREQUAL "${CMAKE_${lang}_FLAGS_${build}_INIT}") + # for some strange reason we cannot clear this flag, only set it to empty + set (CMAKE_${lang}_FLAGS_${build} "") + endif () + endforeach (build) + endforeach (lang) +endmacro (no_default_options) diff --git a/CMakeModules/UseCompVer.cmake b/CMakeModules/UseCompVer.cmake new file mode 100644 index 0000000..e8ac099 --- /dev/null +++ b/CMakeModules/UseCompVer.cmake @@ -0,0 +1,108 @@ +# - Get compiler version + +# probe the GCC version, returns empty string if GCC is not compiler +function (get_gcc_version language ver_name) + if(CMAKE_${language}_COMPILER_ID STREQUAL GNU) + # exec_program is deprecated, but execute_process does't work :-( + exec_program (${CMAKE_${language}_COMPILER} + ARGS ${CMAKE_${language}_COMPILER_ARG1} -dumpversion + OUTPUT_VARIABLE _version + ) + set (${ver_name} ${_version} PARENT_SCOPE) + else (CMAKE_${language}_COMPILER_ID STREQUAL GNU) + set (${ver_name} "" PARENT_SCOPE) + endif (CMAKE_${language}_COMPILER_ID STREQUAL GNU) +endfunction (get_gcc_version ver_name) + +# less reliable, but includes the patch number +function (get_gcc_patch language ver_name) + if(CMAKE_${language}_COMPILER_ID STREQUAL GNU) + # exec_program is deprecated, but execute_process does't work :-( + exec_program (${CMAKE_${language}_COMPILER} + ARGS ${CMAKE_${language}_COMPILER_ARG1} --version + OUTPUT_VARIABLE _version + ) + # split multi-line string into list + if (WIN32) + string (REPLACE "\r\n" ";" _version "${_version}") + else (WIN32) + string (REPLACE "\n" ";" _version "${_version}") + endif (WIN32) + # only keep first line + list (GET _version 0 _version) + # extract version number from it (this is the fragile part) + string (REGEX REPLACE "^[^\\(]+(\\([^\\)]*\\))?[\ \t]*([0-9]+\\.[0-9]+\\.[0-9]+)(.*\\(.*\\))?" "\\2" _version "${_version}") + # return this to the caller + set (${ver_name} ${_version} PARENT_SCOPE) + else (CMAKE_${language}_COMPILER_ID STREQUAL GNU) + set (${ver_name} "" PARENT_SCOPE) + endif (CMAKE_${language}_COMPILER_ID STREQUAL GNU) +endfunction (get_gcc_patch language ver_name) + +function (compiler_info) + if (CMAKE_COMPILER_IS_GNUCXX) + get_gcc_patch (CXX version) + message (STATUS "GNU C++ compiler version: ${version}") + endif (CMAKE_COMPILER_IS_GNUCXX) +endfunction (compiler_info) + +function (get_ld_version ver_name) + # run linker to get the version number. interestingly, this option works + # (for our purposes) on all major platforms (Linux, Mac OS X and Windows); + # it returns the program version although it may have ended in error + exec_program (${CMAKE_LINKER} + ARGS "-v" + OUTPUT_VARIABLE _version + ) + + # keep only first line, even on Mac OS X there is no line end + list (GET _version 0 _version) + + # format of the version string is platform-specific + if (NOT WIN32) + if (APPLE) + string (REGEX REPLACE ".*, from Apple (.*\)" "\\1" _version "${_version}") + else (APPLE) + # assuming some GNU toolchain now + string (REGEX REPLACE "GNU ([a-zA-Z0-9_]*) (version|\\(.*\\)) ([^\\ ]*).*" "\\1 \\3" _version "${_version}") + endif (APPLE) + endif (NOT WIN32) + + # return the string to the caller + set (${ver_name} "${_version}" PARENT_SCOPE) +endfunction (get_ld_version ver_name) + +function (linker_info) + get_ld_version (version) + message (STATUS "Linker: ${version}") +endfunction (linker_info) + +# sets CXX_COMPAT_GCC if we have either GCC or Clang +macro (is_compiler_gcc_compatible) + # is the C++ compiler clang++? + string (TOUPPER "${CMAKE_CXX_COMPILER_ID}" _comp_id) + if (_comp_id MATCHES "CLANG") + set (CMAKE_COMPILER_IS_CLANGXX TRUE) + else () + set (CMAKE_COMPILER_IS_CLANGXX FALSE) + endif () + # is the C++ compiler g++ or clang++? + if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANGXX) + set (CXX_COMPAT_GCC TRUE) + else () + set (CXX_COMPAT_GCC FALSE) + endif () + # is the C compiler clang? + string (TOUPPER "${CMAKE_C_COMPILER_ID}" _comp_id) + if (_comp_id MATCHES "CLANG") + set (CMAKE_COMPILER_IS_CLANG TRUE) + else () + set (CMAKE_COMPILER_IS_CLANG FALSE) + endif () + # is the C compiler gcc or clang? + if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_CLANG) + set (C_COMPAT_GCC TRUE) + else () + set (C_COMPAT_GCC FALSE) + endif () +endmacro (is_compiler_gcc_compatible) diff --git a/CMakeModules/UseDebugSymbols.cmake b/CMakeModules/UseDebugSymbols.cmake new file mode 100644 index 0000000..dbe0a17 --- /dev/null +++ b/CMakeModules/UseDebugSymbols.cmake @@ -0,0 +1,139 @@ +# - Generate debug symbols in a separate file +# +# (1) Include this file in your CMakeLists.txt; it will setup everything +# to compile WITH debug symbols in any case. +# +# (2) Run the strip_debug_symbols function on every target that you want +# to strip. + +# Copyright (C) 2012 Uni Research AS +# This code is licensed under The GNU General Public License v3.0 + +include (AddOptions) +include (UseCompVer) +is_compiler_gcc_compatible () + +# only debugging using the GNU toolchain is supported for now +if (CXX_COMPAT_GCC) + # default debug level, if not specified by the user + set_default_option (CXX _dbg_flag "-ggdb3" "(^|\ )-g") + + # add debug symbols to *all* targets, regardless. there WILL come a + # time when you need to find a bug which only manifests itself in a + # release target on a production system! + if (_dbg_flag) + message (STATUS "Generating debug symbols: ${_dbg_flag}") + add_options (ALL_LANGUAGES ALL_BUILDS "${_dbg_flag}") + endif (_dbg_flag) + + # extracting the debug info is done by a separate utility in the GNU + # toolchain. check that this is actually installed. + message (STATUS "Looking for strip utility") + if (APPLE) + # MacOS X has a duo of utilities; we need both + find_program (OBJCOPY strip) + find_program (DSYMUTIL dsymutil) + mark_as_advanced (DSYMUTIL) + if (NOT DSYMUTIL) + set (OBJCOPY dsymutil-NOTFOUND) + endif (NOT DSYMUTIL) + else (APPLE) + find_program (OBJCOPY + objcopy + ${CYGWIN_INSTALL_PATH}/bin /usr/bin /usr/local/bin + ) + endif (APPLE) + mark_as_advanced (OBJCOPY) + if (OBJCOPY) + message (STATUS "Looking for strip utility - found") + else (OBJCOPY) + message (WARNING "Looking for strip utility - not found") + endif (OBJCOPY) +endif () + +# command to separate the debug information from the executable into +# its own file; this must be called for each target; optionally takes +# the name of a variable to receive the list of .debug files +function (strip_debug_symbols targets) + if (CXX_COMPAT_GCC AND OBJCOPY) + foreach (target IN LISTS targets) + # libraries must retain the symbols in order to link to them, but + # everything can be stripped in an executable + get_target_property (_kind ${target} TYPE) + + # don't strip static libraries + if ("${_kind}" STREQUAL "STATIC_LIBRARY") + return () + endif ("${_kind}" STREQUAL "STATIC_LIBRARY") + + # don't strip public symbols in shared objects + if ("${_kind}" STREQUAL "EXECUTABLE") + set (_inst_dir bin) + set (_strip_args --strip-all --strip-unneeded) + else ("${_kind}" STREQUAL "EXECUTABLE") + set(_inst_dir lib) + set (_strip_args --strip-debug --strip-unneeded) + endif ("${_kind}" STREQUAL "EXECUTABLE") + + # add_custom_command doesn't support generator expressions in the + # working_directory argument (sic; that's what you get when you do + # ad hoc programming all the time), so we need to extract the + # location up front (the location on the other hand should not be + # used for libraries as it does not include the soversion -- sic + # again) + get_target_property (_full ${target} LOCATION) + get_filename_component (_dir ${_full} PATH) + if (NOT (("${_dir}" STREQUAL "") OR ("${_dir}" MATCHES ".*/$"))) + set (_dir "${_dir}/") + endif (NOT (("${_dir}" STREQUAL "") OR ("${_dir}" MATCHES ".*/$"))) + get_filename_component (_name ${_full} NAME_WE) + get_filename_component (_ext ${_full} EXT) + # only libraries have soversion property attached + get_target_property (_target_soversion ${target} SOVERSION) + get_target_property (_target_version ${target} VERSION) + + if (_target_soversion) + # MacOS X puts the version number before the extension + if (APPLE) + set (_target_file_name "${_name}.${_target_version}${_ext}") + else (APPLE) + set (_target_file_name "${_name}${_ext}.${_target_version}") + endif (APPLE) + else (_target_soversion) + set (_target_file_name "${_name}${_ext}") + endif (_target_soversion) + + set (_target_file "${_dir}${_target_file_name}") + # do without generator expressions (which doesn't work everywhere) + if (APPLE) + set (_debug_ext ".dSYM") + add_custom_command (TARGET ${target} + POST_BUILD + WORKING_DIRECTORY ${_dir} + COMMAND ${DSYMUTIL} ARGS --out=${_target_file}${_debug_ext} ${_target_file} + COMMAND ${OBJCOPY} ARGS -S ${_target_file} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${_target_file} ${CMAKE_BINARY_DIR}/install/${_inst_dir} + VERBATIM) + else () + set (_debug_ext ".debug") + add_custom_command (TARGET ${target} + POST_BUILD + WORKING_DIRECTORY ${_dir} + COMMAND ${OBJCOPY} ARGS --only-keep-debug ${_target_file} ${_target_file}${_debug_ext} + COMMAND ${OBJCOPY} ARGS ${_strip_args} ${_target_file} + COMMAND ${OBJCOPY} ARGS --add-gnu-debuglink=${_target_file_name}${_debug_ext} ${_target_file} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${_target_file} ${CMAKE_BINARY_DIR}/install/${_inst_dir} + VERBATIM) + endif () + + # add this .debug file to the list + file (RELATIVE_PATH _this_debug_file "${PROJECT_BINARY_DIR}" "${_target_file}${_debug_ext}") + set (_debug_files ${_debug_files} ${_this_debug_file}) + endforeach (target) + # if optional debug list was requested, then copy to output parameter + if (ARGV1) + set (${ARGV1} ${_debug_files} PARENT_SCOPE) + endif (ARGV1) + endif () +endfunction (strip_debug_symbols targets) + diff --git a/evhtp-internal.h b/evhtp-internal.h index 07798c4..d5ea078 100644 --- a/evhtp-internal.h +++ b/evhtp-internal.h @@ -2,7 +2,7 @@ #define __EVHTP_INTERNAL_H__ #ifdef EVHTP_HAS_VISIBILITY_HIDDEN -#define __visible __attribute__((visibility("default"))) +#define __visible __attribute__((visibility("default"))) #define EXPORT_SYMBOL(x) typeof(x)(x)__visible #else #define EXPORT_SYMBOL(n) diff --git a/evhtp.c b/evhtp.c index 3115be5..b977eb3 100644 --- a/evhtp.c +++ b/evhtp.c @@ -2132,10 +2132,16 @@ evhtp_kv_new(const char * key, const char * val, char kalloc, char valloc) { kv->klen = strlen(key); if (kalloc == 1) { - char * s = malloc(kv->klen + 1); + char * s; + + if (!(s = malloc(kv->klen + 1))) { + free(kv); + return NULL; + } - s[kv->klen] = '\0'; memcpy(s, key, kv->klen); + + s[kv->klen] = '\0'; kv->key = s; } else { kv->key = (char *)key; @@ -2404,14 +2410,19 @@ evhtp_unescape_string(unsigned char ** out, unsigned char * str, size_t str_len) evhtp_query_t * evhtp_parse_query_wflags(const char * query, size_t len, int flags) { evhtp_query_t * query_args; - query_parser_state state = s_query_start; - char * key_buf = NULL; - char * val_buf = NULL; - int key_idx; - int val_idx; + query_parser_state state; + char * key_buf; + char * val_buf; + size_t key_idx; + size_t val_idx; unsigned char ch; size_t i; + + if (len > (SIZE_MAX - (len + 2))) { + return NULL; + } + query_args = evhtp_query_new(); if (!(key_buf = malloc(len + 1))) { @@ -2423,6 +2434,7 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { return NULL; } + state = s_query_start; key_idx = 0; val_idx = 0; @@ -2435,8 +2447,8 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { switch (state) { case s_query_start: - memset(key_buf, 0, len); - memset(val_buf, 0, len); + memset(key_buf, 0, len + 1); + memset(val_buf, 0, len + 1); key_idx = 0; val_idx = 0; @@ -2474,11 +2486,12 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { break; case '%': key_buf[key_idx++] = ch; - key_buf[key_idx] = '\0'; + key_buf[key_idx] = '\0'; if (!(flags & EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX)) { state = s_query_key_hex_1; } + break; case ';': if (!(flags & EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP)) { @@ -2486,6 +2499,8 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { key_buf[key_idx] = '\0'; break; } + + /* otherwise we fallthrough */ case '&': /* in this state, we have a NULL value */ if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS)) { @@ -2497,12 +2512,12 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { */ evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, NULL, 1, 1)); - memset(key_buf, 0, len); - memset(val_buf, 0, len); + memset(key_buf, 0, len + 1); + memset(val_buf, 0, len + 1); - key_idx = 0; - val_idx = 0; - state = s_query_key; + key_idx = 0; + val_idx = 0; + state = s_query_key; break; default: key_buf[key_idx++] = ch; @@ -2518,9 +2533,10 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { goto error; } - key_buf[key_idx++] = '%'; + key_buf[key_idx - 1] = '%'; key_buf[key_idx++] = ch; - key_buf[key_idx] = '\0'; + key_buf[key_idx] = '\0'; + state = s_query_key; break; } @@ -2551,8 +2567,8 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { case '&': evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, val_buf, 1, 1)); - memset(key_buf, 0, len); - memset(val_buf, 0, len); + memset(key_buf, 0, len + 1); + memset(val_buf, 0, len + 1); key_idx = 0; val_idx = 0; @@ -2567,6 +2583,7 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { if (!(flags & EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX)) { state = s_query_val_hex_1; } + break; default: val_buf[val_idx++] = ch; @@ -2583,9 +2600,13 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { goto error; } - val_buf[val_idx++] = '%'; - val_buf[val_idx++] = ch; - val_buf[val_idx] = '\0'; + if (val_idx == 0) { + goto error; + } + + val_buf[val_idx - 1] = '%'; + val_buf[val_idx++] = ch; + val_buf[val_idx] = '\0'; state = s_query_val; break; @@ -2613,19 +2634,27 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { } if (key_idx) { - if (val_idx) { - evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, val_buf, 1, 1)); - } else if (state >= s_query_val) { - if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS)) { - goto error; + do { + if (val_idx) { + evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, val_buf, 1, 1)); + break; } - evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, "", 1, 1)); - } else { + + if (state >= s_query_val) { + if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS)) { + goto error; + } + + evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, "", 1, 1)); + break; + } + if (!(flags & EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS)) { goto error; } - evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, NULL, 1, 1)); - } + + evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, NULL, 1, 0)); + } while (0); } free(key_buf); @@ -3380,8 +3409,8 @@ evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg) { #endif /* OPENSSL_NO_ECDH */ #ifndef OPENSSL_NO_DH if (cfg->dhparams != NULL) { - FILE *fh; - DH *dh; + FILE * fh; + DH * dh; fh = fopen(cfg->dhparams, "r"); if (fh != NULL) { @@ -3967,3 +3996,4 @@ EXPORT_SYMBOL(evhtp_request_new); EXPORT_SYMBOL(evhtp_make_request); EXPORT_SYMBOL(evhtp_request_status); EXPORT_SYMBOL(evhtp_connection_set_ratelimit); +EXPORT_SYMBOL(evhtp_parse_query_wflags); diff --git a/evhtp.h b/evhtp.h index 20eecd5..59962ab 100644 --- a/evhtp.h +++ b/evhtp.h @@ -857,12 +857,16 @@ void evhtp_kvs_add_kvs(evhtp_kvs_t * dst, evhtp_kvs_t * src); int evhtp_kvs_for_each(evhtp_kvs_t * kvs, evhtp_kvs_iterator cb, void * arg); -#define EVHTP_PARSE_QUERY_FLAG_STRICT 0 +#define EVHTP_PARSE_QUERY_FLAG_STRICT 0 #define EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX (1 << 0) #define EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS (1 << 1) #define EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS (1 << 2) #define EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP (1 << 3) -#define EVHTP_PARSE_QUERY_FLAG_LENIENT (unsigned)(-1) +#define EVHTP_PARSE_QUERY_FLAG_LENIENT \ + EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX \ + | EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS \ + | EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS \ + | EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP /** * @brief Parses the query portion of the uri into a set of key/values @@ -890,7 +894,6 @@ evhtp_query_t * evhtp_parse_query_wflags(const char * query, size_t len, int fla */ evhtp_query_t * evhtp_parse_query(const char * query, size_t len); - /** * @brief Unescapes strings like '%7B1,%202,%203%7D' would become '{1, 2, 3}' * diff --git a/examples/test.c b/examples/test.c index 858f623..819796e 100644 --- a/examples/test.c +++ b/examples/test.c @@ -294,7 +294,7 @@ print_data(evhtp_request_t * req, evbuf_t * buf, void * arg) { evbuffer_add_printf(req->buffer_out, "got %zu bytes of data\n", evbuffer_get_length(buf)); - //printf("%.*s", (int)evbuffer_get_length(buf), (char *)evbuffer_pullup(buf, evbuffer_get_length(buf))); + /* printf("%.*s", (int)evbuffer_get_length(buf), (char *)evbuffer_pullup(buf, evbuffer_get_length(buf))); */ #endif evbuffer_drain(buf, -1); return EVHTP_RES_OK; diff --git a/examples/test_query.c b/examples/test_query.c index 2f73230..fe4adc2 100644 --- a/examples/test_query.c +++ b/examples/test_query.c @@ -6,7 +6,7 @@ struct test { const char * raw_query; - int exp_error; + int exp_error; struct expected { char * key; char * val; @@ -44,16 +44,16 @@ test_cmp(evhtp_query_t * query, evhtp_kv_t * kvobj, const char * valstr, struct /* evhtp_kvs_iterator */ int kvs_print(evhtp_kv_t * kvobj, void * arg) { - int *key_idx = arg; + int * key_idx = arg; if (*key_idx) { printf(", "); } printf("\"%s\": %s%s%s", kvobj->key, - kvobj->val ? "\"" : "", - kvobj->val, - kvobj->val ? "\"" : ""); + kvobj->val ? "\"" : "", + kvobj->val, + kvobj->val ? "\"" : ""); *key_idx += 1; @@ -73,7 +73,7 @@ query_test(const char * raw_query, int exp_error, struct expected exp[], int fla query = evhtp_parse_query_wflags(raw_query, strlen(raw_query), flags); if (!query) { printf(""); - return exp_error == 0; + return exp_error == 0; } printf("{"); @@ -105,106 +105,106 @@ query_test(const char * raw_query, int exp_error, struct expected exp[], int fla } return num_errors; -} +} /* query_test */ struct test base_tests[] = { - { "a=b;key&c=val", 0, { - { "a", "b;key" }, - { "c", "val" }, - { NULL, NULL }, - }}, - { "a=b;key=val", 0, { - { "a", "b;key=val" }, - { NULL , NULL }, - }}, - { "a;b=val", 0, { - { "a;b", "val" }, - { NULL, NULL }, - }}, + { "a=b;key&c=val", 0, { + { "a", "b;key" }, + { "c", "val" }, + { NULL, NULL }, + } }, + { "a=b;key=val", 0, { + { "a", "b;key=val" }, + { NULL, NULL }, + } }, + { "a;b=val", 0, { + { "a;b", "val" }, + { NULL, NULL }, + } }, { "end_empty_string=", 1 }, { "end_null", 1 }, { "hexa=some%20&hexb=bla%0", 0, { - { "hexa", "some%20" }, - { "hexb", "bla%0" }, - { NULL, NULL }, - }}, - { "hexa=some%20;hexb=bla", 0, { - { "hexa", "some%20;hexb=bla" }, - { NULL, NULL }, - }}, - { "hexa%z=some", 0, { - { "hexa\%%z", "some" }, - { NULL, NULL }, - }}, - { "aaa=some\%az", 1 }, + { "hexa", "some%20" }, + { "hexb", "bla%0" }, + { NULL, NULL }, + } }, + { "hexa=some%20;hexb=bla", 0, { + { "hexa", "some%20;hexb=bla" }, + { NULL, NULL }, + } }, + { "hexa%z=some", 0, { + { "hexa\%%z", "some" }, + { NULL, NULL }, + } }, + { "aaa=some\%az", 1 }, }; struct test ignore_hex_tests[] = { { "hexa=some%20&hexb=bla%0&hexc=%", 0, { - { "hexa", "some%20" }, - { "hexb", "bla%0" }, - { "hexc", "%" }, - { NULL, NULL }, - }}, - { "hexa%z=some", 0, { - { "hexa%z", "some" }, - { NULL, NULL }, - }}, - { "aaa=some%zz", 0, { - { "aaa", "some%zz" }, - { NULL, NULL }, - }}, + { "hexa", "some%20" }, + { "hexb", "bla%0" }, + { "hexc", "%" }, + { NULL, NULL }, + } }, + { "hexa%z=some", 0, { + { "hexa%z", "some" }, + { NULL, NULL }, + } }, + { "aaa=some%zz", 0, { + { "aaa", "some%zz" }, + { NULL, NULL }, + } }, }; struct test allow_empty_tests[] = { { "end_empty_string=", 0, { - { "end_empty_string", "" }, - { NULL, NULL }, - }}, + { "end_empty_string", "" }, + { NULL, NULL }, + } }, }; struct test allow_null_tests[] = { { "end_null", 0, { - { "end_null", NULL }, - { NULL, NULL }, - }}, + { "end_null", NULL }, + { NULL, NULL }, + } }, }; struct test treat_semicolon_as_sep_tests[] = { { "a=b;key=val", 0, { - { "a", "b" }, - { "key", "val" }, - { NULL, NULL }, - }}, - { "a;b=val", 1 }, + { "a", "b" }, + { "key", "val" }, + { NULL, NULL }, + } }, + { "a;b=val", 1 }, }; struct test lenient_tests[] = { - { "a=b;key&c=val", 0, { - { "a", "b" }, - { "key", NULL }, - { "c", "val" }, - { NULL, NULL }, - }}, - { "a=b;key=val", 0, { - { "a", "b" }, - { "key", "val" }, - { NULL, NULL }, - }}, - { "end_empty_string=", 0, { - { "end_empty_string", "" }, - { NULL, NULL }, - }}, - { "end_null", 0, { - { "end_null", NULL }, - { NULL, NULL }, - }}, + { "a=b;key&c=val", 0, { + { "a", "b" }, + { "key", NULL }, + { "c", "val" }, + { NULL, NULL }, + } }, + { "a=b;key=val", 0, { + { "a", "b" }, + { "key", "val" }, + { NULL, NULL }, + } }, + { "end_empty_string=", 0, { + { "end_empty_string", "" }, + { NULL, NULL }, + } }, + { "end_null", 0, { + { "end_null", NULL }, + { NULL, NULL }, + } }, { "hexa=some\%a;hexb=bl%0&hexc=\%az", 0, { - { "hexa", "some\%a" }, - { "hexb", "bl%0" }, - { "hexc", "\%az" }, - { NULL, NULL }, - }}, + { "hexa", "some\%a" }, + { "hexb", "bl%0" }, + { "hexc", "\%az" }, + { NULL, NULL }, + } }, }; static void @@ -213,24 +213,26 @@ test(const char * raw_query, int exp_error, struct expected exp[], int flags) { printf("\r %s\n", query_test(raw_query, exp_error, exp, flags) ? "ERROR" : "OK"); } -#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof((arr)[0])) +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) int main(int argc, char ** argv) { int i; -#define PARSE_QUERY_TEST(tests, flags) do { \ - printf("- " #tests "\n"); \ - for (i = 0; i < ARRAY_SIZE(tests); i++) \ - test((tests)[i].raw_query, (tests)[i].exp_error, (tests)[i].exp, flags); \ -} while (0) - - PARSE_QUERY_TEST(base_tests, EVHTP_PARSE_QUERY_FLAG_STRICT); - PARSE_QUERY_TEST(ignore_hex_tests, EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX); - PARSE_QUERY_TEST(allow_empty_tests, EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS); - PARSE_QUERY_TEST(allow_null_tests, EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS); + #define PARSE_QUERY_TEST(tests, flags) do { \ + printf("- " # tests "\n"); \ + for (i = 0; i < ARRAY_SIZE(tests); i++) { \ + test((tests)[i].raw_query, (tests)[i].exp_error, (tests)[i].exp, flags); \ + } \ + } while (0) + + PARSE_QUERY_TEST(base_tests, EVHTP_PARSE_QUERY_FLAG_STRICT); + PARSE_QUERY_TEST(ignore_hex_tests, EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX); + PARSE_QUERY_TEST(allow_empty_tests, EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS); + PARSE_QUERY_TEST(allow_null_tests, EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS); PARSE_QUERY_TEST(treat_semicolon_as_sep_tests, EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP); - PARSE_QUERY_TEST(lenient_tests, EVHTP_PARSE_QUERY_FLAG_LENIENT); + PARSE_QUERY_TEST(lenient_tests, EVHTP_PARSE_QUERY_FLAG_LENIENT); return 0; } + From 9318571c7d00cf91361f8765f53523a1156eb539 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Wed, 24 Oct 2012 16:02:56 +0200 Subject: [PATCH 22/59] (evhtp_free): Free ssl_ctx if used. --- evhtp.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/evhtp.c b/evhtp.c index b977eb3..4e0542a 100644 --- a/evhtp.c +++ b/evhtp.c @@ -3776,6 +3776,12 @@ evhtp_free(evhtp_t * evhtp) { } #endif +#ifndef EVHTP_DISABLE_SSL + if (evhtp->ssl_ctx) { + SSL_CTX_free(evhtp->ssl_ctx); + } +#endif + if (evhtp->server_name) { free(evhtp->server_name); } From ab2f567c31474c2afc90bb81f4b5ee82057cfb15 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Tue, 9 Oct 2012 11:39:18 +0200 Subject: [PATCH 23/59] Properly handle errors when allocating connections and requests. --- evhtp.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/evhtp.c b/evhtp.c index 4e0542a..269de25 100644 --- a/evhtp.c +++ b/evhtp.c @@ -633,14 +633,38 @@ _evhtp_request_new(evhtp_connection_t * c) { req->htp = c ? c->htp : NULL; req->status = EVHTP_RES_OK; req->buffer_in = evbuffer_new(); + if (!req->buffer_in) { + goto out_free_req; + } req->buffer_out = evbuffer_new(); + if (!req->buffer_out) { + goto out_free_buf_in; + } req->headers_in = malloc(sizeof(evhtp_headers_t)); + if (!req->headers_in) { + goto out_free_buf_out; + } req->headers_out = malloc(sizeof(evhtp_headers_t)); + if (!req->headers_out) { + goto out_free_hdr_in; + } TAILQ_INIT(req->headers_in); TAILQ_INIT(req->headers_out); return req; + + /* Error path. */ + out_free_hdr_in: + free(req->headers_in); + out_free_buf_out: + evbuffer_free(req->buffer_out); + out_free_buf_in: + evbuffer_free(req->buffer_in); + out_free_req: + free(req); + + return NULL; } /** @@ -1785,6 +1809,10 @@ _evhtp_connection_new(evhtp_t * htp, evutil_socket_t sock, evhtp_type type) { connection->htp = htp; connection->type = type; connection->parser = htparser_new(); + if (!connection->parser) { + free(connection); + return NULL; + } htparser_init(connection->parser, ptype); htparser_set_userdata(connection->parser, connection); From ec445f96825fd3272c0f91ba78446f75b68bdf02 Mon Sep 17 00:00:00 2001 From: Martin Hedenfalk Date: Wed, 10 Jul 2013 17:24:08 +0200 Subject: [PATCH 24/59] Prevent double free of request. When freeing a request, nullify the current request pointer in the associated connection to prevent the request from being doubly freed when the connection is freed. --- evhtp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/evhtp.c b/evhtp.c index 269de25..b869150 100644 --- a/evhtp.c +++ b/evhtp.c @@ -684,6 +684,9 @@ _evhtp_request_free(evhtp_request_t * request) { evhtp_headers_free(request->headers_in); evhtp_headers_free(request->headers_out); + if (request->conn && request->conn->request == request) { + request->conn->request = NULL; + } if (request->buffer_in) { evbuffer_free(request->buffer_in); From 279a7d310643b392caabeedc140ddb1fafe4923b Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Tue, 9 Oct 2012 16:56:03 +0200 Subject: [PATCH 25/59] Stop parsing when we have got a complete response/request. evhtp_connection_t keeps a pointer to the current evhtp_request_t, so starting to parse the next request before the previous is complete will do nothing but break things. Also the old code didn't detect parse errors occuring on the last character... --- evhtp.c | 2 +- htparse/htparse.c | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/evhtp.c b/evhtp.c index b869150..3c89244 100644 --- a/evhtp.c +++ b/evhtp.c @@ -1559,7 +1559,7 @@ _evhtp_connection_readcb(evbev_t * bev, void * arg) { if (c->request && c->request->status == EVHTP_RES_PAUSE) { evhtp_request_pause(c->request); - } else if (avail != nread) { + } else if (htparser_get_error(c->parser) != htparse_error_none) { evhtp_connection_free(c); } } /* _evhtp_connection_readcb */ diff --git a/htparse/htparse.c b/htparse/htparse.c index b4411d3..7663c4a 100644 --- a/htparse/htparse.c +++ b/htparse/htparse.c @@ -1922,6 +1922,13 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) p->error = htparse_error_inval_state; return i + 1; } /* switch */ + + /* If we successfully completed a request/response we return + to caller, and leave it up to him to call us again if + parsing should continue. */ + if (p->state == s_start) { + return i + 1; + } } return i; From c093ff99a38c627e999180671c7f801e9a359103 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Tue, 9 Oct 2012 17:31:40 +0200 Subject: [PATCH 26/59] (evhtp_connection_free): Call hook before freeing request. If the connection is terminated while processing a request we want to be able to inspect it. --- evhtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evhtp.c b/evhtp.c index 3c89244..cf6ad40 100644 --- a/evhtp.c +++ b/evhtp.c @@ -3624,8 +3624,8 @@ evhtp_connection_free(evhtp_connection_t * connection) { return; } - _evhtp_request_free(connection->request); _evhtp_connection_fini_hook(connection); + _evhtp_request_free(connection->request); free(connection->parser); free(connection->hooks); From 76f2830497af7e4acb6d0f11fea3035c3545d519 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Wed, 3 Jul 2013 21:54:24 +0200 Subject: [PATCH 27/59] Properly parse CONNECT request line. For CONNECT the Request-URI portion of the Request-Line is always the host name and port number destination of the requested connection separated by a colon. --- htparse/htparse.c | 53 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/htparse/htparse.c b/htparse/htparse.c index 7663c4a..dee3f8f 100644 --- a/htparse/htparse.c +++ b/htparse/htparse.c @@ -493,6 +493,22 @@ htparser_new(void) { return malloc(sizeof(htparser)); } +static int +is_host_char(unsigned char ch) +{ + char c = (unsigned char)(ch | 0x20); + + if (c >= 'a' && c <= 'z') { + return 1; + } + + if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') { + return 1; + } + + return 0; +} + size_t htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) { unsigned char ch; @@ -693,6 +709,33 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) case s_spaces_before_uri: htparse_log_debug("[%p] s_spaces_before_uri", p); + /* CONNECT is special - RFC 2817 section 5.2: + * The Request-URI portion of the Request-Line is + * always an 'authority' as defined by URI Generic + * Syntax [2], which is to say the host name and port + * number destination of the requested connection + * separated by a colon + */ + if (p->method == htp_method_CONNECT) { + switch (ch) { + case ' ': + break; + default: + if (!is_host_char(ch)) { + p->error = htparse_error_inval_reqline; + return i + 1; + } + p->host_offset = &p->buf[p->buf_idx]; + p->buf[p->buf_idx++] = ch; + p->buf[p->buf_idx] = '\0'; + + p->state = s_host; + break; + } /* switch */ + + break; + } + switch (ch) { case ' ': break; @@ -825,15 +868,7 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) p->state = s_host_ipv6; break; } - c = (unsigned char)(ch | 0x20); - - if (c >= 'a' && c <= 'z') { - p->buf[p->buf_idx++] = ch; - p->buf[p->buf_idx] = '\0'; - break; - } - - if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') { + if (is_host_char(ch)) { p->buf[p->buf_idx++] = ch; p->buf[p->buf_idx] = '\0'; break; From 378b790200ded3c0d2783b23e6cbb531871df768 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Wed, 25 Sep 2013 12:37:42 +0200 Subject: [PATCH 28/59] Support IPv6 literals in CONNECT authority string. --- htparse/htparse.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/htparse/htparse.c b/htparse/htparse.c index dee3f8f..e93975b 100644 --- a/htparse/htparse.c +++ b/htparse/htparse.c @@ -720,6 +720,14 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) switch (ch) { case ' ': break; + case '[': + /* Literal IPv6 address start. */ + p->buf[p->buf_idx++] = ch; + p->buf[p->buf_idx] = '\0'; + p->host_offset = &p->buf[p->buf_idx]; + + p->state = s_host_ipv6; + break; default: if (!is_host_char(ch)) { p->error = htparse_error_inval_reqline; From a9c0679a83e868d0d829e19c41df13e5d7cc4f06 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Fri, 12 Oct 2012 16:06:50 +0200 Subject: [PATCH 29/59] Separate fragment string from query string according to RFC 3986. Saves the fragment string in uri->fragment. --- evhtp.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/evhtp.c b/evhtp.c index cf6ad40..cf9d57c 100644 --- a/evhtp.c +++ b/evhtp.c @@ -884,6 +884,7 @@ static int _evhtp_request_parser_args(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); evhtp_uri_t * uri = c->request->uri; + const char * fragment; if (c->type == evhtp_type_client) { /* as a client, technically we should never get here, but just in case @@ -892,6 +893,28 @@ _evhtp_request_parser_args(htparser * p, const char * data, size_t len) { return 0; } + /* Separate fragment from query according to RFC 3986. */ + fragment = strchr(data, '#'); + if (fragment) { + ptrdiff_t frag_offset = fragment - data; + + if (frag_offset < len) { + size_t fraglen; + + /* Skip '#'. */ + fragment++; + frag_offset++; + fraglen = len - frag_offset; + uri->fragment = calloc(1, fraglen + 1); + if (!uri->fragment) { + c->request->status = EVHTP_RES_ERROR; + return -1; + } + memcpy(uri->fragment, fragment, fraglen); + len -= fraglen + 1; /* Skip '#' + fragment string. */ + } + } + if (!(uri->query = evhtp_parse_query(data, len))) { c->request->status = EVHTP_RES_ERROR; return -1; From 3d96f5e2b28c47d1a3f0638369a6d41f57f36ca4 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Tue, 17 Sep 2013 11:16:51 +0200 Subject: [PATCH 30/59] (evhtp_parse_query): Remove strange handling of '?' and '/'. They have no special meaning in an URI query, as clearly stated by RFC 3986, section 3.4: The query component is indicated by the first question mark ("?") character and terminated by a number sign ("#") character or by the end of the URI. query = *( pchar / "/" / "?" ) The characters slash ("/") and question mark ("?") may represent data within the query component. Beware that some older, erroneous implementations may not handle such data correctly when it is used as the base URI for relative references (Section 5.1), apparently because they fail to distinguish query data from path data when looking for hierarchical separators. libevhtp apparently fell under "older, erroneous implementations". --- evhtp.c | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/evhtp.c b/evhtp.c index cf9d57c..610b789 100644 --- a/evhtp.c +++ b/evhtp.c @@ -2344,7 +2344,6 @@ evhtp_kvs_add_kvs(evhtp_kvs_t * dst, evhtp_kvs_t * src) { typedef enum { s_query_start = 0, - s_query_question_mark, s_query_separator, s_query_key, s_query_val, @@ -2506,33 +2505,7 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { key_idx = 0; val_idx = 0; - - switch (ch) { - case '?': - state = s_query_key; - break; - case '/': - state = s_query_question_mark; - break; - default: - state = s_query_key; - goto query_key; - } - - break; - case s_query_question_mark: - switch (ch) { - case '?': - state = s_query_key; - break; - case '/': - state = s_query_question_mark; - break; - default: - goto error; - } - break; -query_key: + /* Fall through. */ case s_query_key: switch (ch) { case '=': From b67068c42d6c7c5f396561e3eed274f4c3356780 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Tue, 17 Sep 2013 15:10:07 +0200 Subject: [PATCH 31/59] Fix parse errors on trailing whitespace in Content-Length header. --- htparse/htparse.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/htparse/htparse.c b/htparse/htparse.c index e93975b..3319641 100644 --- a/htparse/htparse.c +++ b/htparse/htparse.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "htparse.h" @@ -306,6 +307,11 @@ static inline uint64_t str_to_uint64(char * str, size_t n, int * err) { uint64_t value; + /* Trim whitespace after value. */ + while (n && isblank(str[n-1])) { + n--; + } + if (n > 20) { /* 18446744073709551615 is 20 bytes */ *err = 1; From a5352581fe0224213a53e5e6310e2c24da121ffb Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Mon, 23 Sep 2013 18:10:02 +0200 Subject: [PATCH 32/59] (evhtp_parse_query): Advance state to s_query_key after start. Previously the first query parameter key would become empty. --- evhtp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/evhtp.c b/evhtp.c index 610b789..edaedad 100644 --- a/evhtp.c +++ b/evhtp.c @@ -2505,6 +2505,8 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { key_idx = 0; val_idx = 0; + + state = s_query_key; /* Fall through. */ case s_query_key: switch (ch) { From 1be3a0f3734ea76d6c6163548c8e392b15ff51c3 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Thu, 11 Oct 2012 12:49:56 +0200 Subject: [PATCH 33/59] Add hooks for host and port, and fill in authority struct. --- evhtp.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 108 insertions(+), 9 deletions(-) diff --git a/evhtp.c b/evhtp.c index edaedad..7a55cc3 100644 --- a/evhtp.c +++ b/evhtp.c @@ -26,6 +26,8 @@ #include "evhtp.h" static int _evhtp_request_parser_start(htparser * p); +static int _evhtp_request_parser_host(htparser * p, const char * data, size_t len); +static int _evhtp_request_parser_port(htparser * p, const char * data, size_t len); static int _evhtp_request_parser_path(htparser * p, const char * data, size_t len); static int _evhtp_request_parser_args(htparser * p, const char * data, size_t len); static int _evhtp_request_parser_header_key(htparser * p, const char * data, size_t len); @@ -46,6 +48,9 @@ static evhtp_connection_t * _evhtp_connection_new(evhtp_t * htp, evutil_socket_t static evhtp_uri_t * _evhtp_uri_new(void); static void _evhtp_uri_free(evhtp_uri_t * uri); +static evhtp_authority_t * _evhtp_authority_new(void); +static void _evhtp_authority_free(evhtp_authority_t * authority); + static evhtp_path_t * _evhtp_path_new(const char * data, size_t len); static void _evhtp_path_free(evhtp_path_t * path); @@ -210,8 +215,8 @@ static htparse_hooks request_psets = { .on_msg_begin = _evhtp_request_parser_start, .method = NULL, .scheme = NULL, - .host = NULL, - .port = NULL, + .host = _evhtp_request_parser_host, + .port = _evhtp_request_parser_port, .path = _evhtp_request_parser_path, .args = _evhtp_request_parser_args, .uri = NULL, @@ -713,9 +718,54 @@ _evhtp_uri_new(void) { return NULL; } + uri->authority = _evhtp_authority_new(); + if (!uri->authority) { + _evhtp_uri_free(uri); + return NULL; + } + return uri; } +/** + * @brief frees an authority structure + * + * @param authority evhtp_authority_t + */ +static void +_evhtp_authority_free(evhtp_authority_t * authority) { + if (authority == NULL) { + return; + } + + if (authority->username) { + free(authority->username); + } + if (authority->password) { + free(authority->password); + } + if (authority->hostname) { + free(authority->hostname); + } + free(authority); +} + +/** + * @brief create an authority structure + * + * @return evhtp_authority_t + */ +static evhtp_authority_t * +_evhtp_authority_new(void) { + evhtp_authority_t * authority; + + if (!(authority = calloc(1, sizeof(*authority)))) { + return NULL; + } + + return authority; +} + /** * @brief frees an overlay URI structure * @@ -729,6 +779,7 @@ _evhtp_uri_free(evhtp_uri_t * uri) { evhtp_query_free(uri->query); _evhtp_path_free(uri->path); + _evhtp_authority_free(uri->authority); free(uri->fragment); free(uri->query_raw); @@ -1154,28 +1205,76 @@ _evhtp_request_parser_hostname(htparser * p, const char * data, size_t len) { return 0; } /* _evhtp_request_parser_hostname */ +static int +_evhtp_require_uri(evhtp_connection_t * c) { + if (!c->request->uri) { + if (!(c->request->uri = _evhtp_uri_new())) { + c->request->status = EVHTP_RES_FATAL; + return -1; + } + } + return 0; +} + +static int +_evhtp_request_parser_host(htparser * p, const char * data, size_t len) { + evhtp_connection_t * c = htparser_get_userdata(p); + evhtp_authority_t * authority; + + if (_evhtp_require_uri(c) != 0) { + return -1; + } + authority = c->request->uri->authority; + + authority->hostname = strndup(data, len); + if (!authority->hostname) { + c->request->status = EVHTP_RES_FATAL; + return -1; + } + + return 0; +} + +static int +_evhtp_request_parser_port(htparser * p, const char * data, size_t len) { + evhtp_connection_t * c = htparser_get_userdata(p); + evhtp_authority_t * authority; + char *endptr; + unsigned long port; + + if (_evhtp_require_uri(c) != 0) { + return -1; + } + authority = c->request->uri->authority; + + port = strtoul(data, &endptr, 10); + if (endptr - data != len || port > 65535) { + c->request->status = EVHTP_RES_FATAL; + return -1; + } + authority->port = port; + + return 0; +} + static int _evhtp_request_parser_path(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); - evhtp_uri_t * uri; evhtp_path_t * path; - if (!(uri = _evhtp_uri_new())) { - c->request->status = EVHTP_RES_FATAL; + if (_evhtp_require_uri(c) != 0) { return -1; } if (!(path = _evhtp_path_new(data, len))) { - _evhtp_uri_free(uri); c->request->status = EVHTP_RES_FATAL; return -1; } - uri->path = path; - uri->scheme = htparser_get_scheme(p); + c->request->uri->path = path; + c->request->uri->scheme = htparser_get_scheme(p); c->request->method = htparser_get_method(p); - c->request->uri = uri; _evhtp_lock(c->htp); { From 7d277f8322c6ccc944c501b95700dcc17d69d194 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Wed, 10 Oct 2012 12:51:24 +0200 Subject: [PATCH 34/59] (_evhtp_path_new): If len is 0 we set full to "/", just like path. --- evhtp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/evhtp.c b/evhtp.c index 7a55cc3..17293a8 100644 --- a/evhtp.c +++ b/evhtp.c @@ -880,6 +880,8 @@ _evhtp_path_new(const char * data, size_t len) { if (len != 0) { req_path->full = strndup(data, len); + } else { + req_path->full = strdup("/"); } req_path->path = path; From 39fcb288dfe7c94bcb063a13c1c01b9cbb1758a5 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Fri, 5 Oct 2012 14:29:05 +0200 Subject: [PATCH 35/59] Do not use different API/ABI when regexps are disabled. Put evhtp_callback_type_regex last in enum. --- evhtp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/evhtp.h b/evhtp.h index 59962ab..75edf57 100644 --- a/evhtp.h +++ b/evhtp.h @@ -114,10 +114,10 @@ enum evhtp_hook_type { enum evhtp_callback_type { evhtp_callback_type_hash, + evhtp_callback_type_glob, #ifndef EVHTP_DISABLE_REGEX evhtp_callback_type_regex, #endif - evhtp_callback_type_glob }; enum evhtp_proto { From 05b01cfbdab623fd06789de93c859399277e233e Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Thu, 11 Oct 2012 19:59:36 +0200 Subject: [PATCH 36/59] Fix warnings in test.c when EVHTP_DISABLE_REGEX is defined. --- examples/test.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/test.c b/examples/test.c index 819796e..e22a476 100644 --- a/examples/test.c +++ b/examples/test.c @@ -139,7 +139,6 @@ test_regex(evhtp_request_t * req, void * arg) { evhtp_send_reply(req, EVHTP_RES_OK); } -#endif static void dynamic_cb(evhtp_request_t * r, void * arg) { @@ -169,6 +168,7 @@ create_callback(evhtp_request_t * r, void * arg) { evhtp_send_reply(r, EVHTP_RES_OK); } +#endif static void test_foo_cb(evhtp_request_t * req, void * arg ) { @@ -508,9 +508,13 @@ main(int argc, char ** argv) { evhtp_callback_t * cb_3 = NULL; evhtp_callback_t * cb_4 = NULL; evhtp_callback_t * cb_5 = NULL; +#ifndef EVHTP_DISABLE_REGEX evhtp_callback_t * cb_6 = NULL; +#endif evhtp_callback_t * cb_7 = NULL; +#ifndef EVHTP_DISABLE_REGEX evhtp_callback_t * cb_8 = NULL; +#endif evhtp_callback_t * cb_9 = NULL; evhtp_callback_t * cb_10 = NULL; evhtp_callback_t * cb_11 = NULL; From 74031a7de204a47326e6c608c635a213620bf135 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Fri, 19 Oct 2012 13:31:01 +0200 Subject: [PATCH 37/59] Add keepalive flag to connection. We want to be able to tell the difference between a client closing the connection in the middle of a request or before the first request, and a client simply closing the connection after doing a successful request with Connection: keepalive. --- evhtp.c | 1 + evhtp.h | 1 + 2 files changed, 2 insertions(+) diff --git a/evhtp.c b/evhtp.c index 17293a8..95762ae 100644 --- a/evhtp.c +++ b/evhtp.c @@ -1733,6 +1733,7 @@ _evhtp_connection_writecb(evbev_t * bev, void * arg) { if (c->request->keepalive == 1) { _evhtp_request_free(c->request); + c->keepalive = 1; c->request = NULL; c->body_bytes_read = 0; diff --git a/evhtp.h b/evhtp.h index 75edf57..b02be72 100644 --- a/evhtp.h +++ b/evhtp.h @@ -448,6 +448,7 @@ struct evhtp_connection_s { int8_t connected : 1; /**< upstream connection status, for client */ int8_t wait_4_write : 1; int8_t free_connection : 1; + uint8_t keepalive : 1; /**< set to 1 after the first request has been processed and the connection is kept open */ struct ev_token_bucket_cfg * ratelimit_cfg; /**< connection-specific ratelimiting configuration. */ TAILQ_HEAD(, evhtp_request_s) pending; /**< client pending data */ From 6c3ed3d48857e2477c9254d95715ca651ebba216 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Tue, 9 Oct 2012 10:52:54 +0200 Subject: [PATCH 38/59] Add evhtp_hook_on_conn_error hook for connection errors. The evhtp_hook_on_error hook is useless as it can't be called before the request hooks are setup, and they aren't until we have parsed the request line, which means we don't get errors before then. --- evhtp.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++------- evhtp.h | 6 +++++- 2 files changed, 60 insertions(+), 8 deletions(-) diff --git a/evhtp.c b/evhtp.c index 95762ae..5ba5349 100644 --- a/evhtp.c +++ b/evhtp.c @@ -82,6 +82,21 @@ static void _evhtp_path_free(evhtp_path_t * path); } \ } while (0); +#define HOOK_CONN_RUN(conn, hook_name, ...) do { \ + if (conn->request) { \ + evhtp_request_t * request = conn->request; \ + if (HOOK_AVAIL(request, hook_name)) { \ + return HOOK_FUNC(conn, hook_name) (conn, __VA_ARGS__, \ + HOOK_ARGS(conn, hook_name)); \ + } \ + } \ + \ + if (HOOK_AVAIL(conn, hook_name)) { \ + return HOOK_FUNC(conn, hook_name) (conn, __VA_ARGS__, \ + HOOK_ARGS(conn, hook_name)); \ + } \ +} while (0); + #ifndef EVHTP_DISABLE_EVTHR #define _evhtp_lock(h) do { \ if (h->lock) { \ @@ -483,6 +498,36 @@ _evhtp_connection_fini_hook(evhtp_connection_t * connection) { return EVHTP_RES_OK; } +/** + * @brief runs the user-defined hook when a connection error occurs + * + * @param request the request structure + * @param errtype the error that ocurred + */ +static inline void +_evhtp_error_hook(evhtp_request_t * request, evhtp_error_flags errtype) { + if (request && request->hooks && request->hooks->on_error) { + (*request->hooks->on_error)(request, errtype, + request->hooks->on_error_arg); + } +} + +/** + * @brief runs the user-defined hook when a connection error occurs + * + * @param connection the connection structure + * @param errtype the error that ocurred + */ +static inline evhtp_res +_evhtp_connection_error_hook(evhtp_connection_t * connection, evhtp_error_flags errtype) { + if (connection->request) { + _evhtp_error_hook(connection->request, errtype); + } + HOOK_CONN_RUN(connection, on_connection_error, errtype); + + return EVHTP_RES_OK; +} + static inline evhtp_res _evhtp_hostname_hook(evhtp_request_t * r, const char * hostname) { HOOK_REQUEST_RUN(r, on_hostname, hostname); @@ -1669,9 +1714,7 @@ _evhtp_connection_readcb(evbev_t * bev, void * arg) { if (c->request) { switch (c->request->status) { case EVHTP_RES_DATA_TOO_LONG: - if (c->request->hooks && c->request->hooks->on_error) { - (*c->request->hooks->on_error)(c->request, -1, c->request->hooks->on_error_arg); - } + _evhtp_connection_error_hook(c, -1); evhtp_connection_free(c); return; default: @@ -1809,10 +1852,7 @@ _evhtp_connection_eventcb(evbev_t * bev, short events, void * arg) { c->error = 1; c->connected = 0; - if (c->request && c->request->hooks && c->request->hooks->on_error) { - (*c->request->hooks->on_error)(c->request, events, - c->request->hooks->on_error_arg); - } + _evhtp_connection_error_hook(c, events); if (c->paused == 1) { @@ -3202,6 +3242,10 @@ evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void (*hooks)->on_connection_fini = (evhtp_hook_connection_fini_cb)cb; (*hooks)->on_connection_fini_arg = arg; break; + case evhtp_hook_on_conn_error: + (*hooks)->on_connection_error = (evhtp_hook_conn_err_cb)cb; + (*hooks)->on_connection_error_arg= arg; + break; case evhtp_hook_on_error: (*hooks)->on_error = (evhtp_hook_err_cb)cb; (*hooks)->on_error_arg = arg; @@ -3274,6 +3318,10 @@ evhtp_unset_all_hooks(evhtp_hooks_t ** hooks) { res -= 1; } + if (evhtp_unset_hook(hooks, evhtp_hook_on_conn_error)) { + res -= 1; + } + if (evhtp_unset_hook(hooks, evhtp_hook_on_error)) { res -= 1; } diff --git a/evhtp.h b/evhtp.h index b02be72..b01d5b6 100644 --- a/evhtp.h +++ b/evhtp.h @@ -109,7 +109,8 @@ enum evhtp_hook_type { evhtp_hook_on_error, /**< type which defines to hook whenever an error occurs */ evhtp_hook_on_hostname, evhtp_hook_on_write, - evhtp_hook_on_event + evhtp_hook_on_event, + evhtp_hook_on_conn_error, /**< type which defines to hook whenever a connection error occurs */ }; enum evhtp_callback_type { @@ -145,6 +146,7 @@ typedef void (*evhtp_hook_event_cb)(evhtp_connection_t * conn, short events, voi /* Generic hook for passing ISO tests */ typedef evhtp_res (*evhtp_hook)(); +typedef evhtp_res (*evhtp_hook_conn_err_cb)(evhtp_connection_t * connection, evhtp_error_flags errtype, void * arg); typedef evhtp_res (*evhtp_pre_accept_cb)(evhtp_connection_t * conn, void * arg); typedef evhtp_res (*evhtp_post_accept_cb)(evhtp_connection_t * conn, void * arg); typedef evhtp_res (*evhtp_hook_header_cb)(evhtp_request_t * req, evhtp_header_t * hdr, void * arg); @@ -462,6 +464,7 @@ struct evhtp_hooks_s { evhtp_hook_read_cb on_read; evhtp_hook_request_fini_cb on_request_fini; evhtp_hook_connection_fini_cb on_connection_fini; + evhtp_hook_conn_err_cb on_connection_error; evhtp_hook_err_cb on_error; evhtp_hook_chunk_new_cb on_new_chunk; evhtp_hook_chunk_fini_cb on_chunk_fini; @@ -477,6 +480,7 @@ struct evhtp_hooks_s { void * on_read_arg; void * on_request_fini_arg; void * on_connection_fini_arg; + void * on_connection_error_arg; void * on_error_arg; void * on_new_chunk_arg; void * on_chunk_fini_arg; From 47eecd020e7ddc277fa79da52fe80531a7e9a2a3 Mon Sep 17 00:00:00 2001 From: Jan Edhner Date: Thu, 21 Mar 2013 10:11:16 +0100 Subject: [PATCH 39/59] Added the function evhtp_connection_new_dns(). Allows connecting to hostname using async DNS lookup. --- evhtp.c | 23 +++++++++++++++++------ evhtp.h | 6 ++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/evhtp.c b/evhtp.c index 5ba5349..f95a4e4 100644 --- a/evhtp.c +++ b/evhtp.c @@ -21,6 +21,7 @@ #endif #include +#include #include "evhtp-internal.h" #include "evhtp.h" @@ -4015,6 +4016,12 @@ evhtp_connection_set_rate_limit(evhtp_connection_t * conn, evhtp_connection_t * evhtp_connection_new(evbase_t * evbase, const char * addr, uint16_t port) { + return evhtp_connection_new_dns(evbase, NULL, addr, port); +} + +evhtp_connection_t * +evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, + const char * addr, uint16_t port) { evhtp_connection_t * conn; struct sockaddr_in sin; @@ -4026,10 +4033,6 @@ evhtp_connection_new(evbase_t * evbase, const char * addr, uint16_t port) { return NULL; } - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = inet_addr(addr); - sin.sin_port = htons(port); - conn->evbase = evbase; conn->bev = bufferevent_socket_new(evbase, -1, BEV_OPT_CLOSE_ON_FREE); @@ -4038,8 +4041,16 @@ evhtp_connection_new(evbase_t * evbase, const char * addr, uint16_t port) { bufferevent_setcb(conn->bev, NULL, NULL, _evhtp_connection_eventcb, conn); - bufferevent_socket_connect(conn->bev, - (struct sockaddr *)&sin, sizeof(sin)); + if (dns_base != NULL) { + bufferevent_socket_connect_hostname(conn->bev, + dns_base, AF_UNSPEC, addr, port); + } else { + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr(addr); + sin.sin_port = htons(port); + bufferevent_socket_connect(conn->bev, + (struct sockaddr *)&sin, sizeof(sin)); + } return conn; } diff --git a/evhtp.h b/evhtp.h index b01d5b6..21215f2 100644 --- a/evhtp.h +++ b/evhtp.h @@ -1131,6 +1131,12 @@ int evhtp_connection_set_ratelimit(evhtp_connection_t * c, size_t read_rate, * client request functions * *****************************************************************/ +/** + * @brief allocate a new connection + */ +evhtp_connection_t * evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, + const char * addr, uint16_t port); + /** * @brief allocate a new connection */ From b13994b3c9a6ec669c49cdbce30b64e32897d171 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Fri, 5 Apr 2013 20:01:48 +0200 Subject: [PATCH 40/59] (evhtp_connection_new_dns): Handle errors. --- evhtp.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/evhtp.c b/evhtp.c index f95a4e4..0a2a6c8 100644 --- a/evhtp.c +++ b/evhtp.c @@ -4024,6 +4024,7 @@ evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, const char * addr, uint16_t port) { evhtp_connection_t * conn; struct sockaddr_in sin; + int err; if (evbase == NULL) { return NULL; @@ -4035,6 +4036,10 @@ evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, conn->evbase = evbase; conn->bev = bufferevent_socket_new(evbase, -1, BEV_OPT_CLOSE_ON_FREE); + if (conn->bev == NULL) { + evhtp_connection_free(conn); + return NULL; + } bufferevent_enable(conn->bev, EV_READ); @@ -4042,14 +4047,19 @@ evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, _evhtp_connection_eventcb, conn); if (dns_base != NULL) { - bufferevent_socket_connect_hostname(conn->bev, - dns_base, AF_UNSPEC, addr, port); + err = bufferevent_socket_connect_hostname(conn->bev, dns_base, + AF_UNSPEC, addr, port); } else { sin.sin_family = AF_INET; sin.sin_addr.s_addr = inet_addr(addr); sin.sin_port = htons(port); - bufferevent_socket_connect(conn->bev, - (struct sockaddr *)&sin, sizeof(sin)); + err = bufferevent_socket_connect(conn->bev, + (struct sockaddr *)&sin, sizeof(sin)); + } + + if (err) { + evhtp_connection_free(conn); + conn = NULL; } return conn; From ac9767259892f7028965c4826548ffaa241efffa Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Fri, 5 Apr 2013 20:17:18 +0200 Subject: [PATCH 41/59] (evhtp_connection_new): Handle IPv6 addresses. --- evhtp.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/evhtp.c b/evhtp.c index 0a2a6c8..dcc5dc4 100644 --- a/evhtp.c +++ b/evhtp.c @@ -4023,7 +4023,6 @@ evhtp_connection_t * evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, const char * addr, uint16_t port) { evhtp_connection_t * conn; - struct sockaddr_in sin; int err; if (evbase == NULL) { @@ -4050,11 +4049,27 @@ evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, err = bufferevent_socket_connect_hostname(conn->bev, dns_base, AF_UNSPEC, addr, port); } else { - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = inet_addr(addr); - sin.sin_port = htons(port); - err = bufferevent_socket_connect(conn->bev, - (struct sockaddr *)&sin, sizeof(sin)); + struct sockaddr_in sin4; + struct sockaddr_in6 sin6; + struct sockaddr * sin; + int salen; + + if (inet_pton(AF_INET, addr, &sin4.sin_addr)) { + sin4.sin_family = AF_INET; + sin4.sin_port = htons(port); + sin = (struct sockaddr *)&sin4; + salen = sizeof(sin4); + } else if (inet_pton(AF_INET6, addr, &sin6.sin6_addr)) { + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(port); + sin = (struct sockaddr *)&sin6; + salen = sizeof(sin6); + } else { + /* Not a valid IP. */ + evhtp_connection_free(conn); + return NULL; + } + err = bufferevent_socket_connect(conn->bev, sin, salen); } if (err) { From c4ed326f9f83d3f79058282e30eaaca772415948 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Tue, 16 Oct 2012 16:48:54 +0200 Subject: [PATCH 42/59] (_evhtp_create_headers): Use evbuffer_expand() to reserve space. Avoids doing multiple small allocations when we already know how much space we'll need. --- evhtp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/evhtp.c b/evhtp.c index dcc5dc4..845f2d2 100644 --- a/evhtp.c +++ b/evhtp.c @@ -1527,6 +1527,7 @@ static int _evhtp_create_headers(evhtp_header_t * header, void * arg) { evbuf_t * buf = arg; + evbuffer_expand(buf, header->klen + 2 + header->vlen + 2); evbuffer_add(buf, header->key, header->klen); evbuffer_add(buf, ": ", 2); evbuffer_add(buf, header->val, header->vlen); From 3906a65357ac8af8ed0a9502f7d47d3c8a7681e4 Mon Sep 17 00:00:00 2001 From: Marcus Sundberg Date: Wed, 17 Oct 2012 09:51:16 +0200 Subject: [PATCH 43/59] Use malloc() instead of calloc() for buffers we will immediately fill. --- evhtp.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/evhtp.c b/evhtp.c index 845f2d2..aace0e9 100644 --- a/evhtp.c +++ b/evhtp.c @@ -1004,12 +1004,13 @@ _evhtp_request_parser_args(htparser * p, const char * data, size_t len) { fragment++; frag_offset++; fraglen = len - frag_offset; - uri->fragment = calloc(1, fraglen + 1); + uri->fragment = malloc(fraglen + 1); if (!uri->fragment) { c->request->status = EVHTP_RES_ERROR; return -1; } memcpy(uri->fragment, fragment, fraglen); + uri->fragment[fraglen] = '\0'; len -= fraglen + 1; /* Skip '#' + fragment string. */ } } @@ -1019,8 +1020,9 @@ _evhtp_request_parser_args(htparser * p, const char * data, size_t len) { return -1; } - uri->query_raw = calloc(len + 1, 1); + uri->query_raw = malloc(len + 1); memcpy(uri->query_raw, data, len); + uri->query_raw[len] = '\0'; return 0; } From a2ebece5035077cf434d119907537e726df4af0d Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Sat, 15 Nov 2014 05:36:45 -0500 Subject: [PATCH 44/59] added padding for all structs containing bitfields - some structures were using bitfields without proper padding, so that was fixed. - some cleanups to htparse.c --- evhtp.h | 55 +++++++++++++++++++++++++---------------------- htparse/htparse.c | 12 ++++------- 2 files changed, 33 insertions(+), 34 deletions(-) diff --git a/evhtp.h b/evhtp.h index 59962ab..03ed671 100644 --- a/evhtp.h +++ b/evhtp.h @@ -408,10 +408,11 @@ struct evhtp_request_s { evhtp_proto proto; /**< HTTP protocol used */ htp_method method; /**< HTTP method used */ evhtp_res status; /**< The HTTP response code or other error conditions */ - int8_t keepalive : 1; /**< set to 1 if the connection is keep-alive */ - int8_t finished : 1; /**< set to 1 if the request is fully processed */ - int8_t chunked : 1; /**< set to 1 if the request is chunked */ - int8_t error : 1; + int8_t keepalive : 1, /**< set to 1 if the connection is keep-alive */ + finished : 1, /**< set to 1 if the request is fully processed */ + chunked : 1, /**< set to 1 if the request is chunked */ + error : 1, + pad : 4; evhtp_callback_cb cb; /**< the function to call when fully processed */ void * cbarg; /**< argument which is passed to the cb function */ @@ -429,28 +430,30 @@ struct evhtp_connection_s { #ifndef EVHTP_DISABLE_SSL evhtp_ssl_t * ssl; #endif - evhtp_hooks_t * hooks; - htparser * parser; - event_t * resume_ev; - struct sockaddr * saddr; - struct timeval recv_timeo; /**< conn read timeouts (overrides global) */ - struct timeval send_timeo; /**< conn write timeouts (overrides global) */ - evutil_socket_t sock; - evhtp_request_t * request; /**< the request currently being processed */ - uint64_t max_body_size; - uint64_t body_bytes_read; - uint64_t num_requests; - evhtp_type type; /**< server or client */ - uint8_t error : 1; - uint8_t owner : 1; /**< set to 1 if this structure owns the bufferevent */ - uint8_t vhost_via_sni : 1; /**< set to 1 if the vhost was found via SSL SNI */ - int8_t paused : 1; - int8_t connected : 1; /**< upstream connection status, for client */ - int8_t wait_4_write : 1; - int8_t free_connection : 1; - struct ev_token_bucket_cfg * ratelimit_cfg; /**< connection-specific ratelimiting configuration. */ - - TAILQ_HEAD(, evhtp_request_s) pending; /**< client pending data */ + evhtp_hooks_t * hooks; + htparser * parser; + event_t * resume_ev; + struct sockaddr * saddr; + struct timeval recv_timeo; /**< conn read timeouts (overrides global) */ + struct timeval send_timeo; /**< conn write timeouts (overrides global) */ + evutil_socket_t sock; + evhtp_request_t * request; /**< the request currently being processed */ + uint64_t max_body_size; + uint64_t body_bytes_read; + uint64_t num_requests; + evhtp_type type; /**< server or client */ + int8_t error : 1, + owner : 1, /**< set to 1 if this structure owns the bufferevent */ + vhost_via_sni : 1, /**< set to 1 if the vhost was found via SSL SNI */ + paused : 1, + connected : 1, /**< upstream connection status, for client */ + wait_4_write : 1, + free_connection : 1, + pad : 1; + + struct ev_token_bucket_cfg * ratelimit_cfg; /**< connection-specific ratelimiting configuration. */ + + TAILQ_HEAD(, evhtp_request_s) pending; /**< client pending data */ }; struct evhtp_hooks_s { diff --git a/htparse/htparse.c b/htparse/htparse.c index b4411d3..3f583fd 100644 --- a/htparse/htparse.c +++ b/htparse/htparse.c @@ -49,10 +49,10 @@ enum eval_hdr_val { }; enum parser_flags { - parser_flag_chunked = 1 << 0, - parser_flag_connection_keep_alive = 1 << 1, - parser_flag_connection_close = 1 << 2, - parser_flag_trailing = 1 << 3, + parser_flag_chunked = (1 << 0), + parser_flag_connection_keep_alive = (1 << 1), + parser_flag_connection_close = (1 << 2), + parser_flag_trailing = (1 << 3), }; enum parser_state { @@ -763,10 +763,6 @@ htparser_run(htparser * p, htparse_hooks * hooks, const char * data, size_t len) res = hook_scheme_run(p, hooks, p->scheme_offset, (&p->buf[p->buf_idx] - p->scheme_offset)); -#if 0 - p->buf_idx = 0; - p->buf[0] = '\0'; -#endif p->buf[p->buf_idx++] = ch; p->buf[p->buf_idx] = '\0'; From 1d492cc80d2093f7fbde49f8fbbee55174c7daab Mon Sep 17 00:00:00 2001 From: romange Date: Sat, 27 Dec 2014 20:28:56 +0200 Subject: [PATCH 45/59] Update evthr.c Signed-off-by: Mark Ellzey --- evthr/evthr.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/evthr/evthr.c b/evthr/evthr.c index 32f53a5..c3c14b1 100644 --- a/evthr/evthr.c +++ b/evthr/evthr.c @@ -170,7 +170,8 @@ evthr_stop(evthr_t * thread) { } pthread_mutex_unlock(&thread->rlock); - + + pthread_join(*thread->thr, NULL); return EVTHR_RES_OK; } @@ -226,8 +227,6 @@ evthr_new(evthr_init_cb init_cb, void * args) { int evthr_start(evthr_t * thread) { - int res; - if (thread == NULL || thread->thr == NULL) { return -1; } @@ -236,9 +235,7 @@ evthr_start(evthr_t * thread) { return -1; } - res = pthread_detach(*thread->thr); - - return res; + return 0; } void From c32562ab1f6496b91426929ed30fbbd5e25ca0cb Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Sat, 27 Dec 2014 15:12:44 -0500 Subject: [PATCH 46/59] Cleanup, use EVHTP_DISABLE_SSL for client --- evhtp.c | 3 +-- evthr/evthr.c | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/evhtp.c b/evhtp.c index fc50c3e..92f6653 100644 --- a/evhtp.c +++ b/evhtp.c @@ -3861,7 +3861,7 @@ evhtp_connection_new(evbase_t * evbase, const char * addr, uint16_t port) { return conn; } -#ifndef DISABLE_SSL +#ifndef EVHTP_DISABLE_SSL evhtp_connection_t * evhtp_connection_ssl_new(evbase_t * evbase, const char * addr, uint16_t port, evhtp_ssl_ctx_t* ctx) { evhtp_connection_t * conn; @@ -3897,7 +3897,6 @@ evhtp_connection_ssl_new(evbase_t * evbase, const char * addr, uint16_t port, ev #endif - evhtp_request_t * evhtp_request_new(evhtp_callback_cb cb, void * arg) { evhtp_request_t * r; diff --git a/evthr/evthr.c b/evthr/evthr.c index c3c14b1..486c469 100644 --- a/evthr/evthr.c +++ b/evthr/evthr.c @@ -170,7 +170,6 @@ evthr_stop(evthr_t * thread) { } pthread_mutex_unlock(&thread->rlock); - pthread_join(*thread->thr, NULL); return EVTHR_RES_OK; } From d8a4935951913acc23a7ec5ced64184c9edc5803 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Tue, 13 Jan 2015 02:29:16 -0500 Subject: [PATCH 47/59] Various fixes, see full commit message - Removed all the stuff added to .gitignore - Added new parser flag EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS which can be set if the user does not want the overhead of this new operation. - use memchr instead of strchr for finding the fragment, as it may not always be null terminated. - renamed "wait_4_write" to simply "waiting". In libevhtp2, the act of pausing are defined as various states, but until then, this looks cleaner. - No need to memset buffers in evhtp_parse_query, just set the first byte to 0 and rely on the indexes. --- .gitignore | 26 +----- evhtp.c | 264 +++++++++++++++++++++++++++++++++++------------------ evhtp.h | 119 ++++++++++++------------ 3 files changed, 238 insertions(+), 171 deletions(-) diff --git a/.gitignore b/.gitignore index 95d0775..00eb13f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,5 @@ # cmake manages these; they shouldn't go in version control - -/CMakeCache.txt -/CMakeFiles/** -/Makefile -/cmake_install.cmake - -# generated .h files - -/compat/sys/tree.h -/oniguruma/config.h - -# compiled files - -/libevhtp.a -/test -/test_basic -/test_vhost - -/test_client -/test_proxy - -/build/* +# +# +# they aren't going into version control, but shouldn't be +# completely ignored. I'm removing the mods here. diff --git a/evhtp.c b/evhtp.c index aace0e9..4296c33 100644 --- a/evhtp.c +++ b/evhtp.c @@ -83,19 +83,19 @@ static void _evhtp_path_free(evhtp_path_t * path); } \ } while (0); -#define HOOK_CONN_RUN(conn, hook_name, ...) do { \ - if (conn->request) { \ - evhtp_request_t * request = conn->request; \ - if (HOOK_AVAIL(request, hook_name)) { \ - return HOOK_FUNC(conn, hook_name) (conn, __VA_ARGS__, \ - HOOK_ARGS(conn, hook_name)); \ - } \ - } \ - \ - if (HOOK_AVAIL(conn, hook_name)) { \ - return HOOK_FUNC(conn, hook_name) (conn, __VA_ARGS__, \ - HOOK_ARGS(conn, hook_name)); \ - } \ +#define HOOK_CONN_RUN(conn, hook_name, ...) do { \ + if (conn->request) { \ + evhtp_request_t * request = conn->request; \ + if (HOOK_AVAIL(request, hook_name)) { \ + return HOOK_FUNC(conn, hook_name) (conn, __VA_ARGS__, \ + HOOK_ARGS(conn, hook_name)); \ + } \ + } \ + \ + if (HOOK_AVAIL(conn, hook_name)) { \ + return HOOK_FUNC(conn, hook_name) (conn, __VA_ARGS__, \ + HOOK_ARGS(conn, hook_name)); \ + } \ } while (0); #ifndef EVHTP_DISABLE_EVTHR @@ -290,6 +290,7 @@ strndup(const char * s, size_t n) { ret[n] = '\0'; strncpy(ret, s, n); + return ret; } @@ -637,6 +638,7 @@ _evhtp_callback_find(evhtp_callbacks_t * cbs, if (strcmp(callback->val.path, path) == 0) { *start_offset = 0; *end_offset = (unsigned int)strlen(path); + return callback; } break; @@ -655,6 +657,7 @@ _evhtp_callback_find(evhtp_callbacks_t * cbs, if (_evhtp_glob_match(callback->val.glob, path) == 1) { *start_offset = 0; *end_offset = (unsigned int)strlen(path); + return callback; } default: @@ -706,17 +709,17 @@ _evhtp_request_new(evhtp_connection_t * c) { return req; /* Error path. */ - out_free_hdr_in: +out_free_hdr_in: free(req->headers_in); - out_free_buf_out: +out_free_buf_out: evbuffer_free(req->buffer_out); - out_free_buf_in: +out_free_buf_in: evbuffer_free(req->buffer_in); - out_free_req: +out_free_req: free(req); return NULL; -} +} /* _evhtp_request_new */ /** * @brief frees all data in an evhtp_request_t along with calling finished hooks @@ -767,6 +770,7 @@ _evhtp_uri_new(void) { uri->authority = _evhtp_authority_new(); if (!uri->authority) { _evhtp_uri_free(uri); + return NULL; } @@ -893,6 +897,7 @@ _evhtp_path_new(const char * data, size_t len) { if ((const char *)(data + path_len) > data_end) { fprintf(stderr, "PATH Corrupted.. (path_len > len)\n"); free(req_path); + return NULL; } @@ -900,6 +905,7 @@ _evhtp_path_new(const char * data, size_t len) { if ((const char *)(&data[i + 1] + file_len) > data_end) { fprintf(stderr, "FILE Corrupted.. (file_len > len)\n"); free(req_path); + return NULL; } @@ -984,6 +990,7 @@ _evhtp_request_parser_args(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); evhtp_uri_t * uri = c->request->uri; const char * fragment; + int ignore_fragment; if (c->type == evhtp_type_client) { /* as a client, technically we should never get here, but just in case @@ -992,40 +999,61 @@ _evhtp_request_parser_args(htparser * p, const char * data, size_t len) { return 0; } - /* Separate fragment from query according to RFC 3986. */ - fragment = strchr(data, '#'); - if (fragment) { - ptrdiff_t frag_offset = fragment - data; + + /* if the parser flags has the IGNORE_FRAGMENTS bit set, skip + * the fragment parsing + */ + ignore_fragment = (c->htp->parser_flags & + EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS); + + + if (!ignore_fragment && (fragment = memchr(data, '#', len))) { + /* Separate fragment from query according to RFC 3986. + * + * XXX: not happy about using strchr stuff, maybe this functionality + * is more apt as part of evhtp_parse_query() + */ + + ptrdiff_t frag_offset; + + frag_offset = fragment - data; if (frag_offset < len) { size_t fraglen; /* Skip '#'. */ - fragment++; - frag_offset++; - fraglen = len - frag_offset; - uri->fragment = malloc(fraglen + 1); - if (!uri->fragment) { + fragment += 1; + frag_offset += 1; + fraglen = len - frag_offset; + + if (!(uri->fragment = malloc(fraglen + 1))) { c->request->status = EVHTP_RES_ERROR; + return -1; } + memcpy(uri->fragment, fragment, fraglen); + uri->fragment[fraglen] = '\0'; len -= fraglen + 1; /* Skip '#' + fragment string. */ } } - if (!(uri->query = evhtp_parse_query(data, len))) { + uri->query = evhtp_parse_query_wflags(data, len, c->htp->parser_flags); + + if (!uri->query) { c->request->status = EVHTP_RES_ERROR; + return -1; } - uri->query_raw = malloc(len + 1); + uri->query_raw = malloc(len + 1); memcpy(uri->query_raw, data, len); + uri->query_raw[len] = '\0'; return 0; -} +} /* _evhtp_request_parser_args */ static int _evhtp_request_parser_headers_start(htparser * p) { @@ -1050,10 +1078,12 @@ _evhtp_request_parser_header_key(htparser * p, const char * data, size_t len) { if ((hdr = evhtp_header_key_add(c->request->headers_in, key_s, 0)) == NULL) { c->request->status = EVHTP_RES_FATAL; + return -1; } hdr->k_heaped = 1; + return 0; } @@ -1070,6 +1100,7 @@ _evhtp_request_parser_header_val(htparser * p, const char * data, size_t len) { if ((header = evhtp_header_val_add(c->request->headers_in, val_s, 0)) == NULL) { free(val_s); c->request->status = EVHTP_RES_FATAL; + return -1; } @@ -1260,9 +1291,11 @@ _evhtp_require_uri(evhtp_connection_t * c) { if (!c->request->uri) { if (!(c->request->uri = _evhtp_uri_new())) { c->request->status = EVHTP_RES_FATAL; + return -1; } } + return 0; } @@ -1274,11 +1307,12 @@ _evhtp_request_parser_host(htparser * p, const char * data, size_t len) { if (_evhtp_require_uri(c) != 0) { return -1; } - authority = c->request->uri->authority; + authority = c->request->uri->authority; authority->hostname = strndup(data, len); if (!authority->hostname) { c->request->status = EVHTP_RES_FATAL; + return -1; } @@ -1289,17 +1323,18 @@ static int _evhtp_request_parser_port(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); evhtp_authority_t * authority; - char *endptr; - unsigned long port; + char * endptr; + unsigned long port; if (_evhtp_require_uri(c) != 0) { return -1; } authority = c->request->uri->authority; - port = strtoul(data, &endptr, 10); + port = strtoul(data, &endptr, 10); if (endptr - data != len || port > 65535) { c->request->status = EVHTP_RES_FATAL; + return -1; } authority->port = port; @@ -1318,13 +1353,14 @@ _evhtp_request_parser_path(htparser * p, const char * data, size_t len) { if (!(path = _evhtp_path_new(data, len))) { c->request->status = EVHTP_RES_FATAL; + return -1; } - c->request->uri->path = path; - c->request->uri->scheme = htparser_get_scheme(p); + c->request->uri->path = path; + c->request->uri->scheme = htparser_get_scheme(p); - c->request->method = htparser_get_method(p); + c->request->method = htparser_get_method(p); _evhtp_lock(c->htp); { @@ -1534,6 +1570,7 @@ _evhtp_create_headers(evhtp_header_t * header, void * arg) { evbuffer_add(buf, ": ", 2); evbuffer_add(buf, header->val, header->vlen); evbuffer_add(buf, "\r\n", 2); + return 0; } @@ -1661,6 +1698,7 @@ _evhtp_connection_resumecb(int fd, short events, void * arg) { if (c->free_connection == 1) { evhtp_connection_free(c); + return; } @@ -1673,7 +1711,7 @@ _evhtp_connection_resumecb(int fd, short events, void * arg) { if (evbuffer_get_length(bufferevent_get_output(c->bev))) { bufferevent_enable(c->bev, EV_WRITE); - c->wait_4_write = 1; + c->waiting = 1; } else { bufferevent_enable(c->bev, EV_READ | EV_WRITE); _evhtp_connection_readcb(c->bev, c); @@ -1712,6 +1750,7 @@ _evhtp_connection_readcb(evbev_t * bev, void * arg) { */ evbuffer_drain(bufferevent_get_input(bev), nread); evhtp_connection_free(c); + return; } @@ -1720,6 +1759,7 @@ _evhtp_connection_readcb(evbev_t * bev, void * arg) { case EVHTP_RES_DATA_TOO_LONG: _evhtp_connection_error_hook(c, -1); evhtp_connection_free(c); + return; default: break; @@ -1749,8 +1789,8 @@ _evhtp_connection_writecb(evbev_t * bev, void * arg) { return; } - if (c->wait_4_write == 1) { - c->wait_4_write = 0; + if (c->waiting == 1) { + c->waiting = 0; bufferevent_enable(bev, EV_READ); @@ -1799,9 +1839,11 @@ _evhtp_connection_writecb(evbev_t * bev, void * arg) { htparser_set_userdata(c->parser, c); + return; } else { evhtp_connection_free(c); + return; } @@ -1849,6 +1891,7 @@ _evhtp_connection_eventcb(evbev_t * bev, short events, void * arg) { */ bufferevent_enable(bev, EV_READ); errno = 0; + return; } } @@ -1892,6 +1935,7 @@ _evhtp_connection_accept(evbase_t * evbase, evhtp_connection_t * connection) { if (_evhtp_run_pre_accept(connection->htp, connection) < 0) { evutil_closesocket(connection->sock); + return -1; } @@ -1966,6 +2010,7 @@ _evhtp_connection_new(evhtp_t * htp, evutil_socket_t sock, evhtp_type type) { ptype = htp_type_request; break; default: + return NULL; } @@ -1983,6 +2028,7 @@ _evhtp_connection_new(evhtp_t * htp, evutil_socket_t sock, evhtp_type type) { connection->parser = htparser_new(); if (!connection->parser) { free(connection); + return NULL; } @@ -1992,7 +2038,7 @@ _evhtp_connection_new(evhtp_t * htp, evutil_socket_t sock, evhtp_type type) { TAILQ_INIT(&connection->pending); return connection; -} +} /* _evhtp_connection_new */ #ifdef LIBEVENT_HAS_SHUTDOWN #ifndef EVHTP_DISABLE_SSL @@ -2033,11 +2079,13 @@ _evhtp_run_in_thread(evthr_t * thr, void * arg, void * shared) { if (_evhtp_connection_accept(connection->evbase, connection) < 0) { evhtp_connection_free(connection); + return; } if (_evhtp_run_post_accept(htp, connection) < 0) { evhtp_connection_free(connection); + return; } } @@ -2061,8 +2109,10 @@ _evhtp_accept_cb(evserv_t * serv, int fd, struct sockaddr * s, int sl, void * ar if (evthr_pool_defer(htp->thr_pool, _evhtp_run_in_thread, connection) != EVTHR_RES_OK) { evutil_closesocket(connection->sock); evhtp_connection_free(connection); + return; } + return; } #endif @@ -2070,11 +2120,13 @@ _evhtp_accept_cb(evserv_t * serv, int fd, struct sockaddr * s, int sl, void * ar if (_evhtp_connection_accept(htp->evbase, connection) < 0) { evhtp_connection_free(connection); + return; } if (_evhtp_run_post_accept(htp, connection) < 0) { evhtp_connection_free(connection); + return; } } @@ -2084,8 +2136,10 @@ _evhtp_accept_cb(evserv_t * serv, int fd, struct sockaddr * s, int sl, void * ar static unsigned long _evhtp_ssl_get_thread_id(void) { #ifndef WIN32 + return (unsigned long)pthread_self(); #else + return (unsigned long)(pthread_self().p); #endif } @@ -2220,6 +2274,7 @@ evhtp_connection_pause(evhtp_connection_t * c) { c->paused = 1; bufferevent_disable(c->bev, EV_READ | EV_WRITE); + return; } @@ -2233,6 +2288,7 @@ evhtp_connection_resume(evhtp_connection_t * c) { c->paused = 0; event_active(c->resume_ev, EV_WRITE, 1); + return; } @@ -2310,6 +2366,7 @@ evhtp_kvs_new(void) { evhtp_kvs_t * kvs = malloc(sizeof(evhtp_kvs_t)); TAILQ_INIT(kvs); + return kvs; } @@ -2334,10 +2391,11 @@ evhtp_kv_new(const char * key, const char * val, char kalloc, char valloc) { if (kalloc == 1) { char * s; - if (!(s = malloc(kv->klen + 1))) { - free(kv); - return NULL; - } + if (!(s = malloc(kv->klen + 1))) { + free(kv); + + return NULL; + } memcpy(s, key, kv->klen); @@ -2519,8 +2577,10 @@ evhtp_is_hex_query_char(unsigned char ch) { case '7': case '8': case '9': + return 1; default: + return 0; } /* switch */ } @@ -2619,7 +2679,7 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { if (len > (SIZE_MAX - (len + 2))) { - return NULL; + return NULL; } query_args = evhtp_query_new(); @@ -2630,6 +2690,7 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { if (!(val_buf = malloc(len + 1))) { free(key_buf); + return NULL; } @@ -2646,14 +2707,14 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { switch (state) { case s_query_start: - memset(key_buf, 0, len + 1); - memset(val_buf, 0, len + 1); + key_idx = 0; + val_idx = 0; - key_idx = 0; - val_idx = 0; + key_buf[0] = '\0'; + val_buf[0] = '\0'; - state = s_query_key; - /* Fall through. */ + state = s_query_key; + /* Fall through. */ case s_query_key: switch (ch) { case '=': @@ -2687,11 +2748,12 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { */ evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, NULL, 1, 1)); - memset(key_buf, 0, len + 1); - memset(val_buf, 0, len + 1); - key_idx = 0; val_idx = 0; + + key_buf[0] = '\0'; + val_buf[0] = '\0'; + state = s_query_key; break; default: @@ -2709,8 +2771,8 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { } key_buf[key_idx - 1] = '%'; - key_buf[key_idx++] = ch; - key_buf[key_idx] = '\0'; + key_buf[key_idx++] = ch; + key_buf[key_idx] = '\0'; state = s_query_key; break; @@ -2742,12 +2804,11 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { case '&': evhtp_kvs_add_kv(query_args, evhtp_kv_new(key_buf, val_buf, 1, 1)); - memset(key_buf, 0, len + 1); - memset(val_buf, 0, len + 1); - key_idx = 0; val_idx = 0; + key_buf[0] = '\0'; + val_buf[0] = '\0'; state = s_query_key; break; @@ -2857,6 +2918,7 @@ evhtp_send_reply_start(evhtp_request_t * request, evhtp_res code) { if (!(reply_buf = _evhtp_create_reply(request, code))) { evhtp_connection_free(c); + return; } @@ -2893,6 +2955,7 @@ evhtp_send_reply(evhtp_request_t * request, evhtp_res code) { if (!(reply_buf = _evhtp_create_reply(request, code))) { evhtp_connection_free(request->conn); + return; } @@ -3096,6 +3159,7 @@ evhtp_bind_socket(evhtp_t * htp, const char * baddr, uint16_t port, int backlog) sa = (struct sockaddr *)&sun; #else fprintf(stderr, "System does not support AF_UNIX sockets\n"); + return -1; #endif } else { @@ -3157,6 +3221,7 @@ evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_c if (regcomp(hcb->val.regex, (char *)path, REG_EXTENDED) != 0) { free(hcb->val.regex); free(hcb); + return NULL; } break; @@ -3166,6 +3231,7 @@ evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_c break; default: free(hcb); + return NULL; } /* switch */ @@ -3219,66 +3285,67 @@ evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void switch (type) { case evhtp_hook_on_headers_start: - (*hooks)->on_headers_start = (evhtp_hook_headers_start_cb)cb; - (*hooks)->on_headers_start_arg = arg; + (*hooks)->on_headers_start = (evhtp_hook_headers_start_cb)cb; + (*hooks)->on_headers_start_arg = arg; break; case evhtp_hook_on_header: (*hooks)->on_header = (evhtp_hook_header_cb)cb; - (*hooks)->on_header_arg = arg; + (*hooks)->on_header_arg = arg; break; case evhtp_hook_on_headers: - (*hooks)->on_headers = (evhtp_hook_headers_cb)cb; - (*hooks)->on_headers_arg = arg; + (*hooks)->on_headers = (evhtp_hook_headers_cb)cb; + (*hooks)->on_headers_arg = arg; break; case evhtp_hook_on_path: (*hooks)->on_path = (evhtp_hook_path_cb)cb; - (*hooks)->on_path_arg = arg; + (*hooks)->on_path_arg = arg; break; case evhtp_hook_on_read: (*hooks)->on_read = (evhtp_hook_read_cb)cb; - (*hooks)->on_read_arg = arg; + (*hooks)->on_read_arg = arg; break; case evhtp_hook_on_request_fini: - (*hooks)->on_request_fini = (evhtp_hook_request_fini_cb)cb; - (*hooks)->on_request_fini_arg = arg; + (*hooks)->on_request_fini = (evhtp_hook_request_fini_cb)cb; + (*hooks)->on_request_fini_arg = arg; break; case evhtp_hook_on_connection_fini: - (*hooks)->on_connection_fini = (evhtp_hook_connection_fini_cb)cb; - (*hooks)->on_connection_fini_arg = arg; + (*hooks)->on_connection_fini = (evhtp_hook_connection_fini_cb)cb; + (*hooks)->on_connection_fini_arg = arg; break; case evhtp_hook_on_conn_error: - (*hooks)->on_connection_error = (evhtp_hook_conn_err_cb)cb; - (*hooks)->on_connection_error_arg= arg; + (*hooks)->on_connection_error = (evhtp_hook_conn_err_cb)cb; + (*hooks)->on_connection_error_arg = arg; break; case evhtp_hook_on_error: (*hooks)->on_error = (evhtp_hook_err_cb)cb; - (*hooks)->on_error_arg = arg; + (*hooks)->on_error_arg = arg; break; case evhtp_hook_on_new_chunk: - (*hooks)->on_new_chunk = (evhtp_hook_chunk_new_cb)cb; - (*hooks)->on_new_chunk_arg = arg; + (*hooks)->on_new_chunk = (evhtp_hook_chunk_new_cb)cb; + (*hooks)->on_new_chunk_arg = arg; break; case evhtp_hook_on_chunk_complete: - (*hooks)->on_chunk_fini = (evhtp_hook_chunk_fini_cb)cb; - (*hooks)->on_chunk_fini_arg = arg; + (*hooks)->on_chunk_fini = (evhtp_hook_chunk_fini_cb)cb; + (*hooks)->on_chunk_fini_arg = arg; break; case evhtp_hook_on_chunks_complete: - (*hooks)->on_chunks_fini = (evhtp_hook_chunks_fini_cb)cb; - (*hooks)->on_chunks_fini_arg = arg; + (*hooks)->on_chunks_fini = (evhtp_hook_chunks_fini_cb)cb; + (*hooks)->on_chunks_fini_arg = arg; break; case evhtp_hook_on_hostname: - (*hooks)->on_hostname = (evhtp_hook_hostname_cb)cb; - (*hooks)->on_hostname_arg = arg; + (*hooks)->on_hostname = (evhtp_hook_hostname_cb)cb; + (*hooks)->on_hostname_arg = arg; break; case evhtp_hook_on_write: (*hooks)->on_write = (evhtp_hook_write_cb)cb; - (*hooks)->on_write_arg = arg; + (*hooks)->on_write_arg = arg; break; case evhtp_hook_on_event: (*hooks)->on_event = (evhtp_hook_event_cb)cb; - (*hooks)->on_event_arg = arg; + (*hooks)->on_event_arg = arg; break; default: + return -1; } /* switch */ @@ -3366,6 +3433,7 @@ evhtp_set_cb(evhtp_t * htp, const char * path, evhtp_callback_cb cb, void * arg) if (htp->callbacks == NULL) { if (!(htp->callbacks = calloc(sizeof(evhtp_callbacks_t), 1))) { _evhtp_unlock(htp); + return NULL; } @@ -3374,16 +3442,19 @@ evhtp_set_cb(evhtp_t * htp, const char * path, evhtp_callback_cb cb, void * arg) if (!(hcb = evhtp_callback_new(path, evhtp_callback_type_hash, cb, arg))) { _evhtp_unlock(htp); + return NULL; } if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) { evhtp_callback_free(hcb); _evhtp_unlock(htp); + return NULL; } _evhtp_unlock(htp); + return hcb; } @@ -3411,6 +3482,7 @@ evhtp_use_threads(evhtp_t * htp, evhtp_thread_init_cb init_cb, int nthreads, voi } evthr_pool_start(htp->thr_pool); + return 0; } @@ -3442,6 +3514,7 @@ evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, vo if (htp->callbacks == NULL) { if (!(htp->callbacks = calloc(sizeof(evhtp_callbacks_t), 1))) { _evhtp_unlock(htp); + return NULL; } @@ -3450,16 +3523,19 @@ evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, vo if (!(hcb = evhtp_callback_new(pattern, evhtp_callback_type_regex, cb, arg))) { _evhtp_unlock(htp); + return NULL; } if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) { evhtp_callback_free(hcb); _evhtp_unlock(htp); + return NULL; } _evhtp_unlock(htp); + return hcb; } @@ -3474,6 +3550,7 @@ evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, voi if (htp->callbacks == NULL) { if (!(htp->callbacks = calloc(sizeof(evhtp_callbacks_t), 1))) { _evhtp_unlock(htp); + return NULL; } @@ -3482,16 +3559,19 @@ evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, voi if (!(hcb = evhtp_callback_new(pattern, evhtp_callback_type_glob, cb, arg))) { _evhtp_unlock(htp); + return NULL; } if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) { evhtp_callback_free(hcb); _evhtp_unlock(htp); + return NULL; } _evhtp_unlock(htp); + return hcb; } @@ -3988,7 +4068,7 @@ evhtp_free(evhtp_t * evhtp) { #endif free(evhtp); -} +} /* evhtp_free */ int evhtp_connection_set_rate_limit(evhtp_connection_t * conn, @@ -4036,10 +4116,11 @@ evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, return NULL; } - conn->evbase = evbase; - conn->bev = bufferevent_socket_new(evbase, -1, BEV_OPT_CLOSE_ON_FREE); + conn->evbase = evbase; + conn->bev = bufferevent_socket_new(evbase, -1, BEV_OPT_CLOSE_ON_FREE); if (conn->bev == NULL) { evhtp_connection_free(conn); + return NULL; } @@ -4060,16 +4141,17 @@ evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, if (inet_pton(AF_INET, addr, &sin4.sin_addr)) { sin4.sin_family = AF_INET; sin4.sin_port = htons(port); - sin = (struct sockaddr *)&sin4; + sin = (struct sockaddr *)&sin4; salen = sizeof(sin4); } else if (inet_pton(AF_INET6, addr, &sin6.sin6_addr)) { sin6.sin6_family = AF_INET6; sin6.sin6_port = htons(port); - sin = (struct sockaddr *)&sin6; - salen = sizeof(sin6); + sin = (struct sockaddr *)&sin6; + salen = sizeof(sin6); } else { /* Not a valid IP. */ evhtp_connection_free(conn); + return NULL; } err = bufferevent_socket_connect(conn->bev, sin, salen); @@ -4081,7 +4163,7 @@ evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, } return conn; -} +} /* evhtp_connection_new_dns */ evhtp_request_t * evhtp_request_new(evhtp_callback_cb cb, void * arg) { diff --git a/evhtp.h b/evhtp.h index 21215f2..8c57998 100644 --- a/evhtp.h +++ b/evhtp.h @@ -138,41 +138,41 @@ typedef enum evhtp_proto evhtp_proto; typedef enum evhtp_ssl_scache_type evhtp_ssl_scache_type; typedef enum evhtp_type evhtp_type; -typedef void (*evhtp_thread_init_cb)(evhtp_t * htp, evthr_t * thr, void * arg); -typedef void (*evhtp_callback_cb)(evhtp_request_t * req, void * arg); -typedef void (*evhtp_hook_err_cb)(evhtp_request_t * req, evhtp_error_flags errtype, void * arg); -typedef void (*evhtp_hook_event_cb)(evhtp_connection_t * conn, short events, void * arg); +typedef void (* evhtp_thread_init_cb)(evhtp_t * htp, evthr_t * thr, void * arg); +typedef void (* evhtp_callback_cb)(evhtp_request_t * req, void * arg); +typedef void (* evhtp_hook_err_cb)(evhtp_request_t * req, evhtp_error_flags errtype, void * arg); +typedef void (* evhtp_hook_event_cb)(evhtp_connection_t * conn, short events, void * arg); /* Generic hook for passing ISO tests */ -typedef evhtp_res (*evhtp_hook)(); - -typedef evhtp_res (*evhtp_hook_conn_err_cb)(evhtp_connection_t * connection, evhtp_error_flags errtype, void * arg); -typedef evhtp_res (*evhtp_pre_accept_cb)(evhtp_connection_t * conn, void * arg); -typedef evhtp_res (*evhtp_post_accept_cb)(evhtp_connection_t * conn, void * arg); -typedef evhtp_res (*evhtp_hook_header_cb)(evhtp_request_t * req, evhtp_header_t * hdr, void * arg); -typedef evhtp_res (*evhtp_hook_headers_cb)(evhtp_request_t * req, evhtp_headers_t * hdr, void * arg); -typedef evhtp_res (*evhtp_hook_path_cb)(evhtp_request_t * req, evhtp_path_t * path, void * arg); -typedef evhtp_res (*evhtp_hook_read_cb)(evhtp_request_t * req, evbuf_t * buf, void * arg); -typedef evhtp_res (*evhtp_hook_request_fini_cb)(evhtp_request_t * req, void * arg); -typedef evhtp_res (*evhtp_hook_connection_fini_cb)(evhtp_connection_t * connection, void * arg); -typedef evhtp_res (*evhtp_hook_chunk_new_cb)(evhtp_request_t * r, uint64_t len, void * arg); -typedef evhtp_res (*evhtp_hook_chunk_fini_cb)(evhtp_request_t * r, void * arg); -typedef evhtp_res (*evhtp_hook_chunks_fini_cb)(evhtp_request_t * r, void * arg); -typedef evhtp_res (*evhtp_hook_headers_start_cb)(evhtp_request_t * r, void * arg); -typedef evhtp_res (*evhtp_hook_hostname_cb)(evhtp_request_t * r, const char * hostname, void * arg); -typedef evhtp_res (*evhtp_hook_write_cb)(evhtp_connection_t * conn, void * arg); - -typedef int (*evhtp_kvs_iterator)(evhtp_kv_t * kv, void * arg); -typedef int (*evhtp_headers_iterator)(evhtp_header_t * header, void * arg); +typedef evhtp_res (* evhtp_hook)(); + +typedef evhtp_res (* evhtp_hook_conn_err_cb)(evhtp_connection_t * connection, evhtp_error_flags errtype, void * arg); +typedef evhtp_res (* evhtp_pre_accept_cb)(evhtp_connection_t * conn, void * arg); +typedef evhtp_res (* evhtp_post_accept_cb)(evhtp_connection_t * conn, void * arg); +typedef evhtp_res (* evhtp_hook_header_cb)(evhtp_request_t * req, evhtp_header_t * hdr, void * arg); +typedef evhtp_res (* evhtp_hook_headers_cb)(evhtp_request_t * req, evhtp_headers_t * hdr, void * arg); +typedef evhtp_res (* evhtp_hook_path_cb)(evhtp_request_t * req, evhtp_path_t * path, void * arg); +typedef evhtp_res (* evhtp_hook_read_cb)(evhtp_request_t * req, evbuf_t * buf, void * arg); +typedef evhtp_res (* evhtp_hook_request_fini_cb)(evhtp_request_t * req, void * arg); +typedef evhtp_res (* evhtp_hook_connection_fini_cb)(evhtp_connection_t * connection, void * arg); +typedef evhtp_res (* evhtp_hook_chunk_new_cb)(evhtp_request_t * r, uint64_t len, void * arg); +typedef evhtp_res (* evhtp_hook_chunk_fini_cb)(evhtp_request_t * r, void * arg); +typedef evhtp_res (* evhtp_hook_chunks_fini_cb)(evhtp_request_t * r, void * arg); +typedef evhtp_res (* evhtp_hook_headers_start_cb)(evhtp_request_t * r, void * arg); +typedef evhtp_res (* evhtp_hook_hostname_cb)(evhtp_request_t * r, const char * hostname, void * arg); +typedef evhtp_res (* evhtp_hook_write_cb)(evhtp_connection_t * conn, void * arg); + +typedef int (* evhtp_kvs_iterator)(evhtp_kv_t * kv, void * arg); +typedef int (* evhtp_headers_iterator)(evhtp_header_t * header, void * arg); #ifndef EVHTP_DISABLE_SSL -typedef int (*evhtp_ssl_verify_cb)(int pre_verify, evhtp_x509_store_ctx_t * ctx); -typedef int (*evhtp_ssl_chk_issued_cb)(evhtp_x509_store_ctx_t * ctx, evhtp_x509_t * x, evhtp_x509_t * issuer); +typedef int (* evhtp_ssl_verify_cb)(int pre_verify, evhtp_x509_store_ctx_t * ctx); +typedef int (* evhtp_ssl_chk_issued_cb)(evhtp_x509_store_ctx_t * ctx, evhtp_x509_t * x, evhtp_x509_t * issuer); -typedef int (*evhtp_ssl_scache_add)(evhtp_connection_t * connection, unsigned char * sid, int sid_len, evhtp_ssl_sess_t * sess); -typedef void (*evhtp_ssl_scache_del)(evhtp_t * htp, unsigned char * sid, int sid_len); -typedef evhtp_ssl_sess_t * (*evhtp_ssl_scache_get)(evhtp_connection_t * connection, unsigned char * sid, int sid_len); -typedef void * (*evhtp_ssl_scache_init)(evhtp_t *); +typedef int (* evhtp_ssl_scache_add)(evhtp_connection_t * connection, unsigned char * sid, int sid_len, evhtp_ssl_sess_t * sess); +typedef void (* evhtp_ssl_scache_del)(evhtp_t * htp, unsigned char * sid, int sid_len); +typedef evhtp_ssl_sess_t * (* evhtp_ssl_scache_get)(evhtp_connection_t * connection, unsigned char * sid, int sid_len); +typedef void * (* evhtp_ssl_scache_init)(evhtp_t *); #endif #define EVHTP_VERSION "1.2.9" @@ -273,6 +273,7 @@ struct evhtp_s { uint64_t max_body_size; uint64_t max_keepalive_requests; int disable_100_cont; /**< if set, evhtp will not respond to Expect: 100-continue */ + int parser_flags; /**< default query flags to alter 'strictness' (see EVHTP_PARSE_QUERY_FLAG_*) */ #ifndef EVHTP_DISABLE_SSL evhtp_ssl_ctx_t * ssl_ctx; /**< if ssl enabled, this is the servers CTX */ @@ -410,10 +411,11 @@ struct evhtp_request_s { evhtp_proto proto; /**< HTTP protocol used */ htp_method method; /**< HTTP method used */ evhtp_res status; /**< The HTTP response code or other error conditions */ - int8_t keepalive : 1; /**< set to 1 if the connection is keep-alive */ - int8_t finished : 1; /**< set to 1 if the request is fully processed */ - int8_t chunked : 1; /**< set to 1 if the request is chunked */ - int8_t error : 1; + uint8_t keepalive : 1, /**< set to 1 if the connection is keep-alive */ + finished : 1, /**< set to 1 if the request is fully processed */ + chunked : 1, /**< set to 1 if the request is chunked */ + error : 1, /**< set if any sort of error has occurred. */ + pad : 4; /**< to be used in evhtp2 for new stuff */ evhtp_callback_cb cb; /**< the function to call when fully processed */ void * cbarg; /**< argument which is passed to the cb function */ @@ -431,29 +433,29 @@ struct evhtp_connection_s { #ifndef EVHTP_DISABLE_SSL evhtp_ssl_t * ssl; #endif - evhtp_hooks_t * hooks; - htparser * parser; - event_t * resume_ev; - struct sockaddr * saddr; - struct timeval recv_timeo; /**< conn read timeouts (overrides global) */ - struct timeval send_timeo; /**< conn write timeouts (overrides global) */ - evutil_socket_t sock; - evhtp_request_t * request; /**< the request currently being processed */ - uint64_t max_body_size; - uint64_t body_bytes_read; - uint64_t num_requests; - evhtp_type type; /**< server or client */ - uint8_t error : 1; - uint8_t owner : 1; /**< set to 1 if this structure owns the bufferevent */ - uint8_t vhost_via_sni : 1; /**< set to 1 if the vhost was found via SSL SNI */ - int8_t paused : 1; - int8_t connected : 1; /**< upstream connection status, for client */ - int8_t wait_4_write : 1; - int8_t free_connection : 1; - uint8_t keepalive : 1; /**< set to 1 after the first request has been processed and the connection is kept open */ - struct ev_token_bucket_cfg * ratelimit_cfg; /**< connection-specific ratelimiting configuration. */ - - TAILQ_HEAD(, evhtp_request_s) pending; /**< client pending data */ + evhtp_hooks_t * hooks; + htparser * parser; + event_t * resume_ev; + struct sockaddr * saddr; + struct timeval recv_timeo; /**< conn read timeouts (overrides global) */ + struct timeval send_timeo; /**< conn write timeouts (overrides global) */ + evutil_socket_t sock; + evhtp_request_t * request; /**< the request currently being processed */ + uint64_t max_body_size; + uint64_t body_bytes_read; + uint64_t num_requests; + evhtp_type type; /**< server or client */ + uint8_t error : 1, + owner : 1, /**< set to 1 if this structure owns the bufferevent */ + vhost_via_sni : 1, /**< set to 1 if the vhost was found via SSL SNI */ + paused : 1, /**< this connection has been marked as paused */ + connected : 1, /**< client specific - set after successful connection */ + waiting : 1, /**< used to make sure resuming happens AFTER sending a reply */ + free_connection : 1, + keepalive : 1; /**< set to 1 after the first request has been processed and the connection is kept open */ + struct ev_token_bucket_cfg * ratelimit_cfg; /**< connection-specific ratelimiting configuration. */ + + TAILQ_HEAD(, evhtp_request_s) pending; /**< client pending data */ }; struct evhtp_hooks_s { @@ -867,6 +869,7 @@ int evhtp_kvs_for_each(evhtp_kvs_t * kvs, evhtp_kvs_iterator cb, void * arg); #define EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS (1 << 1) #define EVHTP_PARSE_QUERY_FLAG_ALLOW_NULL_VALS (1 << 2) #define EVHTP_PARSE_QUERY_FLAG_TREAT_SEMICOLON_AS_SEP (1 << 3) +#define EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS (1 << 4) #define EVHTP_PARSE_QUERY_FLAG_LENIENT \ EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX \ | EVHTP_PARSE_QUERY_FLAG_ALLOW_EMPTY_VALS \ @@ -1135,7 +1138,7 @@ int evhtp_connection_set_ratelimit(evhtp_connection_t * c, size_t read_rate, * @brief allocate a new connection */ evhtp_connection_t * evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, - const char * addr, uint16_t port); + const char * addr, uint16_t port); /** * @brief allocate a new connection From 27b5e8a36d37ba9ba99742419cf3bab31f7e5586 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Wed, 14 Jan 2015 12:13:42 -0500 Subject: [PATCH 48/59] Added evhp_set_flags along with some documentation --- evhtp.c | 5 +++++ evhtp.h | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/evhtp.c b/evhtp.c index 4296c33..d40563f 100644 --- a/evhtp.c +++ b/evhtp.c @@ -3930,6 +3930,11 @@ evhtp_disable_100_continue(evhtp_t * htp) { htp->disable_100_cont = 1; } +void +evhtp_set_parser_flags(evhtp_t * htp, int flags) { + htp->parser_flags = flags; +} + int evhtp_add_alias(evhtp_t * evhtp, const char * name) { evhtp_alias_t * alias; diff --git a/evhtp.h b/evhtp.h index 8c57998..00d9a4b 100644 --- a/evhtp.h +++ b/evhtp.h @@ -541,6 +541,29 @@ void evhtp_free(evhtp_t * evhtp); * @param w write-timeout in timeval. */ void evhtp_set_timeouts(evhtp_t * htp, const struct timeval * r, const struct timeval * w); + + +/** + * @brief during the request processing cycle, these flags will be used to + * for query argument parsing. i.e., what to parse and not to parse. + * + * SEE: EVHTP_PARSE_QUERY_* stuff. + * + * For example, if you do not wish for the streaming parser attempting the act + * of fragment parsing: + * evhtp_set_parser_flags(htp, EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS); + * + * @param htp + * @param flags + */ +void evhtp_set_parser_flags(evhtp_t * htp, int flags); + +/** + * @brief bufferevent flags which will be used for bev sockets. + * + * @param htp + * @param flags + */ void evhtp_set_bev_flags(evhtp_t * htp, int flags); #ifndef EVHTP_DISABLE_SSL From 352aebe244bf95f0778dde412d9158fab2482eee Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Wed, 14 Jan 2015 13:46:47 -0500 Subject: [PATCH 49/59] Symbol exports moved into headers, more documentation --- CMakeLists.txt | 52 ++++++--- build/placeholder | 0 evhtp.c | 90 --------------- evhtp.h | 287 ++++++++++++++++++++++++++++++++-------------- 4 files changed, 240 insertions(+), 189 deletions(-) delete mode 100644 build/placeholder diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d7a7de..51f4043 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,6 @@ check_c_compiler_flag(-fvisibility=hidden EVHTP_HAS_VISIBILITY_HIDDEN) if (EVHTP_HAS_VISIBILITY_HIDDEN) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") - add_definitions(-DEVHTP_HAS_VISIBILITY_HIDDEN) endif() if (NOT HAVE_SYS_TREE) @@ -95,11 +94,10 @@ SET(CMAKE_INCLUDE_CURRENT_DIR ON) include(BaseConfig) -message("Build Type: ${CMAKE_BUILD_TYPE}") -message("Std CFLAGS: ${CMAKE_C_FLAGS}") -message("Dbg CFLAGS: ${CMAKE_C_FLAGS_DEBUG}") -message("Rel CFLAGS: ${CMAKE_C_FLAGS_RELEASE}") -message("RelDbg CFLAGS: ${CMAKE_C_FLAGS_RELWITHDEBINFO}") +message(STATUS "Build Type : ${CMAKE_BUILD_TYPE}") +message(STATUS "std Debug CFLAGS : ${CMAKE_C_FLAGS_DEBUG}") +message(STATUS "Std Release CFLAGS : ${CMAKE_C_FLAGS_RELEASE}") +message(STATUS "Std ReleaseWDebug CFLAGS : ${CMAKE_C_FLAGS_RELWITHDEBINFO}") find_package(LibEvent REQUIRED) @@ -118,12 +116,17 @@ if (NOT EVHTP_DISABLE_REGEX) endif() if (NOT OPENSSL_FOUND) - message("Diabling SSL") - set (EVHTP_DISABLE_SSL ON) - set (OPENSSL_CRYPTO_LIBRARY "") - set (OPENSSL_INCLUDE_DIR "") - set (OPENSSL_LIBRARIES "") + message(WARN"Unable to find OpenSSL, will continue, but without the support") + + set (EVHTP_DISABLE_SSL ON) + set (OPENSSL_CRYPTO_LIBRARY "") + set (OPENSSL_INCLUDE_DIR "") + set (OPENSSL_LIBRARIES "") set (LIBEVENT_OPENSSL_LIBRARY "") +elseif(APPLE) + # darwin based hosts have deprecated normal openssl functions, which is + # annoying to see warnings, for now, just ignore them. + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations") endif() if (NOT EVHTP_DISABLE_REGEX) @@ -234,7 +237,6 @@ ELSE () find_library (LIB_RT rt) set (SYS_LIBS ${SYS_LIBS} ${LIB_RT}) endif() - ENDIF (WIN32) add_custom_target(examples) @@ -307,4 +309,28 @@ configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/evhtp.pc.in ${CMAKE_CURRENT_BINARY_DIR}/evhtp.pc @ONLY) -message("CFLAGS: ${CMAKE_C_FLAGS}") +message(STATUS "CMAKE_BUILD_TYPE : " ${CMAKE_BUILD_TYPE}) +message(STATUS "CMAKE_BINARY_DIR : " ${CMAKE_BINARY_DIR}) +message(STATUS "CMAKE_CURRENT_BINARY_DIR : " ${CMAKE_CURRENT_BINARY_DIR}) +message(STATUS "CMAKE_SOURCE_DIR : " ${CMAKE_SOURCE_DIR}) +message(STATUS "CMAKE_CURRENT_SOURCE_DIR : " ${CMAKE_CURRENT_SOURCE_DIR}) +message(STATUS "PROJECT_BINARY_DIR : " ${PROJECT_BINARY_DIR}) +message(STATUS "PROJECT_SOURCE_DIR : " ${PROJECT_SOURCE_DIR}) +message(STATUS "CMAKE_MODULE_PATH : " ${CMAKE_MODULE_PATH}) +message(STATUS "CMAKE_COMMAND : " ${CMAKE_COMMAND}) +message(STATUS "CMAKE_ROOT : " ${CMAKE_ROOT}) +message(STATUS "CMAKE_SYSTEM : " ${CMAKE_SYSTEM}) +message(STATUS "CMAKE_SYSTEM_NAME : " ${CMAKE_SYSTEM_NAME}) +message(STATUS "CMAKE_SYSTEM_VERSION : " ${CMAKE_SYSTEM_VERSION}) +message(STATUS "CMAKE_SYSTEM_PROCESSOR : " ${CMAKE_SYSTEM_PROCESSOR}) +message(STATUS "CMAKE_C_FLAGS : " ${CMAKE_C_FLAGS}) +message(STATUS "CMAKE_CXX_FLAGS : " ${CMAKE_CXX_FLAGS}) +message(STATUS "CMAKE_C_COMPILER : " ${CMAKE_C_COMPILER}) +message(STATUS "CMAKE_CXX_COMPILER : " ${CMAKE_CXX_COMPILER}) +message(STATUS "CMAKE_AR : " ${CMAKE_AR}) +message(STATUS "CMAKE_RANLIB : " ${CMAKE_RANLIB}) +message(STATUS "CMAKE_C_FLAGS_DEBUG : " ${CMAKE_C_FLAGS_DEBUG}) +message(STATUS "CMAKE_C_FLAGS_RELEASE : " ${CMAKE_C_FLAGS_RELEASE}) +message(STATUS "CMAKE_C_FLAGS_RELWDBGIFO : " ${CMAKE_C_FLAGS_RELWITHDEBINFO}) +message(STATUS "CMAKE_INSTALL_PREFIX : " ${CMAKE_INSTALL_PREFIX}) +message(STATUS "Version : " ${PROJECT_VERSION}) diff --git a/build/placeholder b/build/placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/evhtp.c b/evhtp.c index d40563f..b732a89 100644 --- a/evhtp.c +++ b/evhtp.c @@ -4218,93 +4218,3 @@ unsigned int evhtp_request_status(evhtp_request_t * r) { return htparser_get_status(r->conn->parser); } - -EXPORT_SYMBOL(evhtp_new); -EXPORT_SYMBOL(evhtp_free); -EXPORT_SYMBOL(evhtp_set_timeouts); -EXPORT_SYMBOL(evhtp_set_bev_flags); - -#ifndef EVHTP_DISABLE_SSL -EXPORT_SYMBOL(evhtp_ssl_init); -#endif - -EXPORT_SYMBOL(evhtp_disable_100_continue); -EXPORT_SYMBOL(evhtp_use_callback_locks); -EXPORT_SYMBOL(evhtp_set_gencb); -EXPORT_SYMBOL(evhtp_set_pre_accept_cb); -EXPORT_SYMBOL(evhtp_set_post_accept_cb); -EXPORT_SYMBOL(evhtp_set_cb); - -#ifndef EVHTP_DISABLE_REGEX -EXPORT_SYMBOL(evhtp_set_regex_cb); -#endif - -EXPORT_SYMBOL(evhtp_set_glob_cb); -EXPORT_SYMBOL(evhtp_set_hook); -EXPORT_SYMBOL(evhtp_unset_hook); -EXPORT_SYMBOL(evhtp_unset_all_hooks); -EXPORT_SYMBOL(evhtp_bind_socket); -EXPORT_SYMBOL(evhtp_unbind_socket); -EXPORT_SYMBOL(evhtp_bind_sockaddr); - -#ifndef EVHTP_DISABLE_EVTHR -EXPORT_SYMBOL(evhtp_use_threads); -#ifndef EVHTP_DISABLE_SSL -EXPORT_SYMBOL(evhtp_ssl_use_threads); -#endif -#endif - -EXPORT_SYMBOL(evhtp_send_reply); -EXPORT_SYMBOL(evhtp_send_reply_start); -EXPORT_SYMBOL(evhtp_send_reply_body); -EXPORT_SYMBOL(evhtp_send_reply_end); -EXPORT_SYMBOL(evhtp_response_needs_body); -EXPORT_SYMBOL(evhtp_send_reply_chunk_start); -EXPORT_SYMBOL(evhtp_send_reply_chunk); -EXPORT_SYMBOL(evhtp_send_reply_chunk_end); -EXPORT_SYMBOL(evhtp_callback_new); -EXPORT_SYMBOL(evhtp_callback_free); -EXPORT_SYMBOL(evhtp_callbacks_add_callback); -EXPORT_SYMBOL(evhtp_add_vhost); -EXPORT_SYMBOL(evhtp_add_alias); -EXPORT_SYMBOL(evhtp_kv_new); -EXPORT_SYMBOL(evhtp_kvs_new); -EXPORT_SYMBOL(evhtp_kv_free); -EXPORT_SYMBOL(evhtp_kvs_free); -EXPORT_SYMBOL(evhtp_kv_rm_and_free); -EXPORT_SYMBOL(evhtp_kv_find); -EXPORT_SYMBOL(evhtp_kvs_find_kv); -EXPORT_SYMBOL(evhtp_kvs_add_kv); -EXPORT_SYMBOL(evhtp_kvs_add_kvs); -EXPORT_SYMBOL(evhtp_kvs_for_each); -EXPORT_SYMBOL(evhtp_parse_query); -EXPORT_SYMBOL(evhtp_unescape_string); -EXPORT_SYMBOL(evhtp_header_new); -EXPORT_SYMBOL(evhtp_header_key_add); -EXPORT_SYMBOL(evhtp_header_val_add); -EXPORT_SYMBOL(evhtp_headers_add_header); -EXPORT_SYMBOL(evhtp_header_find); -EXPORT_SYMBOL(evhtp_request_get_method); -EXPORT_SYMBOL(evhtp_connection_pause); -EXPORT_SYMBOL(evhtp_connection_resume); -EXPORT_SYMBOL(evhtp_request_pause); -EXPORT_SYMBOL(evhtp_request_resume); -EXPORT_SYMBOL(evhtp_request_get_connection); -EXPORT_SYMBOL(evhtp_connection_set_bev); -EXPORT_SYMBOL(evhtp_request_set_bev); -EXPORT_SYMBOL(evhtp_connection_get_bev); -EXPORT_SYMBOL(evhtp_connection_set_timeouts); -EXPORT_SYMBOL(evhtp_request_get_bev); -EXPORT_SYMBOL(evhtp_connection_take_ownership); -EXPORT_SYMBOL(evhtp_connection_free); -EXPORT_SYMBOL(evhtp_request_free); -EXPORT_SYMBOL(evhtp_set_max_body_size); -EXPORT_SYMBOL(evhtp_connection_set_max_body_size); -EXPORT_SYMBOL(evhtp_request_set_max_body_size); -EXPORT_SYMBOL(evhtp_set_max_keepalive_requests); -EXPORT_SYMBOL(evhtp_connection_new); -EXPORT_SYMBOL(evhtp_request_new); -EXPORT_SYMBOL(evhtp_make_request); -EXPORT_SYMBOL(evhtp_request_status); -EXPORT_SYMBOL(evhtp_connection_set_ratelimit); -EXPORT_SYMBOL(evhtp_parse_query_wflags); diff --git a/evhtp.h b/evhtp.h index 00d9a4b..8e57464 100644 --- a/evhtp.h +++ b/evhtp.h @@ -2,6 +2,15 @@ #define __EVHTP__H__ #include + +#ifndef EVHTP_EXPORT +# if (defined __GNUC__ && __GNUC__ >= 4) || defined __INTEL_COMPILER || defined __clang__ +# define EVHTP_EXPORT __attribute__ ((visibility("default"))) +# else +# define EVHTP_EXPORT +# endif +#endif + #ifndef EVHTP_DISABLE_EVTHR #include #endif @@ -527,8 +536,8 @@ struct evhtp_ssl_cfg_s { * * @return a new evhtp_t structure or NULL on error */ -evhtp_t * evhtp_new(evbase_t * evbase, void * arg); -void evhtp_free(evhtp_t * evhtp); +EVHTP_EXPORT evhtp_t * evhtp_new(evbase_t * evbase, void * arg); +EVHTP_EXPORT void evhtp_free(evhtp_t * evhtp); /** @@ -540,7 +549,7 @@ void evhtp_free(evhtp_t * evhtp); * @param r read-timeout in timeval * @param w write-timeout in timeval. */ -void evhtp_set_timeouts(evhtp_t * htp, const struct timeval * r, const struct timeval * w); +EVHTP_EXPORT void evhtp_set_timeouts(evhtp_t * htp, const struct timeval * r, const struct timeval * w); /** @@ -556,7 +565,7 @@ void evhtp_set_timeouts(evhtp_t * htp, const struct timeval * r, const struct ti * @param htp * @param flags */ -void evhtp_set_parser_flags(evhtp_t * htp, int flags); +EVHTP_EXPORT void evhtp_set_parser_flags(evhtp_t * htp, int flags); /** * @brief bufferevent flags which will be used for bev sockets. @@ -564,11 +573,11 @@ void evhtp_set_parser_flags(evhtp_t * htp, int flags); * @param htp * @param flags */ -void evhtp_set_bev_flags(evhtp_t * htp, int flags); +EVHTP_EXPORT void evhtp_set_bev_flags(evhtp_t * htp, int flags); #ifndef EVHTP_DISABLE_SSL -int evhtp_ssl_use_threads(void); -int evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * ssl_cfg); +EVHTP_EXPORT int evhtp_ssl_use_threads(void); +EVHTP_EXPORT int evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * ssl_cfg); #endif @@ -578,7 +587,7 @@ int evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * ssl_cfg); * * @param htp */ -void evhtp_disable_100_continue(evhtp_t * htp); +EVHTP_EXPORT void evhtp_disable_100_continue(evhtp_t * htp); /** * @brief creates a lock around callbacks and hooks, allowing for threaded @@ -588,7 +597,7 @@ void evhtp_disable_100_continue(evhtp_t * htp); * * @return 0 on success, -1 on error */ -int evhtp_use_callback_locks(evhtp_t * htp); +EVHTP_EXPORT int evhtp_use_callback_locks(evhtp_t * htp); /** * @brief sets a callback which is called if no other callbacks are matched @@ -597,10 +606,9 @@ int evhtp_use_callback_locks(evhtp_t * htp); * @param cb the function to be executed * @param arg user-defined argument passed to the callback */ -void evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * arg); -void evhtp_set_pre_accept_cb(evhtp_t * htp, evhtp_pre_accept_cb, void * arg); -void evhtp_set_post_accept_cb(evhtp_t * htp, evhtp_post_accept_cb, void * arg); - +EVHTP_EXPORT void evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * arg); +EVHTP_EXPORT void evhtp_set_pre_accept_cb(evhtp_t * htp, evhtp_pre_accept_cb, void * arg); +EVHTP_EXPORT void evhtp_set_post_accept_cb(evhtp_t * htp, evhtp_post_accept_cb, void * arg); /** * @brief sets a callback to be executed on a specific path @@ -612,7 +620,9 @@ void evhtp_set_post_accept_cb(evhtp_t * htp, evhtp_post_accept_cb, void * arg); * * @return evhtp_callback_t * on success, NULL on error. */ -evhtp_callback_t * evhtp_set_cb(evhtp_t * htp, const char * path, evhtp_callback_cb cb, void * arg); +EVHTP_EXPORT evhtp_callback_t * evhtp_set_cb(evhtp_t * htp, const char * path, + evhtp_callback_cb cb, void * arg); + /** @@ -626,7 +636,8 @@ evhtp_callback_t * evhtp_set_cb(evhtp_t * htp, const char * path, evhtp_callback * @return evhtp_callback_t * on success, NULL on error */ #ifndef EVHTP_DISABLE_REGEX -evhtp_callback_t * evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg); +EVHTP_EXPORT evhtp_callback_t * evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, + evhtp_callback_cb cb, void * arg); #endif @@ -643,7 +654,8 @@ evhtp_callback_t * evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp * * @return */ -evhtp_callback_t * evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg); +EVHTP_EXPORT evhtp_callback_t * evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, + evhtp_callback_cb cb, void * arg); /** * @brief sets a callback hook for either a connection or a path/regex . @@ -684,7 +696,8 @@ evhtp_callback_t * evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_ * * @return 0 on success, -1 on error (if hooks is NULL, it is allocated) */ -int evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void * arg); +EVHTP_EXPORT int evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, + evhtp_hook cb, void * arg); /** @@ -695,7 +708,7 @@ int evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, * * @return */ -int evhtp_unset_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type); +EVHTP_EXPORT int evhtp_unset_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type); /** @@ -705,8 +718,7 @@ int evhtp_unset_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type); * * @return */ -int evhtp_unset_all_hooks(evhtp_hooks_t ** hooks); - +EVHTP_EXPORT int evhtp_unset_all_hooks(evhtp_hooks_t ** hooks); /** * @brief bind to a socket, optionally with specific protocol support @@ -723,7 +735,7 @@ int evhtp_unset_all_hooks(evhtp_hooks_t ** hooks); * * @return */ -int evhtp_bind_socket(evhtp_t * htp, const char * addr, uint16_t port, int backlog); +EVHTP_EXPORT int evhtp_bind_socket(evhtp_t * htp, const char * addr, uint16_t port, int backlog); /** @@ -731,25 +743,53 @@ int evhtp_bind_socket(evhtp_t * htp, const char * addr, uint16_t port, int backl * * @param htp */ -void evhtp_unbind_socket(evhtp_t * htp); +EVHTP_EXPORT void evhtp_unbind_socket(evhtp_t * htp); /** * @brief bind to an already allocated sockaddr. * * @param htp - * @param + * @parami s * @param sin_len * @param backlog * * @return */ -int evhtp_bind_sockaddr(evhtp_t * htp, struct sockaddr *, size_t sin_len, int backlog); +EVHTP_EXPORT int evhtp_bind_sockaddr(evhtp_t * htp, struct sockaddr *, + size_t sin_len, int backlog); -int evhtp_use_threads(evhtp_t * htp, evhtp_thread_init_cb init_cb, int nthreads, void * arg); -void evhtp_send_reply(evhtp_request_t * request, evhtp_res code); -void evhtp_send_reply_start(evhtp_request_t * request, evhtp_res code); -void evhtp_send_reply_body(evhtp_request_t * request, evbuf_t * buf); -void evhtp_send_reply_end(evhtp_request_t * request); + +/** + * @brief Enable thread-pool support for an evhtp_t context. Connectios are + * distributed across 'nthreads'. An optional "on-start" callback can + * be set which allows you to manipulate the thread-specific inforation + * (such as the thread-specific event_base). + * + * @param htp + * @param init_cb + * @param nthreads + * @param arg + * + * @return + */ +EVHTP_EXPORT int evhtp_use_threads(evhtp_t * htp, evhtp_thread_init_cb init_cb, int nthreads, void * arg); + + +/** + * @brief generates all the right information for a reply to be sent to the client + * + * @param request + * @param code HTTP return status code + */ +EVHTP_EXPORT void evhtp_send_reply(evhtp_request_t * request, evhtp_res code); + + +/* The following three functions allow for the user to do what evhtp_send_reply does at its core + * but for the weak of heart. + */ +EVHTP_EXPORT void evhtp_send_reply_start(evhtp_request_t * request, evhtp_res code); +EVHTP_EXPORT void evhtp_send_reply_body(evhtp_request_t * request, evbuf_t * buf); +EVHTP_EXPORT void evhtp_send_reply_end(evhtp_request_t * request); /** * @brief Determine if a response should have a body. @@ -757,8 +797,7 @@ void evhtp_send_reply_end(evhtp_request_t * request); * @return 1 if the response MUST have a body; 0 if the response MUST NOT have * a body. */ -int evhtp_response_needs_body(const evhtp_res code, const htp_method method); - +EVHTP_EXPORT int evhtp_response_needs_body(const evhtp_res code, const htp_method method); /** * @brief start a chunked response. If data already exists on the output buffer, @@ -767,7 +806,7 @@ int evhtp_response_needs_body(const evhtp_res code, const htp_method method); * @param request * @param code */ -void evhtp_send_reply_chunk_start(evhtp_request_t * request, evhtp_res code); +EVHTP_EXPORT void evhtp_send_reply_chunk_start(evhtp_request_t * request, evhtp_res code); /** @@ -776,8 +815,7 @@ void evhtp_send_reply_chunk_start(evhtp_request_t * request, evhtp_res code); * @param request * @param buf */ -void evhtp_send_reply_chunk(evhtp_request_t * request, evbuf_t * buf); - +EVHTP_EXPORT void evhtp_send_reply_chunk(evhtp_request_t * request, evbuf_t * buf); /** * @brief call when all chunks have been sent and you wish to send the last @@ -785,7 +823,7 @@ void evhtp_send_reply_chunk(evhtp_request_t * request, evbuf_t * buf); * * @param request */ -void evhtp_send_reply_chunk_end(evhtp_request_t * request); +EVHTP_EXPORT void evhtp_send_reply_chunk_end(evhtp_request_t * request); /** * @brief creates a new evhtp_callback_t structure. @@ -806,8 +844,16 @@ void evhtp_send_reply_chunk_end(evhtp_request_t * request); * * @return 0 on success, -1 on error. */ -evhtp_callback_t * evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_cb cb, void * arg); -void evhtp_callback_free(evhtp_callback_t * callback); +EVHTP_EXPORT evhtp_callback_t * +evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_cb cb, void * arg); + + +/** + * @brief frees information associated with a ainflwx callback. + * + * @param callback + */ +EVHTP_EXPORT void evhtp_callback_free(evhtp_callback_t * callback); /** @@ -818,7 +864,7 @@ void evhtp_callback_free(evhtp_callback_t * callback); * * @return 0 on success, -1 on error */ -int evhtp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb); +EVHTP_EXPORT int evhtp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb); /** @@ -833,7 +879,7 @@ int evhtp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb) * * @return */ -int evhtp_add_vhost(evhtp_t * evhtp, const char * name, evhtp_t * vhost); +EVHTP_EXPORT int evhtp_add_vhost(evhtp_t * evhtp, const char * name, evhtp_t * vhost); /** @@ -846,7 +892,7 @@ int evhtp_add_vhost(evhtp_t * evhtp, const char * name, evhtp_t * vhost); * * @return */ -int evhtp_add_alias(evhtp_t * evhtp, const char * name); +EVHTP_EXPORT int evhtp_add_alias(evhtp_t * evhtp, const char * name); /** * @brief Allocates a new key/value structure. @@ -858,15 +904,62 @@ int evhtp_add_alias(evhtp_t * evhtp, const char * name); * * @return evhtp_kv_t * on success, NULL on error. */ -evhtp_kv_t * evhtp_kv_new(const char * key, const char * val, char kalloc, char valloc); -evhtp_kvs_t * evhtp_kvs_new(void); +EVHTP_EXPORT evhtp_kv_t * evhtp_kv_new(const char * key, const char * val, char kalloc, char valloc); -void evhtp_kv_free(evhtp_kv_t * kv); -void evhtp_kvs_free(evhtp_kvs_t * kvs); -void evhtp_kv_rm_and_free(evhtp_kvs_t * kvs, evhtp_kv_t * kv); -const char * evhtp_kv_find(evhtp_kvs_t * kvs, const char * key); -evhtp_kv_t * evhtp_kvs_find_kv(evhtp_kvs_t * kvs, const char * key); +/** + * @brief creates an empty list of key/values + * + * @return + */ +EVHTP_EXPORT evhtp_kvs_t * evhtp_kvs_new(void); + + +/** + * @brief frees resources allocated for a single key/value + * + * @param kv + */ +EVHTP_EXPORT void evhtp_kv_free(evhtp_kv_t * kv); + + +/** + * @brief frees a the list of key/values, and all underlying entries + * + * @param kvs + */ +EVHTP_EXPORT void evhtp_kvs_free(evhtp_kvs_t * kvs); + + +/** + * @brief free's resources associated with 'kv' if ONLY found within the key/value list + * + * @param kvs + * @param kv + */ +EVHTP_EXPORT void evhtp_kv_rm_and_free(evhtp_kvs_t * kvs, evhtp_kv_t * kv); + + +/** + * @brief find the string value of 'key' from the key/value list 'kvs' + * + * @param kvs + * @param key + * + * @return NULL if not found + */ +EVHTP_EXPORT const char * evhtp_kv_find(evhtp_kvs_t * kvs, const char * key); + + +/** + * @brief find the evhtp_kv_t reference 'key' from the k/val list 'kvs' + * + * @param kvs + * @param key + * + * @return + */ +EVHTP_EXPORT evhtp_kv_t * evhtp_kvs_find_kv(evhtp_kvs_t * kvs, const char * key); /** @@ -875,7 +968,7 @@ evhtp_kv_t * evhtp_kvs_find_kv(evhtp_kvs_t * kvs, const char * key); * @param kvs an evhtp_kvs_t structure * @param kv an evhtp_kv_t structure */ -void evhtp_kvs_add_kv(evhtp_kvs_t * kvs, evhtp_kv_t * kv); +EVHTP_EXPORT void evhtp_kvs_add_kv(evhtp_kvs_t * kvs, evhtp_kv_t * kv); /** * @brief appends all key/val structures from src tailq onto dst tailq @@ -883,9 +976,19 @@ void evhtp_kvs_add_kv(evhtp_kvs_t * kvs, evhtp_kv_t * kv); * @param dst an evhtp_kvs_t structure * @param src an evhtp_kvs_t structure */ -void evhtp_kvs_add_kvs(evhtp_kvs_t * dst, evhtp_kvs_t * src); +EVHTP_EXPORT void evhtp_kvs_add_kvs(evhtp_kvs_t * dst, evhtp_kvs_t * src); -int evhtp_kvs_for_each(evhtp_kvs_t * kvs, evhtp_kvs_iterator cb, void * arg); + +/** + * @brief callback iterator which executes 'cb' for every entry in 'kvs' + * + * @param kvs + * @param cb + * @param arg + * + * @return + */ +EVHTP_EXPORT int evhtp_kvs_for_each(evhtp_kvs_t * kvs, evhtp_kvs_iterator cb, void * arg); #define EVHTP_PARSE_QUERY_FLAG_STRICT 0 #define EVHTP_PARSE_QUERY_FLAG_IGNORE_HEX (1 << 0) @@ -910,7 +1013,7 @@ int evhtp_kvs_for_each(evhtp_kvs_t * kvs, evhtp_kvs_iterator cb, void * arg); * * @return evhtp_query_t * on success, NULL on error */ -evhtp_query_t * evhtp_parse_query_wflags(const char * query, size_t len, int flags); +EVHTP_EXPORT evhtp_query_t * evhtp_parse_query_wflags(const char * query, size_t len, int flags); /** * @brief Parses the query portion of the uri into a set of key/values in a @@ -923,7 +1026,7 @@ evhtp_query_t * evhtp_parse_query_wflags(const char * query, size_t len, int fla * * @return evhtp_query_t * on success, NULL on error */ -evhtp_query_t * evhtp_parse_query(const char * query, size_t len); +EVHTP_EXPORT evhtp_query_t * evhtp_parse_query(const char * query, size_t len); /** * @brief Unescapes strings like '%7B1,%202,%203%7D' would become '{1, 2, 3}' @@ -934,7 +1037,7 @@ evhtp_query_t * evhtp_parse_query(const char * query, size_t len); * * @return 0 on success, -1 on error */ -int evhtp_unescape_string(unsigned char ** out, unsigned char * str, size_t str_len); +EVHTP_EXPORT int evhtp_unescape_string(unsigned char ** out, unsigned char * str, size_t str_len); /** * @brief creates a new evhtp_header_t key/val structure @@ -946,7 +1049,8 @@ int evhtp_unescape_string(unsigned char ** out, unsigned char * str, size_t str_ * * @return evhtp_header_t * or NULL on error */ -evhtp_header_t * evhtp_header_new(const char * key, const char * val, char kalloc, char valloc); +EVHTP_EXPORT evhtp_header_t * evhtp_header_new(const char * key, const char * val, + char kalloc, char valloc); /** * @brief creates a new evhtp_header_t, sets only the key, and adds to the @@ -958,7 +1062,8 @@ evhtp_header_t * evhtp_header_new(const char * key, const char * val, char kallo * * @return an evhtp_header_t pointer or NULL on error */ -evhtp_header_t * evhtp_header_key_add(evhtp_headers_t * headers, const char * key, char kalloc); +EVHTP_EXPORT evhtp_header_t * evhtp_header_key_add(evhtp_headers_t * headers, + const char * key, char kalloc); /** @@ -970,7 +1075,8 @@ evhtp_header_t * evhtp_header_key_add(evhtp_headers_t * headers, const char * ke * * @return an evhtp_header_t pointer or NULL on error */ -evhtp_header_t * evhtp_header_val_add(evhtp_headers_t * headers, const char * val, char valloc); +EVHTP_EXPORT evhtp_header_t * evhtp_header_val_add(evhtp_headers_t * headers, + const char * val, char valloc); /** @@ -979,7 +1085,7 @@ evhtp_header_t * evhtp_header_val_add(evhtp_headers_t * headers, const char * va * @param headers * @param header */ -void evhtp_headers_add_header(evhtp_headers_t * headers, evhtp_header_t * header); +EVHTP_EXPORT void evhtp_headers_add_header(evhtp_headers_t * headers, evhtp_header_t * header); /** * @brief finds the value of a key in a evhtp_headers_t structure @@ -989,7 +1095,7 @@ void evhtp_headers_add_header(evhtp_headers_t * headers, evhtp_header_t * header * * @return the value of the header key if found, NULL if not found. */ -const char * evhtp_header_find(evhtp_headers_t * headers, const char * key); +EVHTP_EXPORT const char * evhtp_header_find(evhtp_headers_t * headers, const char * key); #define evhtp_header_find evhtp_kv_find #define evhtp_headers_find_header evhtp_kvs_find_kv @@ -1012,12 +1118,13 @@ const char * evhtp_header_find(evhtp_headers_t * headers, const char * key); * * @return htp_method enum */ -htp_method evhtp_request_get_method(evhtp_request_t * r); +EVHTP_EXPORT htp_method evhtp_request_get_method(evhtp_request_t * r); -void evhtp_connection_pause(evhtp_connection_t * connection); -void evhtp_connection_resume(evhtp_connection_t * connection); -void evhtp_request_pause(evhtp_request_t * request); -void evhtp_request_resume(evhtp_request_t * request); +/* the following functions all do the same thing, pause and the processing */ +EVHTP_EXPORT void evhtp_connection_pause(evhtp_connection_t * connection); +EVHTP_EXPORT void evhtp_connection_resume(evhtp_connection_t * connection); +EVHTP_EXPORT void evhtp_request_pause(evhtp_request_t * request); +EVHTP_EXPORT void evhtp_request_resume(evhtp_request_t * request); /** @@ -1027,7 +1134,7 @@ void evhtp_request_resume(evhtp_request_t * request); * * @return evhtp_connection_t on success, otherwise NULL */ -evhtp_connection_t * evhtp_request_get_connection(evhtp_request_t * request); +EVHTP_EXPORT evhtp_connection_t * evhtp_request_get_connection(evhtp_request_t * request); /** * @brief Sets the connections underlying bufferevent @@ -1035,7 +1142,7 @@ evhtp_connection_t * evhtp_request_get_connection(evhtp_request_t * request); * @param conn * @param bev */ -void evhtp_connection_set_bev(evhtp_connection_t * conn, evbev_t * bev); +EVHTP_EXPORT void evhtp_connection_set_bev(evhtp_connection_t * conn, evbev_t * bev); /** * @brief sets the underlying bufferevent for a evhtp_request @@ -1043,7 +1150,7 @@ void evhtp_connection_set_bev(evhtp_connection_t * conn, evbev_t * bev); * @param request * @param bev */ -void evhtp_request_set_bev(evhtp_request_t * request, evbev_t * bev); +EVHTP_EXPORT void evhtp_request_set_bev(evhtp_request_t * request, evbev_t * bev); /** @@ -1053,8 +1160,7 @@ void evhtp_request_set_bev(evhtp_request_t * request, evbev_t * bev); * * @return bufferevent on success, otherwise NULL */ -evbev_t * evhtp_connection_get_bev(evhtp_connection_t * conn); - +EVHTP_EXPORT evbev_t * evhtp_connection_get_bev(evhtp_connection_t * conn); /** * @brief sets a connection-specific read/write timeout which overrides the @@ -1064,7 +1170,10 @@ evbev_t * evhtp_connection_get_bev(evhtp_connection_t * conn); * @param r timeval for read * @param w timeval for write */ -void evhtp_connection_set_timeouts(evhtp_connection_t * conn, const struct timeval * r, const struct timeval * w); +EVHTP_EXPORT void +evhtp_connection_set_timeouts(evhtp_connection_t * conn, + const struct timeval * r, + const struct timeval * w); /** * @brief returns the underlying requests bufferevent @@ -1073,7 +1182,7 @@ void evhtp_connection_set_timeouts(evhtp_connection_t * conn, const struct timev * * @return bufferevent on success, otherwise NULL */ -evbev_t * evhtp_request_get_bev(evhtp_request_t * request); +EVHTP_EXPORT evbev_t * evhtp_request_get_bev(evhtp_request_t * request); /** @@ -1088,7 +1197,7 @@ evbev_t * evhtp_request_get_bev(evhtp_request_t * request); * * @return underlying connections bufferevent. */ -evbev_t * evhtp_connection_take_ownership(evhtp_connection_t * connection); +EVHTP_EXPORT evbev_t * evhtp_connection_take_ownership(evhtp_connection_t * connection); /** @@ -1097,9 +1206,8 @@ evbev_t * evhtp_connection_take_ownership(evhtp_connection_t * connection); * * @param connection */ -void evhtp_connection_free(evhtp_connection_t * connection); - -void evhtp_request_free(evhtp_request_t * request); +EVHTP_EXPORT void evhtp_connection_free(evhtp_connection_t * connection); +EVHTP_EXPORT void evhtp_request_free(evhtp_request_t * request); /** * @brief set a max body size to accept for an incoming request, this will @@ -1108,8 +1216,7 @@ void evhtp_request_free(evhtp_request_t * request); * @param htp * @param len */ -void evhtp_set_max_body_size(evhtp_t * htp, uint64_t len); - +EVHTP_EXPORT void evhtp_set_max_body_size(evhtp_t * htp, uint64_t len); /** * @brief set a max body size for a specific connection, this will default to @@ -1118,7 +1225,7 @@ void evhtp_set_max_body_size(evhtp_t * htp, uint64_t len); * @param conn * @param len */ -void evhtp_connection_set_max_body_size(evhtp_connection_t * conn, uint64_t len); +EVHTP_EXPORT void evhtp_connection_set_max_body_size(evhtp_connection_t * conn, uint64_t len); /** * @brief just calls evhtp_connection_set_max_body_size for the request. @@ -1126,7 +1233,7 @@ void evhtp_connection_set_max_body_size(evhtp_connection_t * conn, uint64_t len) * @param request * @param len */ -void evhtp_request_set_max_body_size(evhtp_request_t * request, uint64_t len); +EVHTP_EXPORT void evhtp_request_set_max_body_size(evhtp_request_t * request, uint64_t len); /** * @brief sets a maximum number of requests that a single connection can make. @@ -1134,7 +1241,7 @@ void evhtp_request_set_max_body_size(evhtp_request_t * request, uint64_t len); * @param htp * @param num */ -void evhtp_set_max_keepalive_requests(evhtp_t * htp, uint64_t num); +EVHTP_EXPORT void evhtp_set_max_keepalive_requests(evhtp_t * htp, uint64_t num); /** @@ -1150,8 +1257,12 @@ void evhtp_set_max_keepalive_requests(evhtp_t * htp, uint64_t num); * * @return */ -int evhtp_connection_set_ratelimit(evhtp_connection_t * c, size_t read_rate, - size_t read_burst, size_t write_rate, size_t write_burst, const struct timeval * tick); +EVHTP_EXPORT int +evhtp_connection_set_ratelimit(evhtp_connection_t * c, + size_t read_rate, size_t read_burst, + size_t write_rate, + size_t write_burst, + const struct timeval * tick); /***************************************************************** * client request functions * @@ -1160,25 +1271,29 @@ int evhtp_connection_set_ratelimit(evhtp_connection_t * c, size_t read_rate, /** * @brief allocate a new connection */ -evhtp_connection_t * evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, +EVHTP_EXPORT evhtp_connection_t * +evhtp_connection_new_dns(evbase_t * evbase, + struct evdns_base * dns_base, const char * addr, uint16_t port); /** * @brief allocate a new connection */ -evhtp_connection_t * evhtp_connection_new(evbase_t * evbase, const char * addr, uint16_t port); +EVHTP_EXPORT evhtp_connection_t * +evhtp_connection_new(evbase_t * evbase, const char * addr, uint16_t port); /** * @brief allocate a new request */ -evhtp_request_t * evhtp_request_new(evhtp_callback_cb cb, void * arg); +EVHTP_EXPORT evhtp_request_t * evhtp_request_new(evhtp_callback_cb cb, void * arg); /** * @brief make a client request */ -int evhtp_make_request(evhtp_connection_t * c, evhtp_request_t * r, htp_method meth, const char * uri); +EVHTP_EXPORT int evhtp_make_request(evhtp_connection_t * c, + evhtp_request_t * r, htp_method meth, const char * uri); -unsigned int evhtp_request_status(evhtp_request_t *); +EVHTP_EXPORT unsigned int evhtp_request_status(evhtp_request_t *); #ifdef __cplusplus } From 8a44e6feed4108af46207f83e630639ba18743b6 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Mon, 19 Jan 2015 20:52:45 -0500 Subject: [PATCH 50/59] If available, use c99 to our advantage (read commit msg) - if the compiler supports c99, functions which rely on temporary allocations based o the input size, use 'char whatever[size]' instead of malloc. - is_http_XX are now macros. - cleanup function recently added with lots of goto'so --- CMakeLists.txt | 5 ++ evhtp.c | 166 +++++++++++++++++++++++++------------------------ 2 files changed, 89 insertions(+), 82 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 51f4043..2540e79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,11 @@ CHECK_TYPE_SIZE("short" SIZEOF_SHORT) TEST_BIG_ENDIAN(HOST_BIG_ENDIAN) check_c_compiler_flag(-fvisibility=hidden EVHTP_HAS_VISIBILITY_HIDDEN) +check_c_compiler_flag(-std=c99 EVHTP_HAS_C99) + +if (EVHTP_HAS_C99) + add_definitions(-DEVHTP_HAS_C99) +endif() if (EVHTP_HAS_VISIBILITY_HIDDEN) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden") diff --git a/evhtp.c b/evhtp.c index 91b3ea6..feaaab5 100644 --- a/evhtp.c +++ b/evhtp.c @@ -55,6 +55,8 @@ static void _evhtp_authority_free(evhtp_authority_t * authority) static evhtp_path_t * _evhtp_path_new(const char * data, size_t len); static void _evhtp_path_free(evhtp_path_t * path); +static void _evhtp_request_free(evhtp_request_t *); + #define HOOK_AVAIL(var, hook_name) (var->hooks && var->hooks->hook_name) #define HOOK_FUNC(var, hook_name) (var->hooks->hook_name) #define HOOK_ARGS(var, hook_name) var->hooks->hook_name ## _arg @@ -289,7 +291,7 @@ strndup(const char * s, size_t n) { ret = malloc(n + 1); ret[n] = '\0'; - strncpy(ret, s, n); + memcpy(ret, s, n); return ret; } @@ -319,21 +321,17 @@ _evhtp_quick_hash(const char * str) { } /** - * @brief helper function to determine if http version is HTTP/1.0 + * + * @brief helper macro to determine if http version is HTTP/1.0 * * @param major the major version number * @param minor the minor version number * * @return 1 if HTTP/1.0, else 0 */ -static inline int -_evhtp_is_http_10(const char major, const char minor) { - if (major >= 1 && minor <= 0) { - return 1; - } - return 0; -} +#define _evhtp_is_http_11(_major, _minor) \ + (_major >= 1 && _minor >= 1) /** * @brief helper function to determine if http version is HTTP/1.1 @@ -343,14 +341,9 @@ _evhtp_is_http_10(const char major, const char minor) { * * @return 1 if HTTP/1.1, else 0 */ -static inline int -_evhtp_is_http_11(const char major, const char minor) { - if (major >= 1 && minor >= 1) { - return 1; - } +#define _evhtp_is_http_10(_major, _minor) \ + (_major >= 1 && _minor <= 0) - return 0; -} /** * @brief returns the HTTP protocol version @@ -678,45 +671,47 @@ _evhtp_callback_find(evhtp_callbacks_t * cbs, static evhtp_request_t * _evhtp_request_new(evhtp_connection_t * c) { evhtp_request_t * req; + uint8_t error; if (!(req = calloc(sizeof(evhtp_request_t), 1))) { return NULL; } - req->conn = c; - req->htp = c ? c->htp : NULL; - req->status = EVHTP_RES_OK; - req->buffer_in = evbuffer_new(); - if (!req->buffer_in) { - goto out_free_req; - } - req->buffer_out = evbuffer_new(); - if (!req->buffer_out) { - goto out_free_buf_in; - } - req->headers_in = malloc(sizeof(evhtp_headers_t)); - if (!req->headers_in) { - goto out_free_buf_out; - } - req->headers_out = malloc(sizeof(evhtp_headers_t)); - if (!req->headers_out) { - goto out_free_hdr_in; - } + error = 1; + req->conn = c; + req->htp = c ? c->htp : NULL; + req->status = EVHTP_RES_OK; - TAILQ_INIT(req->headers_in); - TAILQ_INIT(req->headers_out); + do { + req->buffer_in = evbuffer_new(); - return req; + if (!(req->buffer_in = evbuffer_new())) { + break; + } + + if (!(req->buffer_out = evbuffer_new())) { + break; + } + + if (!(req->headers_in = malloc(sizeof(evhtp_headers_t)))) { + break; + } + + if (!(req->headers_out = malloc(sizeof(evhtp_headers_t)))) { + break; + } + + TAILQ_INIT(req->headers_in); + TAILQ_INIT(req->headers_out); + + error = 0; + } while (0); - /* Error path. */ -out_free_hdr_in: - free(req->headers_in); -out_free_buf_out: - evbuffer_free(req->buffer_out); -out_free_buf_in: - evbuffer_free(req->buffer_in); -out_free_req: - free(req); + if (error == 0) { + return req; + } + + _evhtp_request_free(req); return NULL; } /* _evhtp_request_new */ @@ -768,6 +763,7 @@ _evhtp_uri_new(void) { } uri->authority = _evhtp_authority_new(); + if (!uri->authority) { _evhtp_uri_free(uri); @@ -788,15 +784,10 @@ _evhtp_authority_free(evhtp_authority_t * authority) { return; } - if (authority->username) { - free(authority->username); - } - if (authority->password) { - free(authority->password); - } - if (authority->hostname) { - free(authority->hostname); - } + free(authority->username); + free(authority->password); + free(authority->hostname); + free(authority); } @@ -828,6 +819,7 @@ _evhtp_uri_free(evhtp_uri_t * uri) { } evhtp_query_free(uri->query); + _evhtp_path_free(uri->path); _evhtp_authority_free(uri->authority); @@ -895,7 +887,6 @@ _evhtp_path_new(const char * data, size_t len) { /* check for overflow */ if ((const char *)(data + path_len) > data_end) { - fprintf(stderr, "PATH Corrupted.. (path_len > len)\n"); free(req_path); return NULL; @@ -903,7 +894,6 @@ _evhtp_path_new(const char * data, size_t len) { /* check for overflow */ if ((const char *)(&data[i + 1] + file_len) > data_end) { - fprintf(stderr, "FILE Corrupted.. (file_len > len)\n"); free(req_path); return NULL; @@ -1069,7 +1059,7 @@ _evhtp_request_parser_headers_start(htparser * p) { static int _evhtp_request_parser_header_key(htparser * p, const char * data, size_t len) { evhtp_connection_t * c = htparser_get_userdata(p); - char * key_s; /* = strndup(data, len); */ + char * key_s; evhtp_header_t * hdr; key_s = malloc(len + 1); @@ -1288,7 +1278,7 @@ _evhtp_request_parser_hostname(htparser * p, const char * data, size_t len) { static int _evhtp_require_uri(evhtp_connection_t * c) { - if (!c->request->uri) { + if (c && c->request && !c->request->uri) { if (!(c->request->uri = _evhtp_uri_new())) { c->request->status = EVHTP_RES_FATAL; @@ -1307,9 +1297,10 @@ _evhtp_request_parser_host(htparser * p, const char * data, size_t len) { if (_evhtp_require_uri(c) != 0) { return -1; } - authority = c->request->uri->authority; + authority = c->request->uri->authority; authority->hostname = strndup(data, len); + if (!authority->hostname) { c->request->status = EVHTP_RES_FATAL; @@ -1329,14 +1320,16 @@ _evhtp_request_parser_port(htparser * p, const char * data, size_t len) { if (_evhtp_require_uri(c) != 0) { return -1; } - authority = c->request->uri->authority; + authority = c->request->uri->authority; port = strtoul(data, &endptr, 10); + if (endptr - data != len || port > 65535) { c->request->status = EVHTP_RES_FATAL; return -1; } + authority->port = port; return 0; @@ -1359,7 +1352,6 @@ _evhtp_request_parser_path(htparser * p, const char * data, size_t len) { c->request->uri->path = path; c->request->uri->scheme = htparser_get_scheme(p); - c->request->method = htparser_get_method(p); _evhtp_lock(c->htp); @@ -2028,6 +2020,7 @@ _evhtp_connection_new(evhtp_t * htp, evutil_socket_t sock, evhtp_type type) { connection->htp = htp; connection->type = type; connection->parser = htparser_new(); + if (!connection->parser) { free(connection); @@ -2672,8 +2665,6 @@ evhtp_query_t * evhtp_parse_query_wflags(const char * query, size_t len, int flags) { evhtp_query_t * query_args; query_parser_state state; - char * key_buf; - char * val_buf; size_t key_idx; size_t val_idx; unsigned char ch; @@ -2686,6 +2677,17 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { query_args = evhtp_query_new(); + state = s_query_start; + key_idx = 0; + val_idx = 0; + +#ifdef EVHTP_HAS_C99 + char key_buf[len + 1]; + char val_buf[len + 1]; +#else + char * key_buf; + char * val_buf; + if (!(key_buf = malloc(len + 1))) { return NULL; } @@ -2695,10 +2697,7 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { return NULL; } - - state = s_query_start; - key_idx = 0; - val_idx = 0; +#endif for (i = 0; i < len; i++) { ch = query[i]; @@ -2895,13 +2894,17 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { } while (0); } +#ifndef EVHTP_HAS_C99 free(key_buf); free(val_buf); +#endif return query_args; error: +#ifndef EVHTP_HAS_C99 free(key_buf); free(val_buf); +#endif return NULL; } /* evhtp_parse_query */ @@ -2940,11 +2943,6 @@ evhtp_send_reply_body(evhtp_request_t * request, evbuf_t * buf) { void evhtp_send_reply_end(evhtp_request_t * request) { request->finished = 1; - -#if 0 - _evhtp_connection_writecb(evhtp_request_get_bev(request), - evhtp_request_get_connection(request)); -#endif } void @@ -3051,14 +3049,18 @@ evhtp_send_reply_chunk(evhtp_request_t * request, evbuf_t * buf) { if (evbuffer_get_length(buf) == 0) { return; } + if (request->chunked == 1) { evbuffer_add_printf(output, "%x\r\n", (unsigned)evbuffer_get_length(buf)); } + evhtp_send_reply_body(request, buf); + if (request->chunked) { evbuffer_add(output, "\r\n", 2); } + bufferevent_flush(request->conn->bev, EV_WRITE, BEV_FLUSH); } @@ -3122,14 +3124,13 @@ evhtp_bind_sockaddr(evhtp_t * htp, struct sockaddr * sa, size_t sin_len, int bac int evhtp_bind_socket(evhtp_t * htp, const char * baddr, uint16_t port, int backlog) { - struct sockaddr_in sin; - struct sockaddr_in6 sin6; - #ifndef NO_SYS_UN - struct sockaddr_un sun; + struct sockaddr_un sun; #endif - struct sockaddr * sa; - size_t sin_len; + struct sockaddr_in6 sin6; + struct sockaddr_in sin; + struct sockaddr * sa; + size_t sin_len; memset(&sin, 0, sizeof(sin)); @@ -3160,7 +3161,6 @@ evhtp_bind_socket(evhtp_t * htp, const char * baddr, uint16_t port, int backlog) sa = (struct sockaddr *)&sun; #else - fprintf(stderr, "System does not support AF_UNIX sockets\n"); return -1; #endif @@ -4174,7 +4174,7 @@ evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, #ifndef EVHTP_DISABLE_SSL evhtp_connection_t * -evhtp_connection_ssl_new(evbase_t * evbase, const char * addr, uint16_t port, evhtp_ssl_ctx_t* ctx) { +evhtp_connection_ssl_new(evbase_t * evbase, const char * addr, uint16_t port, evhtp_ssl_ctx_t * ctx) { evhtp_connection_t * conn; struct sockaddr_in sin; @@ -4205,6 +4205,7 @@ evhtp_connection_ssl_new(evbase_t * evbase, const char * addr, uint16_t port, ev return conn; } + #endif @@ -4256,3 +4257,4 @@ unsigned int evhtp_request_status(evhtp_request_t * r) { return htparser_get_status(r->conn->parser); } + From 5284ce364ce9c4c8044e39ae3cb992e231bb4a87 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Tue, 20 Jan 2015 16:27:51 -0500 Subject: [PATCH 51/59] Remove duplicate evbuffer_new for buffer_in --- evhtp.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/evhtp.c b/evhtp.c index feaaab5..e12d971 100644 --- a/evhtp.c +++ b/evhtp.c @@ -683,8 +683,6 @@ _evhtp_request_new(evhtp_connection_t * c) { req->status = EVHTP_RES_OK; do { - req->buffer_in = evbuffer_new(); - if (!(req->buffer_in = evbuffer_new())) { break; } From 5267ed209372e563e90ba1fc7567787782cdc0d8 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Tue, 20 Jan 2015 19:54:07 -0500 Subject: [PATCH 52/59] Fix for client connect double-free condition - if bufferevent_socket_connect is called, and the return of connect() errno is not retryable, do not call the evhtp_connection_free() function. In this specific case the eventcb is executed prior, which also calls connection_free on error, so that is bad. --- evhtp.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 15 deletions(-) diff --git a/evhtp.c b/evhtp.c index e12d971..6f08700 100644 --- a/evhtp.c +++ b/evhtp.c @@ -540,6 +540,62 @@ _evhtp_connection_write_hook(evhtp_connection_t * connection) { return EVHTP_RES_OK; } +static int +_evhtp_glob_match2(const char * pattern, size_t plen, + const char * string, size_t str_len) { + while (plen) { + switch (pattern[0]) { + case '*': + while (pattern[1] == '*') { + pattern++; + plen--; + } + + if (plen == 1) { + return 1; /* match */ + } + + while (str_len) { + if (_evhtp_glob_match2(pattern + 1, plen - 1, + string, str_len)) { + return 1; /* match */ + } + + string++; + str_len--; + } + + return 0; /* no match */ + default: + if (pattern[0] != string[0]) { + return 0; /* no match */ + } + + string++; + str_len--; + break; + } /* switch */ + + pattern++; + plen--; + + if (str_len == 0) { + while (*pattern == '*') { + pattern++; + plen--; + } + + break; + } + } + + if (plen == 0 && str_len == 0) { + return 1; + } + + return 0; +} /* _evhtp_glob_match2 */ + /** * @brief glob/wildcard type pattern matching. * @@ -551,16 +607,23 @@ _evhtp_connection_write_hook(evhtp_connection_t * connection) { * @return */ static int -_evhtp_glob_match(const char * pattern, const char * string) { - size_t pat_len; - size_t str_len; - +_evhtp_glob_match(const char * pattern, size_t pat_len, const char * string, size_t str_len) { if (!pattern || !string) { return 0; } - pat_len = strlen(pattern); - str_len = strlen(string); + if (pat_len == 0) { + pat_len = strlen(pattern); + } + + if (str_len == 0) { + str_len = strlen(string); + } + + /* XXX still in testing */ +#if 0 + return _evhtp_glob_match2(pattern, pat_len, string, str_len); +#endif while (pat_len) { if (pattern[0] == '*') { @@ -574,7 +637,7 @@ _evhtp_glob_match(const char * pattern, const char * string) { } while (str_len) { - if (_evhtp_glob_match(pattern + 1, string)) { + if (_evhtp_glob_match(pattern + 1, pat_len, string, str_len)) { return 1; } @@ -647,12 +710,18 @@ _evhtp_callback_find(evhtp_callbacks_t * cbs, break; #endif case evhtp_callback_type_glob: - if (_evhtp_glob_match(callback->val.glob, path) == 1) { + { + size_t path_len = strlen(path); + size_t glob_len = strlen(callback->val.glob); + + if (_evhtp_glob_match(callback->val.glob, glob_len, + path, path_len) == 1) { *start_offset = 0; - *end_offset = (unsigned int)strlen(path); + *end_offset = (unsigned int)path_len; return callback; } + } default: break; } /* switch */ @@ -1111,7 +1180,7 @@ _evhtp_request_find_vhost(evhtp_t * evhtp, const char * name) { continue; } - if (_evhtp_glob_match(evhtp_vhost->server_name, name) == 1) { + if (_evhtp_glob_match(evhtp_vhost->server_name, 0, name, 0) == 1) { return evhtp_vhost; } @@ -1120,7 +1189,7 @@ _evhtp_request_find_vhost(evhtp_t * evhtp, const char * name) { continue; } - if (_evhtp_glob_match(evhtp_alias->alias, name) == 1) { + if (_evhtp_glob_match(evhtp_alias->alias, 0, name, 0) == 1) { return evhtp_vhost; } } @@ -1893,7 +1962,6 @@ _evhtp_connection_eventcb(evbev_t * bev, short events, void * arg) { _evhtp_connection_error_hook(c, events); - if (c->paused == 1) { c->free_connection = 1; } else { @@ -4123,6 +4191,7 @@ evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, conn->evbase = evbase; conn->bev = bufferevent_socket_new(evbase, -1, BEV_OPT_CLOSE_ON_FREE); + if (conn->bev == NULL) { evhtp_connection_free(conn); @@ -4130,7 +4199,6 @@ evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, } bufferevent_enable(conn->bev, EV_READ); - bufferevent_setcb(conn->bev, NULL, NULL, _evhtp_connection_eventcb, conn); @@ -4159,12 +4227,16 @@ evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, return NULL; } + err = bufferevent_socket_connect(conn->bev, sin, salen); } + /* not needed since any of the bufferevent errors will go straight to + * the eventcb + */ if (err) { - evhtp_connection_free(conn); - conn = NULL; + conn->bev = NULL; + return NULL; } return conn; From ce2197e3ce778a70e4c2f8e500155f9017527cc4 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Tue, 20 Jan 2015 20:07:17 -0500 Subject: [PATCH 53/59] Don't set conn->bev to NULL if error --- evhtp.c | 1 - 1 file changed, 1 deletion(-) diff --git a/evhtp.c b/evhtp.c index 6f08700..3b9814e 100644 --- a/evhtp.c +++ b/evhtp.c @@ -4235,7 +4235,6 @@ evhtp_connection_new_dns(evbase_t * evbase, struct evdns_base * dns_base, * the eventcb */ if (err) { - conn->bev = NULL; return NULL; } From 4047f1e2f6eea5f37d5354da6a205eb7f667d9b4 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Wed, 21 Jan 2015 16:40:32 -0500 Subject: [PATCH 54/59] Use new and faster wildcard matching function. --- evhtp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/evhtp.c b/evhtp.c index 3b9814e..542f91e 100644 --- a/evhtp.c +++ b/evhtp.c @@ -606,7 +606,7 @@ _evhtp_glob_match2(const char * pattern, size_t plen, * * @return */ -static int +static inline int _evhtp_glob_match(const char * pattern, size_t pat_len, const char * string, size_t str_len) { if (!pattern || !string) { return 0; @@ -621,10 +621,9 @@ _evhtp_glob_match(const char * pattern, size_t pat_len, const char * string, siz } /* XXX still in testing */ -#if 0 return _evhtp_glob_match2(pattern, pat_len, string, str_len); -#endif +#if 0 while (pat_len) { if (pattern[0] == '*') { while (pattern[1] == '*') { @@ -672,6 +671,7 @@ _evhtp_glob_match(const char * pattern, size_t pat_len, const char * string, siz } return 0; +#endif } /* _evhtp_glob_match */ static evhtp_callback_t * From 4189bfa4002b7e20b03edfbe5c4d4242859c8281 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Wed, 21 Jan 2015 20:13:15 -0500 Subject: [PATCH 55/59] Integer to string optimizations. --- CMakeLists.txt | 16 +++++++--- evhtp.c | 64 ++++++++++++++++++++------------------- evhtp_numtoa.c | 69 +++++++++++++++++++++++++++++++++++++++++++ evhtp_numtoa.h | 50 +++++++++++++++++++++++++++++++ examples/test_basic.c | 2 ++ 5 files changed, 166 insertions(+), 35 deletions(-) create mode 100644 evhtp_numtoa.c create mode 100644 evhtp_numtoa.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2540e79..daa4ea8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,12 @@ cmake_minimum_required(VERSION 2.8) -project(reason) +project(libevhtp) set(PROJECT_MAJOR_VERSION 1) set(PROJECT_MINOR_VERSION 2) set(PROJECT_PATCH_VERSION 9) +#add_definitions(-D_FORTIFY_SOURCE=2) -add_definitions(-D_FORTIFY_SOURCE=2) set (PROJECT_VERSION ${PROJECT_MAJOR_VERSION}.${PROJECT_MINOR_VERSION}.${PROJECT_PATCH_VERSION}) set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules) @@ -21,7 +21,6 @@ INCLUDE (UseDebugSymbols) CHECK_FUNCTION_EXISTS(memcmp HAVE_MEMCMP) CHECK_FUNCTION_EXISTS(strndup HAVE_STRNDUP) CHECK_FUNCTION_EXISTS(strnlen HAVE_STRNLEN) - CHECK_INCLUDE_FILES(alloca.h HAVE_ALLOCA_H) CHECK_INCLUDE_FILES(strings.h HAVE_STRINGS_H) CHECK_INCLUDE_FILES(string.h HAVE_STRING_H) @@ -76,6 +75,15 @@ if (HOST_BIG_ENDIAN) add_definitions(-DHOST_BIG_ENDIAN) endif() +# Test 32/64 bits +if("${CMAKE_SIZEOF_VOID_P}" EQUAL "8") + add_definitions(-DEVHTP_SYS_ARCH=64) +elseif("${CMAKE_SIZEOF_VOID_P}" EQUAL "4") + add_definitions(-DEVHTP_SYS_ARCH=32) +else() + message(ERROR "Unable to determine architecture") +endif() + # -DEVHTP_DISABLE_SSL:STRING=ON OPTION(EVHTP_DISABLE_SSL "Disable ssl support" OFF) @@ -220,7 +228,7 @@ if (NOT ${LIBEVENT_OPENSSL_FOUND}) set (EVHTP_DISABLE_SSL ON) endif(NOT ${LIBEVENT_OPENSSL_FOUND}) -set(LIBEVHTP_SOURCES evhtp.c htparse/htparse.c) +set(LIBEVHTP_SOURCES evhtp.c evhtp_numtoa.c htparse/htparse.c) if (NOT EVHTP_DISABLE_EVTHR) set (LIBEVHTP_EXTERNAL_LIBS ${LIBEVHTP_EXTERNAL_LIBS} pthread) diff --git a/evhtp.c b/evhtp.c index 542f91e..66abc90 100644 --- a/evhtp.c +++ b/evhtp.c @@ -24,8 +24,10 @@ #include #include "evhtp-internal.h" +#include "evhtp_numtoa.h" #include "evhtp.h" + static int _evhtp_request_parser_start(htparser * p); static int _evhtp_request_parser_host(htparser * p, const char * data, size_t len); static int _evhtp_request_parser_port(htparser * p, const char * data, size_t len); @@ -1456,9 +1458,9 @@ _evhtp_request_parser_headers(htparser * p) { } evbuffer_add_printf(bufferevent_get_output(c->bev), - "HTTP/%d.%d 100 Continue\r\n\r\n", - htparser_get_major(p), - htparser_get_minor(p)); + "HTTP/%c.%c 100 Continue\r\n\r\n", + evhtp_modp_uchartoa(htparser_get_major(p)), + evhtp_modp_uchartoa(htparser_get_minor(p))); } return 0; @@ -1635,37 +1637,35 @@ _evhtp_create_headers(evhtp_header_t * header, void * arg) { static evbuf_t * _evhtp_create_reply(evhtp_request_t * request, evhtp_res code) { - evbuf_t * buf = evbuffer_new(); - const char * content_type = evhtp_header_find(request->headers_out, "Content-Type"); - char res_buf[1024]; - int sres; + evbuf_t * buf; + const char * content_type; + char res_buf[2048]; + int sres; + size_t out_len; + unsigned char major; + unsigned char minor; + char out_buf[64]; + + + content_type = evhtp_header_find(request->headers_out, "Content-Type"); + out_len = evbuffer_get_length(request->buffer_out); + buf = evbuffer_new(); if (htparser_get_multipart(request->conn->parser) == 1) { goto check_proto; } - if (evbuffer_get_length(request->buffer_out) && request->chunked == 0) { + if (out_len && request->chunked == 0) { /* add extra headers (like content-length/type) if not already present */ if (!evhtp_header_find(request->headers_out, "Content-Length")) { - char lstr[128]; -#ifndef WIN32 - sres = snprintf(lstr, sizeof(lstr), "%zu", - evbuffer_get_length(request->buffer_out)); -#else - sres = snprintf(lstr, sizeof(lstr), "%u", - evbuffer_get_length(request->buffer_out)); -#endif - - if (sres >= sizeof(lstr) || sres < 0) { - /* overflow condition, this should never happen, but if it does, - * well lets just shut the connection down */ - request->keepalive = 0; - goto check_proto; - } + /* convert the buffer_out length to a string and set + * and add the new Content-Length header. + */ + evhtp_modp_sizetoa(out_len, out_buf); evhtp_headers_add_header(request->headers_out, - evhtp_header_new("Content-Length", lstr, 0, 1)); + evhtp_header_new("Content-Length", out_buf, 0, 1)); } if (!content_type) { @@ -1716,18 +1716,20 @@ _evhtp_create_reply(evhtp_request_t * request, evhtp_res code) { * we fallback to using evbuffer_add_printf(). */ - sres = snprintf(res_buf, sizeof(res_buf), "HTTP/%d.%d %d %s\r\n", - htparser_get_major(request->conn->parser), - htparser_get_minor(request->conn->parser), - code, status_code_to_str(code)); + major = evhtp_modp_uchartoa(htparser_get_major(request->conn->parser)); + minor = evhtp_modp_uchartoa(htparser_get_minor(request->conn->parser)); + + evhtp_modp_u32toa((uint32_t)code, out_buf); + + sres = snprintf(res_buf, sizeof(res_buf), "HTTP/%c.%c %s %s\r\n", + major, minor, out_buf, status_code_to_str(code)); if (sres >= sizeof(res_buf) || sres < 0) { /* failed to fit the whole thing in the res_buf, so just fallback to * using evbuffer_add_printf(). */ - evbuffer_add_printf(buf, "HTTP/%d.%d %d %s\r\n", - htparser_get_major(request->conn->parser), - htparser_get_minor(request->conn->parser), + evbuffer_add_printf(buf, "HTTP/%c.%c %d %s\r\n", + major, minor, code, status_code_to_str(code)); } else { /* copy the res_buf using evbuffer_add() instead of add_printf() */ diff --git a/evhtp_numtoa.c b/evhtp_numtoa.c new file mode 100644 index 0000000..5b2a5ac --- /dev/null +++ b/evhtp_numtoa.c @@ -0,0 +1,69 @@ +/* + * DERIVED FROM the stringencoders library's modp_numtoa + * + * Copyright ; 2007, Nick Galbreath -- nickg [at] client9 [dot] com + * All rights reserved. + * http://code.google.com/p/stringencoders/ + * Released under the MIT license. + * + */ + +#include +#include +#include +#include + +static inline void +strreverse(char * begin, char * end) { + char aux; + + while (end > begin) { + aux = *end, *end-- = *begin, *begin++ = aux; + } +} + +size_t +evhtp_modp_u64toa(uint64_t value, char * str) { + char * wstr = str; + + /* Conversion. Number is reversed. */ + do { + *wstr++ = (char)(48 + (value % 10)); + } while (value /= 10); + + *wstr = '\0'; + + /* Reverse string */ + strreverse(str, wstr - 1); + + return (size_t)(wstr - str); +} + +size_t +evhtp_modp_u32toa(uint32_t value, char * str) { + char * wstr = str; + + /* Conversion. Number is reversed. */ + do { + *wstr++ = (char)(48 + (value % 10)); + } while (value /= 10); + + *wstr = '\0'; + + /* Reverse string */ + strreverse(str, wstr - 1); + + return (size_t)(wstr - str); +} + +inline size_t +evhtp_modp_sizetoa(size_t value, char * str) { +#if EVHTP_SYS_ARCH == 64 + return evhtp_modp_u64toa(value, str); +#elif EVHTP_SYS_ARCH == 32 + return evhtp_modp_u32toa(value, str); +#else +#warn "UNKNOWN ARCH" +#endif +} + diff --git a/evhtp_numtoa.h b/evhtp_numtoa.h new file mode 100644 index 0000000..55623f1 --- /dev/null +++ b/evhtp_numtoa.h @@ -0,0 +1,50 @@ +#ifndef __EVHTP_NUMTOA_H__ +#define __EVHTP_NUMTOA_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @brief based on the system architecture, convert a size_t + * number to a string. + * + * @param value the input value + * @param str The output buffer, should be 24 chars or more. + * + * @return + */ +size_t evhtp_modp_sizetoa(size_t value, char * str); + + +/** + * @brief converts uint32_t value to string + * + * @param value input value + * @param str output buffer, should be 16 chars or more + * + * @return + */ +size_t evhtp_modp_u32toa(uint32_t value, char * str); + + +/** + * @brief convert uint64_t value to a string + * + * @param value input value + * @param str output buffer, should be 24 chars or more + * + * @return + */ +size_t evhtp_modp_u64toa(uint64_t value, char * str); + +#define evhtp_modp_uchartoa(_val) (unsigned char)('0' + _val) + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/examples/test_basic.c b/examples/test_basic.c index c676215..64a8e26 100644 --- a/examples/test_basic.c +++ b/examples/test_basic.c @@ -21,8 +21,10 @@ main(int argc, char ** argv) { evhtp_set_cb(htp, "/simple/", testcb, "simple"); evhtp_set_cb(htp, "/1/ping", testcb, "one"); evhtp_set_cb(htp, "/1/ping.json", testcb, "two"); +#if 0 #ifndef EVHTP_DISABLE_EVTHR evhtp_use_threads(htp, NULL, 4, NULL); +#endif #endif evhtp_bind_socket(htp, "0.0.0.0", 8081, 1024); From 037c7664d1d804a2eff4a52fe45519fa431d98ad Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Fri, 23 Jan 2015 19:59:44 -0500 Subject: [PATCH 56/59] Export the numtoa functions. --- evhtp_numtoa.c | 8 ++++++++ evhtp_numtoa.h | 3 --- examples/test.c | 5 ++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/evhtp_numtoa.c b/evhtp_numtoa.c index 5b2a5ac..6ccfb89 100644 --- a/evhtp_numtoa.c +++ b/evhtp_numtoa.c @@ -13,6 +13,9 @@ #include #include +#include "evhtp-internal.h" +#include "evhtp_numtoa.h" + static inline void strreverse(char * begin, char * end) { char aux; @@ -39,6 +42,8 @@ evhtp_modp_u64toa(uint64_t value, char * str) { return (size_t)(wstr - str); } +EXPORT_SYMBOL(evhtp_modp_u64toa); + size_t evhtp_modp_u32toa(uint32_t value, char * str) { char * wstr = str; @@ -56,6 +61,8 @@ evhtp_modp_u32toa(uint32_t value, char * str) { return (size_t)(wstr - str); } +EXPORT_SYMBOL(evhtp_modp_u32toa); + inline size_t evhtp_modp_sizetoa(size_t value, char * str) { #if EVHTP_SYS_ARCH == 64 @@ -67,3 +74,4 @@ evhtp_modp_sizetoa(size_t value, char * str) { #endif } +EXPORT_SYMBOL(evhtp_modp_sizetoa); diff --git a/evhtp_numtoa.h b/evhtp_numtoa.h index 55623f1..de295a4 100644 --- a/evhtp_numtoa.h +++ b/evhtp_numtoa.h @@ -5,7 +5,6 @@ extern "C" { #endif - /** * @brief based on the system architecture, convert a size_t * number to a string. @@ -17,7 +16,6 @@ extern "C" { */ size_t evhtp_modp_sizetoa(size_t value, char * str); - /** * @brief converts uint32_t value to string * @@ -45,6 +43,5 @@ size_t evhtp_modp_u64toa(uint64_t value, char * str); } #endif - #endif diff --git a/examples/test.c b/examples/test.c index a4ead72..5dd919b 100644 --- a/examples/test.c +++ b/examples/test.c @@ -139,7 +139,6 @@ test_regex(evhtp_request_t * req, void * arg) { evhtp_send_reply(req, EVHTP_RES_OK); } - static void dynamic_cb(evhtp_request_t * r, void * arg) { const char * name = arg; @@ -168,6 +167,7 @@ create_callback(evhtp_request_t * r, void * arg) { evhtp_send_reply(r, EVHTP_RES_OK); } + #endif static void @@ -510,6 +510,7 @@ main(int argc, char ** argv) { evhtp_callback_t * cb_3 = NULL; evhtp_callback_t * cb_4 = NULL; evhtp_callback_t * cb_5 = NULL; + #ifndef EVHTP_DISABLE_REGEX evhtp_callback_t * cb_6 = NULL; #endif @@ -530,6 +531,7 @@ main(int argc, char ** argv) { evbase = event_base_new(); htp = evhtp_new(evbase, NULL); + htp->parser_flags = EVHTP_PARSE_QUERY_FLAG_IGNORE_FRAGMENTS; evhtp_set_max_keepalive_requests(htp, max_keepalives); @@ -637,4 +639,5 @@ main(int argc, char ** argv) { return 0; } /* main */ + #pragma GCC diagnostic pop From 3516276d0ce97a73e00977e471c5607596a12fe9 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Sat, 24 Jan 2015 19:43:33 -0500 Subject: [PATCH 57/59] Disable unused tailq for client requests (for future use in pipelined) --- evhtp.c | 2 ++ evhtp.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/evhtp.c b/evhtp.c index 66abc90..533b52a 100644 --- a/evhtp.c +++ b/evhtp.c @@ -2098,7 +2098,9 @@ _evhtp_connection_new(evhtp_t * htp, evutil_socket_t sock, evhtp_type type) { htparser_init(connection->parser, ptype); htparser_set_userdata(connection->parser, connection); +#ifdef EVHTP_FUTURE_USE TAILQ_INIT(&connection->pending); +#endif return connection; } /* _evhtp_connection_new */ diff --git a/evhtp.h b/evhtp.h index aacebad..351422a 100644 --- a/evhtp.h +++ b/evhtp.h @@ -464,7 +464,10 @@ struct evhtp_connection_s { keepalive : 1; /**< set to 1 after the first request has been processed and the connection is kept open */ struct ev_token_bucket_cfg * ratelimit_cfg; /**< connection-specific ratelimiting configuration. */ +#ifdef EVHTP_FUTURE_USE TAILQ_HEAD(, evhtp_request_s) pending; /**< client pending data */ +#endif + }; struct evhtp_hooks_s { From 05fd68c5fab2904daacd14ce0415050ddeb7ca30 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Tue, 24 Feb 2015 02:51:00 -0500 Subject: [PATCH 58/59] More conversions from free to safe_free --- evhtp-internal.h | 7 ++++-- evhtp.c | 56 ++++++++++++++++++++++++------------------------ 2 files changed, 33 insertions(+), 30 deletions(-) diff --git a/evhtp-internal.h b/evhtp-internal.h index d5ea078..7c893d1 100644 --- a/evhtp-internal.h +++ b/evhtp-internal.h @@ -3,7 +3,7 @@ #ifdef EVHTP_HAS_VISIBILITY_HIDDEN #define __visible __attribute__((visibility("default"))) -#define EXPORT_SYMBOL(x) typeof(x)(x)__visible +#define EXPORT_SYMBOL(x) typeof(x)(x)__visible #else #define EXPORT_SYMBOL(n) #endif @@ -15,7 +15,10 @@ (var) = (tvar)) #endif - +#define evhtp_safe_free(_var, _freefn) do { \ + _freefn((_var)); \ + (_var) = NULL; \ +} while (0) #endif diff --git a/evhtp.c b/evhtp.c index 533b52a..24ffd6d 100644 --- a/evhtp.c +++ b/evhtp.c @@ -2090,7 +2090,7 @@ _evhtp_connection_new(evhtp_t * htp, evutil_socket_t sock, evhtp_type type) { connection->parser = htparser_new(); if (!connection->parser) { - free(connection); + evhtp_safe_free(connection, free); return NULL; } @@ -2457,7 +2457,7 @@ evhtp_kv_new(const char * key, const char * val, char kalloc, char valloc) { char * s; if (!(s = malloc(kv->klen + 1))) { - free(kv); + evhtp_safe_free(kv, free); return NULL; } @@ -2495,14 +2495,14 @@ evhtp_kv_free(evhtp_kv_t * kv) { } if (kv->k_heaped) { - free(kv->key); + evhtp_safe_free(kv->key, free); } if (kv->v_heaped) { - free(kv->val); + evhtp_safe_free(kv->val, free); } - free(kv); + evhtp_safe_free(kv, free); } void @@ -2533,7 +2533,7 @@ evhtp_kvs_free(evhtp_kvs_t * kvs) { evhtp_kv_free(kv); } - free(kvs); + evhtp_safe_free(kvs, free); } int @@ -2763,7 +2763,7 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { } if (!(val_buf = malloc(len + 1))) { - free(key_buf); + evhtp_safe_free(key_buf, free); return NULL; } @@ -2965,15 +2965,15 @@ evhtp_parse_query_wflags(const char * query, size_t len, int flags) { } #ifndef EVHTP_HAS_C99 - free(key_buf); - free(val_buf); + evhtp_safe_free(key_buf, free); + evhtp_safe_free(val_buf, free); #endif return query_args; error: #ifndef EVHTP_HAS_C99 - free(key_buf); - free(val_buf); + evhtp_safe_free(key_buf, free); + evhtp_safe_free(val_buf, free); #endif return NULL; @@ -3263,10 +3263,10 @@ evhtp_callbacks_free(evhtp_callbacks_t * callbacks) { TAILQ_FOREACH_SAFE(callback, callbacks, next, tmp) { TAILQ_REMOVE(callbacks, callback, next); - evhtp_callback_free(callback); + evhtp_safe_free(callback, evhtp_callback_free); } - free(callbacks); + evhtp_safe_free(callbacks, free); } evhtp_callback_t * @@ -3291,8 +3291,8 @@ evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_c hcb->val.regex = malloc(sizeof(regex_t)); if (regcomp(hcb->val.regex, (char *)path, REG_EXTENDED) != 0) { - free(hcb->val.regex); - free(hcb); + evhtp_safe_free(hcb->val.regex, free); + evhtp_safe_free(hcb, free); return NULL; } @@ -3302,7 +3302,7 @@ evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_c hcb->val.glob = strdup(path); break; default: - free(hcb); + evhtp_safe_free(hcb, free); return NULL; } /* switch */ @@ -3321,7 +3321,7 @@ evhtp_callback_free(evhtp_callback_t * callback) { free(callback->val.path); break; case evhtp_callback_type_glob: - free(callback->val.glob); + evhtp_safe_free(callback->val.glob, free); break; #ifndef EVHTP_DISABLE_REGEX case evhtp_callback_type_regex: @@ -3332,10 +3332,10 @@ evhtp_callback_free(evhtp_callback_t * callback) { } if (callback->hooks) { - free(callback->hooks); + evhtp_safe_free(callback->hooks, free); } - free(callback); + evhtp_safe_free(callback, free); return; } @@ -3519,7 +3519,7 @@ evhtp_set_cb(evhtp_t * htp, const char * path, evhtp_callback_cb cb, void * arg) } if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) { - evhtp_callback_free(hcb); + evhtp_safe_free(hcb, evhtp_callback_free); _evhtp_unlock(htp); return NULL; @@ -3600,7 +3600,7 @@ evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, vo } if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) { - evhtp_callback_free(hcb); + evhtp_safe_free(hcb, evhtp_callback_free); _evhtp_unlock(htp); return NULL; @@ -3636,7 +3636,7 @@ evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, voi } if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) { - evhtp_callback_free(hcb); + evhtp_safe_free(hcb, evhtp_callback_free); _evhtp_unlock(htp); return NULL; @@ -3929,14 +3929,14 @@ evhtp_connection_free(evhtp_connection_t * connection) { } _evhtp_connection_fini_hook(connection); - _evhtp_request_free(connection->request); + evhtp_safe_free(connection->request, _evhtp_request_free); - free(connection->parser); - free(connection->hooks); - free(connection->saddr); + evhtp_safe_free(connection->parser, free); + evhtp_safe_free(connection->hooks, free); + evhtp_safe_free(connection->saddr, free); if (connection->resume_ev) { - event_free(connection->resume_ev); + evhtp_safe_free(connection->resume_ev, event_free); } if (connection->bev) { @@ -3957,7 +3957,7 @@ evhtp_connection_free(evhtp_connection_t * connection) { ev_token_bucket_cfg_free(connection->ratelimit_cfg); } - free(connection); + evhtp_safe_free(connection, free); } /* evhtp_connection_free */ void From 81fff10f41a5b52c400b7ce87dd0346a7967de4c Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Tue, 24 Feb 2015 02:55:37 -0500 Subject: [PATCH 59/59] Release v1.2.10, see ChangeLog for details --- CMakeLists.txt | 2 +- ChangeLog | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ evhtp.h | 7 +++--- 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index daa4ea8..ee79fa8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ project(libevhtp) set(PROJECT_MAJOR_VERSION 1) set(PROJECT_MINOR_VERSION 2) -set(PROJECT_PATCH_VERSION 9) +set(PROJECT_PATCH_VERSION 10) #add_definitions(-D_FORTIFY_SOURCE=2) diff --git a/ChangeLog b/ChangeLog index 57a102c..4ffb260 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,63 @@ +v1.2.10 + o client ssl connection added. This requires a SSL_CTX and must be set by the user (439431a StunMan) + o Only export public symbols. (cf66c7c Mark Ellzey) + o Export evhtp_connection_set_ratelimit (df2fbd6 Mark Ellzey) + o Uncomplexify evthr - huge performance boost. (8a6873e Mark Ellzey) + o Do backlogs matter for evthr? I am thinking not. (76b4a96 Mark Ellzey) + o Remove all the stupid backlog stuff. (cb4e280 Mark Ellzey) + o Proposed changes for request pause/resume (pipelined) (6cd8946 Mark Ellzey) + o Remove dead code from evthr (3d51c76 Mark Ellzey) + o Remove dead declarations in evthr.h (72488a8 Mark Ellzey) + o Be more consistent and slightly more lenient when handling GET params (86ba10b TJ Koblentz) + o Empty query args processed with a val of NULL, extended the test_query code (bc897d2 Mark Ellzey) + o add connection connected status for client connection (0e839f0 zhangjing) + o Added on_event hook / cleanup (22c0fac Mark Ellzey) + o Fixed bug with calling user-defined event callback :) (246e33d Mark Ellzey) + o Add a pkg-config .pc file (6e85a9b Mark Ellzey) + o TestBigEndian, and add big endian versions of _str*cmp macros (736bc80 Mark Ellzey) + o ignore build directory (submodule doesnt become dirty on build) (1aceedb TJ Koblentz) + o missing SSL guard + test compilation problems (-Wunused-but-set-variable) (787eeb9 TJ Koblentz) + o Fix some empty GET param cases (with tests) (e282b6f TJ Koblentz) + o Formatting cleanup (89f11cd Mark Ellzey) + o Cleanup and fixes (61c7f4f Mark Ellzey) + o (evhtp_free): Free ssl_ctx if used. (9318571 Marcus Sundberg) + o Properly handle errors when allocating connections and requests. (ab2f567 Marcus Sundberg) + o Prevent double free of request. (ec445f9 Martin Hedenfalk) + o Stop parsing when we have got a complete response/request. (279a7d3 Marcus Sundberg) + o (evhtp_connection_free): Call hook before freeing request. (c093ff9 Marcus Sundberg) + o Properly parse CONNECT request line. (76f2830 Marcus Sundberg) + o Support IPv6 literals in CONNECT authority string. (378b790 Marcus Sundberg) + o Separate fragment string from query string according to RFC 3986. (a9c0679 Marcus Sundberg) + o (evhtp_parse_query): Remove strange handling of '?' and '/'. (3d96f5e Marcus Sundberg) + o Fix parse errors on trailing whitespace in Content-Length header. (b67068c Marcus Sundberg) + o (evhtp_parse_query): Advance state to s_query_key after start. (a535258 Marcus Sundberg) + o Add hooks for host and port, and fill in authority struct. (1be3a0f Marcus Sundberg) + o (_evhtp_path_new): If len is 0 we set full to "/", just like path. (7d277f8 Marcus Sundberg) + o Do not use different API/ABI when regexps are disabled. (39fcb28 Marcus Sundberg) + o Fix warnings in test.c when EVHTP_DISABLE_REGEX is defined. (05b01cf Marcus Sundberg) + o Add keepalive flag to connection. (74031a7 Marcus Sundberg) + o Add evhtp_hook_on_conn_error hook for connection errors. (6c3ed3d Marcus Sundberg) + o Added the function evhtp_connection_new_dns(). (47eecd0 Jan Edhner) + o (evhtp_connection_new_dns): Handle errors. (b13994b Marcus Sundberg) + o (evhtp_connection_new): Handle IPv6 addresses. (ac97672 Marcus Sundberg) + o (_evhtp_create_headers): Use evbuffer_expand() to reserve space. (c4ed326 Marcus Sundberg) + o Use malloc() instead of calloc() for buffers we will immediately fill. (3906a65 Marcus Sundberg) + o added padding for all structs containing bitfields (a2ebece Mark Ellzey) + o Update evthr.c (1d492cc romange) + o Cleanup, use EVHTP_DISABLE_SSL for client (c32562a Mark Ellzey) + o Various fixes, see full commit message (d8a4935 Mark Ellzey) + o Added evhp_set_flags along with some documentation (27b5e8a Mark Ellzey) + o Symbol exports moved into headers, more documentation (352aebe Mark Ellzey) + o If available, use c99 to our advantage (read commit msg) (8a44e6f Mark Ellzey) + o Remove duplicate evbuffer_new for buffer_in (5284ce3 Mark Ellzey) + o Fix for client connect double-free condition (5267ed2 Mark Ellzey) + o Don't set conn->bev to NULL if error (ce2197e Mark Ellzey) + o Use new and faster wildcard matching function. (4047f1e Mark Ellzey) + o Integer to string optimizations. (4189bfa Mark Ellzey) + o Export the numtoa functions. (037c766 Mark Ellzey) + o Disable unused tailq for client requests (for future use in pipelined) (3516276 Mark Ellzey) + o More conversions from free to safe_free (05fd68c Mark Ellzey) + v1.2.9 o Accept upper-case "Chunked" in addition to "chunked" for transfer encoding. (07a9322 Tim Burks) o [htparse] Added length checks for various header values (42050e8 Mark Ellzey) diff --git a/evhtp.h b/evhtp.h index 351422a..ddc4996 100644 --- a/evhtp.h +++ b/evhtp.h @@ -184,10 +184,10 @@ typedef evhtp_ssl_sess_t * (* evhtp_ssl_scache_get)(evhtp_connection_t * connect typedef void * (* evhtp_ssl_scache_init)(evhtp_t *); #endif -#define EVHTP_VERSION "1.2.9" +#define EVHTP_VERSION "1.2.10" #define EVHTP_VERSION_MAJOR 1 #define EVHTP_VERSION_MINOR 2 -#define EVHTP_VERSION_PATCH 9 +#define EVHTP_VERSION_PATCH 10 #define evhtp_headers_iterator evhtp_kvs_iterator @@ -467,7 +467,6 @@ struct evhtp_connection_s { #ifdef EVHTP_FUTURE_USE TAILQ_HEAD(, evhtp_request_s) pending; /**< client pending data */ #endif - }; struct evhtp_hooks_s { @@ -1286,7 +1285,7 @@ EVHTP_EXPORT evhtp_connection_t * evhtp_connection_new(evbase_t * evbase, const char * addr, uint16_t port); #ifndef DISABLE_SSL -evhtp_connection_t * evhtp_connection_ssl_new(evbase_t * evbase, const char * addr, uint16_t port, evhtp_ssl_ctx_t* ctx); +evhtp_connection_t * evhtp_connection_ssl_new(evbase_t * evbase, const char * addr, uint16_t port, evhtp_ssl_ctx_t * ctx); #endif