Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added URI Matching Methods (GIT8266O-813) #1236

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
41 changes: 41 additions & 0 deletions components/esp_http_server/include/esp_http_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,15 @@ typedef void (*httpd_free_ctx_fn_t)(void *ctx);
*/
typedef esp_err_t (*httpd_open_func_t)(httpd_handle_t hd, int sockfd);

/**
* @brief Function prototype for URI matching
*
*/

typedef bool (*httpd_uri_match_func_t)(const char *reference_uri,
const char *uri_to_match,
size_t match_upto);

/**
* @brief Function prototype for closing a session.
*
Expand Down Expand Up @@ -195,6 +204,12 @@ typedef struct httpd_config {
* was closed by the network stack - that is, the file descriptor may not be valid anymore.
*/
httpd_close_func_t close_fn;

/**
* Uri matching function
*
*/
httpd_uri_match_func_t uri_match_fn;
} httpd_config_t;

/**
Expand Down Expand Up @@ -979,6 +994,32 @@ esp_err_t httpd_resp_send_408(httpd_req_t *r);
*/
esp_err_t httpd_resp_send_500(httpd_req_t *r);


/**
* @brief Test if a URI matches the given wildcard template.
*
* Template may end with "?" to make the previous character optional (typically a slash),
* "*" for a wildcard match, and "?*" to make the previous character optional, and if present,
* allow anything to follow.
*
* Example:
* - * matches everything
* - /foo/? matches /foo and /foo/
* - /foo/\* (sans the backslash) matches /foo/ and /foo/bar, but not /foo or /fo
* - /foo/?* or /foo/\*? (sans the backslash) matches /foo/, /foo/bar, and also /foo, but not /foox or /fo
*
* The special characters "?" and "*" anywhere else in the template will be taken literally.
*
* @param[in] uri_template URI template (pattern)
* @param[in] uri_to_match URI to be matched
* @param[in] match_upto how many characters of the URI buffer to test
* (there may be trailing query string etc.)
*
* @return true if a match was found
*/
bool httpd_uri_match_wildcard(const char *uri_template, const char *uri_to_match, size_t match_upto);


/**
* @brief Raw HTTP send
*
Expand Down
75 changes: 73 additions & 2 deletions components/esp_http_server/src/httpd_uri.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,71 @@

static const char *TAG = "httpd_uri";

static bool httpd_uri_match_simple(const char *uri1, const char *uri2, size_t len2)
{
return strlen(uri1) == len2 && // First match lengths
(strncmp(uri1, uri2, len2) == 0); // Then match actual URIs
}

bool httpd_uri_match_wildcard(const char *template, const char *uri, size_t len)
{
const size_t tpl_len = strlen(template);
size_t exact_match_chars = tpl_len;

/* Check for trailing question mark and asterisk */
const char last = (const char) (tpl_len > 0 ? template[tpl_len - 1] : 0);
const char prevlast = (const char) (tpl_len > 1 ? template[tpl_len - 2] : 0);
const bool asterisk = last == '*' || (prevlast == '*' && last == '?');
const bool quest = last == '?' || (prevlast == '?' && last == '*');

/* Minimum template string length must be:
* 0 : if neither of '*' and '?' are present
* 1 : if only '*' is present
* 2 : if only '?' is present
* 3 : if both are present
*
* The expression (asterisk + quest*2) serves as a
* case wise generator of these length values
*/

/* abort in cases such as "?" with no preceding character (invalid template) */
if (exact_match_chars < asterisk + quest*2) {
return false;
}

/* account for special characters and the optional character if "?" is used */
exact_match_chars -= asterisk + quest*2;

if (len < exact_match_chars) {
return false;
}

if (!quest) {
if (!asterisk && len != exact_match_chars) {
/* no special characters and different length - strncmp would return false */
return false;
}
/* asterisk allows arbitrary trailing characters, we ignore these using
* exact_match_chars as the length limit */
return (strncmp(template, uri, exact_match_chars) == 0);
} else {
/* question mark present */
if (len > exact_match_chars && template[exact_match_chars] != uri[exact_match_chars]) {
/* the optional character is present, but different */
return false;
}
if (strncmp(template, uri, exact_match_chars) != 0) {
/* the mandatory part differs */
return false;
}
/* Now we know the URI is longer than the required part of template,
* the mandatory part matches, and if the optional character is present, it is correct.
* Match is OK if we have asterisk, i.e. any trailing characters are OK, or if
* there are no characters beyond the optional character. */
return asterisk || len <= exact_match_chars + 1;
}
}

static int httpd_find_uri_handler(struct httpd_data *hd,
const char* uri,
httpd_method_t method)
Expand Down Expand Up @@ -157,12 +222,18 @@ static httpd_uri_t* httpd_find_uri_handler2(httpd_err_resp_t *err,
const char *uri, size_t uri_len,
httpd_method_t method)
{
//if (hd->config.uri_match_fn ?
// hd->config.uri_match_fn(hd->hd_calls[i]->uri, uri, strlen(uri)) :
// httpd_uri_match_simple(hd->hd_calls[i]->uri, uri, strlen(uri))) {
*err = 0;
for (int i = 0; i < hd->config.max_uri_handlers; i++) {
if (hd->hd_calls[i]) {
ESP_LOGD(TAG, LOG_FMT("[%d] = %s"), i, hd->hd_calls[i]->uri);
if ((strlen(hd->hd_calls[i]->uri) == uri_len) && // First match uri length
(strncmp(hd->hd_calls[i]->uri, uri, uri_len) == 0)) { // Then match uri strings
//if ((strlen(hd->hd_calls[i]->uri) == uri_len) && // First match uri length
// (strncmp(hd->hd_calls[i]->uri, uri, uri_len) == 0)) { // Then match uri strings
if (hd->config.uri_match_fn ?
hd->config.uri_match_fn(hd->hd_calls[i]->uri, uri, uri_len) :
httpd_uri_match_simple(hd->hd_calls[i]->uri, uri, uri_len)) {
if (hd->hd_calls[i]->method == method) { // Finally match methods
return hd->hd_calls[i];
}
Expand Down