core: Implement websocket timeout in C.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 9m6s
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 9m6s
This commit is contained in:
@@ -955,6 +955,11 @@ void tf_http_request_websocket_send(tf_http_request_t* request, int op_code, con
|
|||||||
tf_free(copy);
|
tf_free(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tf_http_request_websocket_close(tf_http_request_t* request)
|
||||||
|
{
|
||||||
|
_http_connection_destroy(request->connection, "websocket close");
|
||||||
|
}
|
||||||
|
|
||||||
void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length)
|
void tf_http_respond(tf_http_request_t* request, int status, const char** headers, int headers_count, const void* body, size_t content_length)
|
||||||
{
|
{
|
||||||
if (request->connection->is_response_sent)
|
if (request->connection->is_response_sent)
|
||||||
|
|||||||
@@ -210,6 +210,13 @@ const char* tf_http_get_cookie(const char* cookie_header, const char* name);
|
|||||||
*/
|
*/
|
||||||
void tf_http_request_websocket_send(tf_http_request_t* request, int op_code, const void* data, size_t size);
|
void tf_http_request_websocket_send(tf_http_request_t* request, int op_code, const void* data, size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
** Close a websocket.
|
||||||
|
** @param request The HTTP request which was previously updated to a websocket
|
||||||
|
** session with tf_http_request_websocket_upgrade().
|
||||||
|
*/
|
||||||
|
void tf_http_request_websocket_close(tf_http_request_t* request);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
** Upgrade an HTTP request to a websocket session.
|
** Upgrade an HTTP request to a websocket session.
|
||||||
** @param request The HTTP request.
|
** @param request The HTTP request.
|
||||||
|
|||||||
@@ -218,11 +218,14 @@ void tf_httpd_endpoint_app(tf_http_request_t* request)
|
|||||||
typedef struct _app_t
|
typedef struct _app_t
|
||||||
{
|
{
|
||||||
tf_http_request_t* request;
|
tf_http_request_t* request;
|
||||||
|
uv_timer_t timer;
|
||||||
const char* settings;
|
const char* settings;
|
||||||
JSValue opaque;
|
JSValue opaque;
|
||||||
JSValue credentials;
|
JSValue credentials;
|
||||||
tf_taskstub_t* taskstub;
|
|
||||||
JSValue process;
|
JSValue process;
|
||||||
|
uint64_t last_ping_ms;
|
||||||
|
uint64_t last_active_ms;
|
||||||
|
bool got_hello;
|
||||||
} app_t;
|
} app_t;
|
||||||
|
|
||||||
static void _httpd_auth_query_work(tf_ssb_t* ssb, void* user_data)
|
static void _httpd_auth_query_work(tf_ssb_t* ssb, void* user_data)
|
||||||
@@ -232,14 +235,20 @@ static void _httpd_auth_query_work(tf_ssb_t* ssb, void* user_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void _httpd_app_kill_task(app_t* work)
|
static void _httpd_app_kill_task(app_t* work)
|
||||||
{
|
|
||||||
if (work->taskstub)
|
|
||||||
{
|
{
|
||||||
JSContext* context = work->request->context;
|
JSContext* context = work->request->context;
|
||||||
JSValue result = tf_taskstub_kill(work->taskstub);
|
if (!JS_IsUndefined(work->process))
|
||||||
|
{
|
||||||
|
JSValue task = JS_GetPropertyStr(context, work->process, "task");
|
||||||
|
if (!JS_IsUndefined(task))
|
||||||
|
{
|
||||||
|
JSValue kill = JS_GetPropertyStr(context, task, "kill");
|
||||||
|
JSValue result = JS_Call(context, kill, task, 0, NULL);
|
||||||
tf_util_report_error(context, result);
|
tf_util_report_error(context, result);
|
||||||
JS_FreeValue(context, result);
|
JS_FreeValue(context, result);
|
||||||
work->taskstub = NULL;
|
JS_FreeValue(context, kill);
|
||||||
|
}
|
||||||
|
JS_FreeValue(context, task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,6 +510,7 @@ static void _httpd_app_message_hello(app_t* work, JSValue message)
|
|||||||
tf_task_t* task = tf_task_get(context);
|
tf_task_t* task = tf_task_get(context);
|
||||||
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
tf_ssb_t* ssb = tf_task_get_ssb(task);
|
||||||
tf_http_request_ref(work->request);
|
tf_http_request_ref(work->request);
|
||||||
|
work->got_hello = true;
|
||||||
|
|
||||||
JSValue session = JS_IsObject(work->credentials) ? JS_GetPropertyStr(context, work->credentials, "session") : JS_UNDEFINED;
|
JSValue session = JS_IsObject(work->credentials) ? JS_GetPropertyStr(context, work->credentials, "session") : JS_UNDEFINED;
|
||||||
const char* user = tf_util_get_property_as_string(context, session, "name");
|
const char* user = tf_util_get_property_as_string(context, session, "name");
|
||||||
@@ -561,6 +571,8 @@ static void _httpd_app_on_message(tf_http_request_t* request, int op_code, const
|
|||||||
{
|
{
|
||||||
app_t* work = request->user_data;
|
app_t* work = request->user_data;
|
||||||
JSContext* context = request->context;
|
JSContext* context = request->context;
|
||||||
|
tf_task_t* task = tf_task_get(context);
|
||||||
|
work->last_active_ms = uv_now(tf_task_get_loop(task));
|
||||||
switch (op_code)
|
switch (op_code)
|
||||||
{
|
{
|
||||||
/* TEXT */
|
/* TEXT */
|
||||||
@@ -572,14 +584,13 @@ static void _httpd_app_on_message(tf_http_request_t* request, int op_code, const
|
|||||||
if (JS_IsException(message) || !JS_IsObject(message))
|
if (JS_IsException(message) || !JS_IsObject(message))
|
||||||
{
|
{
|
||||||
tf_util_report_error(context, message);
|
tf_util_report_error(context, message);
|
||||||
_httpd_app_kill_task(work);
|
tf_http_request_websocket_close(request);
|
||||||
/* http close? */
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
JSValue action = JS_GetPropertyStr(context, message, "action");
|
JSValue action = JS_GetPropertyStr(context, message, "action");
|
||||||
const char* action_string = JS_ToCString(context, action);
|
const char* action_string = JS_ToCString(context, action);
|
||||||
if (action_string && !work->taskstub && strcmp(action_string, "hello") == 0)
|
if (action_string && !work->got_hello && strcmp(action_string, "hello") == 0)
|
||||||
{
|
{
|
||||||
_httpd_app_message_hello(work, message);
|
_httpd_app_message_hello(work, message);
|
||||||
}
|
}
|
||||||
@@ -604,6 +615,13 @@ static void _httpd_app_on_message(tf_http_request_t* request, int op_code, const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _httpd_app_on_timer_close(uv_handle_t* handle)
|
||||||
|
{
|
||||||
|
app_t* work = handle->data;
|
||||||
|
handle->data = NULL;
|
||||||
|
tf_free(work);
|
||||||
|
}
|
||||||
|
|
||||||
static void _httpd_app_on_close(tf_http_request_t* request)
|
static void _httpd_app_on_close(tf_http_request_t* request)
|
||||||
{
|
{
|
||||||
JSContext* context = request->context;
|
JSContext* context = request->context;
|
||||||
@@ -614,11 +632,31 @@ static void _httpd_app_on_close(tf_http_request_t* request)
|
|||||||
JS_FreeValue(context, work->process);
|
JS_FreeValue(context, work->process);
|
||||||
JS_FreeValue(context, work->opaque);
|
JS_FreeValue(context, work->opaque);
|
||||||
work->process = JS_UNDEFINED;
|
work->process = JS_UNDEFINED;
|
||||||
tf_free(work);
|
uv_close((uv_handle_t*)&work->timer, _httpd_app_on_timer_close);
|
||||||
|
|
||||||
tf_http_request_unref(request);
|
tf_http_request_unref(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _httpd_app_on_timer(uv_timer_t* timer)
|
||||||
|
{
|
||||||
|
app_t* app = timer->data;
|
||||||
|
uint64_t now_ms = uv_now(timer->loop);
|
||||||
|
uint64_t repeat_ms = uv_timer_get_repeat(timer);
|
||||||
|
if (now_ms - app->last_active_ms < repeat_ms)
|
||||||
|
{
|
||||||
|
/* Active. */
|
||||||
|
}
|
||||||
|
else if (app->last_ping_ms > app->last_active_ms)
|
||||||
|
{
|
||||||
|
/* Timed out. */
|
||||||
|
tf_http_request_websocket_close(app->request);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tf_http_request_websocket_send(app->request, 0x9, NULL, 0);
|
||||||
|
app->last_ping_ms = now_ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void _httpd_auth_query_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
static void _httpd_auth_query_after_work(tf_ssb_t* ssb, int status, void* user_data)
|
||||||
{
|
{
|
||||||
app_t* work = user_data;
|
app_t* work = user_data;
|
||||||
@@ -701,6 +739,8 @@ static void _httpd_auth_query_after_work(tf_ssb_t* ssb, int status, void* user_d
|
|||||||
tf_http_request_websocket_upgrade(request);
|
tf_http_request_websocket_upgrade(request);
|
||||||
tf_http_respond(request, 101, headers, headers_count, NULL, 0);
|
tf_http_respond(request, 101, headers, headers_count, NULL, 0);
|
||||||
|
|
||||||
|
uv_timer_start(&work->timer, _httpd_app_on_timer, 6 * 1000, 6 * 1000);
|
||||||
|
|
||||||
/* What now? */
|
/* What now? */
|
||||||
tf_free((void*)cookie);
|
tf_free((void*)cookie);
|
||||||
JS_FreeCString(context, name_string);
|
JS_FreeCString(context, name_string);
|
||||||
@@ -740,7 +780,9 @@ static void _tf_httpd_endpoint_app_socket_c(tf_http_request_t* request)
|
|||||||
*work = (app_t) {
|
*work = (app_t) {
|
||||||
.request = request,
|
.request = request,
|
||||||
.credentials = credentials,
|
.credentials = credentials,
|
||||||
|
.timer = { .data = work },
|
||||||
};
|
};
|
||||||
|
uv_timer_init(tf_ssb_get_loop(ssb), &work->timer);
|
||||||
tf_ssb_run_work(ssb, _httpd_auth_query_work, _httpd_auth_query_after_work, work);
|
tf_ssb_run_work(ssb, _httpd_auth_query_work, _httpd_auth_query_after_work, work);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user