Example #1
0
  void RemoteObject::onFutureCancelled(unsigned int originalMessageId)
  {
    qiLogDebug() << "Cancel request for message " << originalMessageId;
    TransportSocketPtr sock;
    {
      boost::mutex::scoped_lock lock(_socketMutex);
      sock = _socket;
    }
    Message cancelMessage;

    if (!sock)
    {
      qiLogWarning() << "Tried to cancel a call, but the socket to service "
                   << _service << " is disconnected.";
      return;
    }
    if (!sock->sharedCapability<bool>("RemoteCancelableCalls", false))
    {
      qiLogWarning() << "Remote end does not support cancelable calls.";
      return;
    }
    cancelMessage.setService(_service);
    cancelMessage.setType(Message::Type_Cancel);
    cancelMessage.setValue(AnyReference::from(originalMessageId), "I");
    cancelMessage.setObject(_object);
    sock->send(cancelMessage);
  }
Example #2
0
  void RemoteObject::close(const std::string& reason, bool fromSignal)
  {
    qiLogDebug() << "Closing remote object";
    TransportSocketPtr socket;
    {
       boost::mutex::scoped_lock lock(_socketMutex);
       socket = _socket;
       _socket.reset();
    }
    if (socket)
    { // Do not hold any lock when invoking signals.
        qiLogDebug() << "Removing connection from socket " << (void*)socket.get();
        socket->messagePendingDisconnect(_service, TransportSocket::ALL_OBJECTS, _linkMessageDispatcher);
        if (!fromSignal)
          socket->disconnected.disconnect(_linkDisconnected);
    }
    std::map<int, qi::Promise<AnyReference> > promises;
    {
      boost::mutex::scoped_lock lock(_promisesMutex);
      promises = _promises;
      _promises.clear();
    }
    // Nobody should be able to add anything to promises at this point.
    std::map<int, qi::Promise<AnyReference> >::iterator it;
    for (it = promises.begin(); it != promises.end(); ++it)
    {
      qiLogVerbose() << "Reporting error for request " << it->first << "(" << reason << ")";
      it->second.setError(reason);
    }

    //@warning: remove connection are not removed
    //          not very important ATM, because RemoteObject
    //          cant be reconnected
  }
void TransportSocketCache::close()
{
  qiLogDebug() << "TransportSocketCache is closing";
  ConnectionMap map;
  std::list<TransportSocketPtr> pending;
  {
    boost::mutex::scoped_lock lock(_socketMutex);
    _dying = true;
    std::swap(map, _connections);
    std::swap(pending, _allPendingConnections);
  }
  for (ConnectionMap::iterator mIt = map.begin(), mEnd = map.end(); mIt != mEnd; ++mIt)
  {
    for (std::map<Url, ConnectionAttemptPtr>::iterator uIt = mIt->second.begin(), uEnd = mIt->second.end();
         uIt != uEnd;
         ++uIt)
    {
      TransportSocketPtr endpoint = uIt->second->endpoint;

      // Disconnect any valid socket we were holding.
      if (endpoint)
      {
        endpoint->disconnect();
      }
      else
      {
        uIt->second->state = State_Error;
        uIt->second->promise.setError("TransportSocketCache is closing.");
      }
    }
  }
  for (std::list<TransportSocketPtr>::iterator it = pending.begin(), end = pending.end(); it != end; ++it)
    (*it)->disconnect();
}
 static void sendCapabilities(TransportSocketPtr sock)
 {
   Message msg;
   msg.setType(Message::Type_Capability);
   msg.setService(Message::Service_Server);
   msg.setValue(sock->localCapabilities(), typeOf<CapabilityMap>()->signature());
   sock->send(msg);
 }
Future<TransportSocketPtr> TransportSocketCache::socket(const ServiceInfo& servInfo, const std::string& protocol)
{
  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 = TransportSocketPtr();
  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<TransportSocketPtr>("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())
          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 (UrlVector::iterator it = connectionCandidates.begin(), end = connectionCandidates.end(); it != end; ++it)
    {
      urlMap[*it] = couple;
      TransportSocketPtr socket = makeTransportSocket(it->protocol());
      _allPendingConnections.push_back(socket);
      Future<void> sockFuture = socket->connect(*it);
      qiLogDebug() << "Inserted [" << machineId << "][" << it->str() << "]";
      sockFuture.connect(&TransportSocketCache::onSocketParallelConnectionAttempt, this, _1, socket, *it, servInfo);
    }
  }
  return couple->promise.future();
}
Example #6
0
 void Server::disconnectSignals(const TransportSocketPtr& socket, const SocketSubscriber& subscriber)
 {
   socket->connected.disconnectAll();
   socket->disconnected.disconnect(subscriber.disconnected);
   socket->messageReady.disconnect(subscriber.messageReady);
   socket->disconnect();
 }
Example #7
0
  void Server::onMessageReady(const qi::Message &msg, TransportSocketPtr socket) {
    qi::BoundAnyObject obj;
    // qiLogDebug() << "Server Recv (" << msg.type() << "):" << msg.address();
    {
      boost::mutex::scoped_lock sl(_boundObjectsMutex);
      BoundAnyObjectMap::iterator it;

      it = _boundObjects.find(msg.service());
      if (it == _boundObjects.end())
      {
        // The message could be addressed to a bound object, inside a
        // remoteobject host, or to a remoteobject, using the same socket.
        qiLogVerbose() << "No service for " << msg.address();
        if (msg.object() > Message::GenericObject_Main
          || msg.type() == Message::Type_Reply
          || msg.type() == Message::Type_Event
          || msg.type() == Message::Type_Error
          || msg.type() == Message::Type_Canceled)
          return;
        // ... but only if the object id is >main
        qi::Message       retval(Message::Type_Error, msg.address());
        std::stringstream ss;
        ss << "can't find service, address: " << msg.address();
        retval.setError(ss.str());
        socket->send(retval);
        qiLogError() << "Can't find service: " << msg.service() << " on " << msg.address();
        return;
      }
      obj            = it->second;
    }
    // We were called from the thread pool: synchronous call is ok
    //qi::getEventLoop()->post(boost::bind<void>(&BoundObject::onMessage, obj, msg, socket));
    obj->onMessage(msg, socket);
  }
Example #8
0
  qi::Future<void> RemoteObject::metaDisconnect(SignalLink linkId)
  {
    unsigned int event = linkId >> 32;
    //disconnect locally
    qi::Future<void> fut = DynamicObject::metaDisconnect(linkId);
    if (fut.hasError())
    {
      std::stringstream ss;
      ss << "Disconnection failure for " << linkId << ", error:" << fut.error();
      qiLogWarning() << ss.str();
      return qi::makeFutureError<void>(ss.str());
    }

    boost::recursive_mutex::scoped_lock _lock(_localToRemoteSignalLinkMutex);
    LocalToRemoteSignalLinkMap::iterator it;
    it = _localToRemoteSignalLink.find(event);
    if (it == _localToRemoteSignalLink.end()) {
      qiLogWarning() << "Cant find " << event << " in the localtoremote signal map";
      return fut;
    }
    qi::SignalLink toDisco = qi::SignalBase::invalidSignalLink;
    {
      RemoteSignalLinks &rsl = it->second;
      std::vector<SignalLink>::iterator vslit;
      vslit = std::find(rsl.localSignalLink.begin(), rsl.localSignalLink.end(), linkId);

      if (vslit != rsl.localSignalLink.end()) {
        rsl.localSignalLink.erase(vslit);
      } else {
        qiLogWarning() << "Cant find " << linkId << " in the remote signal vector (event:" << event << ")";
      }

      //only drop the remote connection when no more local connection are registered
      if (rsl.localSignalLink.size() == 0) {
        toDisco = rsl.remoteSignalLink;
        _localToRemoteSignalLink.erase(it);
      }
    }
    if (toDisco != qi::SignalBase::invalidSignalLink) {
      TransportSocketPtr sock = _socket;
      if (sock && sock->isConnected())
        return _self.async<void>("unregisterEvent", _service, event, toDisco);
    }
    return fut;
  }
Example #9
0
 void RemoteObject::metaPost(AnyObject, unsigned int event, const qi::GenericFunctionParameters &in)
 {
   // Bounce the emit request to server
   // TODO: one optimisation that could be done is to trigger the local
   // subscribers immediately.
   // But it is a bit complex, because the server will bounce the
   // event back to us.
   qi::Message msg;
   // apparent signature must match for correct serialization
   qi::Signature argsSig = qi::makeTupleSignature(in, false);
   qi::Signature funcSig;
   const MetaMethod* mm = metaObject().method(event);
   if (mm)
     funcSig = mm->parametersSignature();
   else
   {
     const MetaSignal* ms = metaObject().signal(event);
     if (!ms)
       throw std::runtime_error("Post target id does not exist");
     funcSig = ms->parametersSignature();
   }
    try {
     msg.setValues(in, funcSig, this, _socket.get());
   }
   catch(const std::exception& e)
   {
     qiLogVerbose() << "setValues exception: " << e.what();
     if (!_socket->remoteCapability("MessageFlags", false))
       throw e;
     // Delegate conversion to the remote end.
     msg.addFlags(Message::TypeFlag_DynamicPayload);
     msg.setValues(in, "m", this, _socket.get());
   }
   msg.setType(Message::Type_Post);
   msg.setService(_service);
   msg.setObject(_object);
   msg.setFunction(event);
   TransportSocketPtr sock = _socket;
   if (!sock || !sock->send(msg)) {
     qiLogVerbose() << "error while emitting event";
     return;
   }
 }
Example #10
0
void GwObjectHost::assignClientMessageObjectsGwIds(const Signature& signature, Message& msg, TransportSocketPtr sender)
{
  // if there's no chance of any object being in the call we're done.
  if (!hasObjectsSomewhere(signature))
    return;

  AnyReference callParameters = msg.value(signature, sender);

  // re-serialize the arguments so that the objects can receive a GW-specific objectId
  // ObjectHost uses a static int for its objectId so we're OK instantiating multiple
  // ones.
  Message forward;
  MockObjectHost host(Message::Service_Server);

  forward.setFlags(msg.flags());
  forward.setValue(callParameters, signature, &host, sender.get());
  msg.setBuffer(forward.buffer());

  // The message will store all the objects it serializes in the host.
  const ObjectHost::ObjectMap& objects = host.objects();
  std::map<GwObjectId, MetaObject> newObjectsMetaObjects;
  std::map<GwObjectId, std::pair<TransportSocketPtr, ObjectAddress> > newObjectsOrigin;
  std::map<ObjectAddress, GwObjectId> newHostObjectBank;
  for (ObjectHost::ObjectMap::const_iterator it = objects.begin(), end = objects.end(); it != end; ++it)
  {
    GwObjectId oid = it->first;
    ServiceBoundObject* sbo = static_cast<ServiceBoundObject*>(it->second.get());
    RemoteObject* ro = static_cast<RemoteObject*>(sbo->object().asGenericObject()->value);
    ObjectAddress addr;
    addr.service = ro->service();
    addr.object = ro->object();
    ro->setTransportSocket(TransportSocketPtr());

    newObjectsMetaObjects[oid] = ro->metaObject();
    newObjectsOrigin[oid] = std::make_pair(sender, addr);
    newHostObjectBank[addr] = oid;
    // We set an empty transportsocket.
    // Otherwise when we destroy `passed` below, the remoteobject
    // will attempt to send back home a `terminate` message, which we don't want.
    // By setting a null socket the object will stay alive on the remote end.

    qiLogDebug() << "Message " << msg.address() << ", Object connection: {" << addr.service << "," << addr.object
                 << "} <=> {0," << oid << "}";
  }
  {
    boost::upgrade_lock<boost::shared_mutex> lock(_mutex);
    boost::upgrade_to_unique_lock<boost::shared_mutex> unique_lock(lock);
    _objectsMetaObjects.insert(newObjectsMetaObjects.begin(), newObjectsMetaObjects.end());
    _objectsOrigin.insert(newObjectsOrigin.begin(), newObjectsOrigin.end());
    _hostObjectBank[sender].insert(newHostObjectBank.begin(), newHostObjectBank.end());
  }
  callParameters.destroy();
}
Example #11
0
  void Server::onTransportServerNewConnection(TransportSocketPtr socket, bool startReading)
  {
    boost::recursive_mutex::scoped_lock sl(_socketsMutex);
    if (!socket)
      return;
    if (_dying)
    {
      qiLogDebug() << "Incoming connection while closing, dropping...";
      socket->disconnect().async();
      return;
    }

    auto inserted = _subscribers.insert(std::make_pair(socket, SocketSubscriber{}));
    QI_ASSERT(inserted.second && "Socket insertion failed. Socket already exists.");

    auto& subscriber = inserted.first->second;

    QI_ASSERT(subscriber.disconnected == qi::SignalBase::invalidSignalLink && "Connecting a signal that already exists.");
    subscriber.disconnected =
        socket->disconnected.connect(&Server::onSocketDisconnected, this, socket, _1);

    // If false : the socket is only being registered, and has already been authenticated. The connection
    // was made elsewhere.
    // If true, it's an actual connection to this server.
    if (startReading)
    {
      SignalSubscriberPtr sub(new SignalSubscriber);
      boost::shared_ptr<bool> first = boost::make_shared<bool>(true);
      // We are reading on the socket for the first time : the first message has to be the capabilities
      *sub = socket->messageReady.connect(&Server::onMessageReadyNotAuthenticated, this, _1, socket, _authProviderFactory->newProvider(), first, sub).setCallType(MetaCallType_Direct);
      socket->startReading();
    }
    else
    {
      QI_ASSERT(subscriber.messageReady == qi::SignalBase::invalidSignalLink &&
             "Connecting a signal that already exists.");
      subscriber.messageReady =
          socket->messageReady.connect(&Server::onMessageReady, this, _1, socket).setCallType(MetaCallType_Direct);
    }
  }
  void Session_Service::onTransportSocketResult(qi::Future<TransportSocketPtr> value, long requestId) {
    qiLogDebug() << "Got transport socket for service";
    {
      boost::recursive_mutex::scoped_lock sl(_requestsMutex);
      ServiceRequest *sr = serviceRequest(requestId);
      if (!sr)
        return;

      if (value.hasError()) {
        sr->promise.setError(value.error());
        removeRequest(requestId);
        return;
      }
    }
    TransportSocketPtr socket = value.value();

    // If true, this socket came from the socket cache and has already been identified.
    // This typically happens when two services are behind the same endpoint.
    // We forge a message that just shows we've authenticated successfully.
    if (socket->hasReceivedRemoteCapabilities())
    {
      Message dummy;
      CapabilityMap cm;
      cm[AuthProvider::State_Key] = AnyValue::from(AuthProvider::State_Done);
      dummy.setType(Message::Type_Reply);
      dummy.setFunction(qi::Message::ServerFunction_Authenticate);
      dummy.setValue(AnyValue::from(cm), typeOf<CapabilityMap>()->signature());
      onAuthentication(TransportSocket::SocketEventData(dummy), requestId, socket, ClientAuthenticatorPtr(new NullClientAuthenticator), SignalSubscriberPtr());
      return;
    }
    ClientAuthenticatorPtr authenticator = _authFactory->newAuthenticator();
    CapabilityMap authCaps;
    {
      CapabilityMap tmp = authenticator->initialAuthData();
      for (CapabilityMap::iterator it = tmp.begin(), end = tmp.end(); it != end; ++it)
        authCaps[AuthProvider::UserAuthPrefix + it->first] = it->second;
    }
    SignalSubscriberPtr protSubscriber(new SignalSubscriber);
    *protSubscriber = socket->socketEvent.connect(&Session_Service::onAuthentication, this, _1, requestId, socket, authenticator, protSubscriber);


    Message msgCapabilities;
    msgCapabilities.setFunction(Message::ServerFunction_Authenticate);
    msgCapabilities.setService(Message::Service_Server);
    msgCapabilities.setType(Message::Type_Call);

    TransportSocketPtr sdSocket = _sdClient->socket();
    CapabilityMap socketCaps;
    if (sdSocket)
    {
      socketCaps = sdSocket->localCapabilities();
      socket->advertiseCapabilities(socketCaps);
    }
    socketCaps.insert(authCaps.begin(), authCaps.end());
    msgCapabilities.setValue(socketCaps, typeOf<CapabilityMap>()->signature());
    socket->send(msgCapabilities);
  }
Example #13
0
  void SessionPrivate::addSdSocketToCache(Future<void> f, const qi::Url& url,
                                          qi::Promise<void> p)
  {
    qiLogDebug() << "addSocketToCache processing";
    if (f.hasError())
    {
      qiLogDebug() << "addSdSocketToCache: connect reported failure";
      _serviceHandler.removeService("ServiceDirectory");
      p.setError(f.error());
      return;
    }

    // Allow the SD process to use the existing socket to talk to our services
    _serverObject.registerSocket(_sdClient.socket());

    /* Allow reusing the SD socket for communicating with services.
     * To do this, we must add it to our socket cache, and for this we need
     * to know the sd machineId
     */
     std::string mid;
     try
     {
       mid = _sdClient.machineId();
     }
     catch (const std::exception& e)
     { // Provide a nice message for backward compatibility
       qiLogVerbose() << e.what();
       qiLogWarning() << "Failed to obtain machineId, connection to service directory will not be reused for other services.";
       p.setValue(0);
       return;
     }
     TransportSocketPtr s = _sdClient.socket();
     qiLogVerbose() << "Inserting sd to cache for " << mid <<" " << url.str() << std::endl;
     _socketsCache.insert(mid, s->remoteEndpoint(), s);
     p.setValue(0);
  }
  void Session_Service::onAuthentication(const TransportSocket::SocketEventData& data, long requestId, TransportSocketPtr socket, ClientAuthenticatorPtr auth, SignalSubscriberPtr old)
  {
    static const std::string cmsig = typeOf<CapabilityMap>()->signature().toString();
    boost::recursive_mutex::scoped_lock sl(_requestsMutex);
    ServiceRequest *sr = serviceRequest(requestId);
    if (!sr)
      return;
    if (data.which() == TransportSocket::Event_Error)
    {
      if (old)
        socket->socketEvent.disconnect(*old);
      sr->promise.setError(boost::get<std::string>(data));
      removeRequest(requestId);
      return;
    }

#if BOOST_VERSION < 105800
    const Message& msg = boost::get<const Message&>(data);
#else
    const Message& msg = boost::relaxed_get<const Message&>(data);
#endif
    int function = msg.function();
    bool failure = msg.type() == Message::Type_Error
        || msg.service() != Message::Service_Server
        || function != Message::ServerFunction_Authenticate;

    if (failure)
    {
      if (old)
        socket->socketEvent.disconnect(*old);
      if (_enforceAuth)
      {
        std::stringstream error;
        if (msg.type() == Message::Type_Error)
          error << "Error while authenticating: " << msg.value("s", socket).to<std::string>();
        else
          error << "Expected a message for function #" << Message::ServerFunction_Authenticate << " (authentication), received a message for function " << function;
        sr->promise.setError(error.str());
        removeRequest(requestId);
      }
      else
      {
        session_service_private::sendCapabilities(socket);
        qi::Future<void> metaObjFut;
        sr->remoteObject = new qi::RemoteObject(sr->serviceId, socket);
        metaObjFut = sr->remoteObject->fetchMetaObject();
        metaObjFut.connect(&Session_Service::onRemoteObjectComplete, this, _1, requestId);
      }
      return;
    }

    AnyReference cmref = msg.value(typeOf<CapabilityMap>()->signature(), socket);
    CapabilityMap authData = cmref.to<CapabilityMap>();
    CapabilityMap::iterator authStateIt = authData.find(AuthProvider::State_Key);
    cmref.destroy();

    if (authStateIt == authData.end() || authStateIt->second.to<unsigned int>() < AuthProvider::State_Error
        || authStateIt->second.to<unsigned int>() > AuthProvider::State_Done)
    {
      if (old)
        socket->socketEvent.disconnect(*old);
      std::string error = "Invalid authentication state token.";
      sr->promise.setError(error);
      removeRequest(requestId);
      qiLogInfo() << error;
      return;
    }
    if (authData[AuthProvider::State_Key].to<unsigned int>() == AuthProvider::State_Done)
    {
      qi::Future<void> metaObjFut;
      if (old)
        socket->socketEvent.disconnect(*old);
      sr->remoteObject = new qi::RemoteObject(sr->serviceId, socket);
      //ask the remoteObject to fetch the metaObject
      metaObjFut = sr->remoteObject->fetchMetaObject();
      metaObjFut.connect(&Session_Service::onRemoteObjectComplete, this, _1, requestId);
      return;
    }

    CapabilityMap nextData = auth->processAuth(authData);
    Message authMsg;
    authMsg.setService(Message::Service_Server);
    authMsg.setType(Message::Type_Call);
    authMsg.setValue(nextData, cmsig);
    authMsg.setFunction(Message::ServerFunction_Authenticate);
    socket->send(authMsg);
  }
/*
 * Corner case to manage (TODO):
 *
 * You are connecting to machineId foo, you are machineId bar. foo and bar are
 * on different sub-networks with the same netmask. They sadly got the same IP
 * on their subnet: 192.168.1.42. When trying to connect to foo from bar, we
 * will try to connect its endpoints, basically:
 *   - tcp://1.2.3.4:1333 (public IP)
 *   - tcp://192.168.1.42:1333 (subnet public IP)
 * If bar is listening on port 1333, we may connect to it instead of foo (our
 * real target).
 */
void TransportSocketCache::onSocketParallelConnectionAttempt(Future<void> fut,
                                                             TransportSocketPtr socket,
                                                             Url url,
                                                             const ServiceInfo& info)
{
  boost::mutex::scoped_lock lock(_socketMutex);

  if (_dying)
  {
    qiLogDebug() << "ConnectionAttempt: TransportSocketCache is closed";
    if (!fut.hasError())
    {
      _allPendingConnections.remove(socket);
      socket->disconnect();
    }
    return;
  }

  ConnectionMap::iterator machineIt = _connections.find(info.machineId());
  std::map<Url, ConnectionAttemptPtr>::iterator urlIt;
  if (machineIt != _connections.end())
    urlIt = machineIt->second.find(url);

  if (machineIt == _connections.end() || urlIt == machineIt->second.end())
  {
    // The socket was disconnected at some point, and we removed it from our map:
    // return early.
    _allPendingConnections.remove(socket);
    socket->disconnect();
    return;
  }

  ConnectionAttemptPtr attempt = urlIt->second;
  attempt->attemptCount--;
  if (attempt->state != State_Pending)
  {
    qiLogDebug() << "Already connected: reject socket " << socket.get() << " endpoint " << url.str();
    _allPendingConnections.remove(socket);
    socket->disconnect();
    checkClear(attempt, info.machineId());
    return;
  }
  if (fut.hasError())
  {
    // Failing to connect to some of the endpoint is expected.
    qiLogDebug() << "Could not connect to service #" << info.serviceId() << " through url " << url.str();
    _allPendingConnections.remove(socket);
    // It's a critical error if we've exhausted all available endpoints.
    if (attempt->attemptCount == 0)
    {
      std::stringstream err;
      err << "Could not connect to service #" << info.serviceId() << ": no endpoint answered.";
      qiLogError() << err.str();
      attempt->promise.setError(err.str());
      attempt->state = State_Error;
      checkClear(attempt, info.machineId());
    }
    return;
  }
  socket->disconnected.connect(&TransportSocketCache::onSocketDisconnected, this, socket, url, _1, info)
      .setCallType(MetaCallType_Direct);
  attempt->state = State_Connected;
  attempt->endpoint = socket;
  attempt->promise.setValue(socket);
  qiLogDebug() << "Connected to service #" << info.serviceId() << " through url " << url.str() << " and socket "
               << socket.get();
}
Example #16
0
  void Server::onMessageReadyNotAuthenticated(const Message &msg, TransportSocketPtr socket, AuthProviderPtr auth,
                                              boost::shared_ptr<bool> first, SignalSubscriberPtr oldSignal)
  {
    qiLogVerbose() << "Starting auth message" << msg.address();
    int id = msg.id();
    int service = msg.service();
    int function = msg.action();
    int type = msg.type();
    Message reply;

    reply.setId(id);
    reply.setService(service);
    if (service != Message::Service_Server
        || type != Message::Type_Call
        || function != Message::ServerFunction_Authenticate)
    {
      socket->messageReady.disconnect(*oldSignal);
      if (_enforceAuth)
      {
        std::stringstream err;

        err << "Expected authentication (service #" << Message::Service_Server <<
               ", type #" << Message::Type_Call <<
               ", action #" << Message::ServerFunction_Authenticate <<
               "), received service #" << service << ", type #" << type << ", action #" << function;
        reply.setType(Message::Type_Error);
        reply.setError(err.str());
        socket->send(reply);
        socket->disconnect();
        qiLogVerbose() << err.str();
      }
      else
      {
        server_private::sendCapabilities(socket);
        qiLogVerbose() << "Authentication is not enforced. Skipping...";

        connectMessageReady(socket);
        onMessageReady(msg, socket);
      }
      return;
    }
    // the socket now contains the remote capabilities in socket->remoteCapabilities()
    qiLogVerbose() << "Authenticating client " << socket->remoteEndpoint().str() << "...";

    AnyReference cmref = msg.value(typeOf<CapabilityMap>()->signature(), socket);
    CapabilityMap authData = cmref.to<CapabilityMap>();
    cmref.destroy();
    CapabilityMap authResult = auth->processAuth(authData);
    unsigned int state = authResult[AuthProvider::State_Key].to<unsigned int>();
    std::string cmsig = typeOf<CapabilityMap>()->signature().toString();
    reply.setFunction(function);
    switch (state)
    {
    case AuthProvider::State_Done:
      qiLogVerbose() << "Client " << socket->remoteEndpoint().str() << " successfully authenticated.";
      socket->messageReady.disconnect(*oldSignal);
      connectMessageReady(socket);
      // no break, we know that authentication is done, send the response to the remote end
    case AuthProvider::State_Cont:
      if (*first)
      {
        authResult.insert(socket->localCapabilities().begin(), socket->localCapabilities().end());
        *first = false;
      }
      reply.setValue(authResult, cmsig);
      reply.setType(Message::Type_Reply);
      socket->send(reply);
      break;
    case AuthProvider::State_Error:
    default:{
      std::stringstream builder;
      builder << "Authentication failed";
      if (authResult.find(AuthProvider::Error_Reason_Key) != authResult.end())
      {
        builder << ": " << authResult[AuthProvider::Error_Reason_Key].to<std::string>();
        builder << " [" << _authProviderFactory->authVersionMajor() << "." << _authProviderFactory->authVersionMinor() << "]";
      }
      reply.setType(Message::Type_Error);
      reply.setError(builder.str());
      qiLogVerbose() << builder.str();
      socket->send(reply);
      socket->disconnect();
      }
    }
    qiLogVerbose() << "Auth ends";
  }
Example #17
0
  // We received a ServiceInfo, and want to establish a connection
  void Session_Service::onServiceInfoResult(qi::Future<qi::ServiceInfo> result, long requestId, std::string protocol) {
    qiLogDebug() << "Got serviceinfo message";
    {
      boost::recursive_mutex::scoped_lock sl(_requestsMutex);
      ServiceRequest *sr = serviceRequest(requestId);
      if (!sr)
        return;
      if (result.hasError()) {
        sr->promise.setError(result.error());
        removeRequest(requestId);
        return;
      }
      const qi::ServiceInfo &si = result.value();
      sr->serviceId = si.serviceId();
      if (_sdClient->isLocal())
      { // Wait! If sd is local, we necessarily have an open socket
        // on which service was registered, whose lifetime is bound
        // to the service
        TransportSocketPtr s = _sdClient->_socketOfService(sr->serviceId);
        if (!s) // weird
          qiLogVerbose() << "_socketOfService returned 0";
        else
        {
          // check if the socket support that capability
          if (s->remoteCapability("ClientServerSocket", false))
          {
            qiLogVerbose() << "sd is local and service is capable, going through socketOfService";
            onTransportSocketResult(qi::Future<TransportSocketPtr>(s), requestId);
            return;
          }
        }
      }
      //empty serviceInfo
      if (!si.endpoints().size()) {
        std::stringstream ss;
        ss << "No endpoints returned for service:" << sr->name << " (id:" << sr->serviceId << ")";
        qiLogVerbose() << ss.str();
        sr->promise.setError(ss.str());
        removeRequest(requestId);
        return;
      }

      if (protocol != "")
      {
        std::vector<qi::Url>::const_iterator it = si.endpoints().begin();

        for (;
             it != si.endpoints().end() && it->protocol() != protocol;
             it++)
        {
          continue;
        }

        if (it == si.endpoints().end())
        {
          std::stringstream ss;
          ss << "No " << protocol << " endpoint available for service:" << sr->name << " (id:" << sr->serviceId << ")";
          qiLogVerbose() << ss.str();
          sr->promise.setError(ss.str());
        }
      }
    }
    qiLogDebug() << "Requesting socket from cache";
    qi::Future<qi::TransportSocketPtr> fut = _socketCache->socket(result.value(), protocol);
    fut.connect(&Session_Service::onTransportSocketResult, this, _1, requestId);
  }
Example #18
0
  qi::Future<AnyReference> RemoteObject::metaCall(AnyObject, unsigned int method, const qi::GenericFunctionParameters &in, MetaCallType callType, Signature returnSignature)
  {
    MetaMethod *mm = metaObject().method(method);
    if (!mm) {
      std::stringstream ss;
      ss << "Method " << method << " not found on service " << _service;
      return makeFutureError<AnyReference>(ss.str());
    }
    float canConvert = 1;
    if (returnSignature.isValid())
    {
      canConvert = mm->returnSignature().isConvertibleTo(returnSignature);
      qiLogDebug() << "return type conversion score: " << canConvert;
      if (canConvert == 0)
      {
        // last chance for dynamics and adventurous users
        canConvert = returnSignature.isConvertibleTo(mm->returnSignature());
        if (canConvert == 0)
          return makeFutureError<AnyReference>(
            "Call error: will not be able to convert return type from "
              + mm->returnSignature().toString()
              + " to " + returnSignature.toString());
        else
          qiLogVerbose() << "Return signature might be incorrect depending on the value, from "
            + mm->returnSignature().toString()
            + " to " + returnSignature.toString();
      }
    }
    /* The promise will be set:
     - From here in case of error
     - From a network callback, called asynchronously in thread pool
     So it is safe to use a sync promise.
     */
    qi::Promise<AnyReference> out(&PromiseNoop<AnyReference>, FutureCallbackType_Sync);
    qi::Message msg;
    TransportSocketPtr sock;
    // qiLogDebug() << this << " metacall " << msg.service() << " " << msg.function() <<" " << msg.id();
    {
      boost::mutex::scoped_lock lock(_socketMutex);
      boost::mutex::scoped_lock lock2(_promisesMutex);
      // Check socket while holding the lock to avoid a race with close()
      // where we would add a promise to the map after said map got cleared
      if (!_socket || !_socket->isConnected())
      {
        return makeFutureError<AnyReference>("Socket is not connected");
      }
      // The remote object can be concurrently closed / other operation that modifies _socket
      // (even set it to null). We store the current socket locally so that the behavior
      // of metacall stays consistent throughout the function's execution.
      sock = _socket;
      if (_promises.find(msg.id()) != _promises.end())
      {
        qiLogError() << "There is already a pending promise with id "
                                   << msg.id();
      }
      qiLogDebug() << "Adding promise id:" << msg.id();
      _promises[msg.id()] = out;
    }
    qi::Signature funcSig = mm->parametersSignature();
    try {
      msg.setValues(in, funcSig, this, sock.get());
    }
    catch(const std::exception& e)
    {
      qiLogVerbose() << "setValues exception: " << e.what();
      if (!sock->remoteCapability("MessageFlags", false))
        throw e;
      // Delegate conversion to the remote end.
      msg.addFlags(Message::TypeFlag_DynamicPayload);
      msg.setValues(in, "m", this, sock.get());
    }
    if (canConvert < 0.2)
    {
      msg.addFlags(Message::TypeFlag_ReturnType);
      msg.setValue(returnSignature.toString(), Signature("s"));
    }
    msg.setType(qi::Message::Type_Call);
    msg.setService(_service);
    msg.setObject(_object);
    msg.setFunction(method);

    //error will come back as a error message
    if (!sock->isConnected() || !sock->send(msg)) {
      qi::MetaMethod*   meth = metaObject().method(method);
      std::stringstream ss;
      if (meth) {
        ss << "Network error while sending data to method: '";
        ss << meth->toString();
        ss << "'.";
      } else {
        ss << "Network error while sending data an unknown method (id=" << method << ").";
      }
      if (!sock->isConnected()) {
        ss << " Socket is not connected.";
        qiLogVerbose() << ss.str();
      } else {
        qiLogError() << ss.str();
      }
      out.setError(ss.str());

      {
        boost::mutex::scoped_lock lock(_promisesMutex);
        qiLogDebug() << "Removing promise id:" << msg.id();
        _promises.erase(msg.id());
      }
    }
    else
      out.setOnCancel(qi::bind(&RemoteObject::onFutureCancelled, this, msg.id()));
    return out.future();
  }