static MetaObject extractReturnedMetaObject(const Message& msg, TransportSocketPtr sock) { static const std::string retSig = "({I(Issss[(ss)<MetaMethodParameter,name,description>]s)<MetaMethod,uid,returnSignature,name," "parametersSignature,description,parameters,returnDescription>}{I(Iss)<MetaSignal,uid,name,signature>}{I(Iss)<" "MetaProperty,uid,name,signature>}s)<MetaObject,methods,signals,properties,description>"; AnyReference ref = msg.value((msg.flags() & Message::TypeFlag_DynamicPayload) ? "m" : retSig, sock); MetaObject obj = ref.to<MetaObject>(); ref.destroy(); return obj; }
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(); }
static bool isIntegerOrDouble(AnyReference value) { Signature signature(value.signature()); return signature.isConvertibleTo(Signature("i")) != 0.0 || signature.isConvertibleTo(Signature("d")) != 0.0; }
TEST(Value, DefaultMap) { // this one has tricky code and deserves a test) TypeInterface* dmt = TypeInterface::fromSignature(qi::Signature("{si}")); AnyValue val = AnyValue(AnyReference(dmt), false, true); ASSERT_EQ(0u, val.size()); val["foo"].update(12); ASSERT_EQ(1u, val.size()); ASSERT_EQ(12, val.find("foo").toInt()); ASSERT_EQ(0, val.find("bar").type()); val.insert(std::string("bar"), 13); ASSERT_EQ(13, val.element<int>("bar")); val.element<int>("foo") = 10; ASSERT_EQ(10, val.find("foo").toInt()); AnyIterator b = val.begin(); AnyIterator end = val.end(); qi::int64_t sum = 0; while (b != end) { AnyReference deref = *b; sum += deref[1].toInt(); ++b; } ASSERT_EQ(23, sum); AnyIterator b2 = val.begin(); AnyIterator end2 = val.end(); sum = 0; while (b2 != end2) { AnyReference deref = *b2; AnyIterator it = b2; sum += deref[1].toInt(); ASSERT_EQ(b2++, it); } ASSERT_EQ(23, sum); AnyReference valCopy = val.clone(); ASSERT_EQ(13, valCopy.element<int>("bar")); ASSERT_EQ(2u, valCopy.size()); // reset val, checks valCopy still works val.reset(); val = AnyValue::from(5); ASSERT_EQ(13, valCopy.element<int>("bar")); ASSERT_EQ(2u, valCopy.size()); valCopy.destroy(); }
TEST(Value, Basic) { AnyReference v; int twelve = 12; v = AutoAnyReference(twelve); ASSERT_TRUE(v.type() != 0); ASSERT_TRUE(v.rawValue() != 0); ASSERT_EQ(v.toInt(), 12); ASSERT_EQ(v.toFloat(), 12.0f); ASSERT_EQ(v.toDouble(), 12.0); double five = 5.0; v = AutoAnyReference(five); ASSERT_EQ(v.toInt(), 5); ASSERT_EQ(v.toFloat(), 5.0f); ASSERT_EQ(v.toDouble(), 5.0); v = AutoAnyReference("foo"); ASSERT_EQ("foo", v.toString()); }
TEST(Value, AnyObject) { { // GenericObject uses intrusive refcount and must be valid // Also object does some typechecking qi::DynamicObjectBuilder dynBuild; qi::AnyObject ori = dynBuild.object(¬hing); qi::GenericObject& go = *ori.asGenericObject(); AnyReference v = AnyReference::from(ori); qi::detail::ManagedObjectPtr mo = v.to<qi::detail::ManagedObjectPtr>(); ASSERT_TRUE(!!mo); ASSERT_EQ(mo.get(), &go); qi::AnyObject out = v.to<AnyObject>(); ASSERT_TRUE(out); ASSERT_TRUE(out == ori); out = v.toObject(); ASSERT_TRUE(out); ASSERT_EQ(out.asGenericObject(), mo.get()); } ASSERT_TRUE(triggered); }
bool ParameterModelPrivate::inInterval(AnyReference value, AnyReference min, AnyReference max) const { Signature signature(value.signature()); qiLogDebug() << "Enter in inInterval"; qiLogDebug() << "Signature of value is : " << signature.toString(); //if value is bool string or Resource return true if(signature.isConvertibleTo(Signature::fromType(Signature::Type_Bool)) == 1.0f || signature.isConvertibleTo(Signature::fromType(Signature::Type_String)) == 1.0f || signature.isConvertibleTo(ParameterModel::signatureRessource()) == 1.0f) return true; qiLogDebug() << "Type of value is double or integer"; //if value is not in [min, max] return false if(value < min || max < value) return false; return true; }
TEST(Value, list) { int one = 1; AnyReference v = AnyReference::from(one); v.set(5); ASSERT_ANY_THROW(v.set("foo")); ASSERT_ANY_THROW(v.set(std::vector<int>())); std::vector<int> vint; vint.push_back(12); v = AnyReference::from(vint); v.append(7); ASSERT_EQ(7, v[1].toInt()); ASSERT_EQ(7, v[1].toFloat()); ASSERT_EQ(7, v.element<int>(1)); v[1].setInt(8); ASSERT_EQ(8, v[1].toInt()); v.element<int>(1) = 9; ASSERT_EQ(9, v[1].toInt()); ASSERT_ANY_THROW(v.element<double>(1)); // wrong type ASSERT_ANY_THROW(v.element<int>(17)); // out of bound EXPECT_EQ(v.as<std::vector<int> >().size(), v.size()); }
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 GwObjectHost::harvestServiceOriginatingObjects(Message& msg, TransportSocketPtr sender) { Signature signature; { boost::upgrade_lock<boost::shared_mutex> lock(_mutex); MetaObject* metaObject = NULL; const Signature& (MetaMethod::*signatureGetter)() const = NULL; if (msg.type() == Message::Type_Reply || msg.type() == Message::Type_Error) { std::map<ServiceId, std::map<ObjectId, MetaObject> >::iterator sit = _servicesMetaObjects.find(msg.service()); if (msg.function() == Message::BoundObjectFunction_MetaObject && sit->second.find(msg.object()) == sit->second.end()) { boost::upgrade_to_unique_lock<boost::shared_mutex> unique_lock(lock); _servicesMetaObjects[msg.service()][Message::GenericObject_Main] = extractReturnedMetaObject(msg, sender); return; } metaObject = &_servicesMetaObjects[msg.service()][msg.object()]; signatureGetter = &MetaMethod::returnSignature; } else if (msg.type() == Message::Type_Call || msg.type() == Message::Type_Post) { // if a service does a CALL, he does so on a user-supplied object. std::map<GwObjectId, MetaObject>::iterator mit = _objectsMetaObjects.find(msg.object()); assert(mit != _objectsMetaObjects.end()); metaObject = &mit->second; signatureGetter = &MetaMethod::parametersSignature; } const MetaMethod* method = metaObject->method(msg.function()); if (!method) return; signature = (method->*signatureGetter)(); } if (!hasObjectsSomewhere(signature)) { // no object can be here return; } AnyReference passed = msg.value(signature, sender); StreamContext filler; MockObjectHost host(Message::Service_Server); Message dummy; // we don't want to pollute the original message and potentially change valid id // of contained objects, so we do it in an unrelated message. dummy.setValue(passed, signature, &host, &filler); const ObjectHost::ObjectMap& objects = host.objects(); std::map<ObjectId, MetaObject> newServicesMetaObject; for (ObjectHost::ObjectMap::const_iterator it = objects.begin(), end = objects.end(); it != end; ++it) { ServiceBoundObject* sbo = static_cast<ServiceBoundObject*>(it->second.get()); RemoteObject* ro = static_cast<RemoteObject*>(sbo->object().asGenericObject()->value); // 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. ro->setTransportSocket(TransportSocketPtr()); newServicesMetaObject[ro->object()] = ro->metaObject(); } { boost::upgrade_lock<boost::shared_mutex> lock(_mutex); boost::upgrade_to_unique_lock<boost::shared_mutex> unique_lock(lock); _servicesMetaObjects[msg.service()].insert(newServicesMetaObject.begin(), newServicesMetaObject.end()); } passed.destroy(); }
TEST(Value, list_at) { std::vector<int> v{2, 5, 7}; AnyReference list = AnyReference::from(v); const AnyReference listc = AnyReference::from(v); // at(T) { AnyReference val = list.at(1); EXPECT_EQ(5, val.toInt()); AnyReference valInvalid = list.at(4); EXPECT_EQ(nullptr, valInvalid.type()); } // at(T) const { const AnyReference val = listc.at(1); EXPECT_EQ(5, val.toInt()); const AnyReference valInvalid = listc.at(4); EXPECT_EQ(nullptr, valInvalid.type()); } }
TEST(Value, Map_at) { std::map<std::string, double> map; map["foo"] = 1; map["bar"] = 2; AutoAnyReference v(map); const AutoAnyReference vc(map); // at(T) { AnyReference val1 = v.at("foo"); EXPECT_EQ(1, val1.toInt()); AnyReference valInvalid = v.at("nokey"); EXPECT_EQ(nullptr, valInvalid.type()); } // at(T) const { const AnyReference val1 = vc.at("bar"); EXPECT_EQ(2, val1.toInt()); const AnyReference valInvalid = vc.at("nokey"); EXPECT_EQ(nullptr, valInvalid.type()); } // at(AnyReference) { AnyReference val1 = v.at(AnyReference::from("foo")); EXPECT_EQ(1, val1.toInt()); AnyReference valInvalid = v.at(AnyReference::from("nokey")); EXPECT_EQ(nullptr, valInvalid.type()); } // at(AnyReference) const { const AnyReference val1 = vc.at(AnyReference::from("bar")); EXPECT_EQ(2, val1.toInt()); const AnyReference valInvalid = vc.at(AnyReference::from("nokey")); EXPECT_EQ(nullptr, valInvalid.type()); } }
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"; }
//should be done in the object thread void RemoteObject::onMessagePending(const qi::Message &msg) { qiLogDebug() << this << "(" << _service << '/' << _object << ") msg " << msg.address() << " " << msg.buffer().size(); if (msg.object() != _object) { qiLogDebug() << "Passing message " << msg.address() << " to host " ; { boost::mutex::scoped_lock lock(_socketMutex); if(_socket) ObjectHost::onMessage(msg, _socket); return; } } if (msg.type() == qi::Message::Type_Event) { SignalBase* sb = signal(msg.event()); if (sb) { try { // Signal associated with properties have incorrect signature, // Trust MetaObject. //std::string sig = sb->signature(); const MetaSignal* ms = _self.metaObject().signal(msg.event()); qi::Signature sig = ms->parametersSignature(); // Remove top-level tuple //sig = sig.substr(1, sig.length()-2); //TODO: Optimise AnyReference value = msg.value((msg.flags()&Message::TypeFlag_DynamicPayload)? "m":sig, _socket); { GenericFunctionParameters args; if (sig == "m") args = value.content().asTupleValuePtr(); else args = value.asTupleValuePtr(); qiLogDebug() << "Triggering local event listeners with args : " << args.size(); sb->trigger(args); } value.destroy(); } catch (const std::exception& e) { qiLogWarning() << "Deserialize error on event: " << e.what(); } } else { qiLogWarning() << "Event message on unknown signal " << msg.event(); qiLogDebug() << metaObject().signalMap().size(); } return; } if (msg.type() != qi::Message::Type_Reply && msg.type() != qi::Message::Type_Error && msg.type() != qi::Message::Type_Canceled) { qiLogError() << "Message " << msg.address() << " type not handled: " << msg.type(); return; } qi::Promise<AnyReference> promise; { boost::mutex::scoped_lock lock(_promisesMutex); std::map<int, qi::Promise<AnyReference> >::iterator it; it = _promises.find(msg.id()); if (it != _promises.end()) { promise = _promises[msg.id()]; _promises.erase(it); qiLogDebug() << "Handling promise id:" << msg.id(); } else { qiLogError() << "no promise found for req id:" << msg.id() << " obj: " << msg.service() << " func: " << msg.function() << " type: " << Message::typeToString(msg.type()); return; } } switch (msg.type()) { case qi::Message::Type_Canceled: { qiLogDebug() << "Message " << msg.address() << " has been cancelled."; promise.setCanceled(); return; } case qi::Message::Type_Reply: { // Get call signature MetaMethod* mm = metaObject().method(msg.function()); if (!mm) { qiLogError() << "Result for unknown function " << msg.function(); promise.setError("Result for unknown function"); return; } try { qi::AnyReference val = msg.value( (msg.flags() & Message::TypeFlag_DynamicPayload) ? "m" : mm->returnSignature(), _socket); promise.setValue(val); } catch (std::runtime_error &err) { promise.setError(err.what()); } qiLogDebug() << "Message " << msg.address() << " passed to promise"; return; } case qi::Message::Type_Error: { try { static std::string sigerr("m"); qi::AnyReference gvp = msg.value(sigerr, _socket).content(); std::string err = gvp.asString(); qiLogVerbose() << "Received error message" << msg.address() << ":" << err; promise.setError(err); } catch (std::runtime_error &e) { //houston we have an error about the error.. promise.setError(e.what()); } return; } default: //not possible return; } }
void visitDynamic(AnyReference pointee) { if (pointee.isValid()) { serialize(pointee, out); } }
void visitUnknown(AnyReference v) { qiLogError() << "JSON Error: Type " << v.type()->infoString() <<" not serializable"; out << "\"Error: no serialization for unknown type:" << v.type()->infoString() << "\""; }