Introduce more granular HTTP timeouts

This commit is contained in:
Cameron Gutman 2019-01-06 14:35:33 -08:00
parent 067f5e33aa
commit 89c342bb6f
3 changed files with 38 additions and 29 deletions

View File

@ -13,6 +13,9 @@
#include <QNetworkProxy> #include <QNetworkProxy>
#define REQUEST_TIMEOUT_MS 5000 #define REQUEST_TIMEOUT_MS 5000
#define LAUNCH_TIMEOUT_MS 120000
#define RESUME_TIMEOUT_MS 30000
#define QUIT_TIMEOUT_MS 30000
NvHTTP::NvHTTP(QString address, QSslCertificate serverCert) : NvHTTP::NvHTTP(QString address, QSslCertificate serverCert) :
m_Address(address), m_Address(address),
@ -89,7 +92,7 @@ NvHTTP::getServerInfo(NvLogLevel logLevel)
serverInfo = openConnectionToString(m_BaseUrlHttps, serverInfo = openConnectionToString(m_BaseUrlHttps,
"serverinfo", "serverinfo",
nullptr, nullptr,
true, REQUEST_TIMEOUT_MS,
logLevel); logLevel);
// Throws if the request failed // Throws if the request failed
verifyResponseStatus(serverInfo); verifyResponseStatus(serverInfo);
@ -102,7 +105,7 @@ NvHTTP::getServerInfo(NvLogLevel logLevel)
serverInfo = openConnectionToString(m_BaseUrlHttp, serverInfo = openConnectionToString(m_BaseUrlHttp,
"serverinfo", "serverinfo",
nullptr, nullptr,
true, REQUEST_TIMEOUT_MS,
logLevel); logLevel);
verifyResponseStatus(serverInfo); verifyResponseStatus(serverInfo);
} }
@ -119,7 +122,7 @@ NvHTTP::getServerInfo(NvLogLevel logLevel)
serverInfo = openConnectionToString(m_BaseUrlHttp, serverInfo = openConnectionToString(m_BaseUrlHttp,
"serverinfo", "serverinfo",
nullptr, nullptr,
true, REQUEST_TIMEOUT_MS,
logLevel); logLevel);
verifyResponseStatus(serverInfo); verifyResponseStatus(serverInfo);
} }
@ -180,7 +183,7 @@ NvHTTP::launchApp(int appId,
"&surroundAudioInfo="+getSurroundAudioInfoString(streamConfig->audioConfiguration)+ "&surroundAudioInfo="+getSurroundAudioInfoString(streamConfig->audioConfiguration)+
"&remoteControllersBitmap="+QString::number(gamepadMask)+ "&remoteControllersBitmap="+QString::number(gamepadMask)+
"&gcmap="+QString::number(gamepadMask), "&gcmap="+QString::number(gamepadMask),
false); LAUNCH_TIMEOUT_MS);
// Throws if the request failed // Throws if the request failed
verifyResponseStatus(response); verifyResponseStatus(response);
@ -200,7 +203,7 @@ NvHTTP::resumeApp(PSTREAM_CONFIGURATION streamConfig)
"rikey="+QString(QByteArray(streamConfig->remoteInputAesKey, sizeof(streamConfig->remoteInputAesKey)).toHex())+ "rikey="+QString(QByteArray(streamConfig->remoteInputAesKey, sizeof(streamConfig->remoteInputAesKey)).toHex())+
"&rikeyid="+QString::number(riKeyId)+ "&rikeyid="+QString::number(riKeyId)+
"&surroundAudioInfo="+getSurroundAudioInfoString(streamConfig->audioConfiguration), "&surroundAudioInfo="+getSurroundAudioInfoString(streamConfig->audioConfiguration),
false); RESUME_TIMEOUT_MS);
// Throws if the request failed // Throws if the request failed
verifyResponseStatus(response); verifyResponseStatus(response);
@ -213,7 +216,7 @@ NvHTTP::quitApp()
openConnectionToString(m_BaseUrlHttps, openConnectionToString(m_BaseUrlHttps,
"cancel", "cancel",
nullptr, nullptr,
false); QUIT_TIMEOUT_MS);
// Throws if the request failed // Throws if the request failed
verifyResponseStatus(response); verifyResponseStatus(response);
@ -260,7 +263,7 @@ NvHTTP::getAppList()
QString appxml = openConnectionToString(m_BaseUrlHttps, QString appxml = openConnectionToString(m_BaseUrlHttps,
"applist", "applist",
nullptr, nullptr,
true, REQUEST_TIMEOUT_MS,
NvLogLevel::ERROR); NvLogLevel::ERROR);
verifyResponseStatus(appxml); verifyResponseStatus(appxml);
@ -328,7 +331,7 @@ NvHTTP::getBoxArt(int appId)
"appasset", "appasset",
"appid="+QString::number(appId)+ "appid="+QString::number(appId)+
"&AssetType=2&AssetIdx=0", "&AssetType=2&AssetIdx=0",
true, REQUEST_TIMEOUT_MS,
NvLogLevel::VERBOSE); NvLogLevel::VERBOSE);
QImage image = QImageReader(reply).read(); QImage image = QImageReader(reply).read();
delete reply; delete reply;
@ -375,10 +378,10 @@ QString
NvHTTP::openConnectionToString(QUrl baseUrl, NvHTTP::openConnectionToString(QUrl baseUrl,
QString command, QString command,
QString arguments, QString arguments,
bool enableTimeout, int timeoutMs,
NvLogLevel logLevel) NvLogLevel logLevel)
{ {
QNetworkReply* reply = openConnection(baseUrl, command, arguments, enableTimeout, logLevel); QNetworkReply* reply = openConnection(baseUrl, command, arguments, timeoutMs, logLevel);
QString ret; QString ret;
QTextStream stream(reply); QTextStream stream(reply);
@ -393,7 +396,7 @@ QNetworkReply*
NvHTTP::openConnection(QUrl baseUrl, NvHTTP::openConnection(QUrl baseUrl,
QString command, QString command,
QString arguments, QString arguments,
bool enableTimeout, int timeoutMs,
NvLogLevel logLevel) NvLogLevel logLevel)
{ {
// Build a URL for the request // Build a URL for the request
@ -434,9 +437,8 @@ NvHTTP::openConnection(QUrl baseUrl,
// Run the request with a timeout if requested // Run the request with a timeout if requested
QEventLoop loop; QEventLoop loop;
QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit())); QObject::connect(reply, SIGNAL(finished()), &loop, SLOT(quit()));
if (enableTimeout) if (timeoutMs) {
{ QTimer::singleShot(timeoutMs, &loop, SLOT(quit()));
QTimer::singleShot(REQUEST_TIMEOUT_MS, &loop, SLOT(quit()));
} }
if (logLevel >= NvLogLevel::VERBOSE) { if (logLevel >= NvLogLevel::VERBOSE) {
qInfo() << "Executing request:" << url.toString(); qInfo() << "Executing request:" << url.toString();
@ -470,6 +472,11 @@ NvHTTP::openConnection(QUrl baseUrl,
delete reply; delete reply;
throw exception; throw exception;
} }
else if (reply->error() == QNetworkReply::OperationCanceledError) {
QtNetworkReplyException exception(QNetworkReply::TimeoutError, "Request timed out");
delete reply;
throw exception;
}
else { else {
QtNetworkReplyException exception(reply->error(), reply->errorString()); QtNetworkReplyException exception(reply->error(), reply->errorString());
delete reply; delete reply;

View File

@ -149,7 +149,7 @@ public:
openConnectionToString(QUrl baseUrl, openConnectionToString(QUrl baseUrl,
QString command, QString command,
QString arguments, QString arguments,
bool enableTimeout, int timeoutMs,
NvLogLevel logLevel = NvLogLevel::VERBOSE); NvLogLevel logLevel = NvLogLevel::VERBOSE);
void setServerCert(QSslCertificate serverCert); void setServerCert(QSslCertificate serverCert);
@ -188,7 +188,7 @@ private:
openConnection(QUrl baseUrl, openConnection(QUrl baseUrl,
QString command, QString command,
QString arguments, QString arguments,
bool enableTimeout, int timeoutMs,
NvLogLevel logLevel); NvLogLevel logLevel);
QString m_Address; QString m_Address;

View File

@ -8,6 +8,8 @@
#include <openssl/x509.h> #include <openssl/x509.h>
#include <openssl/evp.h> #include <openssl/evp.h>
#define REQUEST_TIMEOUT_MS 5000
NvPairingManager::NvPairingManager(QString address) : NvPairingManager::NvPairingManager(QString address) :
m_Http(address, QSslCertificate()) m_Http(address, QSslCertificate())
{ {
@ -192,7 +194,7 @@ NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverC
"pair", "pair",
"devicename=roth&updateState=1&phrase=getservercert&salt=" + "devicename=roth&updateState=1&phrase=getservercert&salt=" +
salt.toHex() + "&clientcert=" + IdentityManager::get()->getCertificate().toHex(), salt.toHex() + "&clientcert=" + IdentityManager::get()->getCertificate().toHex(),
false); 0);
NvHTTP::verifyResponseStatus(getCert); NvHTTP::verifyResponseStatus(getCert);
if (NvHTTP::getXmlString(getCert, "paired") != "1") if (NvHTTP::getXmlString(getCert, "paired") != "1")
{ {
@ -204,7 +206,7 @@ NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverC
if (serverCertStr == nullptr) if (serverCertStr == nullptr)
{ {
qCritical() << "Server likely already pairing"; qCritical() << "Server likely already pairing";
m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, true); m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, REQUEST_TIMEOUT_MS);
return PairState::ALREADY_IN_PROGRESS; return PairState::ALREADY_IN_PROGRESS;
} }
@ -213,7 +215,7 @@ NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverC
Q_ASSERT(!serverCert.isNull()); Q_ASSERT(!serverCert.isNull());
qCritical() << "Failed to parse plaincert"; qCritical() << "Failed to parse plaincert";
m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, true); m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, REQUEST_TIMEOUT_MS);
return PairState::FAILED; return PairState::FAILED;
} }
@ -226,12 +228,12 @@ NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverC
"pair", "pair",
"devicename=roth&updateState=1&clientchallenge=" + "devicename=roth&updateState=1&clientchallenge=" +
encryptedChallenge.toHex(), encryptedChallenge.toHex(),
true); REQUEST_TIMEOUT_MS);
NvHTTP::verifyResponseStatus(challengeXml); NvHTTP::verifyResponseStatus(challengeXml);
if (NvHTTP::getXmlString(challengeXml, "paired") != "1") if (NvHTTP::getXmlString(challengeXml, "paired") != "1")
{ {
qCritical() << "Failed pairing at stage #2"; qCritical() << "Failed pairing at stage #2";
m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, true); m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, REQUEST_TIMEOUT_MS);
return PairState::FAILED; return PairState::FAILED;
} }
@ -259,12 +261,12 @@ NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverC
"pair", "pair",
"devicename=roth&updateState=1&serverchallengeresp=" + "devicename=roth&updateState=1&serverchallengeresp=" +
encryptedChallengeResponseHash.toHex(), encryptedChallengeResponseHash.toHex(),
true); REQUEST_TIMEOUT_MS);
NvHTTP::verifyResponseStatus(respXml); NvHTTP::verifyResponseStatus(respXml);
if (NvHTTP::getXmlString(respXml, "paired") != "1") if (NvHTTP::getXmlString(respXml, "paired") != "1")
{ {
qCritical() << "Failed pairing at stage #3"; qCritical() << "Failed pairing at stage #3";
m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, true); m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, REQUEST_TIMEOUT_MS);
return PairState::FAILED; return PairState::FAILED;
} }
@ -277,7 +279,7 @@ NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverC
serverCertStr)) serverCertStr))
{ {
qCritical() << "MITM detected"; qCritical() << "MITM detected";
m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, true); m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, REQUEST_TIMEOUT_MS);
return PairState::FAILED; return PairState::FAILED;
} }
@ -288,7 +290,7 @@ NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverC
if (QCryptographicHash::hash(expectedResponseData, hashAlgo) != serverResponse) if (QCryptographicHash::hash(expectedResponseData, hashAlgo) != serverResponse)
{ {
qCritical() << "Incorrect PIN"; qCritical() << "Incorrect PIN";
m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, true); m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, REQUEST_TIMEOUT_MS);
return PairState::PIN_WRONG; return PairState::PIN_WRONG;
} }
@ -300,24 +302,24 @@ NvPairingManager::pair(QString appVersion, QString pin, QSslCertificate& serverC
"pair", "pair",
"devicename=roth&updateState=1&clientpairingsecret=" + "devicename=roth&updateState=1&clientpairingsecret=" +
clientPairingSecret.toHex(), clientPairingSecret.toHex(),
true); REQUEST_TIMEOUT_MS);
NvHTTP::verifyResponseStatus(secretRespXml); NvHTTP::verifyResponseStatus(secretRespXml);
if (NvHTTP::getXmlString(secretRespXml, "paired") != "1") if (NvHTTP::getXmlString(secretRespXml, "paired") != "1")
{ {
qCritical() << "Failed pairing at stage #4"; qCritical() << "Failed pairing at stage #4";
m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, true); m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, REQUEST_TIMEOUT_MS);
return PairState::FAILED; return PairState::FAILED;
} }
QString pairChallengeXml = m_Http.openConnectionToString(m_Http.m_BaseUrlHttps, QString pairChallengeXml = m_Http.openConnectionToString(m_Http.m_BaseUrlHttps,
"pair", "pair",
"devicename=roth&updateState=1&phrase=pairchallenge", "devicename=roth&updateState=1&phrase=pairchallenge",
true); REQUEST_TIMEOUT_MS);
NvHTTP::verifyResponseStatus(pairChallengeXml); NvHTTP::verifyResponseStatus(pairChallengeXml);
if (NvHTTP::getXmlString(pairChallengeXml, "paired") != "1") if (NvHTTP::getXmlString(pairChallengeXml, "paired") != "1")
{ {
qCritical() << "Failed pairing at stage #5"; qCritical() << "Failed pairing at stage #5";
m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, true); m_Http.openConnectionToString(m_Http.m_BaseUrlHttp, "unpair", nullptr, REQUEST_TIMEOUT_MS);
return PairState::FAILED; return PairState::FAILED;
} }