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); }
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(); }
void Server::disconnectSignals(const TransportSocketPtr& socket, const SocketSubscriber& subscriber) { socket->connected.disconnectAll(); socket->disconnected.disconnect(subscriber.disconnected); socket->messageReady.disconnect(subscriber.messageReady); socket->disconnect(); }
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); }
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; }
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; } }
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(); }
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); }
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(); }
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"; }
// 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); }
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(); }