Skip to content
This repository
  • 6 commits
  • 8 files changed
  • 0 comments
  • 1 contributor
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
Apr 17, 2012
Mark 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
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
2  CMakeLists.txt
@@ -3,7 +3,7 @@ project(reason)
3 3
4 4 set(PROJECT_MAJOR_VERSION 0)
5 5 set(PROJECT_MINOR_VERSION 4)
6   -set(PROJECT_PATCH_VERSION 13)
  6 +set(PROJECT_PATCH_VERSION 14)
7 7
8 8 set (PROJECT_VERSION ${PROJECT_MAJOR_VERSION}.${PROJECT_MINOR_VERSION}.${PROJECT_PATCH_VERSION})
9 9 set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules)
5 ChangeLog
... ... @@ -1,3 +1,8 @@
  1 +v0.4.14
  2 + o Added example and documentation on proper thread-safe design with evhtp. (154017e Mark Ellzey)
  3 + o Added glob/wildcard callback patterns. (d33a416 Mark Ellzey)
  4 + o Fixed issue with path matching. (d0648ff Mark Ellzey)
  5 +
1 6 v0.4.13
2 7 o Decrement pending count when defer fails. (4e99de9 Mark Ellzey)
3 8 o Updating documentation to say there is not much documentation. (184f33e Mark Ellzey)
6 LICENSE
@@ -26,3 +26,9 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 27 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 28 ==============================
  29 +
  30 +Portions of Libevhtp are based on works by others, also made available by them
  31 +under the three-clause BSD license above. The functions include:
  32 +
  33 +evhtp.c: _evhtp_glob_match():
  34 + Copyright (c) 2006-2009, Salvatore Sanfilippo
17 README.markdown
Source Rendered
@@ -74,3 +74,20 @@ Libevhtp attempts to address these problems along with a wide variety of cool me
74 74 }
75 75
76 76
  77 +## Is evhtp thread-safe?
  78 +
  79 +For simple usage with evhtp_use_threads(), yes. But for more extreme cases:
  80 +sorta, you are bound to the thread mechanisms of libevent itself.
  81 +
  82 +But with proper design around libevhtp, thread issues can be out-of-sight,
  83 +out-of-mind.
  84 +
  85 +What do you mean by this "proper design" statement?
  86 +
  87 +Refer to the code in ./examples/thread_design.c. The comments go into great detail
  88 +of the hows and whys for proper design using libevhtp's threading model.
  89 +
  90 +This example uses redis, mainly because most people who have asked me "is evhtp
  91 +thread-safe" were attempting to *other things* before sending a response to a
  92 +request. And on more than one occasion, those *other things* were communicating
  93 +with redis.
170 evhtp.c
@@ -506,6 +506,108 @@ _evhtp_callback_hash_find(evhtp_callbacks_t * callbacks, const char * path) {
506 506 }
507 507
508 508 /**
  509 + * @brief glob/wildcard type pattern matching.
  510 + *
  511 + * Note: This code was derived from redis's (v2.6) stringmatchlen() function.
  512 + *
  513 + * @param pattern
  514 + * @param string
  515 + *
  516 + * @return
  517 + */
  518 +static int
  519 +_evhtp_glob_match(const char * pattern, const char * string) {
  520 + size_t pat_len;
  521 + size_t str_len;
  522 +
  523 + if (!pattern || !string) {
  524 + return 0;
  525 + }
  526 +
  527 + pat_len = strlen(pattern);
  528 + str_len = strlen(string);
  529 +
  530 + while (pat_len) {
  531 + if (pattern[0] == '*') {
  532 + while (pattern[1] == '*') {
  533 + pattern++;
  534 + pat_len--;
  535 + }
  536 +
  537 + if (pat_len == 1) {
  538 + return 1;
  539 + }
  540 +
  541 + while (str_len) {
  542 + if (_evhtp_glob_match(pattern + 1, string)) {
  543 + return 1;
  544 + }
  545 +
  546 + string++;
  547 + str_len--;
  548 + }
  549 +
  550 + return 0;
  551 + } else {
  552 + if (pattern[0] != string[0]) {
  553 + return 0;
  554 + }
  555 +
  556 + string++;
  557 + str_len--;
  558 + }
  559 +
  560 + pattern++;
  561 + pat_len--;
  562 +
  563 + if (str_len == 0) {
  564 + while (*pattern == '*') {
  565 + pattern++;
  566 + pat_len--;
  567 + }
  568 + break;
  569 + }
  570 + }
  571 +
  572 + if (pat_len == 0 && str_len == 0) {
  573 + return 1;
  574 + }
  575 +
  576 + return 0;
  577 +} /* _evhtp_glob_match */
  578 +
  579 +/**
  580 + * @brief iterate through a list of glob/wildcard defined callbacks.
  581 + *
  582 + * @param callbacks
  583 + * @param path
  584 + *
  585 + * @return
  586 + */
  587 +static evhtp_callback_t *
  588 +_evhtp_callback_glob_find(evhtp_callbacks_t * callbacks, const char * path) {
  589 + evhtp_callback_t * callback;
  590 +
  591 + if (!callbacks || !path) {
  592 + return NULL;
  593 + }
  594 +
  595 + callback = callbacks->glob_callbacks;
  596 +
  597 + while (callback != NULL) {
  598 + if (callback->type == evhtp_callback_type_glob) {
  599 + if (_evhtp_glob_match(callback->val.glob, path) == 1) {
  600 + return callback;
  601 + }
  602 + }
  603 +
  604 + callback = callback->next;
  605 + }
  606 +
  607 + return NULL;
  608 +}
  609 +
  610 +/**
509 611 * @brief iterate through a tailq of callback hooks defined as a regex until a
510 612 * match is found.
511 613 *
@@ -549,7 +651,11 @@ _evhtp_callback_regex_find(evhtp_callbacks_t * callbacks, const char * path,
549 651 #endif
550 652
551 653 /**
552   - * @brief A wrapper around both hash and regex hook lookups
  654 + * @brief A wrapper around hash, glob, and regex hook lookups
  655 + * Search is done in this order:
  656 + * hash
  657 + * glob
  658 + * regex
553 659 *
554 660 * @param callbacks
555 661 * @param path
@@ -575,6 +681,12 @@ _evhtp_callback_find(evhtp_callbacks_t * callbacks,
575 681 return callback;
576 682 }
577 683
  684 + if ((callback = _evhtp_callback_glob_find(callbacks, path)) != NULL) {
  685 + *start_offset = 0;
  686 + *end_offset = (unsigned int)strlen(path);
  687 + return callback;
  688 + }
  689 +
578 690 #ifndef EVHTP_DISABLE_REGEX
579 691 if ((callback = _evhtp_callback_regex_find(callbacks, path,
580 692 start_offset, end_offset)) != NULL) {
@@ -914,9 +1026,9 @@ _evhtp_request_parser_path(htparser * p, const char * data, size_t len) {
914 1026 evhtp_hooks_t * hooks = NULL;
915 1027 evhtp_callback_t * callback = NULL;
916 1028 evhtp_callback_cb cb = NULL;
  1029 + void * cbarg = NULL;
917 1030 evhtp_uri_t * uri;
918 1031 evhtp_path_t * path;
919   - void * cbarg = NULL;
920 1032 char * match_start;
921 1033 char * match_end;
922 1034
@@ -958,9 +1070,13 @@ _evhtp_request_parser_path(htparser * p, const char * data, size_t len) {
958 1070 match_start = calloc(strlen(path->full) + 1, 1);
959 1071 match_end = calloc(strlen(path->full) + 1, 1);
960 1072
961   - memcpy(match_start,
962   - (void *)(path->full + path->matched_soff),
963   - path->matched_eoff);
  1073 + if (path->matched_eoff - path->matched_soff) {
  1074 + memcpy(match_start, (void *)(path->full + path->matched_soff),
  1075 + path->matched_eoff - path->matched_soff);
  1076 + } else {
  1077 + memcpy(match_start, (void *)(path->full + path->matched_soff),
  1078 + strlen((const char *)(path->full + path->matched_soff)));
  1079 + }
964 1080
965 1081 memcpy(match_end,
966 1082 (void *)(path->full + path->matched_eoff),
@@ -2409,10 +2525,13 @@ evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_c
2409 2525 }
2410 2526 break;
2411 2527 #endif
  2528 + case evhtp_callback_type_glob:
  2529 + hcb->val.glob = strdup(path);
  2530 + break;
2412 2531 default:
2413 2532 free(hcb);
2414 2533 return NULL;
2415   - }
  2534 + } /* switch */
2416 2535
2417 2536 return hcb;
2418 2537 }
@@ -2429,6 +2548,11 @@ evhtp_callback_free(evhtp_callback_t * callback) {
2429 2548 free(callback->val.path);
2430 2549 }
2431 2550 break;
  2551 + case evhtp_callback_type_glob:
  2552 + if (callback->val.glob) {
  2553 + free(callback->val.glob);
  2554 + }
  2555 + break;
2432 2556 #ifndef EVHTP_DISABLE_REGEX
2433 2557 case evhtp_callback_type_regex:
2434 2558 if (callback->val.regex) {
@@ -2464,9 +2588,13 @@ evhtp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb) {
2464 2588 cbs->regex_callbacks = cb;
2465 2589 break;
2466 2590 #endif
  2591 + case evhtp_callback_type_glob:
  2592 + cb->next = cbs->glob_callbacks;
  2593 + cbs->glob_callbacks = cb;
  2594 + break;
2467 2595 default:
2468 2596 return -1;
2469   - }
  2597 + } /* switch */
2470 2598
2471 2599 return 0;
2472 2600 }
@@ -2691,6 +2819,34 @@ evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, vo
2691 2819
2692 2820 #endif
2693 2821
  2822 +evhtp_callback_t *
  2823 +evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg) {
  2824 + evhtp_callback_t * hcb;
  2825 +
  2826 + _evhtp_lock(htp);
  2827 +
  2828 + if (htp->callbacks == NULL) {
  2829 + if (!(htp->callbacks = evhtp_callbacks_new(1024))) {
  2830 + _evhtp_unlock(htp);
  2831 + return NULL;
  2832 + }
  2833 + }
  2834 +
  2835 + if (!(hcb = evhtp_callback_new(pattern, evhtp_callback_type_glob, cb, arg))) {
  2836 + _evhtp_unlock(htp);
  2837 + return NULL;
  2838 + }
  2839 +
  2840 + if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) {
  2841 + evhtp_callback_free(hcb);
  2842 + _evhtp_unlock(htp);
  2843 + return NULL;
  2844 + }
  2845 +
  2846 + _evhtp_unlock(htp);
  2847 + return hcb;
  2848 +}
  2849 +
2694 2850 void
2695 2851 evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * arg) {
2696 2852 htp->defaults.cb = cb;
42 evhtp.h
@@ -109,8 +109,9 @@ enum evhtp_hook_type {
109 109 enum evhtp_callback_type {
110 110 evhtp_callback_type_hash,
111 111 #ifndef EVHTP_DISABLE_REGEX
112   - evhtp_callback_type_regex
  112 + evhtp_callback_type_regex,
113 113 #endif
  114 + evhtp_callback_type_glob
114 115 };
115 116
116 117 enum evhtp_proto {
@@ -151,10 +152,10 @@ typedef void (*evhtp_ssl_scache_del)(evhtp_t * htp, unsigned char * sid, int sid
151 152 typedef evhtp_ssl_sess_t * (*evhtp_ssl_scache_get)(evhtp_connection_t * connection, unsigned char * sid, int sid_len);
152 153 typedef void * (*evhtp_ssl_scache_init)(evhtp_t *);
153 154
154   -#define EVHTP_VERSION "0.4.13"
  155 +#define EVHTP_VERSION "0.4.14"
155 156 #define EVHTP_VERSION_MAJOR 0
156 157 #define EVHTP_VERSION_MINOR 4
157   -#define EVHTP_VERSION_PATCH 13
  158 +#define EVHTP_VERSION_PATCH 14
158 159
159 160 #define evhtp_headers_iterator evhtp_kvs_iterator
160 161
@@ -263,12 +264,13 @@ struct evhtp_s {
263 264 *
264 265 */
265 266 struct evhtp_callbacks_s {
266   - evhtp_callback_t ** callbacks; /**< hash of path callbacks */
  267 + evhtp_callback_t ** callbacks; /**< hash of path callbacks */
267 268 #ifndef EVHTP_DISABLE_REGEX
268   - evhtp_callback_t * regex_callbacks; /**< list of regex callbacks */
  269 + evhtp_callback_t * regex_callbacks; /**< list of regex callbacks */
269 270 #endif
270   - unsigned int count; /**< number of callbacks defined */
271   - unsigned int buckets; /**< buckets allocated for hash */
  271 + evhtp_callback_t * glob_callbacks; /**< list of wildcard callbacks */
  272 + unsigned int count; /**< number of callbacks defined */
  273 + unsigned int buckets; /**< buckets allocated for hash */
272 274 };
273 275
274 276 /**
@@ -286,14 +288,15 @@ struct evhtp_callbacks_s {
286 288 *
287 289 */
288 290 struct evhtp_callback_s {
289   - evhtp_callback_type type; /**< the type of callback (regex|path) */
290   - evhtp_callback_cb cb; /**< the actual callback function */
291   - unsigned int hash; /**< the full hash generated integer */
292   - void * cbarg; /**< user-defind arguments passed to the cb */
293   - evhtp_hooks_t * hooks; /**< per-callback hooks */
  291 + evhtp_callback_type type; /**< the type of callback (regex|path) */
  292 + evhtp_callback_cb cb; /**< the actual callback function */
  293 + unsigned int hash; /**< the full hash generated integer */
  294 + void * cbarg; /**< user-defind arguments passed to the cb */
  295 + evhtp_hooks_t * hooks; /**< per-callback hooks */
294 296
295 297 union {
296 298 char * path;
  299 + char * glob;
297 300 #ifndef EVHTP_DISABLE_REGEX
298 301 regex_t * regex;
299 302 #endif
@@ -522,6 +525,21 @@ evhtp_callback_t * evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp
522 525 #endif
523 526
524 527
  528 +
  529 +/**
  530 + * @brief sets a callback to to be executed on simple glob/wildcard patterns
  531 + * this is useful if the app does not care about what was matched, but
  532 + * just that it matched. This is technically faster than regex.
  533 + *
  534 + * @param htp
  535 + * @param pattern wildcard pattern, the '*' can be set at either or both the front or end.
  536 + * @param cb
  537 + * @param arg
  538 + *
  539 + * @return
  540 + */
  541 +evhtp_callback_t * evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg);
  542 +
525 543 /**
526 544 * @brief sets a callback hook for either a connection or a path/regex .
527 545 *
280 examples/thread_design.c
... ... @@ -0,0 +1,280 @@
  1 +/*
  2 + * How to exploit the wonders of libevhtp's threading model to avoid using
  3 + * libevent's locking API.
  4 + *
  5 + * In this example we use Redis's Async API (Libhiredis) store and retr the following
  6 + * information for a request:
  7 + *
  8 + * Total requests seen.
  9 + * Total requests seen by the requestors IP address.
  10 + * All of the source ports seen used by the requestors IP address.
  11 + *
  12 + * We do this all using libevhtp's builtin thread-pool model, without the use of
  13 + * mutexes or evthread_use_pthreads() type stuff.
  14 + *
  15 + * The technique is simple:
  16 + * 1. Create your evhtp_t structure, assign callbacks like usual.
  17 + * 2. Call evhtp_use_threads() with a thread init callback.
  18 + * 3. Each time a thread starts, the thread init callback you defined will be
  19 + * called with information about that thread.
  20 + *
  21 + * First a bit of information about how evhtp does threading:
  22 + * libevhtp uses the evthr library, which works more like a threaded
  23 + * co-routine than a threadpool. Each evthr in a pool has its own unique
  24 + * event_base (and each evthr runs its own event_base_loop()). Under the
  25 + * hood when libevhtp sends a request to a thread, it calls
  26 + * "evthr_pool_defer(pool, _run_connection_in_thread, ...).
  27 + *
  28 + * The evthr library then finds a thread inside the pool with the lowest backlog,
  29 + * sends a packet over that threads socketpair containing information about what
  30 + * function to execute. It uses socketpairs because they can be treated as
  31 + * an event, thus able to be processed in a threads own unique
  32 + * event_base_loop().
  33 + *
  34 + * Knowing that, a connection in evhtp is never associated with the initial
  35 + * event_base that was passed to evhtp_new(), but instead the connection
  36 + * uses the evthr's unique event_base. This is what makes libevhtp's
  37 + * safe from thread-related race conditions.
  38 + *
  39 + * 4. Use the thread init callback as a place to put event type things on the
  40 + * threads event_base() instead of using the global one.
  41 + *
  42 + * In this code, that function is app_init_thread(). When this function is
  43 + * called, the first argument is the evthr_t of the thread that just
  44 + * started. This function uses "evthr_get_base(thread)" to get the
  45 + * event_base associated with this specific thread.
  46 + *
  47 + * Using that event_base, the function will start up an async redis
  48 + * connection. This redis connection is now tied to that thread, and can be
  49 + * used on a threaded request without locking (remember that your request
  50 + * has the same event_base as the thread it was executed in).
  51 + *
  52 + * We allocate a dummy structure "struct app" and then call
  53 + * "evthr_set_aux(thread, app)". This function sets some aux data which can
  54 + * be fetched at any point using evthr_get_aux(thread). We use this later on
  55 + * inside process_request()
  56 + *
  57 + * This part is the secret to evhtp threading success.
  58 + *
  59 + * 5. When a request has been fully processed, it will call the function
  60 + * "app_process_request()". Note here that the "arg" argument is NULL since no
  61 + * arguments were passed to evhtp_set_gencb().
  62 + *
  63 + * Since we want to do a bunch of redis stuff before sending a reply to the
  64 + * client, we must fetch the "struct app" data we allocated and set for the
  65 + * thread associated with this request (struct app * app =
  66 + * evthr_get_aux(thread);).
  67 + *
  68 + * struct app has our thread-specific redis connection ctx, so using that
  69 + * redisAsyncCommand() is called a bunch of times to queue up the commands
  70 + * which will be run.
  71 + *
  72 + * The last part of this technique is to call the function
  73 + * "evhtp_request_pause()". This essentially tells evhtp to flip the
  74 + * read-side of the connections file-descriptor OFF (This avoids potential
  75 + * situations where a client disconnected before all of the redis commands
  76 + * executed).
  77 + *
  78 + * 6. Each redis command is executed in order, and each callback will write to
  79 + * the requests output_buffer with relevant information from the result.
  80 + *
  81 + * 7. The last redis callback executed here is "redis_get_srcport_cb". It is
  82 + * the job os this function to call evhtp_send_reply() and then
  83 + * evhtp_request_resume().
  84 + *
  85 + * Using this design in conjunction with libevhtp makes the world an easier
  86 + * place to code.
  87 + *
  88 + * Compile: gcc thread_design.c -o thread_design -levhtp -levent -lhiredis
  89 + *
  90 + */
  91 +#include <stdio.h>
  92 +#include <stdlib.h>
  93 +#include <string.h>
  94 +#include <stdint.h>
  95 +#include <errno.h>
  96 +
  97 +#include <evhtp.h>
  98 +#include <hiredis/hiredis.h>
  99 +#include <hiredis/async.h>
  100 +#include <hiredis/adapters/libevent.h>
  101 +
  102 +struct app_parent {
  103 + evhtp_t * evhtp;
  104 + evbase_t * evbase;
  105 + char * redis_host;
  106 + uint16_t redis_port;
  107 +};
  108 +
  109 +struct app {
  110 + struct app_parent * parent;
  111 + evbase_t * evbase;
  112 + redisAsyncContext * redis;
  113 +};
  114 +
  115 +static evthr_t *
  116 +get_request_thr(evhtp_request_t * request) {
  117 + evhtp_connection_t * htpconn;
  118 + evthr_t * thread;
  119 +
  120 + htpconn = evhtp_request_get_connection(request);
  121 + thread = htpconn->thread;
  122 +
  123 + return thread;
  124 +}
  125 +
  126 +void
  127 +redis_global_incr_cb(redisAsyncContext * redis, void * redis_reply, void * arg) {
  128 + redisReply * reply = redis_reply;
  129 + evhtp_request_t * request = arg;
  130 +
  131 + if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) {
  132 + evbuffer_add_printf(request->buffer_out,
  133 + "redis_global_incr_cb() failed\n");
  134 + return;
  135 + }
  136 +
  137 + evbuffer_add_printf(request->buffer_out,
  138 + "Total requests = %lld\n", reply->integer);
  139 +}
  140 +
  141 +void
  142 +redis_srcaddr_incr_cb(redisAsyncContext * redis, void * redis_reply, void * arg) {
  143 + redisReply * reply = redis_reply;
  144 + evhtp_request_t * request = arg;
  145 +
  146 + if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) {
  147 + evbuffer_add_printf(request->buffer_out,
  148 + "redis_srcaddr_incr_cb() failed\n");
  149 + return;
  150 + }
  151 +
  152 + evbuffer_add_printf(request->buffer_out,
  153 + "Requests from this source IP = %lld\n", reply->integer);
  154 +}
  155 +
  156 +void
  157 +redis_set_srcport_cb(redisAsyncContext * redis, void * redis_reply, void * arg) {
  158 + redisReply * reply = redis_reply;
  159 + evhtp_request_t * request = arg;
  160 +
  161 + if (reply == NULL || reply->type != REDIS_REPLY_INTEGER) {
  162 + evbuffer_add_printf(request->buffer_out,
  163 + "redis_set_srcport_cb() failed\n");
  164 + return;
  165 + }
  166 +
  167 + if (!reply->integer) {
  168 + evbuffer_add_printf(request->buffer_out,
  169 + "This source port has been seen already.\n");
  170 + } else {
  171 + evbuffer_add_printf(request->buffer_out,
  172 + "This source port has never been seen.\n");
  173 + }
  174 +}
  175 +
  176 +void
  177 +redis_get_srcport_cb(redisAsyncContext * redis, void * redis_reply, void * arg) {
  178 + redisReply * reply = redis_reply;
  179 + evhtp_request_t * request = arg;
  180 + int i;
  181 +
  182 + if (reply == NULL || reply->type != REDIS_REPLY_ARRAY) {
  183 + evbuffer_add_printf(request->buffer_out,
  184 + "redis_get_srcport_cb() failed.\n");
  185 + return;
  186 + }
  187 +
  188 + evbuffer_add_printf(request->buffer_out,
  189 + "source ports which have been seen for your ip:\n");
  190 +
  191 + for (i = 0; i < reply->elements; i++) {
  192 + redisReply * elem = reply->element[i];
  193 +
  194 + evbuffer_add_printf(request->buffer_out, "%s ", elem->str);
  195 + }
  196 +
  197 + evbuffer_add(request->buffer_out, "\n", 1);
  198 +
  199 + /* final callback for redis, so send the response */
  200 + evhtp_send_reply(request, EVHTP_RES_OK);
  201 + evhtp_request_resume(request);
  202 +}
  203 +
  204 +void
  205 +app_process_request(evhtp_request_t * request, void * arg) {
  206 + struct sockaddr_in * sin;
  207 + struct app_parent * app_parent;
  208 + struct app * app;
  209 + evthr_t * thread;
  210 + evhtp_connection_t * conn;
  211 + char tmp[1024];
  212 +
  213 + thread = get_request_thr(request);
  214 + conn = evhtp_request_get_connection(request);
  215 + app = (struct app *)evthr_get_aux(thread);
  216 + sin = (struct sockaddr_in *)conn->saddr;
  217 +
  218 + evutil_inet_ntop(AF_INET, &sin->sin_addr, tmp, sizeof(tmp));
  219 +
  220 + /* increment a global counter of hits on redis */
  221 + redisAsyncCommand(app->redis, redis_global_incr_cb,
  222 + (void *)request, "INCR requests:total");
  223 +
  224 + /* increment a counter for hits from this source address on redis */
  225 + redisAsyncCommand(app->redis, redis_srcaddr_incr_cb,
  226 + (void *)request, "INCR requests:ip:%s", tmp);
  227 +
  228 + /* add the source port of this request to a source-specific set */
  229 + redisAsyncCommand(app->redis, redis_set_srcport_cb, (void *)request,
  230 + "SADD requests:ip:%s:ports %d", tmp, ntohs(sin->sin_port));
  231 +
  232 + /* get all of the ports this source address has used */
  233 + redisAsyncCommand(app->redis, redis_get_srcport_cb, (void *)request,
  234 + "SMEMBERS requests:ip:%s:ports", tmp);
  235 +
  236 + /* pause the request processing */
  237 + evhtp_request_pause(request);
  238 +}
  239 +
  240 +void
  241 +app_init_thread(evhtp_t * htp, evthr_t * thread, void * arg) {
  242 + struct app_parent * app_parent;
  243 + struct app * app;
  244 +
  245 + app_parent = (struct app_parent *)arg;
  246 + app = calloc(sizeof(struct app), 1);
  247 +
  248 + app->parent = app_parent;
  249 + app->evbase = evthr_get_base(thread);
  250 + app->redis = redisAsyncConnect(app_parent->redis_host, app_parent->redis_port);
  251 +
  252 + redisLibeventAttach(app->redis, app->evbase);
  253 +
  254 + evthr_set_aux(thread, app);
  255 +}
  256 +
  257 +int
  258 +main(int argc, char ** argv) {
  259 + evbase_t * evbase;
  260 + evhtp_t * evhtp;
  261 + struct app_parent * app_p;
  262 +
  263 + evbase = event_base_new();
  264 + evhtp = evhtp_new(evbase, NULL);
  265 + app_p = calloc(sizeof(struct app_parent), 1);
  266 +
  267 + app_p->evhtp = evhtp;
  268 + app_p->evbase = evbase;
  269 + app_p->redis_host = "127.0.0.1";
  270 + app_p->redis_port = 6379;
  271 +
  272 + evhtp_set_gencb(evhtp, app_process_request, NULL);
  273 + evhtp_use_threads(evhtp, app_init_thread, 4, app_p);
  274 + evhtp_bind_socket(evhtp, "127.0.0.1", 9090, 1024);
  275 +
  276 + event_base_loop(evbase, 0);
  277 +
  278 + return 0;
  279 +}
  280 +
21 test.c
@@ -14,6 +14,7 @@ int num_threads = 0;
14 14 #endif
15 15 char * bind_addr = "0.0.0.0";
16 16 uint16_t bind_port = 8081;
  17 +char * ext_body = NULL;
17 18 char * ssl_pem = NULL;
18 19 char * ssl_ca = NULL;
19 20 char * ssl_capath = NULL;
@@ -135,6 +136,7 @@ test_regex(evhtp_request_t * req, void * arg) {
135 136
136 137 evhtp_send_reply(req, EVHTP_RES_OK);
137 138 }
  139 +
138 140 #endif
139 141
140 142 static void
@@ -218,6 +220,12 @@ test_bar_cb(evhtp_request_t * req, void * arg) {
218 220 }
219 221
220 222 static void
  223 +test_glob_cb(evhtp_request_t * req, void * arg) {
  224 + evbuffer_add(req->buffer_out, "test_glob_cb\n", 13);
  225 + evhtp_send_reply(req, EVHTP_RES_OK);
  226 +}
  227 +
  228 +static void
221 229 test_default_cb(evhtp_request_t * req, void * arg) {
222 230 evbuffer_add_reference(req->buffer_out,
223 231 "test_default_cb\n", 16, NULL, NULL);
@@ -252,6 +260,10 @@ print_kvs(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg ) {
252 260
253 261 static evhtp_res
254 262 print_path(evhtp_request_t * req, evhtp_path_t * path, void * arg) {
  263 + if (ext_body) {
  264 + evbuffer_add_printf(req->buffer_out, "ext_body: '%s'\n", ext_body);
  265 + }
  266 +
255 267 evbuffer_add_printf(req->buffer_out,
256 268 "print_path() full = '%s'\n"
257 269 " path = '%s'\n"
@@ -304,6 +316,7 @@ static evhtp_res
304 316 test_regex_hdrs_cb(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg ) {
305 317 return EVHTP_RES_OK;
306 318 }
  319 +
307 320 #endif
308 321
309 322 static evhtp_res
@@ -368,7 +381,7 @@ dummy_check_issued_cb(X509_STORE_CTX * ctx, X509 * x, X509 * issuer) {
368 381
369 382 #endif
370 383
371   -const char * optstr = "htn:a:p:r:s:c:C:l:";
  384 +const char * optstr = "htn:a:p:r:s:c:C:l:N:";
372 385
373 386 const char * help =
374 387 "Options: \n"
@@ -384,6 +397,7 @@ const char * help =
384 397 #endif
385 398 " -l <int> : Max bandwidth (in bytes) (default: NULL)\n"
386 399 " -r <str> : Document root (default: .)\n"
  400 + " -N <str> : Add this string to body. (default: NULL)\n"
387 401 " -a <str> : Bind Address (default: 0.0.0.0)\n"
388 402 " -p <int> : Bind Port (default: 8081)\n";
389 403
@@ -401,6 +415,9 @@ parse_args(int argc, char ** argv) {
401 415 case 'h':
402 416 printf("Usage: %s [opts]\n%s", argv[0], help);
403 417 return -1;
  418 + case 'N':
  419 + ext_body = strdup(optarg);
  420 + break;
404 421 case 'a':
405 422 bind_addr = strdup(optarg);
406 423 break;
@@ -461,6 +478,7 @@ main(int argc, char ** argv) {
461 478 evhtp_callback_t * cb_6 = NULL;
462 479 evhtp_callback_t * cb_7 = NULL;
463 480 evhtp_callback_t * cb_8 = NULL;
  481 + evhtp_callback_t * cb_9 = NULL;
464 482
465 483 if (parse_args(argc, argv) < 0) {
466 484 exit(1);
@@ -483,6 +501,7 @@ main(int argc, char ** argv) {
483 501 #ifndef EVHTP_DISABLE_REGEX
484 502 cb_8 = evhtp_set_regex_cb(htp, "^/create/(.*)", create_callback, NULL);
485 503 #endif
  504 + cb_9 = evhtp_set_glob_cb(htp, "*/glob/*", test_glob_cb, NULL);
486 505
487 506 /* set a callback to test out chunking API */
488 507 evhtp_set_cb(htp, "/chunkme", test_chunking, NULL);

No commit comments for this range

Something went wrong with that request. Please try again.