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
|
(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
158
evhtp.c
|
@ -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
38
evhtp.h
|
@ -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
10
test.c
|
@ -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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue