Skip to content

Commit

Permalink
New mechanism to tweak/query transport plugins via Admin API (meetech…
Browse files Browse the repository at this point in the history
  • Loading branch information
lminiero authored and PauKerr committed Nov 11, 2020
1 parent 5655b6c commit b08b340
Show file tree
Hide file tree
Showing 11 changed files with 916 additions and 29 deletions.
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 @@ -2200,6 +2204,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

0 comments on commit b08b340

Please sign in to comment.