Example #1
0
Future<MessageSocketPtr> TransportSocketCache::socket(const ServiceInfo& servInfo, const std::string& url)
{
  const std::string& machineId = servInfo.machineId();
  ConnectionAttemptPtr couple = boost::make_shared<ConnectionAttempt>();
  couple->relatedUrls = servInfo.endpoints();
  bool local = machineId == os::getMachineId();
  UrlVector connectionCandidates;

  // If the connection is local, we're mainly interested in localhost endpoint
  if (local)
    connectionCandidates = localhost_only(servInfo.endpoints());

  // If the connection isn't local or if the service doesn't expose local endpoints,
  // try and connect to whatever is available.
  if (connectionCandidates.size() == 0)
    connectionCandidates = servInfo.endpoints();

  couple->endpoint = MessageSocketPtr();
  couple->state = State_Pending;
  {
    // If we already have a pending connection to one of the urls, we return the future in question
    boost::mutex::scoped_lock lock(_socketMutex);

    if (_dying)
      return makeFutureError<MessageSocketPtr>("TransportSocketCache is closed.");

    ConnectionMap::iterator machineIt = _connections.find(machineId);
    if (machineIt != _connections.end())
    {
      // Check if any connection to the machine matches one of our urls
      UrlVector& vurls = couple->relatedUrls;
      for (std::map<Url, ConnectionAttemptPtr>::iterator b = machineIt->second.begin(), e = machineIt->second.end();
           b != e;
           b++)
      {
        UrlVector::iterator uIt = std::find(vurls.begin(), vurls.end(), b->first);
        // We found a matching machineId and URL : return the connected endpoint.
        if (uIt != vurls.end())
        {
          qiLogDebug() << "Found pending promise.";
          return b->second->promise.future();
        }
      }
    }
    // Otherwise, we keep track of all those URLs and assign them the same promise in our map.
    // They will all track the same connection.
    couple->attemptCount = connectionCandidates.size();
    std::map<Url, ConnectionAttemptPtr>& urlMap = _connections[machineId];
    for (const auto& url: connectionCandidates)
    {
      if (!url.isValid())
        continue; // Do not try to connect to an invalid url!

      if (!local && isLocalHost(url.host()))
        continue; // Do not try to connect on localhost when it is a remote!

      urlMap[url] = couple;
      MessageSocketPtr socket = makeMessageSocket(url.protocol());
      _allPendingConnections.push_back(socket);
      Future<void> sockFuture = socket->connect(url);
      qiLogDebug() << "Inserted [" << machineId << "][" << url.str() << "]";
      sockFuture.connect(&TransportSocketCache::onSocketParallelConnectionAttempt, this, _1, socket, url, servInfo);
    }
  }
  return couple->promise.future();
}