Move / redirect handling to C
This commit is contained in:
parent
5fdd461159
commit
c7ab5447ea
29
core/core.js
29
core/core.js
@ -1425,34 +1425,7 @@ loadSettings()
|
|||||||
httpd.all('/app/socket', app.socket);
|
httpd.all('/app/socket', app.socket);
|
||||||
httpd.all('', function default_http_handler(request, response) {
|
httpd.all('', function default_http_handler(request, response) {
|
||||||
let match;
|
let match;
|
||||||
if (request.uri === '/' || request.uri === '') {
|
if ((match = /^(\/~[^\/]+\/[^\/]+)(\/?.*)$/.exec(request.uri))) {
|
||||||
let host = request.headers['x-forwarded-host'] ?? request.headers.host;
|
|
||||||
try {
|
|
||||||
for (let line of (gGlobalSettings.index_map || '').split('\n')) {
|
|
||||||
let parts = line.split('=');
|
|
||||||
if (parts.length == 2 && host.match(new RegExp(parts[0], 'i'))) {
|
|
||||||
response.writeHead(303, {
|
|
||||||
Location:
|
|
||||||
(request.client.tls ? 'https://' : 'http://') +
|
|
||||||
host +
|
|
||||||
parts[1],
|
|
||||||
'Content-Length': '0',
|
|
||||||
});
|
|
||||||
return response.end();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
response.writeHead(303, {
|
|
||||||
Location:
|
|
||||||
(request.client.tls ? 'https://' : 'http://') +
|
|
||||||
host +
|
|
||||||
gGlobalSettings.index,
|
|
||||||
'Content-Length': '0',
|
|
||||||
});
|
|
||||||
return response.end();
|
|
||||||
} else if ((match = /^(\/~[^\/]+\/[^\/]+)(\/?.*)$/.exec(request.uri))) {
|
|
||||||
return blobHandler(request, response, match[1], match[2]);
|
return blobHandler(request, response, match[1], match[2]);
|
||||||
} else if (
|
} else if (
|
||||||
(match = /^\/([&\%][^\.]{44}(?:\.\w+)?)(\/?.*)/.exec(request.uri))
|
(match = /^\/([&\%][^\.]{44}(?:\.\w+)?)(\/?.*)/.exec(request.uri))
|
||||||
|
37
src/http.c
37
src/http.c
@ -67,6 +67,7 @@ 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;
|
||||||
@ -127,12 +128,43 @@ 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)
|
||||||
|
{
|
||||||
|
if (!pattern || !*pattern || (!is_wildcard && strcmp(path, pattern) == 0))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_wildcard)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
int j = 0;
|
||||||
|
while (pattern[i] && path[j] && pattern[i] != '*' && pattern[i] == path[j])
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pattern[i] == '*')
|
||||||
|
{
|
||||||
|
for (; path[j]; j++)
|
||||||
|
{
|
||||||
|
if (_http_pattern_matches(pattern + i + 1, path + j, strchr(pattern + i + 1, '*') != NULL))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !pattern[i] && !path[j];
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static bool _http_find_handler(tf_http_t* http, const char* path, tf_http_callback_t** out_callback, const char** out_trace_name, void** out_user_data)
|
static bool _http_find_handler(tf_http_t* http, const char* path, tf_http_callback_t** out_callback, const char** out_trace_name, void** out_user_data)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < http->handlers_count; i++)
|
for (int i = 0; i < http->handlers_count; i++)
|
||||||
{
|
{
|
||||||
if (!http->handlers[i].pattern || !*http->handlers[i].pattern || strcmp(path, http->handlers[i].pattern) == 0 ||
|
if (_http_pattern_matches(http->handlers[i].pattern, path, http->handlers[i].is_wildcard))
|
||||||
(*http->handlers[i].pattern && strncmp(path, http->handlers[i].pattern, strlen(http->handlers[i].pattern)) == 0 && path[strlen(http->handlers[i].pattern) - 1] == '/'))
|
|
||||||
{
|
{
|
||||||
*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;
|
||||||
@ -694,6 +726,7 @@ 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,
|
||||||
|
@ -783,6 +783,38 @@ static void _httpd_endpoint_static(tf_http_request_t* request)
|
|||||||
tf_file_stat(task, path, _httpd_endpoint_static_stat, request);
|
tf_file_stat(task, path, _httpd_endpoint_static_stat, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _httpd_endpoint_root_callback(const char* path, void* user_data)
|
||||||
|
{
|
||||||
|
tf_http_request_t* request = user_data;
|
||||||
|
const char* host = tf_http_request_get_header(request, "x-forwarded-host");
|
||||||
|
if (!host)
|
||||||
|
{
|
||||||
|
host = tf_http_request_get_header(request, "host");
|
||||||
|
}
|
||||||
|
|
||||||
|
char url[1024];
|
||||||
|
snprintf(url, sizeof(url), "%s%s%s", request->is_tls ? "https://" : "http://", host, path ? path : "/~core/apps/");
|
||||||
|
const char* headers[] = {
|
||||||
|
"Location",
|
||||||
|
url,
|
||||||
|
};
|
||||||
|
tf_http_respond(request, 303, headers, tf_countof(headers) / 2, NULL, 0);
|
||||||
|
tf_http_request_unref(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _httpd_endpoint_root(tf_http_request_t* request)
|
||||||
|
{
|
||||||
|
const char* host = tf_http_request_get_header(request, "x-forwarded-host");
|
||||||
|
if (!host)
|
||||||
|
{
|
||||||
|
host = tf_http_request_get_header(request, "host");
|
||||||
|
}
|
||||||
|
tf_task_t* task = request->user_data;
|
||||||
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
|
tf_http_request_ref(request);
|
||||||
|
tf_ssb_db_resolve_index_async(ssb, host, _httpd_endpoint_root_callback, request);
|
||||||
|
}
|
||||||
|
|
||||||
static void _httpd_endpoint_robots_txt(tf_http_request_t* request)
|
static void _httpd_endpoint_robots_txt(tf_http_request_t* request)
|
||||||
{
|
{
|
||||||
if (_httpd_redirect(request))
|
if (_httpd_redirect(request))
|
||||||
@ -1429,12 +1461,13 @@ void tf_httpd_register(JSContext* context)
|
|||||||
tf_http_set_trace(http, tf_task_get_trace(task));
|
tf_http_set_trace(http, tf_task_get_trace(task));
|
||||||
JS_SetOpaque(httpd, http);
|
JS_SetOpaque(httpd, http);
|
||||||
|
|
||||||
tf_http_add_handler(http, "/codemirror/", _httpd_endpoint_static, NULL, task);
|
tf_http_add_handler(http, "/", _httpd_endpoint_root, NULL, task);
|
||||||
tf_http_add_handler(http, "/lit/", _httpd_endpoint_static, NULL, task);
|
tf_http_add_handler(http, "/codemirror/*", _httpd_endpoint_static, NULL, task);
|
||||||
tf_http_add_handler(http, "/prettier/", _httpd_endpoint_static, NULL, task);
|
tf_http_add_handler(http, "/lit/*", _httpd_endpoint_static, NULL, task);
|
||||||
tf_http_add_handler(http, "/speedscope/", _httpd_endpoint_static, NULL, task);
|
tf_http_add_handler(http, "/prettier/*", _httpd_endpoint_static, NULL, task);
|
||||||
tf_http_add_handler(http, "/static/", _httpd_endpoint_static, NULL, task);
|
tf_http_add_handler(http, "/speedscope/*", _httpd_endpoint_static, NULL, task);
|
||||||
tf_http_add_handler(http, "/.well-known/", _httpd_endpoint_static, NULL, task);
|
tf_http_add_handler(http, "/static/*", _httpd_endpoint_static, NULL, task);
|
||||||
|
tf_http_add_handler(http, "/.well-known/*", _httpd_endpoint_static, NULL, task);
|
||||||
|
|
||||||
tf_http_add_handler(http, "/robots.txt", _httpd_endpoint_robots_txt, NULL, NULL);
|
tf_http_add_handler(http, "/robots.txt", _httpd_endpoint_robots_txt, NULL, NULL);
|
||||||
tf_http_add_handler(http, "/debug", _httpd_endpoint_debug, NULL, task);
|
tf_http_add_handler(http, "/debug", _httpd_endpoint_debug, NULL, task);
|
||||||
|
90
src/ssb.db.c
90
src/ssb.db.c
@ -1754,3 +1754,93 @@ bool tf_ssb_db_identity_get_active(sqlite3* db, const char* user, const char* pa
|
|||||||
}
|
}
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct _resolve_index_t
|
||||||
|
{
|
||||||
|
uv_work_t work;
|
||||||
|
tf_ssb_t* ssb;
|
||||||
|
const char* host;
|
||||||
|
const char* path;
|
||||||
|
void (*callback)(const char* path, void* user_data);
|
||||||
|
void* user_data;
|
||||||
|
} resolve_index_t;
|
||||||
|
|
||||||
|
static void _tf_ssb_db_resolve_index_work(uv_work_t* work)
|
||||||
|
{
|
||||||
|
resolve_index_t* request = work->data;
|
||||||
|
|
||||||
|
sqlite3* db = tf_ssb_acquire_db_reader(request->ssb);
|
||||||
|
sqlite3_stmt* statement;
|
||||||
|
if (sqlite3_prepare(db, "SELECT json_extract(value, '$.index_map') FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
if (sqlite3_step(statement) == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
const char* index_map = (const char*)sqlite3_column_text(statement, 0);
|
||||||
|
const char* start = index_map;
|
||||||
|
while (start)
|
||||||
|
{
|
||||||
|
const char* end = strchr(start, '\n');
|
||||||
|
const char* equals = strchr(start, '=');
|
||||||
|
if (equals && strncasecmp(request->host, start, equals - start) == 0)
|
||||||
|
{
|
||||||
|
size_t value_length = end && equals < end ? (size_t)(end - (equals + 1)) : strlen(equals + 1);
|
||||||
|
char* path = tf_malloc(value_length + 1);
|
||||||
|
memcpy(path, equals + 1, value_length);
|
||||||
|
path[value_length] = '\0';
|
||||||
|
request->path = path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
start = end ? end + 1 : NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!request->path)
|
||||||
|
{
|
||||||
|
if (sqlite3_prepare(db, "SELECT json_extract(value, '$.index') FROM properties WHERE id = 'core' AND key = 'settings'", -1, &statement, NULL) == SQLITE_OK)
|
||||||
|
{
|
||||||
|
if (sqlite3_step(statement) == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
request->path = tf_strdup((const char*)sqlite3_column_text(statement, 0));
|
||||||
|
}
|
||||||
|
sqlite3_finalize(statement);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tf_printf("prepare failed: %s\n", sqlite3_errmsg(db));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tf_ssb_release_db_reader(request->ssb, db);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _tf_ssb_db_resolve_index_after_work(uv_work_t* work, int status)
|
||||||
|
{
|
||||||
|
resolve_index_t* request = work->data;
|
||||||
|
request->callback(request->path, request->user_data);
|
||||||
|
tf_free((void*)request->host);
|
||||||
|
tf_free((void*)request->path);
|
||||||
|
tf_free(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tf_ssb_db_resolve_index_async(tf_ssb_t* ssb, const char* host, void (*callback)(const char* path, void* user_data), void* user_data)
|
||||||
|
{
|
||||||
|
resolve_index_t* request = tf_malloc(sizeof(resolve_index_t));
|
||||||
|
*request = (resolve_index_t)
|
||||||
|
{
|
||||||
|
.work = { .data = request },
|
||||||
|
.ssb = ssb,
|
||||||
|
.host = tf_strdup(host),
|
||||||
|
.callback = callback,
|
||||||
|
.user_data = user_data,
|
||||||
|
};
|
||||||
|
int r = uv_queue_work(tf_ssb_get_loop(ssb), &request->work, _tf_ssb_db_resolve_index_work, _tf_ssb_db_resolve_index_after_work);
|
||||||
|
if (r)
|
||||||
|
{
|
||||||
|
_tf_ssb_db_resolve_index_after_work(&request->work, r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -370,6 +370,15 @@ const char* tf_ssb_db_get_property(tf_ssb_t* ssb, const char* id, const char* ke
|
|||||||
*/
|
*/
|
||||||
bool tf_ssb_db_set_property(tf_ssb_t* ssb, const char* id, const char* key, const char* value);
|
bool tf_ssb_db_set_property(tf_ssb_t* ssb, const char* id, const char* key, const char* value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Resolve a hostname to its index path by global settings.
|
||||||
|
** @param ssb The SSB instance.
|
||||||
|
** @param host The hostname.
|
||||||
|
** @param callback The callback.
|
||||||
|
** @param user_data The callback user data.
|
||||||
|
*/
|
||||||
|
void tf_ssb_db_resolve_index_async(tf_ssb_t* ssb, const char* host, void (*callback)(const char* path, void* user_data), void* user_data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** An SQLite authorizer callback. See https://www.sqlite.org/c3ref/set_authorizer.html for use.
|
** An SQLite authorizer callback. See https://www.sqlite.org/c3ref/set_authorizer.html for use.
|
||||||
** @param user_data User data registered with the authorizer.
|
** @param user_data User data registered with the authorizer.
|
||||||
|
Loading…
Reference in New Issue
Block a user