diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp
index ca95fd190a..9e7cf53e2f 100644
--- a/assignment-client/src/audio/AudioMixer.cpp
+++ b/assignment-client/src/audio/AudioMixer.cpp
@@ -358,11 +358,17 @@ void AudioMixer::readPendingDatagrams() {
void AudioMixer::sendStatsPacket() {
static QJsonObject statsObject;
- statsObject["trailing_sleep_time"] = _trailingSleepRatio;
- statsObject["performance_cutoff"] = _minAudibilityThreshold;
+ statsObject["trailing_sleep_percentage"] = _trailingSleepRatio * 100.0f;
+ statsObject["performance_throttling"] = _performanceThrottling;
statsObject["average_listeners_per_frame"] = _sumListeners / (float) _numStatFrames;
- statsObject["average_mixes_per_listeners"] = _sumMixes / _sumListeners / _numStatFrames;
+
+ if (_sumListeners > 0) {
+ statsObject["average_mixes_per_listener"] = _sumMixes / (float) _sumListeners;
+ } else {
+ statsObject["average_mixes_per_listener"] = 0.0;
+ }
+
_sumListeners = 0;
_sumMixes = 0;
diff --git a/domain-server/resources/web/footer.html b/domain-server/resources/web/footer.html
index 08ea9fba66..d1a3fc29e8 100644
--- a/domain-server/resources/web/footer.html
+++ b/domain-server/resources/web/footer.html
@@ -1,3 +1,3 @@
-
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/domain-server/resources/web/header.html b/domain-server/resources/web/header.html
index 83d7ae5c23..2be603b00e 100644
--- a/domain-server/resources/web/header.html
+++ b/domain-server/resources/web/header.html
@@ -4,8 +4,8 @@
\ No newline at end of file
diff --git a/domain-server/resources/web/js/tables.js b/domain-server/resources/web/js/tables.js
index e32f78a63e..ae5095592b 100644
--- a/domain-server/resources/web/js/tables.js
+++ b/domain-server/resources/web/js/tables.js
@@ -7,7 +7,7 @@ $(document).ready(function(){
$.each(json.nodes, function (uuid, data) {
nodesTableBody += "
";
nodesTableBody += "" + data.type + " | ";
- nodesTableBody += "" + uuid + " | ";
+ nodesTableBody += "" + uuid + " | ";
nodesTableBody += "" + (data.pool ? data.pool : "") + " | ";
nodesTableBody += "" + data.public.ip + ":" + data.public.port + " | ";
nodesTableBody += "" + data.local.ip + ":" + data.local.port + " | ";
diff --git a/domain-server/resources/web/stats/index.shtml b/domain-server/resources/web/stats/index.shtml
new file mode 100644
index 0000000000..62115d18fe
--- /dev/null
+++ b/domain-server/resources/web/stats/index.shtml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/domain-server/resources/web/stats/js/stats.js b/domain-server/resources/web/stats/js/stats.js
new file mode 100644
index 0000000000..e82668ea9e
--- /dev/null
+++ b/domain-server/resources/web/stats/js/stats.js
@@ -0,0 +1,31 @@
+function qs(key) {
+ key = key.replace(/[*+?^$.\[\]{}()|\\\/]/g, "\\$&"); // escape RegEx meta chars
+ var match = location.search.match(new RegExp("[?&]"+key+"=([^&]+)(&|$)"));
+ return match && decodeURIComponent(match[1].replace(/\+/g, " "));
+}
+
+$(document).ready(function(){
+ // setup a function to grab the nodeStats
+ function getNodeStats() {
+
+ var uuid = qs("uuid");
+
+ var statsTableBody = "";
+
+ $.getJSON("/nodes/" + uuid + ".json", function(json){
+ $.each(json, function (key, value) {
+ statsTableBody += "
";
+ statsTableBody += "" + key + " | ";
+ statsTableBody += "" + value + " | ";
+ statsTableBody += "
";
+ });
+
+ $('#stats-table tbody').html(statsTableBody);
+ });
+ }
+
+ // do the first GET on page load
+ getNodeStats();
+ // grab the new assignments JSON every second
+ var getNodeStatsInterval = setInterval(getNodeStats, 1000);
+});
diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp
index 1b867501d2..0a8b70a673 100644
--- a/domain-server/src/DomainServer.cpp
+++ b/domain-server/src/DomainServer.cpp
@@ -651,14 +651,14 @@ QJsonObject DomainServer::jsonObjectForNode(const SharedNodePointer& node) {
return nodeJson;
}
-bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString& path) {
+bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url) {
const QString JSON_MIME_TYPE = "application/json";
const QString URI_ASSIGNMENT = "/assignment";
const QString URI_NODES = "/nodes";
if (connection->requestOperation() == QNetworkAccessManager::GetOperation) {
- if (path == "/assignments.json") {
+ if (url.path() == "/assignments.json") {
// user is asking for json list of assignments
// setup the JSON
@@ -702,7 +702,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
// we've processed this request
return true;
- } else if (path == QString("%1.json").arg(URI_NODES)) {
+ } else if (url.path() == QString("%1.json").arg(URI_NODES)) {
// setup the JSON
QJsonObject rootJSON;
QJsonObject nodesJSON;
@@ -727,10 +727,10 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
return true;
} else {
const QString NODE_REGEX_STRING =
- QString("\\%1\\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\\/?$").arg(URI_NODES);
+ QString("\\%1\\/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}).json\\/?$").arg(URI_NODES);
QRegExp nodeShowRegex(NODE_REGEX_STRING);
- if (nodeShowRegex.indexIn(path) != -1) {
+ if (nodeShowRegex.indexIn(url.path()) != -1) {
QUuid matchingUUID = QUuid(nodeShowRegex.cap(1));
// see if we have a node that matches this ID
@@ -740,7 +740,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
QJsonDocument statsDocument(reinterpret_cast
(matchingNode->getLinkedData())
->getStatsJSONObject());
- // send the resposne
+ // send the response
connection->respond(HTTPConnection::StatusCode200, statsDocument.toJson(), qPrintable(JSON_MIME_TYPE));
// tell the caller we processed the request
@@ -749,7 +749,7 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
}
}
} else if (connection->requestOperation() == QNetworkAccessManager::PostOperation) {
- if (path == URI_ASSIGNMENT) {
+ if (url.path() == URI_ASSIGNMENT) {
// this is a script upload - ask the HTTPConnection to parse the form data
QList formData = connection->parseFormData();
@@ -796,11 +796,11 @@ bool DomainServer::handleHTTPRequest(HTTPConnection* connection, const QString&
return true;
}
} else if (connection->requestOperation() == QNetworkAccessManager::DeleteOperation) {
- if (path.startsWith(URI_NODES)) {
+ if (url.path().startsWith(URI_NODES)) {
// this is a request to DELETE a node by UUID
// pull the UUID from the url
- QUuid deleteUUID = QUuid(path.mid(URI_NODES.size() + sizeof('/')));
+ QUuid deleteUUID = QUuid(url.path().mid(URI_NODES.size() + sizeof('/')));
if (!deleteUUID.isNull()) {
SharedNodePointer nodeToKill = NodeList::getInstance()->nodeWithUUID(deleteUUID);
diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h
index 2d253cc41c..597be7f50d 100644
--- a/domain-server/src/DomainServer.h
+++ b/domain-server/src/DomainServer.h
@@ -30,7 +30,7 @@ public:
bool requiresAuthentication() const { return !_nodeAuthenticationURL.isEmpty(); }
- bool handleHTTPRequest(HTTPConnection* connection, const QString& path);
+ bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
void exit(int retCode = 0);
diff --git a/libraries/embedded-webserver/src/HTTPConnection.cpp b/libraries/embedded-webserver/src/HTTPConnection.cpp
index 50ce72e0cd..8fc0a25dca 100755
--- a/libraries/embedded-webserver/src/HTTPConnection.cpp
+++ b/libraries/embedded-webserver/src/HTTPConnection.cpp
@@ -180,7 +180,7 @@ void HTTPConnection::readHeaders() {
QByteArray clength = _requestHeaders.value("Content-Length");
if (clength.isEmpty()) {
- _parentManager->handleHTTPRequest(this, _requestUrl.path());
+ _parentManager->handleHTTPRequest(this, _requestUrl);
} else {
_requestContent.resize(clength.toInt());
diff --git a/libraries/embedded-webserver/src/HTTPManager.cpp b/libraries/embedded-webserver/src/HTTPManager.cpp
index a217555a78..d106b6df59 100755
--- a/libraries/embedded-webserver/src/HTTPManager.cpp
+++ b/libraries/embedded-webserver/src/HTTPManager.cpp
@@ -15,15 +15,15 @@
#include "HTTPConnection.h"
#include "HTTPManager.h"
-bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& path) {
- if (_requestHandler && _requestHandler->handleHTTPRequest(connection, path)) {
+bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QUrl& url) {
+ if (_requestHandler && _requestHandler->handleHTTPRequest(connection, url)) {
// this request was handled by our _requestHandler object
// so we don't need to attempt to do so in the document root
return true;
}
// check to see if there is a file to serve from the document root for this path
- QString subPath = path;
+ QString subPath = url.path();
// remove any slash at the beginning of the path
if (subPath.startsWith('/')) {
@@ -38,6 +38,10 @@ bool HTTPManager::handleHTTPRequest(HTTPConnection* connection, const QString& p
// this could be a directory with a trailing slash
// send a redirect to the path with a slash so we can
QString redirectLocation = '/' + subPath + '/';
+
+ if (!url.query().isEmpty()) {
+ redirectLocation += "?" + url.query();
+ }
QHash redirectHeader;
redirectHeader.insert(QByteArray("Location"), redirectLocation.toUtf8());
diff --git a/libraries/embedded-webserver/src/HTTPManager.h b/libraries/embedded-webserver/src/HTTPManager.h
index e3b44e7cdc..a8f9d723fa 100755
--- a/libraries/embedded-webserver/src/HTTPManager.h
+++ b/libraries/embedded-webserver/src/HTTPManager.h
@@ -20,7 +20,7 @@ class HTTPConnection;
class HTTPRequestHandler {
public:
/// Handles an HTTP request.
- virtual bool handleHTTPRequest(HTTPConnection* connection, const QString& path) = 0;
+ virtual bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url) = 0;
};
/// Handles HTTP connections
@@ -30,7 +30,7 @@ public:
/// Initializes the manager.
HTTPManager(quint16 port, const QString& documentRoot, HTTPRequestHandler* requestHandler = NULL, QObject* parent = 0);
- bool handleHTTPRequest(HTTPConnection* connection, const QString& path);
+ bool handleHTTPRequest(HTTPConnection* connection, const QUrl& url);
protected slots:
/// Accepts all pending connections