http: Add a more expressive but still nowhere near regex URL pattern matcher.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 14m57s
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 14m57s
This commit is contained in:
parent
f74ca1c236
commit
a09fefab5e
37
src/http.c
37
src/http.c
@ -67,7 +67,6 @@ typedef struct _tf_http_connection_t
|
||||
typedef struct _tf_http_handler_t
|
||||
{
|
||||
const char* pattern;
|
||||
bool is_wildcard;
|
||||
tf_http_callback_t* callback;
|
||||
tf_http_cleanup_t* cleanup;
|
||||
void* user_data;
|
||||
@ -129,9 +128,15 @@ static void _http_allocate_buffer(uv_handle_t* handle, size_t suggested_size, uv
|
||||
*buf = uv_buf_init(connection->incoming, sizeof(connection->incoming));
|
||||
}
|
||||
|
||||
static bool _http_pattern_matches(const char* pattern, const char* path, bool is_wildcard)
|
||||
bool tf_http_pattern_matches(const char* pattern, const char* path)
|
||||
{
|
||||
if (!pattern || !*pattern || (!is_wildcard && strcmp(path, pattern) == 0))
|
||||
if (!pattern || !*pattern)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
const char* k_word = "{word}";
|
||||
bool is_wildcard = strchr(pattern, '*') || strstr(pattern, k_word);
|
||||
if (!is_wildcard && strcmp(path, pattern) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -140,17 +145,34 @@ static bool _http_pattern_matches(const char* pattern, const char* path, bool is
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
while (pattern[i] && path[j] && pattern[i] != '*' && pattern[i] == path[j])
|
||||
while (pattern[i] && path[j] && pattern[i] == path[j])
|
||||
{
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
|
||||
if (pattern[i] == '*')
|
||||
size_t k_word_len = strlen(k_word);
|
||||
if (strncmp(pattern + i, k_word, k_word_len) == 0 && ((path[j] >= 'a' && path[j] <= 'z') || (path[j] >= 'A' && path[j] <= 'Z')))
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (_http_pattern_matches(pattern + i + 1, path + j, strchr(pattern + i + 1, '*') != NULL))
|
||||
if (((path[j] >= 'a' && path[j] <= 'z') || (path[j] >= 'A' && path[j] <= 'Z') || (path[j] >= '0' && path[j] <= '9')) &&
|
||||
tf_http_pattern_matches(pattern + i + k_word_len, path + j + 1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!path[j])
|
||||
{
|
||||
break;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
else if (pattern[i] == '*')
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (tf_http_pattern_matches(pattern + i + 1, path + j))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -170,7 +192,7 @@ static bool _http_find_handler(tf_http_t* http, const char* path, tf_http_callba
|
||||
{
|
||||
for (int i = 0; i < http->handlers_count; i++)
|
||||
{
|
||||
if (_http_pattern_matches(http->handlers[i].pattern, path, http->handlers[i].is_wildcard))
|
||||
if (tf_http_pattern_matches(http->handlers[i].pattern, path))
|
||||
{
|
||||
*out_callback = http->handlers[i].callback;
|
||||
*out_trace_name = http->handlers[i].pattern;
|
||||
@ -741,7 +763,6 @@ void tf_http_add_handler(tf_http_t* http, const char* pattern, tf_http_callback_
|
||||
http->handlers = tf_resize_vec(http->handlers, sizeof(tf_http_handler_t) * (http->handlers_count + 1));
|
||||
http->handlers[http->handlers_count++] = (tf_http_handler_t) {
|
||||
.pattern = tf_strdup(pattern),
|
||||
.is_wildcard = pattern && strchr(pattern, '*') != NULL,
|
||||
.callback = callback,
|
||||
.cleanup = cleanup,
|
||||
.user_data = user_data,
|
||||
|
@ -228,4 +228,12 @@ void tf_http_request_websocket_upgrade(tf_http_request_t* request);
|
||||
*/
|
||||
const char* tf_http_status_text(int status);
|
||||
|
||||
/**
|
||||
** Match URL patterns. "*" matches anything, and "{word}" matches [a-zA-Z][a-zA-Z0-9]*".
|
||||
** @param pattern The pattern to match.
|
||||
** @param path The path to test.
|
||||
** @return true if the path matches the pattern.
|
||||
*/
|
||||
bool tf_http_pattern_matches(const char* pattern, const char* path);
|
||||
|
||||
/** @} */
|
||||
|
22
src/tests.c
22
src/tests.c
@ -4,8 +4,8 @@
|
||||
#include "http.h"
|
||||
#include "log.h"
|
||||
#include "mem.h"
|
||||
#include "ssb.h"
|
||||
#include "ssb.db.h"
|
||||
#include "ssb.h"
|
||||
#include "ssb.tests.h"
|
||||
#include "util.js.h"
|
||||
|
||||
@ -850,16 +850,14 @@ static void _test_httpd(const tf_test_options_t* options)
|
||||
char command[256];
|
||||
snprintf(command, sizeof(command), "%s run -b 0 --db-path=out/test_db0.sqlite" TEST_ARGS, options->exe_path);
|
||||
|
||||
uv_stdio_container_t stdio[] =
|
||||
{
|
||||
uv_stdio_container_t stdio[] = {
|
||||
[STDIN_FILENO] = { .flags = UV_IGNORE },
|
||||
[STDOUT_FILENO] = { .flags = UV_INHERIT_FD },
|
||||
[STDERR_FILENO] = { .flags = UV_INHERIT_FD },
|
||||
};
|
||||
uv_process_t process = { 0 };
|
||||
uv_spawn(&loop, &process,
|
||||
&(uv_process_options_t)
|
||||
{
|
||||
&(uv_process_options_t) {
|
||||
.file = options->exe_path,
|
||||
.args = (char*[]) { (char*)options->exe_path, "run", "-b0", "--db-path=out/test_db0.sqlite", "--http-port=8080", "--https-port=0", NULL },
|
||||
.stdio_count = sizeof(stdio) / sizeof(*stdio),
|
||||
@ -906,6 +904,19 @@ static void _test_httpd(const tf_test_options_t* options)
|
||||
uv_loop_close(&loop);
|
||||
}
|
||||
|
||||
static void _test_pattern(const tf_test_options_t* options)
|
||||
{
|
||||
assert(tf_http_pattern_matches("/~core/test/", "/~core/test/"));
|
||||
assert(tf_http_pattern_matches("/~core/test/*", "/~core/test/"));
|
||||
assert(tf_http_pattern_matches("/~core/test/*", "/~core/test/blah"));
|
||||
assert(tf_http_pattern_matches("*/~core/test/", "/~core/test/"));
|
||||
assert(tf_http_pattern_matches("*/~core/test/", "blah/~core/test/"));
|
||||
assert(tf_http_pattern_matches("/~*/*/", "/~core/test/"));
|
||||
assert(tf_http_pattern_matches("/~{word}/*", "/~core/test"));
|
||||
assert(tf_http_pattern_matches("/~{word}/{word}/", "/~core/test/"));
|
||||
assert(tf_http_pattern_matches("/~{word}/{word}", "/~core/test"));
|
||||
}
|
||||
|
||||
static void _test_auto_process_exit(uv_process_t* process, int64_t status, int termination_signal)
|
||||
{
|
||||
tf_printf("Process exit %d signal=%d.\n", (int)WEXITSTATUS(status), termination_signal);
|
||||
@ -1012,6 +1023,7 @@ void tf_tests(const tf_test_options_t* options)
|
||||
_tf_test_run(options, "bip39", _test_bip39, false);
|
||||
_tf_test_run(options, "http", _test_http, false);
|
||||
_tf_test_run(options, "httpd", _test_httpd, false);
|
||||
_tf_test_run(options, "pattern", _test_pattern, false);
|
||||
_tf_test_run(options, "ssb", tf_ssb_test_ssb, false);
|
||||
_tf_test_run(options, "ssb_id", tf_ssb_test_id_conversion, false);
|
||||
_tf_test_run(options, "ssb_following", tf_ssb_test_following, false);
|
||||
|
Loading…
Reference in New Issue
Block a user