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
|
typedef struct _tf_http_handler_t
|
||||||
{
|
{
|
||||||
const char* pattern;
|
const char* pattern;
|
||||||
bool is_wildcard;
|
|
||||||
tf_http_callback_t* callback;
|
tf_http_callback_t* callback;
|
||||||
tf_http_cleanup_t* cleanup;
|
tf_http_cleanup_t* cleanup;
|
||||||
void* user_data;
|
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));
|
*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;
|
return true;
|
||||||
}
|
}
|
||||||
@ -140,17 +145,34 @@ static bool _http_pattern_matches(const char* pattern, const char* path, bool is
|
|||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
int j = 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++;
|
i++;
|
||||||
j++;
|
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)
|
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;
|
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++)
|
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_callback = http->handlers[i].callback;
|
||||||
*out_trace_name = http->handlers[i].pattern;
|
*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 = tf_resize_vec(http->handlers, sizeof(tf_http_handler_t) * (http->handlers_count + 1));
|
||||||
http->handlers[http->handlers_count++] = (tf_http_handler_t) {
|
http->handlers[http->handlers_count++] = (tf_http_handler_t) {
|
||||||
.pattern = tf_strdup(pattern),
|
.pattern = tf_strdup(pattern),
|
||||||
.is_wildcard = pattern && strchr(pattern, '*') != NULL,
|
|
||||||
.callback = callback,
|
.callback = callback,
|
||||||
.cleanup = cleanup,
|
.cleanup = cleanup,
|
||||||
.user_data = user_data,
|
.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);
|
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 "http.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mem.h"
|
#include "mem.h"
|
||||||
#include "ssb.h"
|
|
||||||
#include "ssb.db.h"
|
#include "ssb.db.h"
|
||||||
|
#include "ssb.h"
|
||||||
#include "ssb.tests.h"
|
#include "ssb.tests.h"
|
||||||
#include "util.js.h"
|
#include "util.js.h"
|
||||||
|
|
||||||
@ -850,16 +850,14 @@ static void _test_httpd(const tf_test_options_t* options)
|
|||||||
char command[256];
|
char command[256];
|
||||||
snprintf(command, sizeof(command), "%s run -b 0 --db-path=out/test_db0.sqlite" TEST_ARGS, options->exe_path);
|
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 },
|
[STDIN_FILENO] = { .flags = UV_IGNORE },
|
||||||
[STDOUT_FILENO] = { .flags = UV_INHERIT_FD },
|
[STDOUT_FILENO] = { .flags = UV_INHERIT_FD },
|
||||||
[STDERR_FILENO] = { .flags = UV_INHERIT_FD },
|
[STDERR_FILENO] = { .flags = UV_INHERIT_FD },
|
||||||
};
|
};
|
||||||
uv_process_t process = { 0 };
|
uv_process_t process = { 0 };
|
||||||
uv_spawn(&loop, &process,
|
uv_spawn(&loop, &process,
|
||||||
&(uv_process_options_t)
|
&(uv_process_options_t) {
|
||||||
{
|
|
||||||
.file = options->exe_path,
|
.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 },
|
.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),
|
.stdio_count = sizeof(stdio) / sizeof(*stdio),
|
||||||
@ -906,6 +904,19 @@ static void _test_httpd(const tf_test_options_t* options)
|
|||||||
uv_loop_close(&loop);
|
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)
|
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);
|
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, "bip39", _test_bip39, false);
|
||||||
_tf_test_run(options, "http", _test_http, false);
|
_tf_test_run(options, "http", _test_http, false);
|
||||||
_tf_test_run(options, "httpd", _test_httpd, 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", tf_ssb_test_ssb, false);
|
||||||
_tf_test_run(options, "ssb_id", tf_ssb_test_id_conversion, false);
|
_tf_test_run(options, "ssb_id", tf_ssb_test_id_conversion, false);
|
||||||
_tf_test_run(options, "ssb_following", tf_ssb_test_following, false);
|
_tf_test_run(options, "ssb_following", tf_ssb_test_following, false);
|
||||||
|
Loading…
Reference in New Issue
Block a user