Merge branch 'protocol' of github.com:birarda/hifi into atp

This commit is contained in:
Stephen Birarda 2015-08-31 16:04:44 -06:00
commit 4255220a69
13 changed files with 158 additions and 168 deletions

View file

@ -125,39 +125,43 @@ void AssetServer::handleAssetGetInfo(QSharedPointer<NLPacket> packet, SharedNode
} }
void AssetServer::handleAssetGet(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) { void AssetServer::handleAssetGet(QSharedPointer<NLPacket> packet, SharedNodePointer senderNode) {
MessageID messageID;
QByteArray assetHash;
uint8_t extensionLength;
DataOffset start;
DataOffset end;
auto minSize = qint64(sizeof(messageID) + SHA256_HASH_LENGTH + sizeof(extensionLength) + sizeof(start) + sizeof(end)); auto minSize = qint64(sizeof(MessageID) + SHA256_HASH_LENGTH + sizeof(uint8_t) + sizeof(DataOffset) + sizeof(DataOffset));
if (packet->getPayloadSize() < minSize) { if (packet->getPayloadSize() < minSize) {
qDebug() << "ERROR bad file request"; qDebug() << "ERROR bad file request";
return; return;
} }
packet->readPrimitive(&messageID);
assetHash = packet->read(SHA256_HASH_LENGTH);
packet->readPrimitive(&extensionLength);
QByteArray extension = packet->read(extensionLength);
packet->readPrimitive(&start);
packet->readPrimitive(&end);
QByteArray hexHash = assetHash.toHex();
qDebug() << "Received a request for the file (" << messageID << "): " << hexHash << " from " << start << " to " << end;
// Queue task // Queue task
QString filePath = _resourcesDirectory.filePath(QString(hexHash) + "." + QString(extension)); auto task = new SendAssetTask(packet, senderNode, _resourcesDirectory);
auto task = new SendAssetTask(messageID, assetHash, filePath, start, end, senderNode);
_taskPool.start(task); _taskPool.start(task);
} }
void AssetServer::handleAssetUpload(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) { void AssetServer::handleAssetUpload(QSharedPointer<NLPacketList> packetList, SharedNodePointer senderNode) {
if (senderNode->getCanRez()) {
qDebug() << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(senderNode->getUUID()); qDebug() << "Starting an UploadAssetTask for upload from" << uuidStringWithoutCurlyBraces(senderNode->getUUID());
auto task = new UploadAssetTask(packetList, senderNode, _resourcesDirectory); auto task = new UploadAssetTask(packetList, senderNode, _resourcesDirectory);
_taskPool.start(task); _taskPool.start(task);
} else {
// this is a node the domain told us is not allowed to rez entities
// for now this also means it isn't allowed to add assets
// so return a packet with error that indicates that
auto permissionErrorPacket = NLPacket::create(PacketType::AssetUploadReply, sizeof(MessageID) + sizeof(AssetServerError));
MessageID messageID;
packetList->readPrimitive(&messageID);
// write the message ID and a permission denied error
permissionErrorPacket->writePrimitive(messageID);
permissionErrorPacket->writePrimitive(AssetServerError::PERMISSION_DENIED);
// send off the packet
auto nodeList = DependencyManager::get<NodeList>();
nodeList->sendPacket(std::move(permissionErrorPacket), *senderNode);
}
} }

View file

@ -18,42 +18,56 @@
#include <NLPacket.h> #include <NLPacket.h>
#include <NLPacketList.h> #include <NLPacketList.h>
#include <NodeList.h> #include <NodeList.h>
#include <udt/Packet.h>
#include "AssetUtils.h" #include "AssetUtils.h"
SendAssetTask::SendAssetTask(MessageID messageID, const QByteArray& assetHash, QString filePath, DataOffset start, DataOffset end, SendAssetTask::SendAssetTask(QSharedPointer<NLPacket> packet, const SharedNodePointer& sendToNode, const QDir& resourcesDir) :
const SharedNodePointer& sendToNode) :
QRunnable(), QRunnable(),
_messageID(messageID), _packet(packet),
_assetHash(assetHash), _senderNode(sendToNode),
_filePath(filePath), _resourcesDir(resourcesDir)
_start(start),
_end(end),
_sendToNode(sendToNode)
{ {
} }
void SendAssetTask::run() { void SendAssetTask::run() {
QString hexHash = _assetHash.toHex(); MessageID messageID;
qDebug() << "Starting task to send asset: " << hexHash << " for messageID " << _messageID; uint8_t extensionLength;
DataOffset start, end;
_packet->readPrimitive(&messageID);
QByteArray assetHash = _packet->read(SHA256_HASH_LENGTH);
_packet->readPrimitive(&extensionLength);
QByteArray extension = _packet->read(extensionLength);
_packet->readPrimitive(&start);
_packet->readPrimitive(&end);
QString hexHash = assetHash.toHex();
qDebug() << "Received a request for the file (" << messageID << "): " << hexHash << " from " << start << " to " << end;
qDebug() << "Starting task to send asset: " << hexHash << " for messageID " << messageID;
auto replyPacketList = std::unique_ptr<NLPacketList>(new NLPacketList(PacketType::AssetGetReply, QByteArray(), true, true)); auto replyPacketList = std::unique_ptr<NLPacketList>(new NLPacketList(PacketType::AssetGetReply, QByteArray(), true, true));
replyPacketList->write(_assetHash); replyPacketList->write(assetHash);
replyPacketList->writePrimitive(_messageID); replyPacketList->writePrimitive(messageID);
if (_end <= _start) { if (end <= start) {
writeError(replyPacketList.get(), AssetServerError::INVALID_BYTE_RANGE); writeError(replyPacketList.get(), AssetServerError::INVALID_BYTE_RANGE);
} else { } else {
QFile file { _filePath }; QString filePath = _resourcesDir.filePath(QString(hexHash) + "." + QString(extension));
QFile file { filePath };
if (file.open(QIODevice::ReadOnly)) { if (file.open(QIODevice::ReadOnly)) {
if (file.size() < _end) { if (file.size() < end) {
writeError(replyPacketList.get(), AssetServerError::INVALID_BYTE_RANGE); writeError(replyPacketList.get(), AssetServerError::INVALID_BYTE_RANGE);
qCDebug(networking) << "Bad byte range: " << hexHash << " " << _start << ":" << _end; qCDebug(networking) << "Bad byte range: " << hexHash << " " << start << ":" << end;
} else { } else {
auto size = _end - _start; auto size = end - start;
file.seek(_start); file.seek(start);
replyPacketList->writePrimitive(AssetServerError::NO_ERROR); replyPacketList->writePrimitive(AssetServerError::NO_ERROR);
replyPacketList->writePrimitive(size); replyPacketList->writePrimitive(size);
replyPacketList->write(file.read(size)); replyPacketList->write(file.read(size));
@ -61,11 +75,11 @@ void SendAssetTask::run() {
} }
file.close(); file.close();
} else { } else {
qCDebug(networking) << "Asset not found: " << _filePath << "(" << hexHash << ")"; qCDebug(networking) << "Asset not found: " << filePath << "(" << hexHash << ")";
writeError(replyPacketList.get(), AssetServerError::ASSET_NOT_FOUND); writeError(replyPacketList.get(), AssetServerError::ASSET_NOT_FOUND);
} }
} }
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
nodeList->sendPacketList(std::move(replyPacketList), *_sendToNode); nodeList->sendPacketList(std::move(replyPacketList), *_senderNode);
} }

View file

@ -12,28 +12,27 @@
#ifndef hifi_SendAssetTask_h #ifndef hifi_SendAssetTask_h
#define hifi_SendAssetTask_h #define hifi_SendAssetTask_h
#include <QByteArray> #include <QtCore/QByteArray>
#include <QString> #include <QtCore/QSharedPointer>
#include <QRunnable> #include <QtCore/QString>
#include <QtCore/QRunnable>
#include "AssetUtils.h" #include "AssetUtils.h"
#include "AssetServer.h" #include "AssetServer.h"
#include "Node.h" #include "Node.h"
class NLPacket;
class SendAssetTask : public QRunnable { class SendAssetTask : public QRunnable {
public: public:
SendAssetTask(MessageID messageID, const QByteArray& assetHash, QString filePath, DataOffset start, DataOffset end, SendAssetTask(QSharedPointer<NLPacket> packet, const SharedNodePointer& sendToNode, const QDir& resourcesDir);
const SharedNodePointer& sendToNode);
void run(); void run();
private: private:
MessageID _messageID; QSharedPointer<NLPacket> _packet;
QByteArray _assetHash; SharedNodePointer _senderNode;
QString _filePath; QDir _resourcesDir;
DataOffset _start;
DataOffset _end;
SharedNodePointer _sendToNode;
}; };
#endif #endif

View file

@ -37,25 +37,6 @@ void UploadAssetTask::run() {
MessageID messageID; MessageID messageID;
buffer.read(reinterpret_cast<char*>(&messageID), sizeof(messageID)); buffer.read(reinterpret_cast<char*>(&messageID), sizeof(messageID));
if (!_senderNode->getCanRez()) {
// this is a node the domain told us is not allowed to rez entities
// for now this also means it isn't allowed to add assets
// so return a packet with error that indicates that
auto permissionErrorPacket = NLPacket::create(PacketType::AssetUploadReply, sizeof(MessageID) + sizeof(AssetServerError));
// write the message ID and a permission denied error
permissionErrorPacket->writePrimitive(messageID);
permissionErrorPacket->writePrimitive(AssetServerError::PERMISSION_DENIED);
// send off the packet
auto nodeList = DependencyManager::get<NodeList>();
nodeList->sendPacket(std::move(permissionErrorPacket), *_senderNode);
// return so we're not attempting to handle upload
return;
}
uint8_t extensionLength; uint8_t extensionLength;
buffer.read(reinterpret_cast<char*>(&extensionLength), sizeof(extensionLength)); buffer.read(reinterpret_cast<char*>(&extensionLength), sizeof(extensionLength));

View file

@ -55,7 +55,7 @@ AssetRequest* AssetClient::createRequest(const QString& hash, const QString& ext
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
if (!assetServer) { if (!assetServer) {
qDebug() << "No Asset Server"; qDebug().nospace() << "Could not request " << hash << "." << extension << " since you are not currently connected to an asset-server.";
return nullptr; return nullptr;
} }
@ -75,11 +75,12 @@ AssetUpload* AssetClient::createUpload(const QString& filename) {
auto nodeList = DependencyManager::get<NodeList>(); auto nodeList = DependencyManager::get<NodeList>();
SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer); SharedNodePointer assetServer = nodeList->soloNodeOfType(NodeType::AssetServer);
if (assetServer) { if (!assetServer) {
return new AssetUpload(this, filename); qDebug() << "Could not upload" << filename << "since you are not currently connected to an asset-server.";
return nullptr;
} }
return nullptr; return new AssetUpload(this, filename);
} }
bool AssetClient::getAsset(const QString& hash, const QString& extension, DataOffset start, DataOffset end, bool AssetClient::getAsset(const QString& hash, const QString& extension, DataOffset start, DataOffset end,

View file

@ -68,6 +68,5 @@ void AssetResourceRequest::doSend() {
} }
void AssetResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { void AssetResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
qDebug() << "Got asset data: " << bytesReceived << " / " << bytesTotal;
emit progress(bytesReceived, bytesTotal); emit progress(bytesReceived, bytesTotal);
} }

View file

@ -41,21 +41,22 @@ void AssetUpload::start() {
// ask the AssetClient to upload the asset and emit the proper signals from the passed callback // ask the AssetClient to upload the asset and emit the proper signals from the passed callback
auto assetClient = DependencyManager::get<AssetClient>(); auto assetClient = DependencyManager::get<AssetClient>();
assetClient->uploadAsset(data, _extension, [this](bool success, const QString& hash){ assetClient->uploadAsset(data, _extension, [this](AssetServerError error, const QString& hash){
if (success) { switch (error) {
// successful upload - emit finished with a point to ourselves and the resulting hash case NO_ERROR:
_result = Success; _result = Success;
break;
emit finished(this, hash); case ASSET_TOO_LARGE:
} else { _result = TooLarge;
// error during upload - emit finished with an empty hash break;
// callers can get the error from this object case PERMISSION_DENIED:
// TODO: get the actual error from the callback
_result = PermissionDenied; _result = PermissionDenied;
break;
emit finished(this, hash); default:
_result = ErrorLoadingFile;
break;
} }
emit finished(this, hash);
}); });
} else { } else {
// we couldn't open the file - set the error result // we couldn't open the file - set the error result

View file

@ -17,8 +17,6 @@ void FileResourceRequest::doSend() {
QString filename = _url.toLocalFile(); QString filename = _url.toLocalFile();
QFile file(filename); QFile file(filename);
_state = Finished;
if (file.exists()) { if (file.exists()) {
if (file.open(QFile::ReadOnly)) { if (file.open(QFile::ReadOnly)) {
_data = file.readAll(); _data = file.readAll();
@ -30,5 +28,6 @@ void FileResourceRequest::doSend() {
_result = ResourceRequest::NotFound; _result = ResourceRequest::NotFound;
} }
_state = Finished;
emit finished(); emit finished();
} }

View file

@ -42,10 +42,10 @@ void HTTPResourceRequest::doSend() {
_reply = networkAccessManager.get(networkRequest); _reply = networkAccessManager.get(networkRequest);
connect(_reply, &QNetworkReply::finished, this, &HTTPResourceRequest::onRequestFinished); connect(_reply, &QNetworkReply::finished, this, &HTTPResourceRequest::onRequestFinished);
connect(_reply, &QNetworkReply::downloadProgress, this, &HTTPResourceRequest::onDownloadProgress);
connect(&_sendTimer, &QTimer::timeout, this, &HTTPResourceRequest::onTimeout);
static const int TIMEOUT_MS = 10000; static const int TIMEOUT_MS = 10000;
connect(&_sendTimer, &QTimer::timeout, this, &HTTPResourceRequest::onTimeout);
_sendTimer.setSingleShot(true); _sendTimer.setSingleShot(true);
_sendTimer.start(TIMEOUT_MS); _sendTimer.start(TIMEOUT_MS);
} }
@ -54,43 +54,46 @@ void HTTPResourceRequest::onRequestFinished() {
Q_ASSERT(_state == InProgress); Q_ASSERT(_state == InProgress);
Q_ASSERT(_reply); Q_ASSERT(_reply);
_state = Finished; _sendTimer.stop();
auto error = _reply->error(); switch(_reply->error()) {
if (error == QNetworkReply::NoError) { case QNetworkReply::NoError:
_data = _reply->readAll(); _data = _reply->readAll();
_loadedFromCache = _reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(); _loadedFromCache = _reply->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool();
_result = ResourceRequest::Success; _result = Success;
emit finished(); break;
} else if (error == QNetworkReply::TimeoutError) { case QNetworkReply::TimeoutError:
_result = ResourceRequest::Timeout; _result = Timeout;
emit finished(); break;
} else { default:
_result = ResourceRequest::Error; _result = Error;
emit finished(); break;
} }
_reply->disconnect(this);
_reply->deleteLater(); _reply->deleteLater();
_reply = nullptr; _reply = nullptr;
_state = Finished;
emit finished();
} }
void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) { void HTTPResourceRequest::onDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
if (_state == InProgress) { Q_ASSERT(_state == InProgress);
// We've received data, so reset the timer // We've received data, so reset the timer
_sendTimer.start(); _sendTimer.start();
}
emit progress(bytesReceived, bytesTotal); emit progress(bytesReceived, bytesTotal);
} }
void HTTPResourceRequest::onTimeout() { void HTTPResourceRequest::onTimeout() {
Q_ASSERT(_state != NotStarted); Q_ASSERT(_state == InProgress);
_reply->disconnect(this);
if (_state == InProgress) {
qCDebug(networking) << "Timed out loading " << _url;
_reply->abort(); _reply->abort();
_state = Finished; _reply->deleteLater();
_reply = nullptr;
_result = Timeout; _result = Timeout;
_state = Finished;
emit finished(); emit finished();
}
} }

View file

@ -21,9 +21,8 @@
class HTTPResourceRequest : public ResourceRequest { class HTTPResourceRequest : public ResourceRequest {
Q_OBJECT Q_OBJECT
public: public:
~HTTPResourceRequest();
HTTPResourceRequest(QObject* parent, const QUrl& url) : ResourceRequest(parent, url) { } HTTPResourceRequest(QObject* parent, const QUrl& url) : ResourceRequest(parent, url) { }
~HTTPResourceRequest();
protected: protected:
virtual void doSend() override; virtual void doSend() override;

View file

@ -158,14 +158,12 @@ void ResourceCache::attemptRequest(Resource* resource) {
// Disable request limiting for ATP // Disable request limiting for ATP
if (resource->getURL().scheme() != URL_SCHEME_ATP) { if (resource->getURL().scheme() != URL_SCHEME_ATP) {
if (_requestLimit <= 0) { if (_requestLimit <= 0) {
qDebug() << "REQUEST LIMIT REACHED (" << _requestLimit << "), queueing: " << resource->getURL();
// wait until a slot becomes available // wait until a slot becomes available
sharedItems->_pendingRequests.append(resource); sharedItems->_pendingRequests.append(resource);
return; return;
} }
qDebug() << "-- Decreasing limit for : " << resource->getURL(); --_requestLimit;
_requestLimit--;
} }
sharedItems->_loadingRequests.append(resource); sharedItems->_loadingRequests.append(resource);
@ -176,8 +174,7 @@ void ResourceCache::requestCompleted(Resource* resource) {
auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>(); auto sharedItems = DependencyManager::get<ResourceCacheSharedItems>();
sharedItems->_loadingRequests.removeOne(resource); sharedItems->_loadingRequests.removeOne(resource);
if (resource->getURL().scheme() != URL_SCHEME_ATP) { if (resource->getURL().scheme() != URL_SCHEME_ATP) {
qDebug() << "++ Increasing limit after finished: " << resource->getURL(); ++_requestLimit;
_requestLimit++;
} }
// look for the highest priority pending request // look for the highest priority pending request
@ -368,62 +365,52 @@ void Resource::handleDownloadProgress(uint64_t bytesReceived, uint64_t bytesTota
void Resource::handleReplyFinished() { void Resource::handleReplyFinished() {
Q_ASSERT(_request); Q_ASSERT(_request);
ResourceCache::requestCompleted(this);
auto result = _request->getResult(); auto result = _request->getResult();
if (result == ResourceRequest::Success) { if (result == ResourceRequest::Success) {
_data = _request->getData(); _data = _request->getData();
qDebug() << "Request finished for " << _url << ", " << _activeUrl; qDebug() << "Request finished for " << _url << ", " << _activeUrl;
_request->disconnect(this); finishedLoading(true);
_request->deleteLater();
_request = nullptr;
ResourceCache::requestCompleted(this);
emit loaded(_data); emit loaded(_data);
downloadFinished(_data); downloadFinished(_data);
} else { } else {
_request->disconnect(this);
_request->deleteLater();
_request = nullptr;
if (result == ResourceRequest::Result::Timeout) {
qDebug() << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal;
} else {
qDebug() << "Error loading " << _url;
}
bool retry = false;
switch (result) { switch (result) {
case ResourceRequest::Result::Timeout: case ResourceRequest::Result::Timeout: {
case ResourceRequest::Result::ServerUnavailable: qDebug() << "Timed out loading" << _url << "received" << _bytesReceived << "total" << _bytesTotal;
case ResourceRequest::Result::Error: { // Fall through to other cases
}
case ResourceRequest::Result::ServerUnavailable: {
// retry with increasing delays // retry with increasing delays
const int MAX_ATTEMPTS = 8; const int MAX_ATTEMPTS = 8;
const int BASE_DELAY_MS = 1000; const int BASE_DELAY_MS = 1000;
if (++_attempts < MAX_ATTEMPTS) { if (_attempts++ < MAX_ATTEMPTS) {
QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(attemptRequest())); auto waitTime = BASE_DELAY_MS * (int)pow(2.0, _attempts);
retry = true; qDebug().nospace() << "Retrying to load the asset in " << waitTime
<< "ms, attempt " << _attempts << " of " << MAX_ATTEMPTS;
QTimer::singleShot(waitTime, this, &Resource::attemptRequest);
break; break;
} }
// fall through to final failure // fall through to final failure
} }
default: default: {
qDebug() << "Error loading " << _url;
auto error = (result == ResourceRequest::Timeout) ? QNetworkReply::TimeoutError
: QNetworkReply::UnknownNetworkError;
emit failed(error);
finishedLoading(false); finishedLoading(false);
break; break;
} }
auto error = result == ResourceRequest::Timeout ? QNetworkReply::TimeoutError : QNetworkReply::UnknownNetworkError;
if (!retry) {
emit failed(error);
ResourceCache::requestCompleted(this);
} }
} }
_request->disconnect(this);
_request->deleteLater();
_request = nullptr;
} }
void Resource::downloadFinished(const QByteArray& data) { void Resource::downloadFinished(const QByteArray& data) {
;
} }
uint qHash(const QPointer<QObject>& value, uint seed) { uint qHash(const QPointer<QObject>& value, uint seed) {

View file

@ -27,7 +27,7 @@ ResourceRequest* ResourceManager::createResourceRequest(QObject* parent, const Q
return new AssetResourceRequest(parent, url); return new AssetResourceRequest(parent, url);
} }
qDebug() << "Failed to load: " << url.url(); qDebug() << "Unknown scheme (" << scheme << ") for URL: " << url.url();
return nullptr; return nullptr;
} }

View file

@ -34,9 +34,11 @@ void BatchLoader::start() {
} }
_started = true; _started = true;
for (QUrl url : _urls) { for (const auto& url : _urls) {
auto request = ResourceManager::createResourceRequest(this, url); auto request = ResourceManager::createResourceRequest(this, url);
if (!request) { if (!request) {
_data.insert(url, QString());
qCDebug(scriptengine) << "Could not load" << url;
continue; continue;
} }
connect(request, &ResourceRequest::finished, this, [=]() { connect(request, &ResourceRequest::finished, this, [=]() {
@ -44,6 +46,7 @@ void BatchLoader::start() {
_data.insert(url, request->getData()); _data.insert(url, request->getData());
} else { } else {
_data.insert(url, QString()); _data.insert(url, QString());
qCDebug(scriptengine) << "Could not load" << url;
} }
request->deleteLater(); request->deleteLater();
checkFinished(); checkFinished();