forked from cory/tildefriends
Serve core static files without leaving C.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4833 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
147
src/httpd.js.c
147
src/httpd.js.c
@ -1,5 +1,6 @@
|
||||
#include "httpd.js.h"
|
||||
|
||||
#include "file.js.h"
|
||||
#include "http.h"
|
||||
#include "log.h"
|
||||
#include "mem.h"
|
||||
@ -10,6 +11,7 @@
|
||||
|
||||
#include "picohttpparser.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@ -508,6 +510,150 @@ static void _httpd_endpoint_hitches(tf_http_request_t* request)
|
||||
tf_free(response);
|
||||
}
|
||||
|
||||
static const char* _after(const char* text, const char* prefix)
|
||||
{
|
||||
size_t prefix_length = strlen(prefix);
|
||||
if (strncmp(text, prefix, prefix_length) == 0)
|
||||
{
|
||||
return text + prefix_length;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
static double _time_spec_to_double(const uv_timespec_t* time_spec)
|
||||
{
|
||||
return (double)time_spec->tv_sec + (double)(time_spec->tv_nsec) / 1e9;
|
||||
}
|
||||
|
||||
typedef struct _http_file_t
|
||||
{
|
||||
tf_http_request_t* request;
|
||||
char etag[512];
|
||||
} http_file_t;
|
||||
|
||||
static const char* _ext_to_content_type(const char* ext)
|
||||
{
|
||||
if (ext)
|
||||
{
|
||||
if (strcmp(ext, ".js") == 0)
|
||||
{
|
||||
return "text/javascript; charset=UTF-8";
|
||||
}
|
||||
if (strcmp(ext, ".css") == 0)
|
||||
{
|
||||
return "text/css; charset=UTF-8";
|
||||
}
|
||||
else if (strcmp(ext, ".png") == 0)
|
||||
{
|
||||
return "image/png";
|
||||
}
|
||||
}
|
||||
return "application/binary";
|
||||
}
|
||||
|
||||
static void _httpd_endpoint_static_read(tf_task_t* task, const char* path, int result, const void* data, void* user_data)
|
||||
{
|
||||
http_file_t* file = user_data;
|
||||
tf_http_request_t* request = file->request;
|
||||
if (result >= 0)
|
||||
{
|
||||
if (strcmp(path, "core/tfrpc.js") == 0)
|
||||
{
|
||||
const char* content_type = _ext_to_content_type(strrchr(path, '.'));
|
||||
const char* headers[] =
|
||||
{
|
||||
"Content-Type", content_type,
|
||||
"etag", file->etag,
|
||||
"Access-Control-Allow-Origin", "null",
|
||||
};
|
||||
tf_http_respond(request, 200, headers, tf_countof(headers) / 2, data, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* content_type = _ext_to_content_type(strrchr(path, '.'));
|
||||
const char* headers[] =
|
||||
{
|
||||
"Content-Type", content_type,
|
||||
"etag", file->etag,
|
||||
};
|
||||
tf_http_respond(request, 200, headers, tf_countof(headers) / 2, data, result);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_printf("404 %s\n", path);
|
||||
const char* k_payload = tf_http_status_text(404);
|
||||
tf_http_respond(request, 404, NULL, 0, k_payload, strlen(k_payload));
|
||||
}
|
||||
tf_http_request_unref(request);
|
||||
tf_free(file);
|
||||
}
|
||||
|
||||
static void _httpd_endpoint_static_stat(tf_task_t* task, const char* path, int result, const uv_stat_t* stat, void* user_data)
|
||||
{
|
||||
tf_http_request_t* request = user_data;
|
||||
const char* match = tf_http_request_get_header(request, "if-none-match");
|
||||
char etag[512];
|
||||
snprintf(etag, sizeof(etag), "\"%f_%zd\"", _time_spec_to_double(&stat->st_mtim), (size_t)stat->st_size);
|
||||
if (match && strcmp(match, etag) == 0)
|
||||
{
|
||||
tf_http_respond(request, 304, NULL, 0, NULL, 0);
|
||||
tf_http_request_unref(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
http_file_t* file = tf_malloc(sizeof(http_file_t));
|
||||
*file = (http_file_t) { .request = request };
|
||||
static_assert(sizeof(file->etag) == sizeof(etag), "Size mismatch");
|
||||
memcpy(file->etag, etag, sizeof(etag));
|
||||
tf_file_read(task, path, _httpd_endpoint_static_read, file);
|
||||
}
|
||||
}
|
||||
|
||||
static void _httpd_endpoint_static(tf_http_request_t* request)
|
||||
{
|
||||
if (_httpd_redirect(request))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const char* k_static_files[] =
|
||||
{
|
||||
"index.html",
|
||||
"client.js",
|
||||
"favicon.png",
|
||||
"jszip.min.js",
|
||||
"style.css",
|
||||
"tfrpc.js",
|
||||
"w3.css",
|
||||
};
|
||||
|
||||
tf_task_t* task = request->user_data;
|
||||
const char* after = _after(request->path, "/static/");
|
||||
bool found = false;
|
||||
for (int i = 0; i < tf_countof(k_static_files); i++)
|
||||
{
|
||||
if (strcmp(after, k_static_files[i]) == 0)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
const char* k_payload = tf_http_status_text(404);
|
||||
tf_http_respond(request, 404, NULL, 0, k_payload, strlen(k_payload));
|
||||
return;
|
||||
}
|
||||
|
||||
size_t size = strlen("core/") + strlen(after) + 1;
|
||||
char* path = alloca(size);
|
||||
snprintf(path, size, "core/%s", after);
|
||||
tf_http_request_ref(request);
|
||||
tf_file_stat(task, path, _httpd_endpoint_static_stat, request);
|
||||
}
|
||||
|
||||
static void _httpd_endpoint_robots_txt(tf_http_request_t* request)
|
||||
{
|
||||
if (_httpd_redirect(request))
|
||||
@ -571,6 +717,7 @@ void tf_httpd_register(JSContext* context)
|
||||
tf_http_set_trace(http, tf_task_get_trace(task));
|
||||
JS_SetOpaque(httpd, http);
|
||||
|
||||
tf_http_add_handler(http, "/static/", _httpd_endpoint_static, NULL, task);
|
||||
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, "/disconnections", _httpd_endpoint_disconnections, NULL, task);
|
||||
|
Reference in New Issue
Block a user