Merge branch 'master' into aidan/dev

This commit is contained in:
R. Aidan Campbell 2018-04-28 17:22:11 -07:00
commit 4d0341fd9a
2 changed files with 260 additions and 2 deletions

View file

@ -2,8 +2,14 @@
#include <QUuid>
#include <QtNetwork/QNetworkReply>
#include <QEventLoop>
#include <QTimer>
#include <QXmlStreamReader>
NvHTTP::NvHTTP(QString address)
#define REQUEST_TIMEOUT_MS 5000
NvHTTP::NvHTTP(QString address) :
m_Address(address)
{
m_BaseUrlHttp.setScheme("http");
m_BaseUrlHttps.setScheme("https");
@ -13,21 +19,191 @@ NvHTTP::NvHTTP(QString address)
m_BaseUrlHttps.setPort(47984);
}
NvComputer
NvHTTP::getComputerInfo()
{
NvComputer computer;
QString serverInfo = getServerInfo();
computer.m_Name = getXmlString(serverInfo, "hostname");
if (computer.m_Name == nullptr)
{
computer.m_Name = "UNKNOWN";
}
computer.m_Uuid = getXmlString(serverInfo, "uniqueid");
computer.m_MacAddress = getXmlString(serverInfo, "mac");
// If there's no LocalIP field, use the address we hit the server on
computer.m_LocalAddress = getXmlString(serverInfo, "LocalIP");
if (computer.m_LocalAddress == nullptr)
{
computer.m_LocalAddress = m_Address;
}
// If there's no ExternalIP field, use the address we hit the server on
computer.m_RemoteAddress = getXmlString(serverInfo, "ExternalIP");
if (computer.m_RemoteAddress == nullptr)
{
computer.m_RemoteAddress = m_Address;
}
computer.m_PairState = getXmlString(serverInfo, "PairStatus") == "1" ?
NvComputer::PS_PAIRED : NvComputer::PS_NOT_PAIRED;
computer.m_RunningGameId = getCurrentGame(serverInfo);
computer.m_State = NvComputer::CS_ONLINE;
return computer;
}
int
NvHTTP::getCurrentGame(QString serverInfo)
{
// GFE 2.8 started keeping currentgame set to the last game played. As a result, it no longer
// has the semantics that its name would indicate. To contain the effects of this change as much
// as possible, we'll force the current game to zero if the server isn't in a streaming session.
QString serverState = getXmlString(serverInfo, "state");
if (serverState != nullptr && serverState.endsWith("_SERVER_BUSY")) {
return getXmlString(serverInfo, "currentgame").toInt();
}
else {
return 0;
}
}
QString
NvHTTP::getServerInfo()
{
QString serverInfo;
try
{
// Always try HTTPS first, since it properly reports
// pairing status (and a few other attributes).
serverInfo = openConnectionToString(m_BaseUrlHttps,
"serverinfo",
nullptr,
true);
// Throws if the request failed
verifyResponseStatus(serverInfo);
}
catch (GfeHttpResponseException& e)
{
if (e.getStatusCode() == 401)
{
// Certificate validation error, fallback to HTTP
serverInfo = openConnectionToString(m_BaseUrlHttps,
"serverinfo",
nullptr,
true);
verifyResponseStatus(serverInfo);
}
}
return serverInfo;
}
void
NvHTTP::verifyResponseStatus(QString xml)
{
QXmlStreamReader xmlReader(xml);
while (xmlReader.readNextStartElement())
{
if (xmlReader.name() == "root")
{
int statusCode = xmlReader.attributes().value("status_code").toInt();
if (statusCode == 200)
{
// Successful
return;
}
else
{
QString statusMessage = xmlReader.attributes().value("status_message").toString();
qDebug() << "Request failed: " << statusCode << " " << statusMessage;
throw GfeHttpResponseException(statusCode, statusMessage);
}
}
}
}
QString
NvHTTP::getXmlString(QString xml,
QString tagName)
{
QXmlStreamReader xmlReader(xml);
while (xmlReader.readNextStartElement())
{
if (xmlReader.name() == tagName)
{
return xmlReader.text().toString();
}
}
return nullptr;
}
QString
NvHTTP::openConnectionToString(QUrl baseUrl,
QString command,
QString arguments,
bool enableTimeout)
{
QNetworkReply* reply = openConnection(baseUrl, command, arguments, enableTimeout);
QString ret;
ret = QTextStream(reply).readAll();
delete reply;
return ret;
}
QNetworkReply*
NvHTTP::openConnection(QUrl baseUrl,
QString command,
QString arguments,
bool enableTimeout)
{
// Build a URL for the request
QUrl url(baseUrl);
url.setPath(command +
"?uniqueid=" + "0" +
"&uuid=" + QUuid::createUuid().toString() +
((arguments != nullptr) ? (arguments + "&") : ""));
QNetworkReply* reply = m_Nam.get(QNetworkRequest(url));
// Ignore self-signed certificate errors (since GFE uses them)
reply->ignoreSslErrors(QList<QSslError>{ QSslError::SelfSignedCertificate });
// Run the request with a timeout if requested
QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
if (enableTimeout)
{
QTimer::singleShot(REQUEST_TIMEOUT_MS, &loop, SLOT(quit()));
}
qDebug() << "Executing request: " << url.toString();
loop.exec(QEventLoop::ExcludeUserInputEvents);
// Abort the request if it timed out
if (!reply->isFinished())
{
qDebug() << "Aborting timed out request for " << url.toString();
reply->abort();
}
// Handle error
if (reply->error() != QNetworkReply::NoError)
{
qDebug() << command << " request for failed with error " << reply->error();
delete reply;
throw new GfeHttpResponseException(reply->error(), reply->errorString());
}
return reply;
}

View file

@ -3,6 +3,65 @@
#include <QUrl>
#include <QtNetwork/QNetworkAccessManager>
class GfeHttpResponseException : public std::exception
{
public:
GfeHttpResponseException(int statusCode, QString message) :
m_StatusCode(statusCode),
m_StatusMessage(message)
{
}
const char* what() const throw()
{
return m_StatusMessage.toStdString().c_str();
}
const char* getStatusMessage()
{
return m_StatusMessage.toStdString().c_str();
}
int getStatusCode()
{
return m_StatusCode;
}
private:
int m_StatusCode;
QString m_StatusMessage;
};
class NvComputer
{
public:
NvComputer() {}
enum PairState
{
PS_UNKNOWN,
PS_PAIRED,
PS_NOT_PAIRED
};
enum ComputerState
{
CS_UNKNOWN,
CS_ONLINE,
CS_OFFLINE
};
QString m_Name;
QString m_Uuid;
QString m_MacAddress;
QString m_LocalAddress;
QString m_RemoteAddress;
int m_RunningGameId;
PairState m_PairState;
ComputerState m_State;
};
class NvHTTP
{
public:
@ -15,6 +74,29 @@ private:
QString arguments,
bool enableTimeout);
NvComputer
getComputerInfo();
int
getCurrentGame(QString serverInfo);
QString
getServerInfo();
void
verifyResponseStatus(QString xml);
QString
getXmlString(QString xml,
QString tagName);
QString
openConnectionToString(QUrl baseUrl,
QString command,
QString arguments,
bool enableTimeout);
QString m_Address;
QUrl m_BaseUrlHttp;
QUrl m_BaseUrlHttps;
QNetworkAccessManager m_Nam;