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/
This commit is contained in:
Mark Ellzey 2012-04-17 13:33:12 -04:00
parent 154017ef22
commit d33a416cb2
4 changed files with 199 additions and 13 deletions

View file

@ -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 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 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

158
evhtp.c
View file

@ -505,6 +505,108 @@ _evhtp_callback_hash_find(evhtp_callbacks_t * callbacks, const char * path) {
return NULL; 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 * @brief iterate through a tailq of callback hooks defined as a regex until a
* match is found. * match is found.
@ -549,7 +651,11 @@ _evhtp_callback_regex_find(evhtp_callbacks_t * callbacks, const char * path,
#endif #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 callbacks
* @param path * @param path
@ -575,6 +681,12 @@ _evhtp_callback_find(evhtp_callbacks_t * callbacks,
return callback; 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 #ifndef EVHTP_DISABLE_REGEX
if ((callback = _evhtp_callback_regex_find(callbacks, path, if ((callback = _evhtp_callback_regex_find(callbacks, path,
start_offset, end_offset)) != NULL) { start_offset, end_offset)) != NULL) {
@ -2409,10 +2521,13 @@ evhtp_callback_new(const char * path, evhtp_callback_type type, evhtp_callback_c
} }
break; break;
#endif #endif
case evhtp_callback_type_glob:
hcb->val.glob = strdup(path);
break;
default: default:
free(hcb); free(hcb);
return NULL; return NULL;
} } /* switch */
return hcb; return hcb;
} }
@ -2429,6 +2544,11 @@ evhtp_callback_free(evhtp_callback_t * callback) {
free(callback->val.path); free(callback->val.path);
} }
break; break;
case evhtp_callback_type_glob:
if (callback->val.glob) {
free(callback->val.glob);
}
break;
#ifndef EVHTP_DISABLE_REGEX #ifndef EVHTP_DISABLE_REGEX
case evhtp_callback_type_regex: case evhtp_callback_type_regex:
if (callback->val.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; cbs->regex_callbacks = cb;
break; break;
#endif #endif
case evhtp_callback_type_glob:
cb->next = cbs->glob_callbacks;
cbs->glob_callbacks = cb;
break;
default: default:
return -1; return -1;
} } /* switch */
return 0; return 0;
} }
@ -2691,6 +2815,34 @@ evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp_callback_cb cb, vo
#endif #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 void
evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * arg) { evhtp_set_gencb(evhtp_t * htp, evhtp_callback_cb cb, void * arg) {
htp->defaults.cb = cb; htp->defaults.cb = cb;

38
evhtp.h
View file

@ -109,8 +109,9 @@ enum evhtp_hook_type {
enum evhtp_callback_type { enum evhtp_callback_type {
evhtp_callback_type_hash, evhtp_callback_type_hash,
#ifndef EVHTP_DISABLE_REGEX #ifndef EVHTP_DISABLE_REGEX
evhtp_callback_type_regex evhtp_callback_type_regex,
#endif #endif
evhtp_callback_type_glob
}; };
enum evhtp_proto { enum evhtp_proto {
@ -263,12 +264,13 @@ struct evhtp_s {
* *
*/ */
struct evhtp_callbacks_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 #ifndef EVHTP_DISABLE_REGEX
evhtp_callback_t * regex_callbacks; /**< list of regex callbacks */ evhtp_callback_t * regex_callbacks; /**< list of regex callbacks */
#endif #endif
unsigned int count; /**< number of callbacks defined */ evhtp_callback_t * glob_callbacks; /**< list of wildcard callbacks */
unsigned int buckets; /**< buckets allocated for hash */ 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 { struct evhtp_callback_s {
evhtp_callback_type type; /**< the type of callback (regex|path) */ evhtp_callback_type type; /**< the type of callback (regex|path) */
evhtp_callback_cb cb; /**< the actual callback function */ evhtp_callback_cb cb; /**< the actual callback function */
unsigned int hash; /**< the full hash generated integer */ unsigned int hash; /**< the full hash generated integer */
void * cbarg; /**< user-defind arguments passed to the cb */ void * cbarg; /**< user-defind arguments passed to the cb */
evhtp_hooks_t * hooks; /**< per-callback hooks */ evhtp_hooks_t * hooks; /**< per-callback hooks */
union { union {
char * path; char * path;
char * glob;
#ifndef EVHTP_DISABLE_REGEX #ifndef EVHTP_DISABLE_REGEX
regex_t * regex; regex_t * regex;
#endif #endif
@ -522,6 +525,21 @@ evhtp_callback_t * evhtp_set_regex_cb(evhtp_t * htp, const char * pattern, evhtp
#endif #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 . * @brief sets a callback hook for either a connection or a path/regex .
* *

10
test.c
View file

@ -135,6 +135,7 @@ test_regex(evhtp_request_t * req, void * arg) {
evhtp_send_reply(req, EVHTP_RES_OK); evhtp_send_reply(req, EVHTP_RES_OK);
} }
#endif #endif
static void static void
@ -217,6 +218,12 @@ test_bar_cb(evhtp_request_t * req, void * arg) {
evhtp_send_reply(req, EVHTP_RES_OK); 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 static void
test_default_cb(evhtp_request_t * req, void * arg) { test_default_cb(evhtp_request_t * req, void * arg) {
evbuffer_add_reference(req->buffer_out, 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 ) { test_regex_hdrs_cb(evhtp_request_t * req, evhtp_headers_t * hdrs, void * arg ) {
return EVHTP_RES_OK; return EVHTP_RES_OK;
} }
#endif #endif
static evhtp_res static evhtp_res
@ -461,6 +469,7 @@ main(int argc, char ** argv) {
evhtp_callback_t * cb_6 = NULL; evhtp_callback_t * cb_6 = NULL;
evhtp_callback_t * cb_7 = NULL; evhtp_callback_t * cb_7 = NULL;
evhtp_callback_t * cb_8 = NULL; evhtp_callback_t * cb_8 = NULL;
evhtp_callback_t * cb_9 = NULL;
if (parse_args(argc, argv) < 0) { if (parse_args(argc, argv) < 0) {
exit(1); exit(1);
@ -483,6 +492,7 @@ main(int argc, char ** argv) {
#ifndef EVHTP_DISABLE_REGEX #ifndef EVHTP_DISABLE_REGEX
cb_8 = evhtp_set_regex_cb(htp, "^/create/(.*)", create_callback, NULL); cb_8 = evhtp_set_regex_cb(htp, "^/create/(.*)", create_callback, NULL);
#endif #endif
cb_9 = evhtp_set_glob_cb(htp, "*/glob/*", test_glob_cb, NULL);
/* set a callback to test out chunking API */ /* set a callback to test out chunking API */
evhtp_set_cb(htp, "/chunkme", test_chunking, NULL); evhtp_set_cb(htp, "/chunkme", test_chunking, NULL);