handle path query response from DS in NL/AM

This commit is contained in:
Stephen Birarda 2015-05-12 13:48:41 -07:00
parent 993984134c
commit 7330e5255d
5 changed files with 270 additions and 179 deletions

View file

@ -2207,9 +2207,9 @@ void DomainServer::respondToPathQuery(const QByteArray& receivedPacket, const Hi
// figure out how many bytes the sender said this path is
quint16 numPathBytes = *packetDataStart;
if (numPathBytes <= receivedPacket.size() - numHeaderBytes - 1) {
if (numPathBytes <= receivedPacket.size() - numHeaderBytes - sizeof(numPathBytes)) {
// the number of path bytes makes sense for the sent packet - pull out the path
QString pathQuery = QString::fromUtf8(packetDataStart + 1, numPathBytes);
QString pathQuery = QString::fromUtf8(packetDataStart + sizeof(numPathBytes), numPathBytes);
// our settings contain paths that start with a leading slash, so make sure this query has that
if (!pathQuery.startsWith("/")) {
@ -2252,6 +2252,8 @@ void DomainServer::respondToPathQuery(const QByteArray& receivedPacket, const Hi
// append the viewpoint itself
pathResponsePacket.append(viewpointUTF8);
qDebug() << "Sending a viewpoint response for path query" << pathQuery << "-" << viewpointUTF8;
// send off the packet - see if we can associate this outbound data to a particular node
SharedNodePointer matchingNode = nodeList->sendingNodeForPacket(receivedPacket);
nodeList->writeUnverifiedDatagram(pathResponsePacket, matchingNode, senderSockAddr);

View file

@ -43,11 +43,11 @@ bool AddressManager::isConnected() {
const QUrl AddressManager::currentAddress() const {
QUrl hifiURL;
hifiURL.setScheme(HIFI_URL_SCHEME);
hifiURL.setHost(_rootPlaceName);
hifiURL.setPath(currentPath());
return hifiURL;
}
@ -64,10 +64,10 @@ void AddressManager::storeCurrentAddress() {
}
const QString AddressManager::currentPath(bool withOrientation) const {
if (_positionGetter) {
QString pathString = "/" + createByteArray(_positionGetter());
if (withOrientation) {
if (_orientationGetter) {
QString orientationString = createByteArray(_orientationGetter());
@ -76,9 +76,9 @@ const QString AddressManager::currentPath(bool withOrientation) const {
qCDebug(networking) << "Cannot add orientation to path without a getter for position."
<< "Call AddressManager::setOrientationGetter to pass a function that will return a glm::quat";
}
}
return pathString;
} else {
qCDebug(networking) << "Cannot create address path without a getter for position."
@ -90,29 +90,29 @@ const QString AddressManager::currentPath(bool withOrientation) const {
const JSONCallbackParameters& AddressManager::apiCallbackParameters() {
static bool hasSetupParameters = false;
static JSONCallbackParameters callbackParams;
if (!hasSetupParameters) {
callbackParams.jsonCallbackReceiver = this;
callbackParams.jsonCallbackMethod = "handleAPIResponse";
callbackParams.errorCallbackReceiver = this;
callbackParams.errorCallbackMethod = "handleAPIError";
}
return callbackParams;
}
bool AddressManager::handleUrl(const QUrl& lookupUrl) {
if (lookupUrl.scheme() == HIFI_URL_SCHEME) {
qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString();
// there are 4 possible lookup strings
// 1. global place name (name of domain or place) - example: sanfrancisco
// 2. user name (prepended with @) - example: @philip
// 3. location string (posX,posY,posZ/eulerX,eulerY,eulerZ)
// 4. domain network address (IP or dns resolvable hostname)
// use our regex'ed helpers to figure out what we're supposed to do with this
if (!handleUsername(lookupUrl.authority())) {
// we're assuming this is either a network address or global place name
@ -120,24 +120,24 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) {
if (handleNetworkAddress(lookupUrl.host()
+ (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())))) {
// we may have a path that defines a relative viewpoint - if so we should jump to that now
handleRelativeViewpoint(lookupUrl.path());
handlePath(lookupUrl.path());
} else {
// wasn't an address - lookup the place name
// we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after
attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path());
}
}
return true;
} else if (lookupUrl.toString().startsWith('/')) {
qCDebug(networking) << "Going to relative path" << lookupUrl.path();
// if this is a relative path then handle it as a relative viewpoint
handleRelativeViewpoint(lookupUrl.path());
handlePath(lookupUrl.path());
emit lookupResultsFinished();
}
return false;
}
@ -146,16 +146,16 @@ void AddressManager::handleLookupString(const QString& lookupString) {
// make this a valid hifi URL and handle it off to handleUrl
QString sanitizedString = lookupString.trimmed();
QUrl lookupURL;
if (!lookupString.startsWith('/')) {
const QRegExp HIFI_SCHEME_REGEX = QRegExp(HIFI_URL_SCHEME + ":\\/{1,2}", Qt::CaseInsensitive);
sanitizedString = sanitizedString.remove(HIFI_SCHEME_REGEX);
lookupURL = QUrl(HIFI_URL_SCHEME + "://" + sanitizedString);
} else {
lookupURL = QUrl(lookupString);
}
handleUrl(lookupURL);
}
}
@ -163,101 +163,100 @@ void AddressManager::handleLookupString(const QString& lookupString) {
void AddressManager::handleAPIResponse(QNetworkReply& requestReply) {
QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object();
QJsonObject dataObject = responseObject["data"].toObject();
goToAddressFromObject(dataObject.toVariantMap(), requestReply);
emit lookupResultsFinished();
}
const char OVERRIDE_PATH_KEY[] = "override_path";
void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const QNetworkReply& reply) {
const QString DATA_OBJECT_PLACE_KEY = "place";
const QString DATA_OBJECT_USER_LOCATION_KEY = "location";
QVariantMap locationMap;
if (dataObject.contains(DATA_OBJECT_PLACE_KEY)) {
locationMap = dataObject[DATA_OBJECT_PLACE_KEY].toMap();
} else {
locationMap = dataObject[DATA_OBJECT_USER_LOCATION_KEY].toMap();
}
if (!locationMap.isEmpty()) {
const QString LOCATION_API_ROOT_KEY = "root";
const QString LOCATION_API_DOMAIN_KEY = "domain";
const QString LOCATION_API_ONLINE_KEY = "online";
if (!locationMap.contains(LOCATION_API_ONLINE_KEY)
|| locationMap[LOCATION_API_ONLINE_KEY].toBool()) {
QVariantMap rootMap = locationMap[LOCATION_API_ROOT_KEY].toMap();
if (rootMap.isEmpty()) {
rootMap = locationMap;
}
QVariantMap domainObject = rootMap[LOCATION_API_DOMAIN_KEY].toMap();
if (!domainObject.isEmpty()) {
const QString DOMAIN_NETWORK_ADDRESS_KEY = "network_address";
const QString DOMAIN_NETWORK_PORT_KEY = "network_port";
const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address";
if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) {
QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString();
quint16 domainPort = domainObject.contains(DOMAIN_NETWORK_PORT_KEY)
? domainObject[DOMAIN_NETWORK_PORT_KEY].toUInt()
: DEFAULT_DOMAIN_SERVER_PORT;
qCDebug(networking) << "Possible domain change required to connect to" << domainHostname
<< "on" << domainPort;
emit possibleDomainChangeRequired(domainHostname, domainPort);
} else {
QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString();
const QString DOMAIN_ID_KEY = "id";
QString domainIDString = domainObject[DOMAIN_ID_KEY].toString();
QUuid domainID(domainIDString);
qCDebug(networking) << "Possible domain change required to connect to domain with ID" << domainID
<< "via ice-server at" << iceServerAddress;
emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID);
}
// set our current root place id to the ID that came back
const QString PLACE_ID_KEY = "id";
_rootPlaceID = rootMap[PLACE_ID_KEY].toUuid();
// set our current root place name to the name that came back
const QString PLACE_NAME_KEY = "name";
QString newRootPlaceName = rootMap[PLACE_NAME_KEY].toString();
setRootPlaceName(newRootPlaceName);
// check if we had a path to override the path returned
QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString();
if (!overridePath.isEmpty()) {
if (!handleRelativeViewpoint(overridePath)){
qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << overridePath;
}
handlePath(overridePath);
} else {
// take the path that came back
const QString PLACE_PATH_KEY = "path";
QString returnedPath = locationMap[PLACE_PATH_KEY].toString();
bool shouldFaceViewpoint = locationMap.contains(LOCATION_API_ONLINE_KEY);
if (!returnedPath.isEmpty()) {
// try to parse this returned path as a viewpoint, that's the only thing it could be for now
if (!handleRelativeViewpoint(returnedPath, shouldFaceViewpoint)) {
qCDebug(networking) << "Received a location path that was could not be handled as a viewpoint -" << returnedPath;
if (!handleViewpoint(returnedPath, shouldFaceViewpoint)) {
qCDebug(networking) << "Received a location path that was could not be handled as a viewpoint -"
<< returnedPath;
}
}
}
} else {
qCDebug(networking) << "Received an address manager API response with no domain key. Cannot parse.";
qCDebug(networking) << locationMap;
@ -274,7 +273,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const
void AddressManager::handleAPIError(QNetworkReply& errorReply) {
qCDebug(networking) << "AddressManager API error -" << errorReply.error() << "-" << errorReply.errorString();
if (errorReply.error() == QNetworkReply::ContentNotFoundError) {
emit lookupResultIsNotFound();
}
@ -286,12 +285,12 @@ const QString GET_PLACE = "/api/v1/places/%1";
void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath) {
// assume this is a place name and see if we can get any info on it
QString placeName = QUrl::toPercentEncoding(lookupString);
QVariantMap requestParams;
if (!overridePath.isEmpty()) {
requestParams.insert(OVERRIDE_PATH_KEY, overridePath);
}
AccountManager::getInstance().sendRequest(GET_PLACE.arg(placeName),
AccountManagerAuth::None,
QNetworkAccessManager::GetOperation,
@ -302,47 +301,55 @@ void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const Q
bool AddressManager::handleNetworkAddress(const QString& lookupString) {
const QString IP_ADDRESS_REGEX_STRING = "^((?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}"
"(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))(?::(\\d{1,5}))?$";
const QString HOSTNAME_REGEX_STRING = "^((?:[A-Z0-9]|[A-Z0-9][A-Z0-9\\-]{0,61}[A-Z0-9])"
"(?:\\.(?:[A-Z0-9]|[A-Z0-9][A-Z0-9\\-]{0,61}[A-Z0-9]))+|localhost)(?::(\\d{1,5}))?$";
QRegExp ipAddressRegex(IP_ADDRESS_REGEX_STRING);
if (ipAddressRegex.indexIn(lookupString) != -1) {
QString domainIPString = ipAddressRegex.cap(1);
qint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
if (!ipAddressRegex.cap(2).isEmpty()) {
domainPort = (qint16) ipAddressRegex.cap(2).toInt();
}
emit lookupResultsFinished();
setDomainInfo(domainIPString, domainPort);
return true;
}
QRegExp hostnameRegex(HOSTNAME_REGEX_STRING, Qt::CaseInsensitive);
if (hostnameRegex.indexIn(lookupString) != -1) {
QString domainHostname = hostnameRegex.cap(1);
quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
if (!hostnameRegex.cap(2).isEmpty()) {
domainPort = (qint16) hostnameRegex.cap(2).toInt();
}
emit lookupResultsFinished();
setDomainInfo(domainHostname, domainPort);
return true;
}
return false;
}
bool AddressManager::handleRelativeViewpoint(const QString& lookupString, bool shouldFace) {
void AddressManager::handlePath(const QString& path) {
if (!handleViewpoint(path)) {
qCDebug(networking) << "User entered path could not be handled as a viewpoint - " << path <<
"- wll attempt to ask domain-server to resolve.";
emit pathChangeRequired(path);
}
}
bool AddressManager::handleViewpoint(const QString& viewpointString, bool shouldFace) {
const QString FLOAT_REGEX_STRING = "([-+]?[0-9]*\\.?[0-9]+(?:[eE][-+]?[0-9]+)?)";
const QString SPACED_COMMA_REGEX_STRING = "\\s*,\\s*";
const QString POSITION_REGEX_STRING = QString("\\/") + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING +
@ -350,29 +357,29 @@ bool AddressManager::handleRelativeViewpoint(const QString& lookupString, bool s
const QString QUAT_REGEX_STRING = QString("\\/") + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING +
FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING + FLOAT_REGEX_STRING + SPACED_COMMA_REGEX_STRING +
FLOAT_REGEX_STRING + "\\s*$";
QRegExp positionRegex(POSITION_REGEX_STRING);
if (positionRegex.indexIn(lookupString) != -1) {
if (positionRegex.indexIn(viewpointString) != -1) {
// we have at least a position, so emit our signal to say we need to change position
glm::vec3 newPosition(positionRegex.cap(1).toFloat(),
positionRegex.cap(2).toFloat(),
positionRegex.cap(3).toFloat());
if (!isNaN(newPosition.x) && !isNaN(newPosition.y) && !isNaN(newPosition.z)) {
glm::quat newOrientation;
QRegExp orientationRegex(QUAT_REGEX_STRING);
// we may also have an orientation
if (lookupString[positionRegex.matchedLength() - 1] == QChar('/')
&& orientationRegex.indexIn(lookupString, positionRegex.matchedLength() - 1) != -1) {
if (viewpointString[positionRegex.matchedLength() - 1] == QChar('/')
&& orientationRegex.indexIn(viewpointString, positionRegex.matchedLength() - 1) != -1) {
glm::quat newOrientation = glm::normalize(glm::quat(orientationRegex.cap(4).toFloat(),
orientationRegex.cap(1).toFloat(),
orientationRegex.cap(2).toFloat(),
orientationRegex.cap(3).toFloat()));
if (!isNaN(newOrientation.x) && !isNaN(newOrientation.y) && !isNaN(newOrientation.z)
&& !isNaN(newOrientation.w)) {
emit locationChangeRequired(newPosition, true, newOrientation, shouldFace);
@ -381,31 +388,31 @@ bool AddressManager::handleRelativeViewpoint(const QString& lookupString, bool s
qCDebug(networking) << "Orientation parsed from lookup string is invalid. Will not use for location change.";
}
}
emit locationChangeRequired(newPosition, false, newOrientation, shouldFace);
} else {
qCDebug(networking) << "Could not jump to position from lookup string because it has an invalid value.";
}
return true;
} else {
return false;
}
return false;
}
const QString GET_USER_LOCATION = "/api/v1/users/%1/location";
bool AddressManager::handleUsername(const QString& lookupString) {
const QString USERNAME_REGEX_STRING = "^@(\\S+)";
QRegExp usernameRegex(USERNAME_REGEX_STRING);
if (usernameRegex.indexIn(lookupString) != -1) {
goToUser(usernameRegex.cap(1));
return true;
}
return false;
}
@ -420,9 +427,9 @@ void AddressManager::setRootPlaceName(const QString& rootPlaceName) {
void AddressManager::setDomainInfo(const QString& hostname, quint16 port) {
_rootPlaceName = hostname;
_rootPlaceID = QUuid();
qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port;
emit possibleDomainChangeRequired(hostname, port);
}

View file

@ -39,32 +39,34 @@ class AddressManager : public QObject, public Dependency {
public:
bool isConnected();
const QString& getProtocol() { return HIFI_URL_SCHEME; };
const QUrl currentAddress() const;
const QString currentPath(bool withOrientation = true) const;
const QUuid& getRootPlaceID() const { return _rootPlaceID; }
const QString& getRootPlaceName() const { return _rootPlaceName; }
void setRootPlaceName(const QString& rootPlaceName);
void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath = QString());
void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; }
void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; }
void loadSettings(const QString& lookupString = QString());
public slots:
void handleLookupString(const QString& lookupString);
void goToUser(const QString& username);
void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply);
bool goToViewpoint(const QString& viewpointString) { return handleViewpoint(viewpointString); }
void storeCurrentAddress();
void copyAddress();
void copyPath();
signals:
void lookupResultsFinished();
void lookupResultIsOffline();
@ -74,6 +76,7 @@ signals:
void locationChangeRequired(const glm::vec3& newPosition,
bool hasOrientationChange, const glm::quat& newOrientation,
bool shouldFaceLocation);
void pathChangeRequired(const QString& newPath);
void rootPlaceNameChanged(const QString& newRootPlaceName);
protected:
AddressManager();
@ -82,15 +85,16 @@ private slots:
void handleAPIError(QNetworkReply& errorReply);
private:
void setDomainInfo(const QString& hostname, quint16 port);
const JSONCallbackParameters& apiCallbackParameters();
bool handleUrl(const QUrl& lookupUrl);
bool handleNetworkAddress(const QString& lookupString);
bool handleRelativeViewpoint(const QString& pathSubsection, bool shouldFace = false);
void handlePath(const QString& path);
bool handleViewpoint(const QString& viewpointString, bool shouldFace = false);
bool handleUsername(const QString& lookupString);
QString _rootPlaceName;
QUuid _rootPlaceID;
PositionGetter _positionGetter;

View file

@ -46,23 +46,25 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned
firstCall = false;
}
auto addressManager = DependencyManager::get<AddressManager>();
// handle domain change signals from AddressManager
connect(addressManager.data(), &AddressManager::possibleDomainChangeRequired,
&_domainHandler, &DomainHandler::setHostnameAndPort);
connect(addressManager.data(), &AddressManager::possibleDomainChangeRequiredViaICEForID,
&_domainHandler, &DomainHandler::setIceServerHostnameAndID);
connect(addressManager.data(), &AddressManager::pathChangeRequired, this, &NodeList::sendDSPathQuery);
// clear our NodeList when the domain changes
connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, this, &NodeList::reset);
// handle ICE signal from DS so connection is attempted immediately
connect(&_domainHandler, &DomainHandler::requestICEConnectionAttempt, this, &NodeList::handleICEConnectionToDomainServer);
// clear out NodeList when login is finished
connect(&AccountManager::getInstance(), &AccountManager::loginComplete , this, &NodeList::reset);
// clear our NodeList when logout is requested
connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset);
}
@ -75,8 +77,8 @@ qint64 NodeList::sendStats(const QJsonObject& statsObject, const HifiSockAddr& d
QStringList statsStringList = JSONBreakableMarshal::toStringList(statsObject, "");
int numBytesWritten = numBytesForPacketHeader;
// enumerate the resulting strings - pack them and send off packets once we hit MTU size
// enumerate the resulting strings - pack them and send off packets once we hit MTU size
foreach(const QString& statsItem, statsStringList) {
QByteArray utf8String = statsItem.toUtf8();
utf8String.append('\0');
@ -85,14 +87,14 @@ qint64 NodeList::sendStats(const QJsonObject& statsObject, const HifiSockAddr& d
// send off the current packet since the next string will make us too big
statsPacket.resize(numBytesWritten);
writeUnverifiedDatagram(statsPacket, destination);
// reset the number of bytes written to the size of our packet header
numBytesWritten = numBytesForPacketHeader;
}
// write this string into the stats packet
statsPacket.replace(numBytesWritten, utf8String.size(), utf8String);
// keep track of the number of bytes we have written
numBytesWritten += utf8String.size();
}
@ -102,7 +104,7 @@ qint64 NodeList::sendStats(const QJsonObject& statsObject, const HifiSockAddr& d
statsPacket.resize(numBytesWritten);
writeUnverifiedDatagram(statsPacket, destination);
}
// enumerate the resulting strings, breaking them into MTU sized packets
return 0;
}
@ -114,26 +116,26 @@ qint64 NodeList::sendStatsToDomainServer(const QJsonObject& statsObject) {
void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode) {
QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet));
quint8 pingType;
quint64 ourOriginalTime, othersReplyTime;
packetStream >> pingType >> ourOriginalTime >> othersReplyTime;
quint64 now = usecTimestampNow();
int pingTime = now - ourOriginalTime;
int oneWayFlightTime = pingTime / 2; // half of the ping is our one way flight
// The other node's expected time should be our original time plus the one way flight time
// anything other than that is clock skew
quint64 othersExprectedReply = ourOriginalTime + oneWayFlightTime;
int clockSkew = othersReplyTime - othersExprectedReply;
sendingNode->setPingMs(pingTime / 1000);
sendingNode->updateClockSkewUsec(clockSkew);
const bool wantDebug = false;
if (wantDebug) {
qCDebug(networking) << "PING_REPLY from node " << *sendingNode << "\n" <<
" now: " << now << "\n" <<
@ -172,7 +174,7 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
matchingNode->setLastHeardMicrostamp(usecTimestampNow());
QByteArray replyPacket = constructPingReplyPacket(packet);
writeDatagram(replyPacket, matchingNode, senderSockAddr);
// If we don't have a symmetric socket for this node and this socket doesn't match
// what we have for public and local then set it as the symmetric.
// This allows a server on a reachable port to communicate with nodes on symmetric NATs
@ -182,7 +184,7 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
}
}
}
break;
}
case PacketTypePingReply: {
@ -190,14 +192,14 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
if (sendingNode) {
sendingNode->setLastHeardMicrostamp(usecTimestampNow());
// activate the appropriate socket for this node, if not yet updated
activateSocketFromNodeCommunication(packet, sendingNode);
// set the ping time for this node for stat collection
timePingReply(packet, sendingNode);
}
break;
}
case PacketTypeUnverifiedPing: {
@ -208,7 +210,7 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
}
case PacketTypeUnverifiedPingReply: {
qCDebug(networking) << "Received reply from domain-server on" << senderSockAddr;
// for now we're unsafely assuming this came back from the domain
if (senderSockAddr == _domainHandler.getICEPeer().getLocalSocket()) {
qCDebug(networking) << "Connecting to domain using local socket";
@ -226,6 +228,10 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
processSTUNResponse(packet);
break;
}
case PacketTypeDomainServerPathResponse: {
handleDSPathQueryResponse(packet);
break;
}
default:
LimitedNodeList::processNodeData(senderSockAddr, packet);
break;
@ -234,17 +240,17 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr
void NodeList::reset() {
LimitedNodeList::reset();
_numNoReplyDomainCheckIns = 0;
// refresh the owner UUID to the NULL UUID
setSessionUUID(QUuid());
if (sender() != &_domainHandler) {
// clear the domain connection information, unless they're the ones that asked us to reset
_domainHandler.softReset();
}
// if we setup the DTLS socket, also disconnect from the DTLS socket readyRead() so it can handle handshaking
if (_dtlsSocket) {
disconnect(_dtlsSocket, 0, this, 0);
@ -267,7 +273,7 @@ void NodeList::sendSTUNRequest() {
if (!_hasCompletedInitialSTUNFailure) {
qCDebug(networking) << "Sending intial stun request to" << STUN_SERVER_HOSTNAME;
}
LimitedNodeList::sendSTUNRequest();
_stunRequestsSinceSuccess++;
@ -292,9 +298,9 @@ bool NodeList::processSTUNResponse(const QByteArray& packet) {
if (LimitedNodeList::processSTUNResponse(packet)) {
// reset the number of failed STUN requests since last success
_stunRequestsSinceSuccess = 0;
_hasCompletedInitialSTUNFailure = true;
return true;
} else {
return false;
@ -309,23 +315,23 @@ void NodeList::sendDomainServerCheckIn() {
} else if (_domainHandler.getIP().isNull() && _domainHandler.requiresICE()) {
handleICEConnectionToDomainServer();
} else if (!_domainHandler.getIP().isNull()) {
bool isUsingDTLS = false;
PacketType domainPacketType = !_domainHandler.isConnected()
? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest;
if (!_domainHandler.isConnected()) {
qCDebug(networking) << "Sending connect request to domain-server at" << _domainHandler.getHostname();
// is this our localhost domain-server?
// if so we need to make sure we have an up-to-date local port in case it restarted
if (_domainHandler.getSockAddr().getAddress() == QHostAddress::LocalHost
|| _domainHandler.getHostname() == "localhost") {
static QSharedMemory* localDSPortSharedMem = NULL;
quint16 domainPort = DEFAULT_DOMAIN_SERVER_PORT;
getLocalServerPortFromSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY,
localDSPortSharedMem,
@ -333,12 +339,12 @@ void NodeList::sendDomainServerCheckIn() {
qCDebug(networking) << "Local domain-server port read from shared memory (or default) is" << domainPort;
_domainHandler.setPort(domainPort);
}
}
// construct the DS check in packet
QUuid packetUUID = _sessionUUID;
if (domainPacketType == PacketTypeDomainConnectRequest) {
if (!_domainHandler.getAssignmentUUID().isNull()) {
// this is a connect request and we're an assigned node
@ -351,70 +357,139 @@ void NodeList::sendDomainServerCheckIn() {
packetUUID = _domainHandler.getICEClientID();
}
}
QByteArray domainServerPacket = byteArrayWithUUIDPopulatedHeader(domainPacketType, packetUUID);
QDataStream packetStream(&domainServerPacket, QIODevice::Append);
// pack our data to send to the domain-server
packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList();
// if this is a connect request, and we can present a username signature, send it along
if (!_domainHandler.isConnected()) {
DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo();
packetStream << accountInfo.getUsername();
const QByteArray& usernameSignature = AccountManager::getInstance().getAccountInfo().getUsernameSignature();
if (!usernameSignature.isEmpty()) {
qCDebug(networking) << "Including username signature in domain connect request.";
packetStream << usernameSignature;
}
}
if (!isUsingDTLS) {
writeUnverifiedDatagram(domainServerPacket, _domainHandler.getSockAddr());
}
const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5;
static unsigned int numDomainCheckins = 0;
// send a STUN request every Nth domain server check in so we update our public socket, if required
if (numDomainCheckins++ % NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST == 0) {
sendSTUNRequest();
}
if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) {
// we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS
// so emit our signal that says that
emit limitOfSilentDomainCheckInsReached();
}
// increment the count of un-replied check-ins
_numNoReplyDomainCheckIns++;
}
}
void NodeList::sendDSPathQuery(const QString& newPath) {
// only send a path query if we know who our DS is or is going to be
if (!_domainHandler.getSockAddr().isNull()) {
// construct the path query packet
QByteArray pathQueryPacket = byteArrayWithPopulatedHeader(PacketTypeDomainServerPathQuery);
// get the UTF8 representation of path query
QByteArray pathQueryUTF8 = newPath.toUtf8();
// get the size of the UTF8 representation of the desired path
quint16 numPathBytes = pathQueryUTF8.size();
if (pathQueryPacket.size() + numPathBytes + sizeof(numPathBytes) < MAX_PACKET_SIZE) {
// append the size of the path to the query packet
pathQueryPacket.append(reinterpret_cast<char*>(&numPathBytes), sizeof(numPathBytes));
// append the path itself to the query packet
pathQueryPacket.append(pathQueryUTF8);
qDebug() << "Sending a path query packet for path" << newPath << "to domain-server at" << _domainHandler.getSockAddr();
// send off the path query
writeUnverifiedDatagram(pathQueryPacket, _domainHandler.getSockAddr());
} else {
qDebug() << "Path" << newPath << "would make PacketTypeDomainServerPathQuery packet > MAX_PACKET_SIZE." <<
"Will not send query.";
}
}
}
void NodeList::handleDSPathQueryResponse(const QByteArray& packet) {
// This is a response to a path query we theoretically made.
// In the future we may want to check that this was actually from our DS and for a query we actually made.
int numHeaderBytes = numBytesForPacketHeaderGivenPacketType(PacketTypeDomainServerPathResponse);
const char* startPosition = packet.data() + numHeaderBytes;
const char* currentPosition = startPosition;
// figure out how many bytes the path query is
qint16 numPathBytes;
memcpy(&numPathBytes, currentPosition, sizeof(numPathBytes));
currentPosition += sizeof(numPathBytes);
// make sure it is safe to pull the path
if (numPathBytes <= packet.size() - numHeaderBytes - (currentPosition - startPosition)) {
// pull the path from the packet
QString pathQuery = QString::fromUtf8(currentPosition, numPathBytes);
currentPosition += numPathBytes;
// figure out how many bytes the viewpoint is
qint16 numViewpointBytes;
memcpy(&numViewpointBytes, currentPosition, sizeof(numViewpointBytes));
currentPosition += sizeof(numViewpointBytes);
// make sure it is safe to pull the viewpoint
if (numViewpointBytes <= packet.size() - numHeaderBytes - (currentPosition - startPosition)) {
// pull the viewpoint from the packet
QString viewpoint = QString::fromUtf8(currentPosition, numViewpointBytes);
// Hand it off to the AddressManager so it can handle it as a relative viewpoint
if (DependencyManager::get<AddressManager>()->goToViewpoint(viewpoint)) {
qDebug() << "Going to viewpoint" << viewpoint << "which was the lookup result for path" << pathQuery;
} else {
qDebug() << "Could not go to viewpoint" << viewpoint << "which was the lookup result for path" << pathQuery;
}
}
}
}
void NodeList::handleICEConnectionToDomainServer() {
if (_domainHandler.getICEPeer().isNull()
|| _domainHandler.getICEPeer().getConnectionAttempts() >= MAX_ICE_CONNECTION_ATTEMPTS) {
_domainHandler.getICEPeer().resetConnectionAttemps();
LimitedNodeList::sendHeartbeatToIceServer(_domainHandler.getICEServerSockAddr(),
_domainHandler.getICEClientID(),
_domainHandler.getICEDomainID());
} else {
qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID"
<< uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID());
// send the ping packet to the local and public sockets for this node
QByteArray localPingPacket = constructPingPacket(PingType::Local, false, _domainHandler.getICEClientID());
writeUnverifiedDatagram(localPingPacket, _domainHandler.getICEPeer().getLocalSocket());
QByteArray publicPingPacket = constructPingPacket(PingType::Public, false, _domainHandler.getICEClientID());
writeUnverifiedDatagram(publicPingPacket, _domainHandler.getICEPeer().getPublicSocket());
_domainHandler.getICEPeer().incrementConnectionAttempts();
}
}
@ -422,18 +497,18 @@ void NodeList::handleICEConnectionToDomainServer() {
int NodeList::processDomainServerList(const QByteArray& packet) {
// this is a packet from the domain server, reset the count of un-replied check-ins
_numNoReplyDomainCheckIns = 0;
// if this was the first domain-server list from this domain, we've now connected
if (!_domainHandler.isConnected()) {
_domainHandler.setUUID(uuidFromPacketHeader(packet));
_domainHandler.setIsConnected(true);
}
int readNodes = 0;
QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet));
// pull our owner UUID from the packet, it's always the first thing
QUuid newUUID;
packetStream >> newUUID;
@ -446,7 +521,7 @@ int NodeList::processDomainServerList(const QByteArray& packet) {
bool thisNodeCanRez;
packetStream >> thisNodeCanRez;
setThisNodeCanRez(thisNodeCanRez);
// pull each node in the packet
while(packetStream.device()->pos() < packet.size()) {
// setup variables to read into from QDataStream
@ -466,11 +541,11 @@ int NodeList::processDomainServerList(const QByteArray& packet) {
SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket,
nodeLocalSocket, canAdjustLocks, canRez);
packetStream >> connectionUUID;
node->setConnectionSecret(connectionUUID);
}
// ping inactive nodes in conjunction with receipt of list from domain-server
// this makes it happen every second and also pings any newly added nodes
pingInactiveNodes();
@ -479,25 +554,25 @@ int NodeList::processDomainServerList(const QByteArray& packet) {
}
void NodeList::sendAssignment(Assignment& assignment) {
PacketType assignmentPacketType = assignment.getCommand() == Assignment::CreateCommand
? PacketTypeCreateAssignment
: PacketTypeRequestAssignment;
QByteArray packet = byteArrayWithPopulatedHeader(assignmentPacketType);
QDataStream packetStream(&packet, QIODevice::Append);
packetStream << assignment;
_nodeSocket.writeDatagram(packet, _assignmentServerSocket.getAddress(), _assignmentServerSocket.getPort());
}
void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) {
// send the ping packet to the local and public sockets for this node
QByteArray localPingPacket = constructPingPacket(PingType::Local);
writeDatagram(localPingPacket, node, node->getLocalSocket());
QByteArray publicPingPacket = constructPingPacket(PingType::Public);
writeDatagram(publicPingPacket, node, node->getPublicSocket());
@ -520,10 +595,10 @@ void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, con
// deconstruct this ping packet to see if it is a public or local reply
QDataStream packetStream(packet);
packetStream.skipRawData(numBytesForPacketHeader(packet));
quint8 pingType;
packetStream >> pingType;
// if this is a local or public ping then we can activate a socket
// we do nothing with agnostic pings, those are simply for timing
if (pingType == PingType::Local && sendingNode->getActiveSocket() != &sendingNode->getLocalSocket()) {

View file

@ -43,7 +43,7 @@ class Assignment;
class NodeList : public LimitedNodeList {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
NodeType_t getOwnerType() const { return _ownerType; }
void setOwnerType(NodeType_t ownerType) { _ownerType = ownerType; }
@ -53,24 +53,25 @@ public:
int getNumNoReplyDomainCheckIns() const { return _numNoReplyDomainCheckIns; }
DomainHandler& getDomainHandler() { return _domainHandler; }
const NodeSet& getNodeInterestSet() const { return _nodeTypesOfInterest; }
void addNodeTypeToInterestSet(NodeType_t nodeTypeToAdd);
void addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes);
void resetNodeInterestSet() { _nodeTypesOfInterest.clear(); }
void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet);
int processDomainServerList(const QByteArray& packet);
void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; }
void sendAssignment(Assignment& assignment);
void pingPunchForInactiveNode(const SharedNodePointer& node);
public slots:
void reset();
void sendDomainServerCheckIn();
void pingInactiveNodes();
void sendDSPathQuery(const QString& newPath);
signals:
void limitOfSilentDomainCheckInsReached();
private:
@ -78,17 +79,19 @@ private:
NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0);
NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton
void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton
void sendSTUNRequest();
bool processSTUNResponse(const QByteArray& packet);
void handleICEConnectionToDomainServer();
void processDomainServerAuthRequest(const QByteArray& packet);
void requestAuthForDomainServer();
void activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode);
void timePingReply(const QByteArray& packet, const SharedNodePointer& sendingNode);
void handleDSPathQueryResponse(const QByteArray& packet);
NodeType_t _ownerType;
NodeSet _nodeTypesOfInterest;
DomainHandler _domainHandler;
@ -96,7 +99,7 @@ private:
HifiSockAddr _assignmentServerSocket;
bool _hasCompletedInitialSTUNFailure;
unsigned int _stunRequestsSinceSuccess;
friend class Application;
};