Skip to content
This repository
Browse code

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/
  • Loading branch information...
commit d33a416cb2853e679f552a329a001e8937855714 1 parent 154017e
Mark Ellzey authored

Showing 4 changed files with 199 additions and 13 deletions. Show diff stats Hide diff stats

  1. +6 0 LICENSE
  2. +155 3 evhtp.c
  3. +28 10 evhtp.h
  4. +10 0 test.c
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
158 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) {
@@ -2409,10 +2521,13 @@ evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_c
2409 2521 }
2410 2522 break;
2411 2523 #endif
  2524 + case evhtp_callback_type_glob:
  2525 + hcb->val.glob = strdup(path);
  2526 + break;
2412 2527 default:
2413 2528 free(hcb);
2414 2529 return NULL;
2415   - }
  2530 + } /* switch */
2416 2531
2417 2532 return hcb;
2418 2533 }
@@ -2429,6 +2544,11 @@ evhtp_callback_free(evhtp_callback_t * callback) {
2429 2544 free(callback->val.path);
2430 2545 }
2431 2546 break;
  2547 + case evhtp_callback_type_glob:
  2548 + if (callback->val.glob) {
  2549 + free(callback->val.glob);
  2550 + }
  2551 + break;
2432 2552 #ifndef EVHTP_DISABLE_REGEX
2433 2553 case evhtp_callback_type_regex:
2434 2554 if (callback->val.regex) {
@@ -2464,9 +2584,13 @@ evhtp_callbacks_add_callback(evhtp_callbacks_t * cbs, evhtp_callback_t * cb) {
2464 2584 cbs->regex_callbacks = cb;
2465 2585 break;
2466 2586 #endif
  2587 + case evhtp_callback_type_glob:
  2588 + cb->next = cbs->glob_callbacks;
  2589 + cbs->glob_callbacks = cb;
  2590 + break;
2467 2591 default:
2468 2592 return -1;
2469   - }
  2593 + } /* switch */
2470 2594
2471 2595 return 0;
2472 2596 }
@@ -2691,6 +2815,34 @@ evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, vo
2691 2815
2692 2816 #endif
2693 2817
  2818 +evhtp_callback_t *
  2819 +evhtp_set_glob_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, void * arg) {
  2820 + evhtp_callback_t * hcb;
  2821 +
  2822 + _evhtp_lock(htp);
  2823 +
  2824 + if (htp->callbacks == NULL) {
  2825 + if (!(htp->callbacks = evhtp_callbacks_new(1024))) {
  2826 + _evhtp_unlock(htp);
  2827 + return NULL;
  2828 + }
  2829 + }
  2830 +
  2831 + if (!(hcb = evhtp_callback_new(pattern, evhtp_callback_type_glob, cb, arg))) {
  2832 + _evhtp_unlock(htp);
  2833 + return NULL;
  2834 + }
  2835 +
  2836 + if (evhtp_callbacks_add_callback(htp->callbacks, hcb)) {
  2837 + evhtp_callback_free(hcb);
  2838 + _evhtp_unlock(htp);
  2839 + return NULL;
  2840 + }
  2841 +
  2842 + _evhtp_unlock(htp);
  2843 + return hcb;
  2844 +}
  2845 +
2694 2846 void
2695 2847 evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * arg) {
2696 2848 htp->defaults.cb = cb;
38 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 {
@@ -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 *
10 test.c
@@ -135,6 +135,7 @@ test_regex(evhtp_request_t * req, void * arg) {
135 135
136 136 evhtp_send_reply(req, EVHTP_RES_OK);
137 137 }
  138 +
138 139 #endif
139 140
140 141 static void
@@ -218,6 +219,12 @@ test_bar_cb(evhtp_request_t * req, void * arg) {
218 219 }
219 220
220 221 static void
  222 +test_glob_cb(evhtp_request_t * req, void * arg) {
  223 + evbuffer_add(req->buffer_out, "test_glob_cb\n", 13);
  224 + evhtp_send_reply(req, EVHTP_RES_OK);
  225 +}
  226 +
  227 +static void
221 228 test_default_cb(evhtp_request_t * req, void * arg) {
222 229 evbuffer_add_reference(req->buffer_out,
223 230 "test_default_cb\n", 16, NULL, NULL);
@@ -304,6 +311,7 @@ static evhtp_res
304 311 test_regex_hdrs_cb(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg ) {
305 312 return EVHTP_RES_OK;
306 313 }
  314 +
307 315 #endif
308 316
309 317 static evhtp_res
@@ -461,6 +469,7 @@ main(int argc, char ** argv) {
461 469 evhtp_callback_t * cb_6 = NULL;
462 470 evhtp_callback_t * cb_7 = NULL;
463 471 evhtp_callback_t * cb_8 = NULL;
  472 + evhtp_callback_t * cb_9 = NULL;
464 473
465 474 if (parse_args(argc, argv) < 0) {
466 475 exit(1);
@@ -483,6 +492,7 @@ main(int argc, char ** argv) {
483 492 #ifndef EVHTP_DISABLE_REGEX
484 493 cb_8 = evhtp_set_regex_cb(htp, "^/create/(.*)", create_callback, NULL);
485 494 #endif
  495 + cb_9 = evhtp_set_glob_cb(htp, "*/glob/*", test_glob_cb, NULL);
486 496
487 497 /* set a callback to test out chunking API */
488 498 evhtp_set_cb(htp, "/chunkme", test_chunking, NULL);

0 comments on commit d33a416

Please sign in to comment.
Something went wrong with that request. Please try again.