Merge branch 'release/1.2.15'
diff --git a/.travis.yml b/.travis.yml
index 840b997..56b6873 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,11 +4,11 @@
- clang
sudo: required
-iinstall:
+before_install:
- sudo apt-get update -qq
- - sudo apt-get install libevent-dev
+ - sudo apt-get install libevent-dev libonig-dev
script:
- - cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. && cmake --build .
+ - cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. && make examples
notifications:
- irc "irc.oftc.net#libevhtp"
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5ba3a6f..3617d97 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,16 +24,19 @@
add_definitions(-DEVTHR_SHARED_PIPE)
endif()
-CHECK_include_FILES(strings.h HAVE_STRINGS_H)
-CHECK_include_FILES(string.h HAVE_STRING_H)
-CHECK_include_FILES(stdlib.h HAVE_STDLIB_H)
-CHECK_include_FILES(sys/time.h HAVE_SYS_TIME_H)
-CHECK_include_FILES(sys/times.h HAVE_SYS_TIMES_H)
-CHECK_include_FILES(unistd.h HAVE_UNISTD_H)
-CHECK_include_FILES(stdarg.h HAVE_STDARG_PROTOTYPES)
-CHECK_include_FILES(sys/tree.h HAVE_SYS_TREE)
-CHECK_include_FILES(sys/queue.h HAVE_SYS_QUEUE)
-CHECK_include_FILES(sys/un.h HAVE_SYS_UN)
+check_include_files (stdlib.h HAVE_STDLIB_H)
+check_include_files (string.h HAVE_STRING_H)
+check_include_files (stdint.h HAVE_STDINT_H)
+check_include_files (errno.h HAVE_ERRNO_H)
+check_include_files (strings.h HAVE_STRINGS_H)
+check_include_files (inttypes.h HAVE_INTTYPES_H)
+check_include_files (limits.h HAVE_LIMITS_H)
+
+check_include_files (unistd.h HAVE_UNISTD_H)
+check_include_files (stdarg.h HAVE_STDARG_PROTOTYPES)
+check_include_files (sys/tree.h HAVE_SYS_TREE)
+check_include_files (sys/queue.h HAVE_SYS_QUEUE)
+check_include_files (sys/un.h HAVE_SYS_UN)
CHECK_TYPE_SIZE("int" SIZEOF_INT)
CHECK_TYPE_SIZE("long" SIZEOF_LONG)
@@ -189,11 +192,11 @@
list (APPEND LIBEVHTP_EXTERNAL_LIBS pthread)
endif()
-add_library(evhtp ${EVHTP_LIBTYPE} ${LIBEVHTP_SOURCE_FILES})
-target_link_libraries(evhtp ${LIBEVHTP_EXTERNAL_LIBS})
+add_library (evhtp ${EVHTP_LIBTYPE} ${LIBEVHTP_SOURCE_FILES})
+target_link_libraries (evhtp ${LIBEVHTP_EXTERNAL_LIBS})
if (EVHTP_BUILD_SHARED)
- set_target_properties(evhtp PROPERTIES SOVERSION "${PROJECT_VERSION}")
+ set_target_properties(evhtp PROPERTIES VERSION "${PROJECT_VERSION}" 0 OUTPUT_NAME "evhtp")
endif()
add_subdirectory(examples)
@@ -212,6 +215,7 @@
FILES
${PROJECT_SOURCE_DIR}/include/evhtp/evhtp.h
${PROJECT_SOURCE_DIR}/include/evhtp/parser.h
+ ${PROJECT_BINARY_DIR}/include/evhtp/config.h
DESTINATION
${INCLUDE_INSTALL_DIR}/evhtp)
@@ -221,12 +225,6 @@
DESTINATION
${INCLUDE_INSTALL_DIR})
-install (
- FILES
- ${CMAKE_CURRENT_BINARY_DIR}/include/evhtp/config.h
- DESTINATION
- ${INCLUDE_INSTALL_DIR}/evhtp)
-
if (NOT EVHTP_DISABLE_EVTHR)
install (
FILES
diff --git a/ChangeLog b/ChangeLog
index 1f03277..8ada5aa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+v1.2.15
+ o deprecated unset_hook and set_hook / cleanup (f1d2bd1 Nathan French)
+ o updated travis configuration (45003e1 Nathan French)
+ o Latest revision to support multiple SSL versions (4e353ba Tony Lambiris)
+ o remove silly comment (d3da401 Nathan French)
+ o check for errors in SSL RAND_(poll|bytes) calls (018dec8 Nathan French)
+ o Added new virtualhost examples and functions (3467382 Nathan French)
+ o added example request pausing app (282a1c9 Nathan French)
+ o Added a more extensive SSL sandbox. (1e0c241 Nathan French)
+ o Add examples/https/README (6ecf7e7 Nathan French)
+ o exit failure in example_https when SSL is disabled (0848e08 Nathan French)
+ o [#26] Use SSL_CTX_use_certificate_chain_file (4c4eb3a Nathan French)
+
v1.2.14 - !!!! SECURITY UPDATE !!!!
o Added doxygen tags. Updated Doxyfile to include more sources and headers. (50ab41d Dan Henderson)
o SSL logging on handshake errors (0fff6bc Nathan French)
diff --git a/README.markdown b/README.markdown
index fff71d4..c965a8a 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1,7 +1,7 @@
|  | <h1>Libevhtp</h1> |
| :------------- | -------------: |
-[](https://travis-ci.org/ellzey/libevhtp)
+[](https://travis-ci.org/criticalstack/libevhtp)
<a href="https://scan.coverity.com/projects/ellzey-libevhtp">
<img alt="Coverity Scan Build Status"
src="https://scan.coverity.com/projects/5084/badge.svg"/>
@@ -17,6 +17,7 @@
## Optional Dependencies
* [OpenSSL](http://openssl.org)
* pthreads
+* [onig (regex)](https://github.com/kkos/oniguruma)
## Building
* cd build
@@ -51,7 +52,7 @@
1. Create a parent evhtp_t structure.
2. Assign callbacks to the parent for specific URIs or posix-regex based URI's
3. Optionally assign per-connection hooks (see hooks) to the callbacks.
-4. Optionally assign pre-accept and post-accept callbacks for incoming connections.
+4. Optionally assign pre-accept and post-accept callbacks for incoming connections.
5. Optionally enable built-in threadpool for connection handling (lock-free, and non-blocking).
6. Optionally morph your server to HTTPS.
7. Start the evhtp listener.
@@ -78,7 +79,7 @@
main(int argc, char ** argv) {
evbase_t * evbase = event_base_new();
evhtp_t * htp = evhtp_new(evbase, NULL);
-
+
evhtp_set_cb(htp, "/test", testcb, NULL);
evhtp_bind_socket(htp, "0.0.0.0", 8080, 1024);
event_base_loop(evbase, 0);
@@ -89,12 +90,12 @@
## Is evhtp thread-safe?
For simple usage with evhtp_use_threads(), yes. But for more extreme cases:
-sorta, you are bound to the thread mechanisms of libevent itself.
+sorta, you are bound to the thread mechanisms of libevent itself.
But with proper design around libevhtp, thread issues can be out-of-sight,
-out-of-mind.
+out-of-mind.
-What do you mean by this "proper design" statement?
+What do you mean by this "proper design" statement?
Refer to the code in ./examples/thread_design.c. The comments go into great detail
of the hows and whys for proper design using libevhtp's threading model.
@@ -113,7 +114,7 @@
## Performance stuff
-While we never documented any benchmark publically,
+While we never documented any benchmark publically,
the popular open source project [ZIMG](http://zimg.buaa.us) did a bit of that
for us.The ZIMG team decided to move away from NGINX to libevhtp for their
software, and the results were pretty outstanding. Here is a graph showing their
diff --git a/cmake/version.cmake b/cmake/version.cmake
index d7c14ed..f88c98f 100644
--- a/cmake/version.cmake
+++ b/cmake/version.cmake
@@ -1,5 +1,5 @@
set (PROJECT_MAJOR_VERSION 1)
set (PROJECT_MINOR_VERSION 2)
-set (PROJECT_PATCH_VERSION 14)
+set (PROJECT_PATCH_VERSION 15)
set (PROJECT_VERSION
"${PROJECT_MAJOR_VERSION}.${PROJECT_MINOR_VERSION}.${PROJECT_PATCH_VERSION}")
diff --git a/evhtp.c b/evhtp.c
index c818a9f..d9e1bb1 100644
--- a/evhtp.c
+++ b/evhtp.c
@@ -2754,10 +2754,6 @@
htparser_init(connection->parser, ptype);
htparser_set_userdata(connection->parser, connection);
-#ifdef EVHTP_FUTURE_USE
- TAILQ_INIT(&connection->pending);
-#endif
-
return connection;
} /* htp__connection_new_ */
@@ -2904,16 +2900,14 @@
static void
htp__ssl_delete_scache_ent_(evhtp_ssl_ctx_t * ctx, evhtp_ssl_sess_t * sess)
{
- evhtp_t * htp;
- evhtp_ssl_cfg_t * cfg;
- unsigned char * sid;
- unsigned int slen;
+ evhtp_t * htp;
+ evhtp_ssl_cfg_t * cfg;
+ evhtp_ssl_data_t * sid;
+ unsigned int slen;
- htp = (evhtp_t *)SSL_CTX_get_app_data(ctx);
- cfg = htp->ssl_cfg;
-
- sid = sess->session_id;
- slen = sess->session_id_length;
+ htp = (evhtp_t *)SSL_CTX_get_app_data(ctx);
+ cfg = htp->ssl_cfg;
+ sid = (evhtp_ssl_data_t *)SSL_SESSION_get_id(sess, &slen);
if (cfg->scache_del)
{
@@ -2926,7 +2920,7 @@
{
evhtp_connection_t * connection;
evhtp_ssl_cfg_t * cfg;
- unsigned char * sid;
+ evhtp_ssl_data_t * sid;
int slen;
connection = (evhtp_connection_t *)SSL_get_app_data(ssl);
@@ -2934,10 +2928,9 @@
{
return 0; /* We cannot get the ssl_cfg */
}
- cfg = connection->htp->ssl_cfg;
- sid = sess->session_id;
- slen = sess->session_id_length;
+ cfg = connection->htp->ssl_cfg;
+ sid = (evhtp_ssl_data_t *)SSL_SESSION_get_id(sess, &slen);
SSL_set_timeout(sess, cfg->scache_timeout);
@@ -2950,19 +2943,20 @@
}
static evhtp_ssl_sess_t *
-htp__ssl_get_scache_ent_(evhtp_ssl_t * ssl, unsigned char * sid, int sid_len, int * copy)
+htp__ssl_get_scache_ent_(evhtp_ssl_t * ssl, evhtp_ssl_data_t * sid, int sid_len, int * copy)
{
evhtp_connection_t * connection;
evhtp_ssl_cfg_t * cfg;
evhtp_ssl_sess_t * sess;
connection = (evhtp_connection_t * )SSL_get_app_data(ssl);
+
if (connection->htp == NULL)
{
return NULL; /* We have no way of getting ssl_cfg */
}
- cfg = connection->htp->ssl_cfg;
- sess = NULL;
+ cfg = connection->htp->ssl_cfg;
+ sess = NULL;
if (cfg->scache_get)
{
@@ -3004,18 +2998,20 @@
if ((evhtp_vhost = htp__request_find_vhost_(evhtp, sname)))
{
+ SSL_CTX * ctx = SSL_get_SSL_CTX(ssl);
+
connection->htp = evhtp_vhost;
HTP_FLAG_ON(connection, EVHTP_CONN_FLAG_VHOST_VIA_SNI);
SSL_set_SSL_CTX(ssl, evhtp_vhost->ssl_ctx);
- SSL_set_options(ssl, SSL_CTX_get_options(ssl->ctx));
+ SSL_set_options(ssl, SSL_CTX_get_options(ctx));
if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) ||
(SSL_num_renegotiations(ssl) == 0))
{
- SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ssl->ctx),
- SSL_CTX_get_verify_callback(ssl->ctx));
+ SSL_set_verify(ssl, SSL_CTX_get_verify_mode(ctx),
+ SSL_CTX_get_verify_callback(ctx));
}
return SSL_TLSEXT_ERR_OK;
@@ -4352,10 +4348,27 @@
return 0;
} /* htp__set_hook_ */
+static int
+htp__unset_hook_(evhtp_hooks_t ** hooks, evhtp_hook_type type) {
+ return htp__set_hook_(hooks, type, NULL, NULL);
+}
+
int
-evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void * arg)
+evhtp_callback_unset_hook(evhtp_callback_t * callback, evhtp_hook_type type)
{
- return htp__set_hook_(hooks, type, cb, arg);
+ return htp__unset_hook_(&callback->hooks, type);
+}
+
+int
+evhtp_request_unset_hook(evhtp_request_t * req, evhtp_hook_type type)
+{
+ return htp__unset_hook_(&req->hooks, type);
+}
+
+int
+evhtp_connection_unset_hook(evhtp_connection_t * conn, evhtp_hook_type type)
+{
+ return htp__unset_hook_(&conn->hooks, type);
}
int
@@ -4377,12 +4390,6 @@
}
int
-evhtp_unset_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type)
-{
- return evhtp_set_hook(hooks, type, NULL, NULL);
-}
-
-int
evhtp_unset_all_hooks(evhtp_hooks_t ** hooks)
{
int i;
@@ -4413,7 +4420,7 @@
}
for (i = 0; hooklist_[i].type != -1; i++) {
- if (evhtp_unset_hook(hooks, hooklist_[i].type) == -1) {
+ if (htp__unset_hook_(hooks, hooklist_[i].type) == -1) {
return -1;
}
}
@@ -4752,13 +4759,8 @@
int
evhtp_ssl_init(evhtp_t * htp, evhtp_ssl_cfg_t * cfg)
{
-#ifdef EVHTP_ENABLE_FUTURE_STUFF
- evhtp_ssl_scache_init init_cb = NULL;
- evhtp_ssl_scache_add add_cb = NULL;
- evhtp_ssl_scache_get get_cb = NULL;
- evhtp_ssl_scache_del del_cb = NULL;
-#endif
- long cache_mode;
+ long cache_mode;
+ unsigned char c;
if (cfg == NULL || htp == NULL || cfg->pemfile == NULL)
{
@@ -4769,7 +4771,16 @@
ERR_load_crypto_strings();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
- RAND_poll();
+
+ if (RAND_poll() != 1) {
+ log_error("RAND_poll");
+ return -1;
+ }
+
+ if (RAND_bytes(&c, 1) != 1) {
+ log_error("RAND_bytes");
+ return -1;
+ }
#if OPENSSL_VERSION_NUMBER < 0x10000000L
STACK_OF(SSL_COMP) * comp_methods = SSL_COMP_get_compression_methods();
@@ -4789,21 +4800,22 @@
SSL_CTX_set_options(htp->ssl_ctx, cfg->ssl_opts);
#ifndef OPENSSL_NO_ECDH
- if (cfg->named_curve != NULL)
- {
+ if (cfg->named_curve != NULL) {
EC_KEY * ecdh = NULL;
int nid = 0;
- nid = OBJ_sn2nid(cfg->named_curve);
- if (nid == 0)
- {
- fprintf(stderr, "ECDH initialization failed: unknown curve %s\n", cfg->named_curve);
+ nid = OBJ_sn2nid(cfg->named_curve);
+
+ if (nid == 0) {
+ log_error("ECDH initialization failed: unknown curve %s", cfg->named_curve);
}
+
ecdh = EC_KEY_new_by_curve_name(nid);
- if (ecdh == NULL)
- {
- fprintf(stderr, "ECDH initialization failed for curve %s\n", cfg->named_curve);
+
+ if (ecdh == NULL) {
+ log_error("ECDH initialization failed for curve %s", cfg->named_curve);
}
+
SSL_CTX_set_tmp_ecdh(htp->ssl_ctx, ecdh);
EC_KEY_free(ecdh);
}
@@ -4815,6 +4827,7 @@
DH * dh;
fh = fopen(cfg->dhparams, "r");
+
if (fh != NULL)
{
dh = PEM_read_DHparams(fh, NULL, NULL, NULL);
@@ -4823,30 +4836,36 @@
SSL_CTX_set_tmp_dh(htp->ssl_ctx, dh);
DH_free(dh);
} else {
- fprintf(stderr, "DH initialization failed: unable to parse file %s\n", cfg->dhparams);
+ log_error("DH initialization failed: unable to parse file %s", cfg->dhparams);
}
+
fclose(fh);
} else {
- fprintf(stderr, "DH initialization failed: unable to open file %s\n", cfg->dhparams);
+ log_error("DH initialization failed: unable to open file %s", cfg->dhparams);
}
}
#endif /* OPENSSL_NO_DH */
if (cfg->ciphers != NULL) {
- SSL_CTX_set_cipher_list(htp->ssl_ctx, cfg->ciphers);
+ if (SSL_CTX_set_cipher_list(htp->ssl_ctx, cfg->ciphers) == 0) {
+ log_error("set_cipher_list");
+ return -1;
+ }
}
SSL_CTX_load_verify_locations(htp->ssl_ctx, cfg->cafile, cfg->capath);
X509_STORE_set_flags(SSL_CTX_get_cert_store(htp->ssl_ctx), cfg->store_flags);
SSL_CTX_set_verify(htp->ssl_ctx, cfg->verify_peer, cfg->x509_verify_cb);
- if (cfg->x509_chk_issued_cb != NULL)
- {
+ if (cfg->x509_chk_issued_cb != NULL) {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
htp->ssl_ctx->cert_store->check_issued = cfg->x509_chk_issued_cb;
+#else
+ X509_STORE_set_check_issued(SSL_CTX_get_cert_store(htp->ssl_ctx), cfg->x509_chk_issued_cb);
+#endif
}
- if (cfg->verify_depth)
- {
+ if (cfg->verify_depth) {
SSL_CTX_set_verify_depth(htp->ssl_ctx, cfg->verify_depth);
}
@@ -4854,35 +4873,12 @@
case evhtp_ssl_scache_type_disabled:
cache_mode = SSL_SESS_CACHE_OFF;
break;
-#ifdef EVHTP_ENABLE_FUTURE_STUFF
- case evhtp_ssl_scache_type_user:
- cache_mode = SSL_SESS_CACHE_SERVER |
- SSL_SESS_CACHE_NO_INTERNAL |
- SSL_SESS_CACHE_NO_INTERNAL_LOOKUP;
-
- init_cb = cfg->scache_init;
- add_cb = cfg->scache_add;
- get_cb = cfg->scache_get;
- del_cb = cfg->scache_del;
- break;
- case evhtp_ssl_scache_type_builtin:
- cache_mode = SSL_SESS_CACHE_SERVER |
- SSL_SESS_CACHE_NO_INTERNAL |
- SSL_SESS_CACHE_NO_INTERNAL_LOOKUP;
-
- init_cb = htp__ssl_builtin_init_;
- add_cb = htp__ssl_builtin_add_;
- get_cb = htp__ssl_builtin_get_;
- del_cb = htp__ssl_builtin_del_;
- break;
-#endif
- case evhtp_ssl_scache_type_internal:
default:
cache_mode = SSL_SESS_CACHE_SERVER;
break;
} /* switch */
- SSL_CTX_use_certificate_file(htp->ssl_ctx, cfg->pemfile, SSL_FILETYPE_PEM);
+ SSL_CTX_use_certificate_chain_file(htp->ssl_ctx, cfg->pemfile);
char * const key = cfg->privfile ? cfg->privfile : cfg->pemfile;
@@ -5168,6 +5164,8 @@
return -1;
}
+ log_debug("Adding %s to aliases", name);
+
alias->alias = htp__strdup_(name);
evhtp_alloc_assert(alias->alias);
@@ -5177,6 +5175,31 @@
}
int
+evhtp_add_aliases(evhtp_t * htp, const char * name, ...) {
+ va_list argp;
+ size_t len;
+
+ if (evhtp_add_alias(htp, name) == -1) {
+ return -1;
+ }
+
+ va_start(argp, name);
+ {
+ const char * p;
+
+ while ((p = va_arg(argp, const char *)) != NULL) {
+ if (evhtp_add_alias(htp, p) == -1) {
+ log_error("Unable to add %s alias", p);
+ return -1;
+ }
+ }
+ }
+ va_end(argp);
+
+ return 0;
+}
+
+int
evhtp_add_vhost(evhtp_t * evhtp, const char * name, evhtp_t * vhost)
{
if (evhtp == NULL || name == NULL || vhost == NULL)
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index c69aac5..31c3b8b 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -6,6 +6,9 @@
add_executable(test_client EXCLUDE_FROM_ALL test_client.c)
add_executable(test_query EXCLUDE_FROM_ALL test_query.c)
add_executable(test_perf EXCLUDE_FROM_ALL test_perf.c)
+add_executable(example_vhost EXCLUDE_FROM_ALL example_vhost.c)
+add_executable(example_pause EXCLUDE_FROM_ALL example_pause.c)
+add_executable(example_https EXCLUDE_FROM_ALL https/example_https.c)
if (NOT EVHTP_DISABLE_EVTHR)
add_executable(test_proxy EXCLUDE_FROM_ALL test_proxy.c)
@@ -19,7 +22,18 @@
target_link_libraries(test_client evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
target_link_libraries(test_query evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
target_link_libraries(test_perf evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
+target_link_libraries(example_vhost evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
+target_link_libraries(example_pause evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
+target_link_libraries(example_https evhtp ${LIBEVHTP_EXTERNAL_LIBS} ${SYS_LIBS})
-add_dependencies(examples test_extensive test_basic test_vhost test_client test_query test_perf)
+add_dependencies(examples example_https example_pause example_vhost test_extensive test_basic test_vhost test_client test_query test_perf)
+file (COPY
+ https/etc/ca.cnf
+ https/etc/client1.cnf
+ https/etc/client2.cnf
+ https/etc/server.cnf
+ DESTINATION
+ https/etc/)
+configure_file(https/bin/generate.sh.in https/bin/generate.sh @ONLY)
diff --git a/examples/example_pause.c b/examples/example_pause.c
new file mode 100644
index 0000000..247a7da
--- /dev/null
+++ b/examples/example_pause.c
@@ -0,0 +1,125 @@
+/*
+ * Quick example of how to pause a request, and in this case, simply
+ * set a timer to emit the response 10 seconds later.
+ *
+ * This is a good way to long running tasks before responding (thus
+ * not blocking any other processing).
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "../log.h"
+#include "internal.h"
+#include "evhtp/evhtp.h"
+
+struct paused_request_ {
+ struct event * _timeoutev;
+ struct timeval _timeout;
+ evhtp_request_t * _request;
+};
+
+/* once 10 seconds has passed, this function is called, it will
+ * resume the request, and send the final response back to the
+ * client.
+ */
+static void
+http_resume__callback_(int sock, short events, void * arg) {
+ struct paused_request_ * preq;
+ evhtp_request_t * req;
+
+ evhtp_assert(arg != NULL);
+
+ preq = (struct paused_request_ *)arg;
+ req = preq->_request;
+ evhtp_assert(req != NULL);
+
+ event_free(preq->_timeoutev);
+ free(preq);
+
+ /* add the current time to our output buffer to the client */
+ evbuffer_add_printf(req->buffer_out, "time end %ld\n", time(NULL));
+
+ /* inform the evhtp API to resume this connection request */
+ evhtp_request_resume(req);
+
+ /* finally send the response to the client, YAY! */
+ evhtp_send_reply(req, EVHTP_RES_OK);
+}
+
+/* this is our default callback, it is the one who sets up the evtimer
+ * that triggers the response after 10 seconds.
+ */
+static void
+http_pause__callback_(evhtp_request_t * req, void * arg) {
+ struct timeval * tv = (struct timeval *)arg;
+ struct paused_request_ * preq;
+
+ /* allocate a little structure that holds our evtimer and the
+ * pending request, se the timeout to 10 seconds.
+ */
+ preq = malloc(sizeof(*preq));
+ evhtp_alloc_assert(preq);
+
+ preq->_request = req;
+ preq->_timeout.tv_sec = tv->tv_sec;
+ preq->_timeout.tv_usec = tv->tv_usec;
+
+ /* when 10 seconds is up, the function http_resume__callback_ will
+ * be called, this function will actually send the response.
+ */
+ preq->_timeoutev = evtimer_new(req->htp->evbase, http_resume__callback_, preq);
+ evhtp_alloc_assert(preq->_timeoutev);
+
+ /* just for debugging, add the time the request was first seen */
+ evbuffer_add_printf(req->buffer_out, "time start %ld\n", time(NULL));
+
+ /* add the timer to the event loop */
+ evtimer_add(preq->_timeoutev, &preq->_timeout);
+
+ /* notify the evhtp API to "pause" this request (meaning it will no
+ * longer do any work on this connection until it is "resumed".
+ */
+ evhtp_request_pause(req);
+}
+
+int
+main(int argc, char ** argv) {
+ evhtp_t * htp;
+ struct event_base * evbase;
+ struct timeval timeo = { 10, 0 };
+
+ evbase = event_base_new();
+ evhtp_alloc_assert(evbase);
+
+ htp = evhtp_new(evbase, NULL);
+ evhtp_alloc_assert(htp);
+
+ /* we just set the default callback for any requests to
+ * the function that pauses the session, sets a timer,
+ * and 10 seconds later, sends the response.
+ */
+ evhtp_set_gencb(htp, http_pause__callback_, &timeo);
+
+ evhtp_bind_socket(htp, "127.0.0.1", 0, 128);
+ {
+ struct sockaddr_in sin;
+ socklen_t len = sizeof(struct sockaddr);
+ uint16_t port;
+
+ getsockname(
+ evconnlistener_get_fd(htp->server),
+ (struct sockaddr *)&sin, &len);
+
+ port = ntohs(sin.sin_port);
+
+ log_info("request will be delayed for 10 seconds with: curl http://127.0.0.1:%d/", port);
+ }
+
+ event_base_loop(evbase, 0);
+
+
+ return 0;
+}
diff --git a/examples/example_vhost.c b/examples/example_vhost.c
new file mode 100644
index 0000000..4a61003
--- /dev/null
+++ b/examples/example_vhost.c
@@ -0,0 +1,127 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include "internal.h"
+#include "../log.h"
+#include "evhtp/evhtp.h"
+
+#define make_response(cb) do { \
+ evbuffer_add_printf(req->buffer_out, \
+ "%s = host:%s, arg:%s\n", cb, \
+ evhtp_header_find(req->headers_in, "Host"), \
+ (char *)arg); \
+} while (0)
+
+
+static void
+vhost_1__callback_(evhtp_request_t * req, void * arg) {
+ /* these should be our callbacks for our evhtp.io hosts */
+ make_response("vhost_1__callback_");
+
+ evhtp_send_reply(req, EVHTP_RES_OK);
+}
+
+static void
+vhost_2__callback_(evhtp_request_t * req, void * arg) {
+ /* these should be our callbacks for our google hosts */
+ make_response("vhost_2__callback_");
+
+ evhtp_send_reply(req, EVHTP_RES_OK);
+}
+
+int
+main(int argc, char ** argv) {
+ struct event_base * evbase;
+ evhtp_t * htp;
+ evhtp_t * htp_vhost_1;
+ evhtp_t * htp_vhost_2;
+
+ evbase = event_base_new();
+ evhtp_alloc_assert(evbase);
+
+ /* allocate our main evhtp structure which vhosts are
+ * nested under
+ */
+ htp = evhtp_new(evbase, NULL);
+ evhtp_alloc_assert(htp);
+
+ /* create a evhtp structure for vhost_1 specific hostnames,
+ * this will match hostnames for 'evhtp.io'
+ * */
+ htp_vhost_1 = evhtp_new(evbase, NULL);
+ evhtp_alloc_assert(htp_vhost_1);
+
+ /* running a get on /vhost for htp_vhost_1 will be different
+ * from htp_vhost_2 due to the hostname we set below.
+ */
+ evhtp_set_cb(htp_vhost_1, "/vhost", vhost_1__callback_, "evhtp.io domains");
+
+ /* create a evhtp structure for vhost_2 specific hostnames,
+ * this will match hostnames for 'google.com'
+ */
+ htp_vhost_2 = evhtp_new(evbase, NULL);
+ evhtp_alloc_assert(htp_vhost_2);
+
+ /* running a get on /vhost for http_vhost_2 will be different
+ * from the http_vhost_1 due to the hostname we set below.
+ */
+ evhtp_set_cb(htp_vhost_2, "/vhost", vhost_2__callback_, "google.com domains");
+
+ /* if Host: evhtp.io is present, the callbacks fro htp_vhost_1 are
+ * used. We do this by adding the vhost_1 evhtp to the main htp ctx.
+ */
+ evhtp_add_vhost(htp, "evhtp.io", htp_vhost_1);
+
+ /* now lets set some virtual host aliases to evhtp.io */
+ evhtp_add_aliases(htp_vhost_1,
+ "www.evhtp.io",
+ "web.evhtp.io", NULL);
+
+ /* If Host: google.com is present, the callbacks for htp_vhost_2 are
+ * used instead. This must be attached to the main htp context.
+ */
+ evhtp_add_vhost(htp, "google.com", htp_vhost_2);
+
+ /* now add some virtual host aliases for google.com */
+ evhtp_add_aliases(htp_vhost_2,
+ "www.google.com",
+ "web.google.com",
+ "inbox.google.com", NULL);
+
+ /* we can also append a single alias to vhost_2 like this */
+ evhtp_add_alias(htp_vhost_2, "gmail.google.com");
+
+ /* now bind and listen on our server */
+ evhtp_bind_socket(htp, "127.0.0.1", 0, 128);
+
+ {
+ struct sockaddr_in sin;
+ socklen_t len = sizeof(struct sockaddr);
+ uint16_t port;
+
+ getsockname(
+ evconnlistener_get_fd(htp->server),
+ (struct sockaddr *)&sin, &len);
+
+ port = ntohs(sin.sin_port);
+
+ log_info("[[ try the following commands and you should see 'evhtp.io domains' ]]");
+ log_info("=====================================================================");
+ log_info("curl -H'Host: evhtp.io' http://127.0.0.1:%d/vhost", port);
+ log_info("curl -H'Host: www.evhtp.io' http://127.0.0.1:%d/vhost", port);
+ log_info("curl -H'Host: web.evhtp.io' http://127.0.0.1:%d/vhost", port);
+ log_info("========================================================================");
+ log_info("[[ try the following commands and you should see 'google.com domains' ]]");
+ log_info("========================================================================");
+ log_info("curl -H'Host: google.com' http://127.0.0.1:%d/vhost", port);
+ log_info("curl -H'Host: www.google.com' http://127.0.0.1:%d/vhost", port);
+ log_info("curl -H'Host: web.google.com' http://127.0.0.1:%d/vhost", port);
+ log_info("curl -H'Host: inbox.google.com' http://127.0.0.1:%d/vhost", port);
+ log_info("curl -H'Host: gmail.google.com' http://127.0.0.1:%d/vhost", port);
+ }
+
+ event_base_loop(evbase, 0);
+} /* main */
diff --git a/examples/https/README.md b/examples/https/README.md
new file mode 100644
index 0000000..0e1b539
--- /dev/null
+++ b/examples/https/README.md
@@ -0,0 +1,32 @@
+After running `make examples`, if SSL is enabled, you can quickly test HTTPS, with optional client-based certificate authentication using the following process within the build directory:
+
+```
+# do all the stupid ssl generation
+./examples/https/bin/generate.sh
+
+# Test without client auth
+
+# Run the server
+./examples/example_https \
+ -cert examples/https/server-crt.pem \
+ -key examples/https/server-key.pem
+
+# Make a request
+curl -vk https://localhost:4443/
+
+# Test WITH client auth
+
+./examples/example_https \
+ -cert examples/https/server-crt.pem \
+ -key examples/https/server-key.pem \
+ -ca examples/https/ca-crt.pem \
+ -verify-peer \
+ -verify-depth 2 \
+ -enforce-peer-cert
+
+# Make a request with the client key
+curl -kv \
+ --key examples/https/client1-key.pem \
+ --cert examples/https/client1-crt.pem \
+ https://localhost:4443/
+```
diff --git a/examples/https/bin/generate.sh.in b/examples/https/bin/generate.sh.in
new file mode 100755
index 0000000..6f78154
--- /dev/null
+++ b/examples/https/bin/generate.sh.in
@@ -0,0 +1,58 @@
+#!/usr/bin/env bash
+
+CONFIG_DIR="@PROJECT_BINARY_DIR@/examples/https"
+
+# Create new CA
+openssl req -new -x509 -days 9999 \
+ -config "$CONFIG_DIR/etc/ca.cnf" \
+ -keyout "$CONFIG_DIR/ca-key.pem" \
+ -out "$CONFIG_DIR/ca-crt.pem"
+
+# Generate private key for server
+openssl genrsa -out "$CONFIG_DIR/server-key.pem" 4096
+
+# Generate cert signing request
+openssl req -new \
+ -config "$CONFIG_DIR/etc/server.cnf" \
+ -key "$CONFIG_DIR/server-key.pem" \
+ -out "$CONFIG_DIR/server-csr.pem"
+
+# Sign the request
+openssl x509 -req \
+ -extfile "$CONFIG_DIR/etc/server.cnf" \
+ -days 999 \
+ -passin "pass:password" \
+ -in "$CONFIG_DIR/server-csr.pem" \
+ -CA "$CONFIG_DIR/ca-crt.pem" \
+ -CAkey "$CONFIG_DIR/ca-key.pem" \
+ -CAcreateserial \
+ -out "$CONFIG_DIR/server-crt.pem"
+
+# Generate a few client certs
+openssl genrsa -out "$CONFIG_DIR/client1-key.pem" 4096
+openssl genrsa -out "$CONFIG_DIR/client2-key.pem" 4096
+
+# create two cert sign requests
+openssl req -new -config "$CONFIG_DIR/etc/client1.cnf" -key $CONFIG_DIR/client1-key.pem -out $CONFIG_DIR/client1-csr.pem
+openssl req -new -config $CONFIG_DIR/etc/client2.cnf -key $CONFIG_DIR/client2-key.pem -out $CONFIG_DIR/client2-csr.pem
+
+# sign the above client certs
+openssl x509 -req \
+ -extfile $CONFIG_DIR/etc/client1.cnf \
+ -days 999 \
+ -passin "pass:password" \
+ -in $CONFIG_DIR/client1-csr.pem \
+ -CA $CONFIG_DIR/ca-crt.pem \
+ -CAkey $CONFIG_DIR/ca-key.pem \
+ -CAcreateserial \
+ -out $CONFIG_DIR/client1-crt.pem
+
+openssl x509 -req \
+ -extfile $CONFIG_DIR/etc/client2.cnf \
+ -days 999 \
+ -passin "pass:password" \
+ -in $CONFIG_DIR/client2-csr.pem \
+ -CA $CONFIG_DIR/ca-crt.pem \
+ -CAkey $CONFIG_DIR/ca-key.pem \
+ -CAcreateserial \
+ -out $CONFIG_DIR/client2-crt.pem
diff --git a/examples/https/etc/ca.cnf b/examples/https/etc/ca.cnf
new file mode 100644
index 0000000..53eab64
--- /dev/null
+++ b/examples/https/etc/ca.cnf
@@ -0,0 +1,31 @@
+[ ca ]
+default_ca = CA_default
+
+[ CA_default ]
+serial = ca-serial
+crl = ca-crl.pem
+database = ca-database.txt
+name_opt = CA_default
+cert_opt = CA_default
+default_crl_days = 9999
+default_md = md5
+
+[ req ]
+default_bits = 4096
+days = 9999
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+prompt = no
+output_password = password
+
+[ req_distinguished_name ]
+C = US
+ST = MA
+L = Boston
+O = Critical Stack
+OU = evhtp
+CN = ca
+emailAddress = nate@cl0d.com
+
+[ req_attributes ]
+challengePassword = test
diff --git a/examples/https/etc/client1.cnf b/examples/https/etc/client1.cnf
new file mode 100644
index 0000000..6e881ff
--- /dev/null
+++ b/examples/https/etc/client1.cnf
@@ -0,0 +1,26 @@
+[ req ]
+default_bits = 4096
+days = 9999
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+prompt = no
+x509_extensions = v3_ca
+
+[ req_distinguished_name ]
+C = US
+ST = MA
+L = Boston
+O = Critical Stack
+OU = evhtp
+CN = client1
+emailAddress = nate@cl0d.com
+
+[ req_attributes ]
+challengePassword = password
+
+[ v3_ca ]
+authorityInfoAccess = @issuer_info
+
+[ issuer_info ]
+OCSP;URI.0 = http://ocsp.example.com/
+caIssuers;URI.0 = http://example.com/ca.cert
diff --git a/examples/https/etc/client2.cnf b/examples/https/etc/client2.cnf
new file mode 100644
index 0000000..2cbf570
--- /dev/null
+++ b/examples/https/etc/client2.cnf
@@ -0,0 +1,26 @@
+[ req ]
+default_bits = 4096
+days = 9999
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+prompt = no
+x509_extensions = v3_ca
+
+[ req_distinguished_name ]
+C = US
+ST = MA
+L = Boston
+O = Critical Stack
+OU = evhtp
+CN = client2
+emailAddress = nate@cl0d.com
+
+[ req_attributes ]
+challengePassword = password
+
+[ v3_ca ]
+authorityInfoAccess = @issuer_info
+
+[ issuer_info ]
+OCSP;URI.0 = http://ocsp.example.com/
+caIssuers;URI.0 = http://example.com/ca.cert
diff --git a/examples/https/etc/server.cnf b/examples/https/etc/server.cnf
new file mode 100644
index 0000000..2c36ee4
--- /dev/null
+++ b/examples/https/etc/server.cnf
@@ -0,0 +1,26 @@
+[ req ]
+default_bits = 4096
+days = 9999
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+prompt = no
+x509_extensions = v3_ca
+
+[ req_distinguished_name ]
+C = US
+ST = MA
+L = Boston
+O = Critical Stack
+OU = evhtp
+CN = localhost
+emailAddress = nate@cl0d.com
+
+[ req_attributes ]
+challengePassword = password
+
+[ v3_ca ]
+authorityInfoAccess = @issuer_info
+
+[ issuer_info ]
+OCSP;URI.0 = http://ocsp.example.com/
+caIssuers;URI.0 = http://example.com/ca.cert
diff --git a/examples/https/example_https.c b/examples/https/example_https.c
new file mode 100644
index 0000000..18a4723
--- /dev/null
+++ b/examples/https/example_https.c
@@ -0,0 +1,261 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <getopt.h>
+
+#include "../log.h"
+#include "internal.h"
+#include "evhtp/evhtp.h"
+
+#ifndef EVHTP_DISABLE_SSL
+static void
+http__callback_(evhtp_request_t * req, void * arg) {
+ return evhtp_send_reply(req, EVHTP_RES_OK);
+}
+
+static int
+ssl__x509_verify_(int ok, X509_STORE_CTX * store) {
+ char buf[256];
+ X509 * err_cert;
+ int err;
+ int depth;
+ SSL * ssl;
+ evhtp_connection_t * connection;
+ evhtp_ssl_cfg_t * ssl_cfg;
+
+ err_cert = X509_STORE_CTX_get_current_cert(store);
+ err = X509_STORE_CTX_get_error(store);
+ depth = X509_STORE_CTX_get_error_depth(store);
+ ssl = X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx());
+
+ X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);
+
+ connection = SSL_get_app_data(ssl);
+ ssl_cfg = connection->htp->ssl_cfg;
+
+ if (depth > ssl_cfg->verify_depth) {
+ ok = 0;
+ err = X509_V_ERR_CERT_CHAIN_TOO_LONG;
+
+ X509_STORE_CTX_set_error(store, err);
+ }
+
+ if (!ok) {
+ log_error("SSL: verify error:num=%d:%s:depth=%d:%s", err,
+ X509_verify_cert_error_string(err), depth, buf);
+ }
+
+ return ok;
+}
+
+enum {
+ OPTARG_CERT = 1000,
+ OPTARG_KEY,
+ OPTARG_CA,
+ OPTARG_CAPATH,
+ OPTARG_CIPHERS,
+ OPTARG_VERIFY_PEER,
+ OPTARG_ENFORCE_PEER_CERT,
+ OPTARG_VERIFY_DEPTH,
+ OPTARG_ENABLE_CACHE,
+ OPTARG_CACHE_TIMEOUT,
+ OPTARG_CACHE_SIZE,
+ OPTARG_CTX_TIMEOUT,
+ OPTARG_ENABLE_PROTOCOL,
+ OPTARG_DISABLE_PROTOCOL
+};
+
+static const char * help =
+ "Usage %s [opts] <host>:<port>\n"
+ " -cert <file> : Server PEM-encoded X.509 Certificate file\n"
+ " -key <file> : Server PEM-encoded Private Key file\n"
+ " -ca <file> : File of PEM-encoded Server CA Certificates\n"
+ " -capath <path> : Directory of PEM-encoded CA Certificates for Client Auth\n"
+ " -ciphers <str> : Accepted SSL Ciphers\n"
+ " -verify-peer : Enable SSL client verification\n"
+ " -enforce-peer-cert : Reject clients without a cert\n"
+ " -verify-depth <n> : Maximum depth of CA Certificates in Client Certificate verification\n"
+ " -enable-protocol <p> : Enable one of the following protocols: SSLv2, SSLv3, TLSv1, or ALL\n"
+ " -disable-protocol <p> : Disable one of the following protocols: SSLv2, SSLv3, TLSv1, or ALL\n"
+ " -ctx-timeout <n> : SSL Session Timeout (SSL >= 1.0)\n";
+
+evhtp_ssl_cfg_t *
+parse__ssl_opts_(int argc, char ** argv) {
+ int opt = 0;
+ int long_index = 0;
+ int ssl_verify_mode = 0;
+ struct stat f_stat;
+ evhtp_ssl_cfg_t * ssl_config = calloc(1, sizeof(evhtp_ssl_cfg_t));
+
+
+ ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
+
+ static struct option long_options[] = {
+ { "cert", required_argument, 0, OPTARG_CERT },
+ { "key", required_argument, 0, OPTARG_KEY },
+ { "ca", required_argument, 0, OPTARG_CA },
+ { "capath", required_argument, 0, OPTARG_CAPATH },
+ { "ciphers", required_argument, 0, OPTARG_CIPHERS },
+ { "verify-peer", no_argument, 0, OPTARG_VERIFY_PEER },
+ { "enforce-peer-cert", no_argument, 0, OPTARG_ENFORCE_PEER_CERT },
+ { "verify-depth", required_argument, 0, OPTARG_VERIFY_DEPTH },
+ { "enable-cache", no_argument, 0, OPTARG_ENABLE_CACHE },
+ { "cache-timeout", required_argument, 0, OPTARG_CACHE_TIMEOUT },
+ { "cache-size", required_argument, 0, OPTARG_CACHE_SIZE },
+ { "enable-protocol", required_argument, 0, OPTARG_ENABLE_PROTOCOL },
+ { "disable-protocol", required_argument, 0, OPTARG_DISABLE_PROTOCOL },
+ { "ctx-timeout", required_argument, 0, OPTARG_CTX_TIMEOUT },
+ { "help", no_argument, 0, 'h' },
+ { NULL, 0, 0, 0 }
+ };
+
+ while ((opt = getopt_long_only(argc, argv, "", long_options, &long_index)) != -1) {
+ switch (opt) {
+ case 'h':
+ printf(help, argv[0]);
+ exit(EXIT_FAILURE);
+ case OPTARG_CERT:
+ ssl_config->pemfile = strdup(optarg);
+ break;
+ case OPTARG_KEY:
+ ssl_config->privfile = strdup(optarg);
+ break;
+ case OPTARG_CA:
+ ssl_config->cafile = strdup(optarg);
+ break;
+ case OPTARG_CAPATH:
+ ssl_config->capath = strdup(optarg);
+ break;
+ case OPTARG_CIPHERS:
+ ssl_config->ciphers = strdup(optarg);
+ break;
+ case OPTARG_VERIFY_DEPTH:
+ ssl_config->verify_depth = atoi(optarg);
+ break;
+ case OPTARG_VERIFY_PEER:
+ ssl_verify_mode |= SSL_VERIFY_PEER;
+ break;
+ case OPTARG_ENFORCE_PEER_CERT:
+ ssl_verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
+ break;
+ case OPTARG_ENABLE_CACHE:
+ ssl_config->scache_type = evhtp_ssl_scache_type_internal;
+ break;
+ case OPTARG_CACHE_TIMEOUT:
+ ssl_config->scache_timeout = atoi(optarg);
+ break;
+ case OPTARG_CACHE_SIZE:
+ ssl_config->scache_size = atoi(optarg);
+ break;
+ case OPTARG_CTX_TIMEOUT:
+ ssl_config->ssl_ctx_timeout = atoi(optarg);
+ break;
+ case OPTARG_ENABLE_PROTOCOL:
+ if (!strcasecmp(optarg, "SSLv2")) {
+ ssl_config->ssl_opts &= ~SSL_OP_NO_SSLv2;
+ } else if (!strcasecmp(optarg, "SSLv3")) {
+ ssl_config->ssl_opts &= ~SSL_OP_NO_SSLv3;
+ } else if (!strcasecmp(optarg, "TLSv1")) {
+ ssl_config->ssl_opts &= ~SSL_OP_NO_TLSv1;
+ } else if (!strcasecmp(optarg, "ALL")) {
+ ssl_config->ssl_opts = 0;
+ }
+
+ break;
+ case OPTARG_DISABLE_PROTOCOL:
+ if (!strcasecmp(optarg, "SSLv2")) {
+ ssl_config->ssl_opts |= SSL_OP_NO_SSLv2;
+ } else if (!strcasecmp(optarg, "SSLv3")) {
+ ssl_config->ssl_opts |= SSL_OP_NO_SSLv3;
+ } else if (!strcasecmp(optarg, "TLSv1")) {
+ ssl_config->ssl_opts |= SSL_OP_NO_TLSv1;
+ } else if (!strcasecmp(optarg, "ALL")) {
+ ssl_config->ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1;
+ }
+ break;
+
+ default:
+ break;
+ } /* switch */
+ }
+
+ if (ssl_verify_mode != 0) {
+ ssl_config->verify_peer = ssl_verify_mode;
+ ssl_config->x509_verify_cb = ssl__x509_verify_;
+ }
+
+
+ if (ssl_config->pemfile) {
+ if (stat(ssl_config->pemfile, &f_stat) != 0) {
+ log_error("Cannot load SSL cert '%s' (%s)", ssl_config->pemfile, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (ssl_config->privfile) {
+ if (stat(ssl_config->privfile, &f_stat) != 0) {
+ log_error("Cannot load SSL key '%s' (%s)", ssl_config->privfile, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (ssl_config->cafile) {
+ if (stat(ssl_config->cafile, &f_stat) != 0) {
+ log_error("Cannot find SSL CA File '%s' (%s)", ssl_config->cafile, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (ssl_config->capath) {
+ if (stat(ssl_config->capath, &f_stat) != 0) {
+ log_error("Cannot find SSL CA PATH '%s' (%s)", ssl_config->capath, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ return ssl_config;
+} /* parse__ssl_opts_ */
+#endif
+
+int
+main(int argc, char ** argv) {
+#ifndef EVHTP_DISABLE_SSL
+ evhtp_t * htp;
+ struct event_base * evbase;
+
+ evbase = event_base_new();
+ evhtp_alloc_assert(evbase);
+
+ htp = evhtp_new(evbase, NULL);
+ evhtp_alloc_assert(htp);
+
+ evhtp_ssl_init(htp, parse__ssl_opts_(argc, argv));
+ evhtp_set_gencb(htp, http__callback_, NULL);
+
+ evhtp_bind_socket(htp, "127.0.0.1", 4443, 128);
+ {
+ struct sockaddr_in sin;
+ socklen_t len = sizeof(struct sockaddr);
+ uint16_t port;
+
+ getsockname(
+ evconnlistener_get_fd(htp->server),
+ (struct sockaddr *)&sin, &len);
+
+ port = ntohs(sin.sin_port);
+
+ log_info("curl https://127.0.0.1:%d/", port);
+ }
+
+ event_base_loop(evbase, 0);
+ return 0;
+#else
+ log_error("Not compiled with SSL support, go away");
+ return EXIT_FAILURE;
+#endif
+}
diff --git a/include/evhtp/evhtp.h b/include/evhtp/evhtp.h
index 0c66428..f7cd3d9 100644
--- a/include/evhtp/evhtp.h
+++ b/include/evhtp/evhtp.h
@@ -44,6 +44,11 @@
typedef SSL_CTX evhtp_ssl_ctx_t;
typedef X509 evhtp_x509_t;
typedef X509_STORE_CTX evhtp_x509_store_ctx_t;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+typedef unsigned char evhtp_ssl_data_t;
+#else
+typedef const unsigned char evhtp_ssl_data_t;
+#endif
#else
typedef void evhtp_ssl_sess_t;
typedef void evhtp_ssl_t;
@@ -162,7 +167,7 @@
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_read_cb)(evhtp_request_t * req, struct evbuffer * 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);
@@ -180,16 +185,17 @@
typedef int (* evhtp_ssl_chk_issued_cb)(evhtp_x509_store_ctx_t * ctx, evhtp_x509_t * x, evhtp_x509_t * issuer);
typedef EVP_PKEY * (* evhtp_ssl_decrypt_cb)(char * privfile);
-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 int (* evhtp_ssl_scache_add)(evhtp_connection_t * connection, evhtp_ssl_data_t * sid, int sid_len, evhtp_ssl_sess_t * sess);
+typedef void (* evhtp_ssl_scache_del)(evhtp_t * htp, evhtp_ssl_data_t * sid, int sid_len);
+typedef evhtp_ssl_sess_t * (* evhtp_ssl_scache_get)(evhtp_connection_t * connection, evhtp_ssl_data_t * sid, int sid_len);
+
typedef void * (* evhtp_ssl_scache_init)(evhtp_t *);
#endif
-#define EVHTP_VERSION "1.2.14"
+#define EVHTP_VERSION "1.2.15"
#define EVHTP_VERSION_MAJOR 1
#define EVHTP_VERSION_MINOR 2
-#define EVHTP_VERSION_PATCH 14
+#define EVHTP_VERSION_PATCH 15
#define evhtp_headers_iterator evhtp_kvs_iterator
@@ -395,8 +401,8 @@
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 */
+ struct evbuffer * buffer_in; /**< buffer containing data from client */
+ struct evbuffer * 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 */
@@ -765,25 +771,11 @@
*
* @return 0 on success, -1 on error (if hooks is NULL, it is allocated)
*/
-EVHTP_EXPORT int evhtp_set_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type, evhtp_hook cb, void * arg)
-DEPRECATED("use evhtp_[connection|request|callback]_set_hook() instead of set_hook directly");
-
EVHTP_EXPORT int evhtp_connection_set_hook(evhtp_connection_t * c, evhtp_hook_type type, evhtp_hook cb, void * arg);
EVHTP_EXPORT int evhtp_request_set_hook(evhtp_request_t * r, evhtp_hook_type type, evhtp_hook cb, void * arg);
EVHTP_EXPORT int evhtp_callback_set_hook(evhtp_callback_t * cb, evhtp_hook_type type, evhtp_hook hookcb, void * arg);
/**
- * @brief remove a specific hook from being called.
- *
- * @param hooks
- * @param type
- *
- * @return
- */
-EVHTP_EXPORT int evhtp_unset_hook(evhtp_hooks_t ** hooks, evhtp_hook_type type);
-
-
-/**
* @brief removes all hooks.
*
* @param hooks
@@ -887,7 +879,7 @@
* 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_body(evhtp_request_t * request, struct evbuffer * buf);
EVHTP_EXPORT void evhtp_send_reply_end(evhtp_request_t * request);
/**
@@ -914,7 +906,7 @@
* @param request
* @param buf
*/
-EVHTP_EXPORT void evhtp_send_reply_chunk(evhtp_request_t * request, evbuf_t * buf);
+EVHTP_EXPORT void evhtp_send_reply_chunk(evhtp_request_t * request, struct evbuffer * buf);
/**
* @brief call when all chunks have been sent and you wish to send the last
@@ -996,6 +988,20 @@
*/
EVHTP_EXPORT int evhtp_add_alias(evhtp_t * evhtp, const char * name);
+
+/**
+ * @brief set a variable number of aliases in one call
+ * @reference evhtp_add_alias
+ * @note last argument must be NULL terminated
+ *
+ * @param evhtp
+ * @param name
+ * @param ...
+ *
+ * @return 0 on success, -1 on error
+ */
+EVHTP_EXPORT int evhtp_add_aliases(evhtp_t * evhtp, const char * name, ...);
+
/**
* @brief Allocates a new key/value structure.
*
diff --git a/parser.c b/parser.c
index f8dfeb3..cda246f 100644
--- a/parser.c
+++ b/parser.c
@@ -1,12 +1,6 @@
-#include <stdio.h>
-#include <time.h>
#include <stdlib.h>
#include <stddef.h>
-#include <string.h>
-#include <stdint.h>
-#include <errno.h>
#include <ctype.h>
-#include <unistd.h>
#include "internal.h"
#include "evhtp/parser.h"
diff --git a/thread.c b/thread.c
index 8f03832..7659cee 100644
--- a/thread.c
+++ b/thread.c
@@ -1,15 +1,9 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
#include <stdint.h>
-#include <inttypes.h>
#include <limits.h>
-#include <errno.h>
-#include <fcntl.h>
#ifndef WIN32
-#include <sys/syscall.h>
-#include <sys/ioctl.h>
#include <sys/queue.h>
#endif