Skip to content

Commit

Permalink
Added support for admin-protected custom session timeouts (#2577)
Browse files Browse the repository at this point in the history
  • Loading branch information
alg committed Mar 25, 2021
1 parent 414edca commit e935cdc
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 19 deletions.
78 changes: 61 additions & 17 deletions janus.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ static struct janus_json_parameter debug_parameters[] = {
static struct janus_json_parameter timeout_parameters[] = {
{"timeout", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
};
static struct janus_json_parameter session_timeout_parameters[] = {
{"timeout", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED}
};
static struct janus_json_parameter level_parameters[] = {
{"level", JSON_INTEGER, JANUS_JSON_PARAM_REQUIRED | JANUS_JSON_PARAM_POSITIVE}
};
Expand Down Expand Up @@ -275,7 +278,7 @@ static json_t *janus_create_message(const char *status, uint64_t session_id, con
* incurring in unexpected timeouts (when HTTP is used in janus.js, the
* long poll is used as a keepalive mechanism). */
#define DEFAULT_SESSION_TIMEOUT 60
static uint session_timeout = DEFAULT_SESSION_TIMEOUT;
static uint global_session_timeout = DEFAULT_SESSION_TIMEOUT;

#define DEFAULT_RECLAIM_SESSION_TIMEOUT 0
static uint reclaim_session_timeout = DEFAULT_RECLAIM_SESSION_TIMEOUT;
Expand Down Expand Up @@ -326,7 +329,7 @@ static json_t *janus_info(const char *transaction) {
json_object_set_new(info, "data_channels", json_false());
#endif
json_object_set_new(info, "accepting-new-sessions", accept_new_sessions ? json_true() : json_false());
json_object_set_new(info, "session-timeout", json_integer(session_timeout));
json_object_set_new(info, "session-timeout", json_integer(global_session_timeout));
json_object_set_new(info, "reclaim-session-timeout", json_integer(reclaim_session_timeout));
json_object_set_new(info, "candidates-timeout", json_integer(candidates_timeout));
json_object_set_new(info, "server-name", json_string(server_name ? server_name : JANUS_SERVER_NAME));
Expand Down Expand Up @@ -655,8 +658,6 @@ static void janus_request_unref(janus_request *request) {
}

static gboolean janus_check_sessions(gpointer user_data) {
if(session_timeout < 1 && reclaim_session_timeout < 1) /* Session timeouts are disabled */
return G_SOURCE_CONTINUE;
janus_mutex_lock(&sessions_mutex);
if(sessions && g_hash_table_size(sessions) > 0) {
GHashTableIter iter;
Expand All @@ -668,10 +669,15 @@ static gboolean janus_check_sessions(gpointer user_data) {
continue;
}
gint64 now = janus_get_monotonic_time();
if ((session_timeout > 0 && (now - session->last_activity >= (gint64)session_timeout * G_USEC_PER_SEC) &&
!g_atomic_int_compare_and_exchange(&session->timeout, 0, 1)) ||

/* Use either session-specific timeout or global. */
gint64 timeout = (gint64)session->timeout;
if (timeout == -1) timeout = (gint64)global_session_timeout;

if ((timeout > 0 && (now - session->last_activity >= timeout * G_USEC_PER_SEC) &&
!g_atomic_int_compare_and_exchange(&session->timedout, 0, 1)) ||
((g_atomic_int_get(&session->transport_gone) && now - session->last_activity >= (gint64)reclaim_session_timeout * G_USEC_PER_SEC) &&
!g_atomic_int_compare_and_exchange(&session->timeout, 0, 1))) {
!g_atomic_int_compare_and_exchange(&session->timedout, 0, 1))) {
JANUS_LOG(LOG_INFO, "Timeout expired for session %"SCNu64"...\n", session->session_id);
/* Mark the session as over, we'll deal with it later */
janus_session_handles_clear(session);
Expand Down Expand Up @@ -739,8 +745,9 @@ janus_session *janus_session_create(guint64 session_id) {
session->session_id = session_id;
janus_refcount_init(&session->ref, janus_session_free);
session->source = NULL;
session->timeout = -1; /* Negative means rely on global timeout */
g_atomic_int_set(&session->destroyed, 0);
g_atomic_int_set(&session->timeout, 0);
g_atomic_int_set(&session->timedout, 0);
g_atomic_int_set(&session->transport_gone, 0);
session->last_activity = janus_get_monotonic_time();
session->ice_handles = NULL;
Expand Down Expand Up @@ -1063,6 +1070,7 @@ int janus_process_incoming_request(janus_request *request) {
goto jsondone;
}
}

/* Handle it */
session = janus_session_create(session_id);
if(session == NULL) {
Expand Down Expand Up @@ -1981,7 +1989,7 @@ int janus_process_incoming_admin_request(janus_request *request) {
json_t *reply = janus_create_message("success", 0, transaction_text);
json_t *status = json_object();
json_object_set_new(status, "token_auth", janus_auth_is_enabled() ? json_true() : json_false());
json_object_set_new(status, "session_timeout", json_integer(session_timeout));
json_object_set_new(status, "session_timeout", json_integer(global_session_timeout));
json_object_set_new(status, "reclaim_session_timeout", json_integer(reclaim_session_timeout));
json_object_set_new(status, "candidates_timeout", json_integer(candidates_timeout));
json_object_set_new(status, "log_level", json_integer(janus_log_level));
Expand All @@ -1999,7 +2007,6 @@ int janus_process_incoming_admin_request(janus_request *request) {
ret = janus_process_success(request, reply);
goto jsondone;
} else if(!strcasecmp(message_text, "set_session_timeout")) {
/* Change the session timeout value */
JANUS_VALIDATE_JSON_OBJECT(root, timeout_parameters,
error_code, error_cause, FALSE,
JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
Expand All @@ -2013,12 +2020,16 @@ int janus_process_incoming_admin_request(janus_request *request) {
ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (timeout should be a positive integer)");
goto jsondone;
}
session_timeout = timeout_num;

/* Set global session timeout */
global_session_timeout = timeout_num;

/* Prepare JSON reply */
json_t *reply = json_object();
json_object_set_new(reply, "janus", json_string("success"));
json_object_set_new(reply, "transaction", json_string(transaction_text));
json_object_set_new(reply, "timeout", json_integer(session_timeout));
json_object_set_new(reply, "timeout", json_integer(timeout_num));

/* Send the success reply */
ret = janus_process_success(request, reply);
goto jsondone;
Expand Down Expand Up @@ -2668,12 +2679,45 @@ int janus_process_incoming_admin_request(janus_request *request) {
janus_events_notify_handlers(JANUS_EVENT_TYPE_SESSION, JANUS_EVENT_SUBTYPE_NONE,
session_id, "destroyed", NULL);
goto jsondone;
}
/* If this is not a request to destroy a session, it must be a request to list the handles */
if(strcasecmp(message_text, "list_handles")) {
} else if (!strcasecmp(message_text, "set_session_timeout")) {
/* Specific session timeout setting. */
JANUS_VALIDATE_JSON_OBJECT(root, session_timeout_parameters,
error_code, error_cause, FALSE,
JANUS_ERROR_MISSING_MANDATORY_ELEMENT, JANUS_ERROR_INVALID_ELEMENT_TYPE);
if(error_code != 0) {
ret = janus_process_error_string(request, session_id, transaction_text, error_code, error_cause);
goto jsondone;
}

/* Positive timeout is seconds, 0 is unlimited, -1 is global session timeout */
json_t *timeout = json_object_get(root, "timeout");
int timeout_num = json_integer_value(timeout);
if(timeout_num < -1) {
ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_ELEMENT_TYPE, "Invalid element type (timeout should be a non-negative integer or -1)");
goto jsondone;
}

/* Set specific session timeout */
janus_mutex_lock(&session->mutex);
session->timeout = timeout_num;
janus_mutex_unlock(&session->mutex);

/* Prepare JSON reply */
json_t *reply = json_object();
json_object_set_new(reply, "janus", json_string("success"));
json_object_set_new(reply, "transaction", json_string(transaction_text));
json_object_set_new(reply, "timeout", json_integer(timeout_num));
json_object_set_new(reply, "session_id", json_integer(session_id));

/* Send the success reply */
ret = janus_process_success(request, reply);
goto jsondone;
} else if(strcasecmp(message_text, "list_handles")) {
/* If this is not a request to destroy a session, it must be a request to list the handles */
ret = janus_process_error(request, session_id, transaction_text, JANUS_ERROR_INVALID_REQUEST_PATH, "Unhandled request '%s' at this path", message_text);
goto jsondone;
}

/* List handles */
json_t *list = janus_session_handles_list_json(session);
/* Prepare JSON reply */
Expand Down Expand Up @@ -3229,7 +3273,7 @@ void janus_transport_gone(janus_transport *plugin, janus_transport_session *tran
g_hash_table_iter_init(&iter, sessions);
while(g_hash_table_iter_next(&iter, NULL, &value)) {
janus_session *session = (janus_session *) value;
if(!session || g_atomic_int_get(&session->destroyed) || g_atomic_int_get(&session->timeout) || session->last_activity == 0)
if(!session || g_atomic_int_get(&session->destroyed) || g_atomic_int_get(&session->timedout) || session->last_activity == 0)
continue;
if(session->source && session->source->instance == transport) {
JANUS_LOG(LOG_VERB, " -- Session %"SCNu64" will be over if not reclaimed\n", session->session_id);
Expand Down Expand Up @@ -4577,7 +4621,7 @@ gint main(int argc, char *argv[])
if(st == 0) {
JANUS_LOG(LOG_WARN, "Session timeouts have been disabled (note, may result in orphaned sessions)\n");
}
session_timeout = st;
global_session_timeout = st;
}
}

Expand Down
4 changes: 3 additions & 1 deletion janus.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ typedef struct janus_session {
/*! \brief Pointer to the request instance (and the transport that originated the session) */
janus_request *source;
/*! \brief Flag to notify there's been a session timeout */
volatile gint timeout;
volatile gint timedout;
/*! \brief Timeout value in seconds to use with this session, 0 is unlimited, -1 is global session timeout setting */
gint timeout;
/*! \brief Flag to notify that transport is gone */
volatile gint transport_gone;
/*! \brief Mutex to lock/unlock this session */
Expand Down
3 changes: 2 additions & 1 deletion mainpage.dox
Original file line number Diff line number Diff line change
Expand Up @@ -2276,7 +2276,7 @@ const token = getJanusToken('janus', ['janus.plugin.videoroom']),
* \subsection adminreqc Configuration-related requests
* - \c get_status: returns the current value for the settings that can be
* modified at runtime via the Admin API (see below);
* - \c set_session_timeout: change the session timeout value in Janus;
* - \c set_session_timeout: change global session timeout value in Janus;
* - \c set_log_level: change the log level in Janus;
* - \c set_log_timestamps: selectively enable/disable adding a timestamp
* to all log lines Janus writes on the console and/or to file;
Expand Down Expand Up @@ -2306,6 +2306,7 @@ const token = getJanusToken('janus', ['janus.plugin.videoroom']),
* you want to stop accepting new sessions because you're draining this instance;
* - \c list_sessions: list all the sessions currently active in Janus
* (returns an array of session identifiers);
* - \c set_session_timeout: change session timeout value in Janus;
* - \c destroy_session: destroy a specific session; this behaves exactly
* as the \c destroy request does in the Janus API.
*
Expand Down

0 comments on commit e935cdc

Please sign in to comment.