Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: ellzey/libevhtp
base: 0096ec33f7
...
head fork: ellzey/libevhtp
compare: 30f28b0812
  • 6 commits
  • 8 files changed
  • 0 commit comments
  • 1 contributor
Commits on Apr 05, 2012
Mark Ellzey Merge branch 'release/0.4.13' into develop ffd1858
Mark Ellzey Added example and documentation on proper thread-safe design with evhtp. 154017e
Commits on Apr 17, 2012
@ellzey Added glob/wildcard callback patterns.
If the application which uses libevhtp wishes to use simple wildcards to match
on a callback, yet does not want the overhead of regular expressions, and does
not care about what specifically matched, just that something did match, a new
glob API was introduced.

evhtp_set_glob_cb(evhtp, "/pattern/*", callback, userdata);

Glob patterns use can use the '*' character at either or both the start and
end of a pattern.

*/stuff/* matches /anything/stuff/anything
*/stuff/  matches /anything/stuff/
/stuff/*  matches /stuff/anything/
d33a416
Commits on Apr 19, 2012
Mark Ellzey Fixed issue with path matching. d0648ff
Mark Ellzey Prep Release v0.4.14 ddc9218
Mark Ellzey Merge branch 'release/0.4.14' 30f28b0
View
2  CMakeLists.txt
@@ -3,7 +3,7 @@ project(reason)
set(PROJECT_MAJOR_VERSION 0)
set(PROJECT_MINOR_VERSION 4)
-set(PROJECT_PATCH_VERSION 13)
+set(PROJECT_PATCH_VERSION 14)
set (PROJECT_VERSION ${PROJECT_MAJOR_VERSION}.${PROJECT_MINOR_VERSION}.${PROJECT_PATCH_VERSION})
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules)
View
5 ChangeLog
@@ -1,3 +1,8 @@
+v0.4.14
+ o Added example and documentation on proper thread-safe design with evhtp. (154017e Mark Ellzey)
+ o Added glob/wildcard callback patterns. (d33a416 Mark Ellzey)
+ o Fixed issue with path matching. (d0648ff Mark Ellzey)
+
v0.4.13
o Decrement pending count when defer fails. (4e99de9 Mark Ellzey)
o Updating documentation to say there is not much documentation. (184f33e Mark Ellzey)
View
6 LICENSE
@@ -26,3 +26,9 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
==============================
+
+Portions of Libevhtp are based on works by others, also made available by them
+under the three-clause BSD license above. The functions include:
+
+evhtp.c: _evhtp_glob_match():
+ Copyright (c) 2006-2009, Salvatore Sanfilippo
View
17 README.markdown
@@ -74,3 +74,20 @@ Libevhtp attempts to address these problems along with a wide variety of cool me
}
+## 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.
+
+But with proper design around libevhtp, thread issues can be out-of-sight,
+out-of-mind.
+
+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.
+
+This example uses redis, mainly because most people who have asked me "is evhtp
+thread-safe" were attempting to *other things* before sending a response to a
+request. And on more than one occasion, those *other things* were communicating
+with redis.
View
170 evhtp.c
@@ -506,6 +506,108 @@ _evhtp_callback_hash_find(evhtp_callbacks_t * callbacks, const char * path) {
}
/**
+ * @brief glob/wildcard type pattern matching.
+ *
+ * Note: This code was derived from redis's (v2.6) stringmatchlen() function.
+ *
+ * @param pattern
+ * @param string
+ *
+ * @return
+ */
+static int
+_evhtp_glob_match(const char * pattern, const char * string) {
+ size_t pat_len;
+ size_t str_len;
+
+ if (!pattern || !string) {
+ return 0;
+ }
+
+ pat_len = strlen(pattern);
+ str_len = strlen(string);
+
+ while (pat_len) {
+ if (pattern[0] == '*') {
+ while (pattern[1] == '*') {
+ pattern++;
+ pat_len--;
+ }
+
+ if (pat_len == 1) {
+ return 1;
+ }
+
+ while (str_len) {
+ if (_evhtp_glob_match(pattern + 1, string)) {
+ return 1;
+ }
+
+ string++;
+ str_len--;
+ }
+
+ return 0;
+ } else {
+ if (pattern[0] != string[0]) {
+ return 0;
+ }
+
+ string++;
+ str_len--;
+ }
+
+ pattern++;
+ pat_len--;
+
+ if (str_len == 0) {
+ while (*pattern == '*') {
+ pattern++;
+ pat_len--;
+ }
+ break;
+ }
+ }
+
+ if (pat_len == 0 && str_len == 0) {
+ return 1;
+ }
+
+ return 0;
+} /* _evhtp_glob_match */
+
+/**
+ * @brief iterate through a list of glob/wildcard defined callbacks.
+ *
+ * @param callbacks
+ * @param path
+ *
+ * @return
+ */
+static evhtp_callback_t *
+_evhtp_callback_glob_find(evhtp_callbacks_t * callbacks, const char * path) {
+ evhtp_callback_t * callback;
+
+ if (!callbacks || !path) {
+ return NULL;
+ }
+
+ callback = callbacks->glob_callbacks;
+
+ while (callback != NULL) {
+ if (callback->type == evhtp_callback_type_glob) {
+ if (_evhtp_glob_match(callback->val.glob, path) == 1) {
+ return callback;
+ }
+ }
+
+ callback = callback->next;
+ }
+
+ return NULL;
+}
+
+/**
* @brief iterate through a tailq of callback hooks defined as a regex until a
* match is found.
*
@@ -549,7 +651,11 @@ _evhtp_callback_regex_find(evhtp_callbacks_t * callbacks, const char * path,
#endif
/**
- * @brief A wrapper around both hash and regex hook lookups
+ * @brief A wrapper around hash, glob, and regex hook lookups
+ * Search is done in this order:
+ * hash
+ * glob
+ * regex
*
* @param callbacks
* @param path
@@ -575,6 +681,12 @@ _evhtp_callback_find(evhtp_callbacks_t * callbacks,
return callback;
}
+ if ((callback = _evhtp_callback_glob_find(callbacks, path)) != NULL) {
+ *start_offset = 0;
+ *end_offset = (unsigned int)strlen(path);
+ return callback;
+ }
+
#ifndef EVHTP_DISABLE_REGEX
if ((callback = _evhtp_callback_regex_find(callbacks, path,
start_offset, end_offset)) != NULL) {
@@ -914,9 +1026,9 @@ _evhtp_request_parser_path(htparser * p, const char * data, size_t len) {
evhtp_hooks_t * hooks = NULL;
evhtp_callback_t * callback = NULL;
evhtp_callback_cb cb = NULL;
+ void * cbarg = NULL;
evhtp_uri_t * uri;
evhtp_path_t * path;
- void * cbarg = NULL;
char * match_start;
char * match_end;
@@ -958,9 +1070,13 @@ _evhtp_request_parser_path(htparser * p, const char * data, size_t len) {
match_start = calloc(strlen(path->full) + 1, 1);
match_end = calloc(strlen(path->full) + 1, 1);
- memcpy(match_start,
- (void *)(path->full + path->matched_soff),
- path->matched_eoff);
+ if (path->matched_eoff - path->matched_soff) {
+ memcpy(match_start, (void *)(path->full + path->matched_soff),
+ path->matched_eoff - path->matched_soff);
+ } else {
+ memcpy(match_start, (void *)(path->full + path->matched_soff),
+ strlen((const char *)(path->full + path->matched_soff)));
+ }
memcpy(match_end,
(void *)(path->full + path->matched_eoff),
@@ -2409,10 +2525,13 @@ evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_c
}
break;
#endif
+ case evhtp_callback_type_glob:
+ hcb->val.glob = strdup(path);
+ break;
default:
free(hcb);
return NULL;
- }
+ } /* switch */
return hcb;
}
@@ -2429,6 +2548,11 @@ evhtp_callback_free(evhtp_callback_t * callback) {
free(callback->val.path);
}
break;
+ case evhtp_callback_type_glob:
+ if (callback->val.glob) {
+ free(callback->val.glob);
+ }
+ break;
#ifndef EVHTP_DISABLE_REGEX
case evhtp_callback_type_regex:
if (callback->val.regex) {
@@ -2464,9 +2588,13 @@ evhtp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb) {
cbs->regex_callbacks = cb;
break;
#endif
+ case evhtp_callback_type_glob:
+ cb->next = cbs->glob_callbacks;
+ cbs->glob_callbacks = cb;
+ break;
default:
return -1;
- }
+ } /* switch */
return 0;
}
@@ -2691,6 +2819,34 @@ evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, vo
#endif
+evhtp_callback_t *
+evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg) {
+ evhtp_callback_t * hcb;
+
+ _evhtp_lock(htp);
+
+ if (htp->callbacks == NULL) {
+ if (!(htp->callbacks = evhtp_callbacks_new(1024))) {
+ _evhtp_unlock(htp);
+ return NULL;
+ }
+ }
+
+ 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;
+}
+
void
evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * arg) {
htp->defaults.cb = cb;
View
42 evhtp.h
@@ -109,8 +109,9 @@ enum evhtp_hook_type {
enum evhtp_callback_type {
evhtp_callback_type_hash,
#ifndef EVHTP_DISABLE_REGEX
- evhtp_callback_type_regex
+ evhtp_callback_type_regex,
#endif
+ evhtp_callback_type_glob
};
enum evhtp_proto {
@@ -151,10 +152,10 @@ typedef void (*evhtp_ssl_scache_del)(evhtp_t * htp, unsigned char * sid, int sid
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 *);
-#define EVHTP_VERSION "0.4.13"
+#define EVHTP_VERSION "0.4.14"
#define EVHTP_VERSION_MAJOR 0
#define EVHTP_VERSION_MINOR 4
-#define EVHTP_VERSION_PATCH 13
+#define EVHTP_VERSION_PATCH 14
#define evhtp_headers_iterator evhtp_kvs_iterator
@@ -263,12 +264,13 @@ struct evhtp_s {
*
*/
struct evhtp_callbacks_s {
- evhtp_callback_t ** callbacks; /**< hash of path callbacks */
+ evhtp_callback_t ** callbacks; /**< hash of path callbacks */
#ifndef EVHTP_DISABLE_REGEX
- evhtp_callback_t * regex_callbacks; /**< list of regex callbacks */
+ evhtp_callback_t * regex_callbacks; /**< list of regex callbacks */
#endif
- unsigned int count; /**< number of callbacks defined */
- unsigned int buckets; /**< buckets allocated for hash */
+ evhtp_callback_t * glob_callbacks; /**< list of wildcard callbacks */
+ unsigned int count; /**< number of callbacks defined */
+ unsigned int buckets; /**< buckets allocated for hash */
};
/**
@@ -286,14 +288,15 @@ struct evhtp_callbacks_s {
*
*/
struct evhtp_callback_s {
- evhtp_callback_type type; /**< the type of callback (regex|path) */
- evhtp_callback_cb cb; /**< the actual callback function */
- unsigned int hash; /**< the full hash generated integer */
- void * cbarg; /**< user-defind arguments passed to the cb */
- evhtp_hooks_t * hooks; /**< per-callback hooks */
+ evhtp_callback_type type; /**< the type of callback (regex|path) */
+ evhtp_callback_cb cb; /**< the actual callback function */
+ unsigned int hash; /**< the full hash generated integer */
+ void * cbarg; /**< user-defind arguments passed to the cb */
+ evhtp_hooks_t * hooks; /**< per-callback hooks */
union {
char * path;
+ char * glob;
#ifndef EVHTP_DISABLE_REGEX
regex_t * regex;
#endif
@@ -522,6 +525,21 @@ evhtp_callback_t * evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp
#endif
+
+/**
+ * @brief sets a callback to to be executed on simple glob/wildcard patterns
+ * this is useful if the app does not care about what was matched, but
+ * just that it matched. This is technically faster than regex.
+ *
+ * @param htp
+ * @param pattern wildcard pattern, the '*' can be set at either or both the front or end.
+ * @param cb
+ * @param arg
+ *
+ * @return
+ */
+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 .
*
View
280 examples/thread_design.c
@@ -0,0 +1,280 @@
+/*
+ * How to exploit the wonders of libevhtp's threading model to avoid using
+ * libevent's locking API.
+ *
+ * In this example we use Redis's Async API (Libhiredis) store and retr the following
+ * information for a request:
+ *
+ * Total requests seen.
+ * Total requests seen by the requestors IP address.
+ * All of the source ports seen used by the requestors IP address.
+ *
+ * We do this all using libevhtp's builtin thread-pool model, without the use of
+ * mutexes or evthread_use_pthreads() type stuff.
+ *
+ * The technique is simple:
+ * 1. Create your evhtp_t structure, assign callbacks like usual.
+ * 2. Call evhtp_use_threads() with a thread init callback.
+ * 3. Each time a thread starts, the thread init callback you defined will be
+ * called with information about that thread.
+ *
+ * First a bit of information about how evhtp does threading:
+ * libevhtp uses the evthr library, which works more like a threaded
+ * co-routine than a threadpool. Each evthr in a pool has its own unique
+ * event_base (and each evthr runs its own event_base_loop()). Under the
+ * hood when libevhtp sends a request to a thread, it calls
+ * "evthr_pool_defer(pool, _run_connection_in_thread, ...).
+ *
+ * The evthr library then finds a thread inside the pool with the lowest backlog,
+ * sends a packet over that threads socketpair containing information about what
+ * function to execute. It uses socketpairs because they can be treated as
+ * an event, thus able to be processed in a threads own unique
+ * event_base_loop().
+ *
+ * Knowing that, a connection in evhtp is never associated with the initial
+ * event_base that was passed to evhtp_new(), but instead the connection
+ * uses the evthr's unique event_base. This is what makes libevhtp's
+ * safe from thread-related race conditions.
+ *
+ * 4. Use the thread init callback as a place to put event type things on the
+ * threads event_base() instead of using the global one.
+ *
+ * In this code, that function is app_init_thread(). When this function is
+ * called, the first argument is the evthr_t of the thread that just
+ * started. This function uses "evthr_get_base(thread)" to get the
+ * event_base associated with this specific thread.
+ *
+ * Using that event_base, the function will start up an async redis
+ * connection. This redis connection is now tied to that thread, and can be
+ * used on a threaded request without locking (remember that your request
+ * has the same event_base as the thread it was executed in).
+ *
+ * We allocate a dummy structure "struct app" and then call
+ * "evthr_set_aux(thread, app)". This function sets some aux data which can
+ * be fetched at any point using evthr_get_aux(thread). We use this later on
+ * inside process_request()
+ *
+ * This part is the secret to evhtp threading success.
+ *
+ * 5. When a request has been fully processed, it will call the function
+ * "app_process_request()". Note here that the "arg" argument is NULL since no
+ * arguments were passed to evhtp_set_gencb().
+ *
+ * Since we want to do a bunch of redis stuff before sending a reply to the
+ * client, we must fetch the "struct app" data we allocated and set for the
+ * thread associated with this request (struct app * app =
+ * evthr_get_aux(thread);).
+ *
+ * struct app has our thread-specific redis connection ctx, so using that
+ * redisAsyncCommand() is called a bunch of times to queue up the commands
+ * which will be run.
+ *
+ * The last part of this technique is to call the function
+ * "evhtp_request_pause()". This essentially tells evhtp to flip the
+ * read-side of the connections file-descriptor OFF (This avoids potential
+ * situations where a client disconnected before all of the redis commands
+ * executed).
+ *
+ * 6. Each redis command is executed in order, and each callback will write to
+ * the requests output_buffer with relevant information from the result.
+ *
+ * 7. The last redis callback executed here is "redis_get_srcport_cb". It is
+ * the job os this function to call evhtp_send_reply() and then
+ * evhtp_request_resume().
+ *
+ * Using this design in conjunction with libevhtp makes the world an easier
+ * place to code.
+ *
+ * Compile: gcc thread_design.c -o thread_design -levhtp -levent -lhiredis
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+
+#include <evhtp.h>
+#include <hiredis/hiredis.h>
+#include <hiredis/async.h>
+#include <hiredis/adapters/libevent.h>
+
+struct app_parent {
+ evhtp_t * evhtp;
+ evbase_t * evbase;
+ char * redis_host;
+ uint16_t redis_port;
+};
+
+struct app {
+ struct app_parent * parent;
+ evbase_t * evbase;
+ redisAsyncContext * redis;
+};
+
+static evthr_t *
+get_request_thr(evhtp_request_t * request) {
+ evhtp_connection_t * htpconn;
+ evthr_t * thread;
+
+ htpconn = evhtp_request_get_connection(request);
+ thread = htpconn->thread;
+
+ return thread;
+}
+
+void
+redis_global_incr_cb(redisAsyncContext * redis, void * redis_reply, void * arg) {
+ redisReply * reply = redis_reply;
+ evhtp_request_t * request = arg;
+
+ if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) {
+ evbuffer_add_printf(request->buffer_out,
+ "redis_global_incr_cb() failed\n");
+ return;
+ }
+
+ evbuffer_add_printf(request->buffer_out,
+ "Total requests = %lld\n", reply->integer);
+}
+
+void
+redis_srcaddr_incr_cb(redisAsyncContext * redis, void * redis_reply, void * arg) {
+ redisReply * reply = redis_reply;
+ evhtp_request_t * request = arg;
+
+ if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) {
+ evbuffer_add_printf(request->buffer_out,
+ "redis_srcaddr_incr_cb() failed\n");
+ return;
+ }
+
+ evbuffer_add_printf(request->buffer_out,
+ "Requests from this source IP = %lld\n", reply->integer);
+}
+
+void
+redis_set_srcport_cb(redisAsyncContext * redis, void * redis_reply, void * arg) {
+ redisReply * reply = redis_reply;
+ evhtp_request_t * request = arg;
+
+ if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) {
+ evbuffer_add_printf(request->buffer_out,
+ "redis_set_srcport_cb() failed\n");
+ return;
+ }
+
+ if (!reply->integer) {
+ evbuffer_add_printf(request->buffer_out,
+ "This source port has been seen already.\n");
+ } else {
+ evbuffer_add_printf(request->buffer_out,
+ "This source port has never been seen.\n");
+ }
+}
+
+void
+redis_get_srcport_cb(redisAsyncContext * redis, void * redis_reply, void * arg) {
+ redisReply * reply = redis_reply;
+ evhtp_request_t * request = arg;
+ int i;
+
+ if (reply == NULL || reply->type != REDIS_REPLY_ARRAY) {
+ evbuffer_add_printf(request->buffer_out,
+ "redis_get_srcport_cb() failed.\n");
+ return;
+ }
+
+ evbuffer_add_printf(request->buffer_out,
+ "source ports which have been seen for your ip:\n");
+
+ for (i = 0; i < reply->elements; i++) {
+ redisReply * elem = reply->element[i];
+
+ evbuffer_add_printf(request->buffer_out, "%s ", elem->str);
+ }
+
+ evbuffer_add(request->buffer_out, "\n", 1);
+
+ /* final callback for redis, so send the response */
+ evhtp_send_reply(request, EVHTP_RES_OK);
+ evhtp_request_resume(request);
+}
+
+void
+app_process_request(evhtp_request_t * request, void * arg) {
+ struct sockaddr_in * sin;
+ struct app_parent * app_parent;
+ struct app * app;
+ evthr_t * thread;
+ evhtp_connection_t * conn;
+ char tmp[1024];
+
+ thread = get_request_thr(request);
+ conn = evhtp_request_get_connection(request);
+ app = (struct app *)evthr_get_aux(thread);
+ sin = (struct sockaddr_in *)conn->saddr;
+
+ evutil_inet_ntop(AF_INET, &sin->sin_addr, tmp, sizeof(tmp));
+
+ /* increment a global counter of hits on redis */
+ redisAsyncCommand(app->redis, redis_global_incr_cb,
+ (void *)request, "INCR requests:total");
+
+ /* increment a counter for hits from this source address on redis */
+ redisAsyncCommand(app->redis, redis_srcaddr_incr_cb,
+ (void *)request, "INCR requests:ip:%s", tmp);
+
+ /* add the source port of this request to a source-specific set */
+ redisAsyncCommand(app->redis, redis_set_srcport_cb, (void *)request,
+ "SADD requests:ip:%s:ports %d", tmp, ntohs(sin->sin_port));
+
+ /* get all of the ports this source address has used */
+ redisAsyncCommand(app->redis, redis_get_srcport_cb, (void *)request,
+ "SMEMBERS requests:ip:%s:ports", tmp);
+
+ /* pause the request processing */
+ evhtp_request_pause(request);
+}
+
+void
+app_init_thread(evhtp_t * htp, evthr_t * thread, void * arg) {
+ struct app_parent * app_parent;
+ struct app * app;
+
+ app_parent = (struct app_parent *)arg;
+ app = calloc(sizeof(struct app), 1);
+
+ app->parent = app_parent;
+ app->evbase = evthr_get_base(thread);
+ app->redis = redisAsyncConnect(app_parent->redis_host, app_parent->redis_port);
+
+ redisLibeventAttach(app->redis, app->evbase);
+
+ evthr_set_aux(thread, app);
+}
+
+int
+main(int argc, char ** argv) {
+ evbase_t * evbase;
+ evhtp_t * evhtp;
+ struct app_parent * app_p;
+
+ evbase = event_base_new();
+ evhtp = evhtp_new(evbase, NULL);
+ app_p = calloc(sizeof(struct app_parent), 1);
+
+ app_p->evhtp = evhtp;
+ app_p->evbase = evbase;
+ app_p->redis_host = "127.0.0.1";
+ app_p->redis_port = 6379;
+
+ evhtp_set_gencb(evhtp, app_process_request, NULL);
+ evhtp_use_threads(evhtp, app_init_thread, 4, app_p);
+ evhtp_bind_socket(evhtp, "127.0.0.1", 9090, 1024);
+
+ event_base_loop(evbase, 0);
+
+ return 0;
+}
+
View
21 test.c
@@ -14,6 +14,7 @@ int num_threads = 0;
#endif
char * bind_addr = "0.0.0.0";
uint16_t bind_port = 8081;
+char * ext_body = NULL;
char * ssl_pem = NULL;
char * ssl_ca = NULL;
char * ssl_capath = NULL;
@@ -135,6 +136,7 @@ test_regex(evhtp_request_t * req, void * arg) {
evhtp_send_reply(req, EVHTP_RES_OK);
}
+
#endif
static void
@@ -218,6 +220,12 @@ test_bar_cb(evhtp_request_t * req, void * arg) {
}
static void
+test_glob_cb(evhtp_request_t * req, void * arg) {
+ evbuffer_add(req->buffer_out, "test_glob_cb\n", 13);
+ evhtp_send_reply(req, EVHTP_RES_OK);
+}
+
+static void
test_default_cb(evhtp_request_t * req, void * arg) {
evbuffer_add_reference(req->buffer_out,
"test_default_cb\n", 16, NULL, NULL);
@@ -252,6 +260,10 @@ print_kvs(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg ) {
static evhtp_res
print_path(evhtp_request_t * req, evhtp_path_t * path, void * arg) {
+ if (ext_body) {
+ evbuffer_add_printf(req->buffer_out, "ext_body: '%s'\n", ext_body);
+ }
+
evbuffer_add_printf(req->buffer_out,
"print_path() full = '%s'\n"
" path = '%s'\n"
@@ -304,6 +316,7 @@ static evhtp_res
test_regex_hdrs_cb(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg ) {
return EVHTP_RES_OK;
}
+
#endif
static evhtp_res
@@ -368,7 +381,7 @@ dummy_check_issued_cb(X509_STORE_CTX * ctx, X509 * x, X509 * issuer) {
#endif
-const char * optstr = "htn:a:p:r:s:c:C:l:";
+const char * optstr = "htn:a:p:r:s:c:C:l:N:";
const char * help =
"Options: \n"
@@ -384,6 +397,7 @@ const char * help =
#endif
" -l <int> : Max bandwidth (in bytes) (default: NULL)\n"
" -r <str> : Document root (default: .)\n"
+ " -N <str> : Add this string to body. (default: NULL)\n"
" -a <str> : Bind Address (default: 0.0.0.0)\n"
" -p <int> : Bind Port (default: 8081)\n";
@@ -401,6 +415,9 @@ parse_args(int argc, char ** argv) {
case 'h':
printf("Usage: %s [opts]\n%s", argv[0], help);
return -1;
+ case 'N':
+ ext_body = strdup(optarg);
+ break;
case 'a':
bind_addr = strdup(optarg);
break;
@@ -461,6 +478,7 @@ main(int argc, char ** argv) {
evhtp_callback_t * cb_6 = NULL;
evhtp_callback_t * cb_7 = NULL;
evhtp_callback_t * cb_8 = NULL;
+ evhtp_callback_t * cb_9 = NULL;
if (parse_args(argc, argv) < 0) {
exit(1);
@@ -483,6 +501,7 @@ main(int argc, char ** argv) {
#ifndef EVHTP_DISABLE_REGEX
cb_8 = evhtp_set_regex_cb(htp, "^/create/(.*)", create_callback, NULL);
#endif
+ cb_9 = evhtp_set_glob_cb(htp, "*/glob/*", test_glob_cb, NULL);
/* set a callback to test out chunking API */
evhtp_set_cb(htp, "/chunkme", test_chunking, NULL);

No commit comments for this range

Something went wrong with that request. Please try again.