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); }
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); }
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); }
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 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 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); }
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"; }
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(); }