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:
		
							
								
								
									
										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); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user