Skip to content
Browse files

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...
1 parent 154017e commit d33a416cb2853e679f552a329a001e8937855714 @ellzey committed Apr 17, 2012
Showing with 199 additions and 13 deletions.
  1. +6 −0 LICENSE
  2. +155 −3 evhtp.c
  3. +28 −10 evhtp.h
  4. +10 −0 test.c
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
158 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) {
@@ -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;
View
38 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 .
*
View
10 test.c
@@ -135,6 +135,7 @@ test_regex(evhtp_request_t * req, void * arg) {
evhtp_send_reply(req, EVHTP_RES_OK);
}
+
#endif
static void
@@ -218,6 +219,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);
@@ -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);

0 comments on commit d33a416

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