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:
parent
154017ef22
commit
d33a416cb2
4 changed files with 199 additions and 13 deletions
6
LICENSE
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
|
||||
|
|
158
evhtp.c
158
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;
|
||||
|
|
38
evhtp.h
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 .
|
||||
*
|
||||
|
|
10
test.c
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
|
||||
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue