Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New mechanism to tweak/query transport plugins via Admin API #2354

Merged
merged 2 commits into from
Oct 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion html/admin.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ <h1>Janus WebRTC Server: Admin/Monitor</h1>
<li role="presentation" class="disabled"><a href="#serverinfo" aria-controls="serverinfo" role="tab" data-toggle="tab">Server Info</a></li>
<li role="presentation" class="disabled"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">Settings</a></li>
<li role="presentation" class="disabled"><a href="#plugins" aria-controls="plugins" role="tab" data-toggle="tab">Plugins</a></li>
<li role="presentation" class="disabled"><a href="#transports" aria-controls="transports" role="tab" data-toggle="tab">Transports</a></li>
<li role="presentation" class="disabled"><a href="#handlesinfo" aria-controls="handlesinfo" role="tab" data-toggle="tab">Handles</a></li>
<li role="presentation" class="disabled"><a href="#tokens" aria-controls="tokens" role="tab" data-toggle="tab">Stored Tokens</a></li>
</ul>
Expand All @@ -64,7 +65,10 @@ <h1>Janus WebRTC Server: Admin/Monitor</h1>
and provides you with a way to change them dynamically.</p>
<p>The <code>Plugins</code> tab presents the list of media plugins
available in this Janus instance, and allows you to interact with
them, assuming they implement the <code>handle_admin_message</code> API.</p>
them, assuming they implement the <code>handle_admin_message</code> API.
The <code>Transports</code> tab does the same for transport plugins,
and allows you to send requests to tweak the behaviour of the plugin
or query its internal state, assuming they support this somehow.</p>
<p>The <code>Handles</code> tab allows you to browse the currently active sessions
and handles in Janus. Selecting a specific handle will provide you
with all the current info related to it, including plugin it is
Expand Down Expand Up @@ -132,6 +136,26 @@ <h5>Response</h5>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane fade" id="transports">
<h4>Transports</h4>
<div class="row">
<div class="col-md-3">
<table class="table" id="transports-list">
</table>
</div>
<div id="transport-message" class="col-md-9 hide">
<div class="row">
<h5>Tweak/Query</h5>
<table class="table" id="transport-request">
</table>
</div>
<div class="row">
<h5>Response</h5>
<pre id="transport-response"></pre>
</div>
</div>
</div>
</div>
<div role="tabpanel" class="tab-pane fade" id="handlesinfo">
<div id="sessions" class="col-md-2">
<h4>Sessions (<span id="sessions-num">0</span>) <i id="update-sessions" class="fa fa-refresh" title="Refresh list of sessions" style="cursor: pointer;"></i></h4>
Expand Down
140 changes: 140 additions & 0 deletions html/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ var session = null; // Selected session
var handle = null; // Selected handle

var plugins = [], pluginsIndex = [];
var transports = [], transportsIndex = [];
var settings = {};

var currentHandle = null;
Expand Down Expand Up @@ -81,6 +82,8 @@ function randomString(len) {
function updateServerInfo() {
plugins = [];
pluginsIndex = [];
transports = [];
transportsIndex = [];
$.ajax({
type: 'GET',
url: server + "/info",
Expand Down Expand Up @@ -176,6 +179,7 @@ function updateServerInfo() {
'</tr>'
);
for(var t in transportsJson) {
transports.push(t);
var v = transportsJson[t];
$('#server-transports').append(
'<tr>' +
Expand All @@ -184,6 +188,19 @@ function updateServerInfo() {
' <td>' + v.description + '</td>' +
' <td>' + v.version_string + '</td>' +
'</tr>');
transportsIndex.push(t);
$('#transports-list').append(
'<a id="transport-'+(transportsIndex.length-1)+'" href="#" class="list-group-item">'+t+'</a>'
);
$('#transport-'+(transportsIndex.length-1)).click(function(event) {
event.preventDefault();
var ti = parseInt($(this).attr('id').split('transport-')[1]);
var transport = transportsIndex[ti];
console.log("Selected transport:", transport);
$('#transports-list a').removeClass('active');
$('#transport-'+ti).addClass('active');
resetTransportRequest();
});
}
$('#server-handlers').html(
'<tr>' +
Expand Down Expand Up @@ -688,6 +705,129 @@ function sendPluginMessage(plugin, message) {
});
}

// Transports
function resetTransportRequest() {
$('#transport-request').empty().append(
'<tr style="background: #f9f9f9;">' +
' <th width="25%">Name</th>' +
' <th width="25%">Value</th>' +
' <th width="25%">Type</th>' +
' <th></th>' +
'</tr>' +
'<tr>' +
' <td><i id="addattr" class="fa fa-plus-circle" style="cursor: pointer;"></i></td>' +
' <td></td>' +
' <td></td>' +
' <td><button id="sendmsg" type="button" class="btn btn-xs btn-success pull-right">Send message</button></td>' +
'</tr>');
$('#addattr').click(addTransportMessageAttribute).click();
$('#sendmsg').click(function() {
var message = {};
var num = $('.pm-property').length;
for(var i=0; i<num; i++) {
var name = $('#attrname'+i).val();
if(name === '') {
bootbox.alert("Missing name in attribute #" + (i+1));
return;
}
if(message[name] !== null && message[name] !== undefined) {
bootbox.alert("Duplicate attribute '" + name + "'");
return;
}
var value = $('#attrvalue'+i).val();
if(value === '') {
bootbox.alert("Missing value in attribute #" + (i+1));
return;
}
var type = $('#attrtype'+i).val();
if(type === "number") {
value = parseInt(value);
if(isNaN(value)) {
bootbox.alert("Invalid value in attribute #" + (i+1) + " (expecting a number)");
return;
}
} else if(type === "boolean") {
if(value.toLowerCase() === "true") {
value = true;
} else if(value.toLowerCase() === "false") {
value = false;
} else {
bootbox.alert("Invalid value in attribute #" + (i+1) + " (expecting a boolean)");
return;
}
}
console.log("Type:", type);
message[name] = value;
}
sendTransportMessage($('#transports-list .active').text(), message);
});
$('#transport-message').removeClass('hide');
}

function addTransportMessageAttribute() {
var num = $('.pm-property').length;
$('#addattr').parent().parent().before(
'<tr>' +
' <td><input type="text" id="attrname' + num + '" placeholder="Attribute name" onkeypress="return checkEnter(this, event);" style="width: 100%;" class="pm-property form-control input-sm"></td>' +
' <td><input type="text" id="attrvalue' + num + '" placeholder="Attribute value" onkeypress="return checkEnter(this, event);" style="width: 100%;" class="form-control input-sm"></td>' +
' <td>' +
' <select id="attrtype' + num + '" class="form-control input-sm">' +
' <option>string</option>' +
' <option>number</option>' +
' <option>boolean</option>' +
' </select>' +
' </td>' +
' <td></td>' +
'</tr>'
);
}

function sendTransportMessage(transport, message) {
console.log("Sending message to " + transport + ":", message);
var request = {
janus: "query_transport",
transaction: randomString(12),
admin_secret: secret,
transport: transport,
request: message
};
$.ajax({
type: 'POST',
url: server,
cache: false,
contentType: "application/json",
data: JSON.stringify(request),
success: function(json) {
if(json["janus"] !== "success") {
console.log("Ooops: " + json["error"].code + " " + json["error"].reason); // FIXME
var authenticate = (json["error"].code === 403);
if(!authenticate || (authenticate && !prompting && !alerted)) {
if(authenticate)
alerted = true;
bootbox.alert(json["error"].reason, function() {
if(authenticate) {
promptAccessDetails();
alerted = false;
}
});
}
}
$('#transport-response').text(JSON.stringify(json, null, 4));
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log(textStatus + ": " + errorThrown); // FIXME
if(!prompting && !alerted) {
alerted = true;
bootbox.alert("Couldn't contact the backend: is Janus down, or is the Admin/Monitor interface disabled?", function() {
promptAccessDetails();
alerted = false;
});
}
},
dataType: "json"
});
}


// Handles
function updateSessions() {
Expand Down
38 changes: 38 additions & 0 deletions janus.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,10 @@ static struct janus_json_parameter st_parameters[] = {
static struct janus_json_parameter ans_parameters[] = {
{"accept", JANUS_JSON_BOOL, JANUS_JSON_PARAM_REQUIRED}
};
static struct janus_json_parameter querytransport_parameters[] = {
{"transport", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
{"request", JSON_OBJECT, 0}
};
static struct janus_json_parameter queryhandler_parameters[] = {
{"handler", JSON_STRING, JANUS_JSON_PARAM_REQUIRED},
{"request", JSON_OBJECT, 0}
Expand Down Expand Up @@ -2197,6 +2201,40 @@ int janus_process_incoming_admin_request(janus_request *request) {
/* Send the success reply */
ret = janus_process_success(request, reply);
goto jsondone;
} else if(!strcasecmp(message_text, "query_transport")) {
/* Contact a transport and expect a response */
JANUS_VALIDATE_JSON_OBJECT(root, querytransport_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;
}
json_t *transport = json_object_get(root, "transport");
const char *transport_value = json_string_value(transport);
janus_transport *t = g_hash_table_lookup(transports, transport_value);
if(t == NULL) {
/* No such transport... */
g_snprintf(error_cause, sizeof(error_cause), "%s", "Invalid transport");
ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_PLUGIN_NOT_FOUND, error_cause);
goto jsondone;
}
if(t->query_transport == NULL) {
/* Transport doesn't implement the hook... */
g_snprintf(error_cause, sizeof(error_cause), "%s", "Transport plugin doesn't support queries");
ret = janus_process_error_string(request, session_id, transaction_text, JANUS_ERROR_UNKNOWN, error_cause);
goto jsondone;
}
json_t *query = json_object_get(root, "request");
json_t *response = t->query_transport(query);
/* 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, "response", response ? response : json_object());
/* Send the success reply */
ret = janus_process_success(request, reply);
goto jsondone;
} else if(!strcasecmp(message_text, "query_eventhandler")) {
/* Contact an event handler and expect a response */
JANUS_VALIDATE_JSON_OBJECT(root, queryhandler_parameters,
Expand Down
7 changes: 7 additions & 0 deletions mainpage.dox
Original file line number Diff line number Diff line change
Expand Up @@ -2316,6 +2316,13 @@ const token = getJanusToken('janus', ['janus.plugin.videoroom']),
* - \c detach_handle: detached a specific handle; this behaves exactly
* as the \c detach request does in the Janus API.
*
* \subsection adminreqt Transport-related requests
* - \c query_transport: send a synchronous request to a transport plugin and
* return a response; whether this is implemented, and what functionality is
* provided, can vary from transport to transport, but in general this feature
* is available to tweak some setting dynamically and/or query some internal
* transport-specific information (e.g., the number of served connections).
*
* \subsection adminreqe Event handlers-related requests
* - \c query_eventhandler: send a synchronous request to an event handler and
* return a response; implemented by most event handlers to dynamically
Expand Down
Loading