This simplifies upgrading an HTTP request to a websocket, I believe, and fixes sending refresh auth tokens.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4791 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
parent
44b5ba1a9a
commit
00ba74a6c4
@ -202,11 +202,9 @@ function socket(request, response, client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refresh) {
|
response.upgrade(100, refresh ? {
|
||||||
return {
|
|
||||||
'Set-Cookie': `session=${refresh.token}; path=/; Max-Age=${refresh.interval}; Secure; SameSite=Strict`,
|
'Set-Cookie': `session=${refresh.token}; path=/; Max-Age=${refresh.interval}; Secure; SameSite=Strict`,
|
||||||
};
|
} : {});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export { socket, App };
|
export { socket, App };
|
||||||
|
@ -982,7 +982,7 @@ loadSettings().then(function() {
|
|||||||
httpd.set_http_redirect(gGlobalSettings.http_redirect);
|
httpd.set_http_redirect(gGlobalSettings.http_redirect);
|
||||||
}
|
}
|
||||||
httpd.all("/login", auth.handler);
|
httpd.all("/login", auth.handler);
|
||||||
httpd.registerSocketHandler("/app/socket", app.socket);
|
httpd.all("/app/socket", app.socket);
|
||||||
httpd.all("", function(request, response) {
|
httpd.all("", function(request, response) {
|
||||||
let match;
|
let match;
|
||||||
if (request.uri === "/" || request.uri === "") {
|
if (request.uri === "/" || request.uri === "") {
|
||||||
|
139
src/httpd.js.c
139
src/httpd.js.c
@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#define tf_countof(a) ((int)(sizeof((a)) / sizeof(*(a))))
|
#define tf_countof(a) ((int)(sizeof((a)) / sizeof(*(a))))
|
||||||
|
|
||||||
|
static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv);
|
||||||
|
|
||||||
static JSClassID _httpd_class_id;
|
static JSClassID _httpd_class_id;
|
||||||
static JSClassID _httpd_request_class_id;
|
static JSClassID _httpd_request_class_id;
|
||||||
|
|
||||||
@ -42,6 +44,34 @@ static JSValue _httpd_response_write_head(JSContext* context, JSValueConst this_
|
|||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _object_to_headers(JSContext* context, JSValue object, const char** headers, int headers_length)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
JSPropertyEnum* ptab = NULL;
|
||||||
|
uint32_t plen = 0;
|
||||||
|
JS_GetOwnPropertyNames(context, &ptab, &plen, object, JS_GPN_STRING_MASK);
|
||||||
|
for (; count < (int)plen && count < headers_length / 2; ++count)
|
||||||
|
{
|
||||||
|
JSValue key = JS_AtomToString(context, ptab[count].atom);
|
||||||
|
JSPropertyDescriptor desc;
|
||||||
|
JSValue key_value = JS_NULL;
|
||||||
|
if (JS_GetOwnProperty(context, &desc, object, ptab[count].atom) == 1)
|
||||||
|
{
|
||||||
|
key_value = desc.value;
|
||||||
|
JS_FreeValue(context, desc.setter);
|
||||||
|
JS_FreeValue(context, desc.getter);
|
||||||
|
}
|
||||||
|
headers[count * 2 + 0] = JS_ToCString(context, key);
|
||||||
|
headers[count * 2 + 1] = JS_ToCString(context, key_value);
|
||||||
|
}
|
||||||
|
for (uint32_t i = 0; i < plen; ++i)
|
||||||
|
{
|
||||||
|
JS_FreeAtom(context, ptab[i].atom);
|
||||||
|
}
|
||||||
|
js_free(context, ptab);
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
static JSValue _httpd_response_end(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
static JSValue _httpd_response_end(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
{
|
{
|
||||||
tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id);
|
tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id);
|
||||||
@ -71,40 +101,16 @@ static JSValue _httpd_response_end(JSContext* context, JSValueConst this_val, in
|
|||||||
JS_ToInt32(context, &status, response_status);
|
JS_ToInt32(context, &status, response_status);
|
||||||
JS_FreeValue(context, response_status);
|
JS_FreeValue(context, response_status);
|
||||||
|
|
||||||
const char** headers = NULL;
|
const char* headers[64] = { 0 };
|
||||||
int headers_length = 0;
|
|
||||||
JSValue response_headers = JS_GetPropertyStr(context, this_val, "response_headers");
|
JSValue response_headers = JS_GetPropertyStr(context, this_val, "response_headers");
|
||||||
JSPropertyEnum* ptab;
|
int headers_count = _object_to_headers(context, response_headers, headers, tf_countof(headers));
|
||||||
uint32_t plen;
|
|
||||||
JS_GetOwnPropertyNames(context, &ptab, &plen, response_headers, JS_GPN_STRING_MASK);
|
|
||||||
headers = alloca(sizeof(const char*) * plen * 2);
|
|
||||||
headers_length = plen;
|
|
||||||
for (uint32_t i = 0; i < plen; ++i)
|
|
||||||
{
|
|
||||||
JSValue key = JS_AtomToString(context, ptab[i].atom);
|
|
||||||
JSPropertyDescriptor desc;
|
|
||||||
JSValue key_value = JS_NULL;
|
|
||||||
if (JS_GetOwnProperty(context, &desc, response_headers, ptab[i].atom) == 1)
|
|
||||||
{
|
|
||||||
key_value = desc.value;
|
|
||||||
JS_FreeValue(context, desc.setter);
|
|
||||||
JS_FreeValue(context, desc.getter);
|
|
||||||
}
|
|
||||||
headers[i * 2 + 0] = JS_ToCString(context, key);
|
|
||||||
headers[i * 2 + 1] = JS_ToCString(context, key_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
tf_http_respond(request, status, headers, headers_length, data, length);
|
tf_http_respond(request, status, headers, headers_count, data, length);
|
||||||
|
|
||||||
for (int i = 0; i < headers_length * 2; i++)
|
for (int i = 0; i < headers_count * 2; i++)
|
||||||
{
|
{
|
||||||
JS_FreeCString(context, headers[i]);
|
JS_FreeCString(context, headers[i]);
|
||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < plen; ++i)
|
|
||||||
{
|
|
||||||
JS_FreeAtom(context, ptab[i].atom);
|
|
||||||
}
|
|
||||||
js_free(context, ptab);
|
|
||||||
JS_FreeValue(context, buffer);
|
JS_FreeValue(context, buffer);
|
||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
@ -204,6 +210,7 @@ static void _httpd_callback_internal(tf_http_request_t* request, bool is_websock
|
|||||||
JS_SetPropertyStr(context, response_object, "writeHead", JS_NewCFunction(context, _httpd_response_write_head, "writeHead", 2));
|
JS_SetPropertyStr(context, response_object, "writeHead", JS_NewCFunction(context, _httpd_response_write_head, "writeHead", 2));
|
||||||
JS_SetPropertyStr(context, response_object, "end", JS_NewCFunction(context, _httpd_response_end, "end", 1));
|
JS_SetPropertyStr(context, response_object, "end", JS_NewCFunction(context, _httpd_response_end, "end", 1));
|
||||||
JS_SetPropertyStr(context, response_object, "send", JS_NewCFunction(context, _httpd_response_send, "send", 2));
|
JS_SetPropertyStr(context, response_object, "send", JS_NewCFunction(context, _httpd_response_send, "send", 2));
|
||||||
|
JS_SetPropertyStr(context, response_object, "upgrade", JS_NewCFunction(context, _httpd_websocket_upgrade, "upgrade", 2));
|
||||||
JSValue args[] =
|
JSValue args[] =
|
||||||
{
|
{
|
||||||
request_object,
|
request_object,
|
||||||
@ -213,17 +220,7 @@ static void _httpd_callback_internal(tf_http_request_t* request, bool is_websock
|
|||||||
tf_util_report_error(context, response);
|
tf_util_report_error(context, response);
|
||||||
JS_FreeValue(context, request_object);
|
JS_FreeValue(context, request_object);
|
||||||
JS_FreeValue(context, response);
|
JS_FreeValue(context, response);
|
||||||
|
|
||||||
if (is_websocket)
|
|
||||||
{
|
|
||||||
request->on_message = _httpd_message_callback;
|
|
||||||
request->context = context;
|
|
||||||
request->user_data = JS_VALUE_GET_PTR(response_object);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
JS_FreeValue(context, response_object);
|
JS_FreeValue(context, response_object);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool _httpd_redirect(tf_http_request_t* request)
|
static bool _httpd_redirect(tf_http_request_t* request)
|
||||||
@ -255,13 +252,10 @@ static void _httpd_callback(tf_http_request_t* request)
|
|||||||
_httpd_callback_internal(request, false);
|
_httpd_callback_internal(request, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _httpd_websocket_callback(tf_http_request_t* request)
|
static JSValue _httpd_websocket_upgrade(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
{
|
{
|
||||||
if (_httpd_redirect(request))
|
tf_http_request_t* request = JS_GetOpaque(this_val, _httpd_request_class_id);
|
||||||
{
|
tf_http_request_ref(request);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* header_connection = tf_http_request_get_header(request, "connection");
|
const char* header_connection = tf_http_request_get_header(request, "connection");
|
||||||
const char* header_upgrade = tf_http_request_get_header(request, "upgrade");
|
const char* header_upgrade = tf_http_request_get_header(request, "upgrade");
|
||||||
const char* header_sec_websocket_key = tf_http_request_get_header(request, "sec-websocket-key");
|
const char* header_sec_websocket_key = tf_http_request_get_header(request, "sec-websocket-key");
|
||||||
@ -281,26 +275,51 @@ static void _httpd_websocket_callback(tf_http_request_t* request)
|
|||||||
SHA1(key_magic, size, digest);
|
SHA1(key_magic, size, digest);
|
||||||
char key[41] = { 0 };
|
char key[41] = { 0 };
|
||||||
tf_base64_encode(digest, sizeof(digest), key, sizeof(key));
|
tf_base64_encode(digest, sizeof(digest), key, sizeof(key));
|
||||||
enum { k_headers_count = 4 };
|
|
||||||
const char* headers[k_headers_count * 2] =
|
const char* headers[64] = { 0 };
|
||||||
{
|
int headers_count = 0;
|
||||||
"Upgrade", "websocket",
|
|
||||||
"Connection", "Upgrade",
|
headers[headers_count * 2 + 0] = "Upgrade";
|
||||||
"Sec-WebSocket-Accept", key,
|
headers[headers_count * 2 + 1] = "websocket";
|
||||||
"Sec-WebSocket-Version", "13",
|
headers_count++;
|
||||||
};
|
|
||||||
|
headers[headers_count * 2 + 0] = "Connection";
|
||||||
|
headers[headers_count * 2 + 1] = "Upgrade";
|
||||||
|
headers_count++;
|
||||||
|
|
||||||
|
headers[headers_count * 2 + 0] = "Sec-WebSocket-Accept";
|
||||||
|
headers[headers_count * 2 + 1] = key;
|
||||||
|
headers_count++;
|
||||||
|
|
||||||
bool send_version =
|
bool send_version =
|
||||||
!tf_http_request_get_header(request, "sec-websocket-version") ||
|
!tf_http_request_get_header(request, "sec-websocket-version") ||
|
||||||
strcmp(tf_http_request_get_header(request, "sec-websocket-version"), "13") != 0;
|
strcmp(tf_http_request_get_header(request, "sec-websocket-version"), "13") != 0;
|
||||||
|
if (send_version)
|
||||||
|
{
|
||||||
|
headers[headers_count * 2 + 0] = "Sec-WebSocket-Accept";
|
||||||
|
headers[headers_count * 2 + 1] = key;
|
||||||
|
headers_count++;
|
||||||
|
}
|
||||||
|
headers_count += _object_to_headers(context, argv[1], headers + headers_count * 2, tf_countof(headers) - headers_count * 2);
|
||||||
|
|
||||||
|
for (int i = 0; i < headers_count; i += 2)
|
||||||
|
{
|
||||||
|
tf_printf("[%d] %s = %s\n", i / 2, headers[i * 2 + 0], headers[i * 2 + 1]);
|
||||||
|
}
|
||||||
|
|
||||||
tf_http_request_websocket_upgrade(request);
|
tf_http_request_websocket_upgrade(request);
|
||||||
tf_http_respond(request, 101, headers, send_version ? k_headers_count : (k_headers_count - 1), NULL, 0);
|
tf_http_respond(request, 101, headers, headers_count, NULL, 0);
|
||||||
|
|
||||||
|
request->on_message = _httpd_message_callback;
|
||||||
|
request->context = context;
|
||||||
|
request->user_data = JS_VALUE_GET_PTR(JS_DupValue(context, this_val));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
tf_http_respond(request, 400, NULL, 0, NULL, 0);
|
tf_http_respond(request, 400, NULL, 0, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
_httpd_callback_internal(request, true);
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSValue _httpd_endpoint_all(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
static JSValue _httpd_endpoint_all(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
@ -314,17 +333,6 @@ static JSValue _httpd_endpoint_all(JSContext* context, JSValueConst this_val, in
|
|||||||
return JS_UNDEFINED;
|
return JS_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static JSValue _httpd_register_socket_handler(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
|
||||||
{
|
|
||||||
tf_http_t* http = JS_GetOpaque(this_val, _httpd_class_id);
|
|
||||||
const char* pattern = JS_ToCString(context, argv[0]);
|
|
||||||
http_handler_data_t* data = tf_malloc(sizeof(http_handler_data_t));
|
|
||||||
*data = (http_handler_data_t) { .context = context, .callback = JS_DupValue(context, argv[1]) };
|
|
||||||
tf_http_add_handler(http, pattern, _httpd_websocket_callback, data);
|
|
||||||
JS_FreeCString(context, pattern);
|
|
||||||
return JS_UNDEFINED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static JSValue _httpd_endpoint_start(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
static JSValue _httpd_endpoint_start(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv)
|
||||||
{
|
{
|
||||||
tf_http_t* http = JS_GetOpaque(this_val, _httpd_class_id);
|
tf_http_t* http = JS_GetOpaque(this_val, _httpd_class_id);
|
||||||
@ -513,7 +521,6 @@ void tf_httpd_register(JSContext* context)
|
|||||||
|
|
||||||
JS_SetPropertyStr(context, httpd, "handlers", JS_NewObject(context));
|
JS_SetPropertyStr(context, httpd, "handlers", JS_NewObject(context));
|
||||||
JS_SetPropertyStr(context, httpd, "all", JS_NewCFunction(context, _httpd_endpoint_all, "all", 2));
|
JS_SetPropertyStr(context, httpd, "all", JS_NewCFunction(context, _httpd_endpoint_all, "all", 2));
|
||||||
JS_SetPropertyStr(context, httpd, "registerSocketHandler", JS_NewCFunction(context, _httpd_register_socket_handler, "register_socket_handler", 2));
|
|
||||||
JS_SetPropertyStr(context, httpd, "start", JS_NewCFunction(context, _httpd_endpoint_start, "start", 2));
|
JS_SetPropertyStr(context, httpd, "start", JS_NewCFunction(context, _httpd_endpoint_start, "start", 2));
|
||||||
JS_SetPropertyStr(context, httpd, "set_http_redirect", JS_NewCFunction(context, _httpd_set_http_redirect, "set_http_redirect", 1));
|
JS_SetPropertyStr(context, httpd, "set_http_redirect", JS_NewCFunction(context, _httpd_set_http_redirect, "set_http_redirect", 1));
|
||||||
JS_SetPropertyStr(context, global, "httpd", httpd);
|
JS_SetPropertyStr(context, global, "httpd", httpd);
|
||||||
|
Loading…
Reference in New Issue
Block a user