From d33a416cb2853e679f552a329a001e8937855714 Mon Sep 17 00:00:00 2001 From: Mark Ellzey Date: Tue, 17 Apr 2012 13:33:12 -0400 Subject: [PATCH] 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/ --- LICENSE | 6 +++ evhtp.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- evhtp.h | 38 ++++++++++---- test.c | 10 ++++ 4 files changed, 199 insertions(+), 13 deletions(-) diff --git a/LICENSE b/LICENSE index d1b540c..cf4e8a2 100644 --- a/LICENSE +++ b/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 diff --git a/evhtp.c b/evhtp.c index 1aedc1b..af5c802 100644 --- a/evhtp.c +++ b/evhtp.c @@ -505,6 +505,108 @@ _evhtp_callback_hash_find(evhtp_callbacks_t * callbacks, const char * path) { return NULL; } +/** + * @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) { @@ -2409,10 +2521,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 +2544,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 +2584,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 +2815,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; diff --git a/evhtp.h b/evhtp.h index 6b35897..263c98b 100644 --- a/evhtp.h +++ b/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 { @@ -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 . * diff --git a/test.c b/test.c index d1516c8..82ba986 100644 --- a/test.c +++ b/test.c @@ -135,6 +135,7 @@ test_regex(evhtp_request_t * req, void * arg) { evhtp_send_reply(req, EVHTP_RES_OK); } + #endif static void @@ -217,6 +218,12 @@ test_bar_cb(evhtp_request_t * req, void * arg) { evhtp_send_reply(req, EVHTP_RES_OK); } +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, @@ -304,6 +311,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 @@ -461,6 +469,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 +492,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);