void wamp_broker::process_publish_message(const wamp_session_id& session_id, wamp_publish_message* publish_message) { auto session_itr = m_sessions.find(session_id); if (session_itr == m_sessions.end()) { throw std::logic_error("broker session does not exist"); } BONEFISH_TRACE("%1%, %2%", *session_itr->second % *publish_message); const std::string topic = publish_message->get_topic(); const wamp_publication_id publication_id = m_publication_id_generator.generate(); // Since a publish message fans out to potentially numerous event messages // we cannot need to be a bit smarter with how we deal with passing zone // ownership. For all but the last subscription we make a deep copy of the // transient fields for the event messages. When we reach the last subscription // it is then safe to just pass ownership of the zone. auto topic_subscriptions_itr = m_topic_subscriptions.find(topic); if (topic_subscriptions_itr != m_topic_subscriptions.end()) { const auto& subscriptions = topic_subscriptions_itr->second->get_subscriptions(); std::size_t num_subscriptions = subscriptions.size(); std::size_t current_subscription = 0; for (const auto& subscription : subscriptions) { const auto& subscription_id = subscription.first; const auto& session = subscription.second; std::unique_ptr<wamp_event_message> event_message; if (++current_subscription < num_subscriptions) { event_message.reset(new wamp_event_message()); event_message->set_subscription_id(subscription_id); event_message->set_publication_id(publication_id); event_message->set_arguments( msgpack::object(publish_message->get_arguments(), event_message->get_zone())); event_message->set_arguments_kw( msgpack::object(publish_message->get_arguments_kw(), event_message->get_zone())); } else { event_message.reset(new wamp_event_message(std::move(publish_message->release_zone()))); event_message->set_subscription_id(subscription_id); event_message->set_publication_id(publication_id); event_message->set_arguments(publish_message->get_arguments()); event_message->set_arguments_kw(publish_message->get_arguments_kw()); } BONEFISH_TRACE("%1%, %2%", *session % *event_message); session->get_transport()->send_message(std::move(*event_message)); } } // TODO: Publish acknowledgements require support for publish options which // we currently do not yet have working. // //std::unique_ptr<wamp_published_message> published_message(new wamp_published_message); //published_message->set_request_id(publish_message->get_request_id()); //published_message->set_publication_id(publication_id); //session_itr->second->get_transport()->send_message(published_message.get()); }
void wamp_dealer::process_register_message(const wamp_session_id& session_id, wamp_register_message* register_message) { auto session_itr = m_sessions.find(session_id); if (session_itr == m_sessions.end()) { throw std::logic_error("dealer session does not exist"); } BONEFISH_TRACE("%1%, %2%", *session_itr->second % *register_message); // If the session registering the procedure does not support the callee // role than do not allow the call to be processed and send an error. if (!session_itr->second->get_role(wamp_role_type::CALLEE)) { send_error(session_itr->second->get_transport(), register_message->get_type(), register_message->get_request_id(), "wamp.error.role_violation"); return; } const auto procedure = register_message->get_procedure(); if (!is_valid_uri(procedure)) { send_error(session_itr->second->get_transport(), register_message->get_type(), register_message->get_request_id(), "wamp.error.invalid_uri"); return; } auto procedure_registrations_itr = m_procedure_registrations.find(procedure); if (procedure_registrations_itr != m_procedure_registrations.end()) { send_error(session_itr->second->get_transport(), register_message->get_type(), register_message->get_request_id(), "wamp.error.procedure_already_exists"); return; } const wamp_registration_id registration_id = m_registration_id_generator.generate(); std::unique_ptr<wamp_dealer_registration> dealer_registration( new wamp_dealer_registration(session_itr->second, registration_id)); m_procedure_registrations[procedure] = std::move(dealer_registration); m_session_registrations[session_id].insert(registration_id); m_registered_procedures[registration_id] = procedure; std::unique_ptr<wamp_registered_message> registered_message( new wamp_registered_message(register_message->release_zone())); registered_message->set_request_id(register_message->get_request_id()); registered_message->set_registration_id(registration_id); // If we fail to send the registered message it is most likely that // the underlying network connection has been closed/lost which means // that the callee is no longer reachable on this session. So all we // do here is trace the fact that this event occured. BONEFISH_TRACE("%1%, %2%", *session_itr->second % *registered_message); if (!session_itr->second->get_transport()->send_message(std::move(*registered_message))) { BONEFISH_TRACE("failed to send registered message to caller: network failure"); } }
void wamp_broker::process_unsubscribe_message(const wamp_session_id& session_id, wamp_unsubscribe_message* unsubscribe_message) { auto session_itr = m_sessions.find(session_id); if (session_itr == m_sessions.end()) { throw std::logic_error("broker session does not exist"); } BONEFISH_TRACE("%1%, %2%", *session_itr->second % *unsubscribe_message); auto session_subscriptions_itr = m_session_subscriptions.find(session_id); if (session_subscriptions_itr == m_session_subscriptions.end()) { send_error(session_itr->second->get_transport(), unsubscribe_message->get_type(), unsubscribe_message->get_request_id(), std::string("wamp.error.no_subscriptions_for_session")); return; } const wamp_subscription_id& subscription_id = unsubscribe_message->get_subscription_id(); if (session_subscriptions_itr->second.erase(subscription_id) == 0) { send_error(session_itr->second->get_transport(), unsubscribe_message->get_type(), unsubscribe_message->get_request_id(), std::string("wamp.error.no_such_subscription")); return; } auto subscription_topics_itr = m_subscription_topics.find(subscription_id); if (subscription_topics_itr == m_subscription_topics.end()) { BONEFISH_TRACE("error: broker subscription topics are out of sync"); } else { std::string topic = subscription_topics_itr->second->get_topic(); subscription_topics_itr->second->remove_session(session_itr->second); if (subscription_topics_itr->second->get_sessions().size() == 0) { m_subscription_topics.erase(subscription_id); } auto topic_subscriptions_itr = m_topic_subscriptions.find(topic); if (topic_subscriptions_itr == m_topic_subscriptions.end()) { BONEFISH_TRACE("error: broker topic subscription out of sync"); } else { topic_subscriptions_itr->second->remove_subscription(subscription_id); if (topic_subscriptions_itr->second->get_subscriptions().size() == 0) { m_topic_subscriptions.erase(topic); } } } std::unique_ptr<wamp_unsubscribed_message> unsubscribed_message( new wamp_unsubscribed_message(std::move(unsubscribe_message->release_zone()))); unsubscribed_message->set_request_id(unsubscribe_message->get_request_id()); BONEFISH_TRACE("%1%, %2%", *session_itr->second % *unsubscribed_message); session_itr->second->get_transport()->send_message(std::move(*unsubscribed_message)); }
void wamp_dealer::send_error(const std::unique_ptr<wamp_transport>& transport, const wamp_message_type request_type, const wamp_request_id& request_id, const std::string& error) const { std::unique_ptr<wamp_error_message> error_message(new wamp_error_message); error_message->set_request_type(request_type); error_message->set_request_id(request_id); error_message->set_error(error); BONEFISH_TRACE("%1%", *error_message); if (!transport->send_message(std::move(*error_message))) { BONEFISH_TRACE("failed to send error message"); } }
void wamp_dealer::process_error_message(const wamp_session_id& session_id, wamp_error_message* error_message) { auto session_itr = m_sessions.find(session_id); if (session_itr == m_sessions.end()) { throw std::logic_error("dealer session does not exist"); } BONEFISH_TRACE("%1%, %2%", *session_itr->second % *error_message); const auto request_id = error_message->get_request_id(); auto pending_invocations_itr = m_pending_invocations.find(request_id); if (pending_invocations_itr == m_pending_invocations.end()) { // This is a mormal condition. It means that the caller has ended its // session after issuing a call. There is nothing to report to the // callee in this case so we just silently drop the message. BONEFISH_TRACE("unable to find invocation ... dropping error message"); return; } const auto& dealer_invocation = pending_invocations_itr->second; std::unique_ptr<wamp_error_message> caller_error_message( new wamp_error_message(error_message->release_zone())); caller_error_message->set_request_type(wamp_message_type::CALL); caller_error_message->set_request_id(dealer_invocation->get_request_id()); caller_error_message->set_details(error_message->get_details()); caller_error_message->set_error(error_message->get_error()); caller_error_message->set_arguments(error_message->get_arguments()); caller_error_message->set_arguments_kw(error_message->get_arguments_kw()); BONEFISH_TRACE("%1%, %2%", *session_itr->second % *caller_error_message); std::shared_ptr<wamp_session> session = dealer_invocation->get_session(); if (!session->get_transport()->send_message(std::move(*caller_error_message))) { // There is no error message to propogate in this case as this error // message was initiated by the callee and sending the callee an error // message in response to an error message would not make any sense. // Besides, the callers session has ended. BONEFISH_TRACE("failed to send error message to caller: network failure"); } // The failure to send a message in the event of a network failure // will detach the session. When this happens the pending invocations // will be cleaned up. So we don't use an iterator here to erase the // pending invocation because it may have just been invalidated above. m_pending_callee_invocations[session_id].erase(request_id); m_pending_caller_invocations[session->get_session_id()].erase(request_id); m_pending_invocations.erase(request_id); }
void native_server_impl::shutdown() { BONEFISH_TRACE("stopping native server"); assert(m_connector); m_connector.reset(); // FIXME: Walk all of the connections and disconnect them? }
inline void rawsocket_connection::handle_system_error(const boost::system::error_code& error_code) { // NOTE: The boost documentation does not indicate what all of the possible error // codes are that can occur for the async receive handlers. So it will be an // ongoing exercise in trying to figure this out. if (error_code == boost::asio::error::eof) { BONEFISH_TRACE("connection closed: %1%", error_code); const auto& close_handler = get_close_handler(); close_handler(shared_from_this()); } else if (error_code != boost::asio::error::operation_aborted) { BONEFISH_TRACE("connection failed: %1%", error_code); const auto& fail_handler = get_fail_handler(); fail_handler(shared_from_this(), error_code.message().c_str()); } else { BONEFISH_TRACE("unhandled system error: %1%", error_code); assert(0); } }
void wamp_broker::attach_session(const std::shared_ptr<wamp_session>& session) { BONEFISH_TRACE("attach session: %1%", *session); auto result = m_sessions.insert( std::make_pair(session->get_session_id(), std::move(session))); if (!result.second) { throw std::logic_error("broker session already registered"); } }
void wamp_dealer::attach_session(const std::shared_ptr<wamp_session>& session) { assert(session->get_role(wamp_role_type::CALLER) || session->get_role(wamp_role_type::CALLEE)); BONEFISH_TRACE("attach session: %1%", *session); auto result = m_sessions.insert(std::make_pair(session->get_session_id(), session)); if (!result.second) { throw std::logic_error("dealer session already registered"); } }
bool websocket_transport::send_message(wamp_message&& message) { BONEFISH_TRACE("sending message: %1%", message_type_to_string(message.get_type())); expandable_buffer buffer = m_serializer->serialize(message); auto opcode = (m_serializer->get_type() == wamp_serializer_type::JSON) ? websocketpp::frame::opcode::TEXT : websocketpp::frame::opcode::BINARY; m_server->send(m_handle, buffer.data(), buffer.size(), opcode); return true; }
void wamp_broker::process_subscribe_message(const wamp_session_id& session_id, wamp_subscribe_message* subscribe_message) { auto session_itr = m_sessions.find(session_id); if (session_itr == m_sessions.end()) { throw std::logic_error("broker session does not exist"); } BONEFISH_TRACE("%1%, %2%", *session_itr->second % *subscribe_message); wamp_subscription_id subscription_id = m_subscription_id_generator.generate(); auto& session = session_itr->second; { auto result = m_topic_subscriptions.insert( std::make_pair(subscribe_message->get_topic(), nullptr)); if (result.second) { result.first->second.reset(new wamp_broker_subscriptions()); } result.first->second->add_subscription(subscription_id, session); } { auto result = m_subscription_topics.insert(std::make_pair(subscription_id, nullptr)); if (result.second) { result.first->second.reset(new wamp_broker_topic(subscribe_message->get_topic())); result.first->second->add_session(session); } else { result.first->second->add_session(session); } } m_session_subscriptions[session_id].insert(subscription_id); std::unique_ptr<wamp_subscribed_message> subscribed_message( new wamp_subscribed_message(std::move(subscribe_message->release_zone()))); subscribed_message->set_request_id(subscribe_message->get_request_id()); subscribed_message->set_subscription_id(subscription_id); BONEFISH_TRACE("%1%, %2%", *session % *subscribed_message); session->get_transport()->send_message(std::move(*subscribed_message)); }
void wamp_dealer::process_unregister_message(const wamp_session_id& session_id, const wamp_unregister_message* unregister_message) { auto session_itr = m_sessions.find(session_id); if (session_itr == m_sessions.end()) { throw std::logic_error("dealer session does not exist"); } BONEFISH_TRACE("%1%, %2%", *session_itr->second % *unregister_message); auto session_registrations_itr = m_session_registrations.find(session_id); if (session_registrations_itr == m_session_registrations.end()) { send_error(session_itr->second->get_transport(), unregister_message->get_type(), unregister_message->get_request_id(), "wamp.error.no_such_registration"); return; } auto& registrations = session_registrations_itr->second; auto registrations_itr = registrations.find(unregister_message->get_registration_id()); if (registrations_itr == registrations.end()) { BONEFISH_TRACE("error: dealer session registration id does not exist"); send_error(session_itr->second->get_transport(), unregister_message->get_type(), unregister_message->get_request_id(), "wamp.error.no_such_registration"); return; } auto registered_procedures_itr = m_registered_procedures.find(*registrations_itr); if (registered_procedures_itr == m_registered_procedures.end()) { BONEFISH_TRACE("error: dealer registered procedures out of sync"); send_error(session_itr->second->get_transport(), unregister_message->get_type(), unregister_message->get_request_id(), "wamp.error.no_such_registration"); return; } auto procedure_registrations_itr = m_procedure_registrations.find(registered_procedures_itr->second); if (procedure_registrations_itr == m_procedure_registrations.end()) { BONEFISH_TRACE("error: dealer procedure registrations out of sync"); send_error(session_itr->second->get_transport(), unregister_message->get_type(), unregister_message->get_request_id(), "wamp.error.no_such_registration"); return; } m_procedure_registrations.erase(procedure_registrations_itr); m_registered_procedures.erase(registered_procedures_itr); registrations.erase(registrations_itr); std::unique_ptr<wamp_unregistered_message> unregistered_message(new wamp_unregistered_message); unregistered_message->set_request_id(unregister_message->get_request_id()); // If we fail to send the unregistered message it is most likely that // the underlying network connection has been closed/lost which means // that the callee is no longer reachable on this session. So all we // do here is trace the fact that this event occured. BONEFISH_TRACE("%1%, %2%", *session_itr->second % *unregistered_message); if (!session_itr->second->get_transport()->send_message(std::move(*unregistered_message))) { BONEFISH_TRACE("failed to send unregistered message to caller: network failure"); } }
void wamp_dealer::invocation_timeout_handler(const wamp_request_id& request_id, const boost::system::error_code& error) { if (error == boost::asio::error::operation_aborted) { return; } auto pending_invocations_itr = m_pending_invocations.find(request_id); if (pending_invocations_itr == m_pending_invocations.end()) { BONEFISH_TRACE("error: unable to find pending invocation"); return; } BONEFISH_TRACE("timing out a pending invocation"); const auto& call_request_id = pending_invocations_itr->second->get_request_id(); std::shared_ptr<wamp_session> session = pending_invocations_itr->second->get_session(); send_error(session->get_transport(), wamp_message_type::CALL, call_request_id, "wamp.error.call_timed_out"); auto pending_callee_invocations_itr = m_pending_callee_invocations.find(session->get_session_id()); if (pending_callee_invocations_itr != m_pending_callee_invocations.end()) { pending_callee_invocations_itr->second.erase(request_id); } auto pending_caller_invocations_itr = m_pending_caller_invocations.find(session->get_session_id()); if (pending_caller_invocations_itr != m_pending_caller_invocations.end()) { pending_caller_invocations_itr->second.erase(request_id); } // The failure to send a message in the event of a network failure // will detach the session. When this happens the pending invocations // be cleaned up. So we don't use an iterator here to erase the pending // invocation because it may have just been invalidated above. m_pending_invocations.erase(request_id); }
void native_server_impl::on_message( const std::shared_ptr<native_connection>& connection, const std::vector<msgpack::object>& fields, msgpack::zone&& zone) { try { if (fields.size() < 1) { throw std::runtime_error("invalid message"); } auto type = static_cast<wamp_message_type>(fields[0].as<unsigned>()); std::unique_ptr<wamp_message> message(wamp_message_factory::create_message(type)); if (!message) { throw std::runtime_error("message type not supported"); } message->unmarshal(fields, std::move(zone)); std::unique_ptr<wamp_transport> transport(new native_transport(connection)); m_message_processor.process_message(message, std::move(transport), connection.get()); } catch (const std::exception& e) { BONEFISH_TRACE("unhandled exception: %1%", e.what()); } }
inline void rawsocket_connection::receive_message_header_handler( const boost::system::error_code& error_code, size_t bytes_transferred) { if (error_code) { handle_system_error(error_code); return; } // Convert the message length to host order for convenience. uint32_t message_length = ntohl(m_message_length); // We cannot be guaranteed that a client implementation won't accidentally // introduce this protocol violation. In the event that we ever encounter // a message that reports a zero length we fail that connection gracefully. static const uint32_t MAX_MESSAGE_LENGTH = 16*1024*1024; // 16MB if (message_length == 0 || message_length > MAX_MESSAGE_LENGTH) { BONEFISH_TRACE("invalid message length: %1%", message_length); const auto& fail_handler = get_fail_handler(); fail_handler(shared_from_this(), "invalid message length"); return; } m_message_buffer.reserve(message_length); std::weak_ptr<rawsocket_connection> weak_self = std::static_pointer_cast<rawsocket_connection>(shared_from_this()); auto handler = [weak_self]( const boost::system::error_code& error_code, size_t bytes_transferred) { auto shared_self = weak_self.lock(); if (shared_self) { shared_self->receive_message_body_handler(error_code, bytes_transferred); } }; async_read(m_message_buffer.data(), message_length, handler); }
void wamp_broker::detach_session(const wamp_session_id& session_id) { auto session_itr = m_sessions.find(session_id); if (session_itr == m_sessions.end()) { throw std::logic_error("broker session does not exist"); } BONEFISH_TRACE("detach session: %1%", session_itr->second.get()); auto session_subscriptions_itr = m_session_subscriptions.find(session_id); if (session_subscriptions_itr != m_session_subscriptions.end()) { BONEFISH_TRACE("cleaning up session subscriptions"); for (const auto& subscription_id : session_subscriptions_itr->second) { auto subscription_topics_itr = m_subscription_topics.find(subscription_id); if (subscription_topics_itr == m_subscription_topics.end()) { BONEFISH_TRACE("error: broker subscription topics are out of sync"); continue; } std::string topic = subscription_topics_itr->second->get_topic(); BONEFISH_TRACE("cleaning up subscription topic"); subscription_topics_itr->second->remove_session(session_itr->second); if (subscription_topics_itr->second->get_sessions().size() == 0) { m_subscription_topics.erase(subscription_id); } auto topic_subscriptions_itr = m_topic_subscriptions.find(topic); if (topic_subscriptions_itr == m_topic_subscriptions.end()) { BONEFISH_TRACE("error: broker topic subscriptions are out of sync"); continue; } BONEFISH_TRACE("cleaning up topic subscriptions"); topic_subscriptions_itr->second->remove_subscription(subscription_id); if (topic_subscriptions_itr->second->get_subscriptions().size() == 0) { m_topic_subscriptions.erase(topic); } } m_session_subscriptions.erase(session_subscriptions_itr); } m_sessions.erase(session_itr); }
void wamp_dealer::detach_session(const wamp_session_id& session_id) { auto session_itr = m_sessions.find(session_id); if (session_itr == m_sessions.end()) { throw std::logic_error("dealer session does not exist"); } assert(session_itr->second->get_role(wamp_role_type::CALLER) || session_itr->second->get_role(wamp_role_type::CALLEE)); BONEFISH_TRACE("detach session: %1%", *session_itr->second); // Cleanup all of the procedures that were registered by the session. // No messages need to be sent here it is strictly just cleaning up any // state left behind by a session. BONEFISH_TRACE("cleaning up session registrations"); auto session_registrations_itr = m_session_registrations.find(session_id); if (session_registrations_itr != m_session_registrations.end()) { auto& registration_ids = session_registrations_itr->second; for (const auto& registration_id : registration_ids) { auto registered_procedures_itr = m_registered_procedures.find(registration_id); if (registered_procedures_itr == m_registered_procedures.end()) { throw std::logic_error("dealer registered procedures out of sync"); } auto procedure_registrations_itr = m_procedure_registrations.find(registered_procedures_itr->second); if (procedure_registrations_itr == m_procedure_registrations.end()) { throw std::logic_error("dealer procedure registrations out of sync"); } BONEFISH_TRACE("removing registration: %1%, procedure %2%", *session_itr->second % procedure_registrations_itr->first); m_procedure_registrations.erase(procedure_registrations_itr); m_registered_procedures.erase(registered_procedures_itr); } m_session_registrations.erase(session_registrations_itr); } // Cleanup any pending caller invocations associated with the session. // Since the session would be the caller we do not have to send any // messages here. Just simply cleanup any pending invocations. BONEFISH_TRACE("cleaning up pending caller invocations"); auto pending_caller_invocations_itr = m_pending_caller_invocations.find(session_id); if (pending_caller_invocations_itr != m_pending_caller_invocations.end()) { for (const auto& request_id : pending_caller_invocations_itr->second) { auto pending_invocations_itr = m_pending_invocations.find(request_id); if (pending_invocations_itr != m_pending_invocations.end()) { const auto& dealer_invocation = pending_invocations_itr->second; std::shared_ptr<wamp_session> session = dealer_invocation->get_session(); BONEFISH_TRACE("cleaning up pending caller invocation: %1%, request_id %2%", *session % request_id); m_pending_invocations.erase(pending_invocations_itr); } } } // Cleanup any pending callee invocations associated with the session. // Since the session would be the callee we need to send an error response // to the caller(s) and cleanup the pending invocations. BONEFISH_TRACE("cleaning up pending callee invocations"); auto pending_callee_invocations_itr = m_pending_callee_invocations.find(session_id); if (pending_callee_invocations_itr != m_pending_callee_invocations.end()) { for (const auto& request_id : pending_callee_invocations_itr->second) { auto pending_invocations_itr = m_pending_invocations.find(request_id); if (pending_invocations_itr != m_pending_invocations.end()) { const auto& dealer_invocation = pending_invocations_itr->second; std::shared_ptr<wamp_session> session = dealer_invocation->get_session(); BONEFISH_TRACE("cleaning up pending callee invocation: %1%, request_id %2%", *session, request_id); send_error(session->get_transport(), wamp_message_type::CALL, dealer_invocation->get_request_id(), "wamp.error.callee_session_closed"); m_pending_invocations.erase(pending_invocations_itr); } } } m_sessions.erase(session_itr); }
void native_server_impl::start() { BONEFISH_TRACE("starting native server"); assert(!m_connector); m_connector = std::make_shared<native_connector>(); std::weak_ptr<native_server_impl> weak_this = shared_from_this(); auto connect_handler = [this, weak_this]( const std::shared_ptr<native_endpoint>& component_endpoint) { auto connected = std::make_shared<native_endpoint_promise>(); auto shared_this = weak_this.lock(); if (!shared_this) { connected->set_exception(boost::copy_exception(std::runtime_error("connect failed"))); return connected->get_future(); } m_io_service.post([this, weak_this, connected, component_endpoint] () { auto shared_this = weak_this.lock(); if (!shared_this) { connected->set_exception(boost::copy_exception(std::runtime_error("server shutdown"))); return; } try { auto server_endpoint = on_connect(component_endpoint); connected->set_value(server_endpoint); } catch (...) { connected->set_exception(boost::current_exception()); } }); return connected->get_future(); }; m_connector->set_connect_handler(connect_handler); auto disconnect_handler = [this, weak_this]( const std::shared_ptr<native_endpoint>& server_endpoint) { auto disconnected = std::make_shared<boost::promise<void>>(); auto shared_this = weak_this.lock(); if (shared_this) { disconnected->set_exception(boost::copy_exception( std::runtime_error("disconnect failed"))); return disconnected->get_future(); } m_io_service.post([this, weak_this, disconnected, server_endpoint] () { auto shared_this = weak_this.lock(); if (!shared_this) { disconnected->set_exception(boost::copy_exception(std::runtime_error("server shutdown"))); return; } try { on_disconnect(server_endpoint); disconnected->set_value(); } catch (...) { disconnected->set_exception(boost::current_exception()); } }); return disconnected->get_future(); }; m_connector->set_disconnect_handler(disconnect_handler); }
void wamp_dealer::process_call_message(const wamp_session_id& session_id, wamp_call_message* call_message) { auto session_itr = m_sessions.find(session_id); if (session_itr == m_sessions.end()) { throw std::logic_error("dealer session does not exist"); } BONEFISH_TRACE("%1%, %2%", *session_itr->second % *call_message); // If the session placing the call does not support the caller role // than do not allow the call to be processed and send an error. if (!session_itr->second->get_role(wamp_role_type::CALLER)) { send_error(session_itr->second->get_transport(), call_message->get_type(), call_message->get_request_id(), "wamp.error.role_violation"); return; } const auto procedure = call_message->get_procedure(); if (!is_valid_uri(procedure)) { send_error(session_itr->second->get_transport(), call_message->get_type(), call_message->get_request_id(), "wamp.error.invalid_uri"); return; } auto procedure_registrations_itr = m_procedure_registrations.find(procedure); if (procedure_registrations_itr == m_procedure_registrations.end()) { send_error(session_itr->second->get_transport(), call_message->get_type(), call_message->get_request_id(), "wamp.error.no_such_procedure"); return; } std::shared_ptr<wamp_session> session = procedure_registrations_itr->second->get_session(); const wamp_request_id request_id = m_request_id_generator.generate(); const wamp_registration_id& registration_id = procedure_registrations_itr->second->get_registration_id(); std::unique_ptr<wamp_invocation_message> invocation_message( new wamp_invocation_message(std::move(call_message->release_zone()))); invocation_message->set_request_id(request_id); invocation_message->set_registration_id(registration_id); invocation_message->set_arguments(call_message->get_arguments()); invocation_message->set_arguments_kw(call_message->get_arguments_kw()); BONEFISH_TRACE("%1%, %2%", *session_itr->second % *invocation_message); if (!session->get_transport()->send_message(std::move(*invocation_message))) { BONEFISH_TRACE("sending invocation message to callee failed: network failure"); send_error(session_itr->second->get_transport(), call_message->get_type(), call_message->get_request_id(), "wamp.error.network_failure"); return; } else { wamp_call_options options; options.unmarshal(call_message->get_options()); unsigned timeout_ms = options.get_option_or<unsigned>("timeout", 0); // We only setup the invocation state after sending the message is successful. // This saves us from having to cleanup any state if the send fails. std::unique_ptr<wamp_dealer_invocation> dealer_invocation( new wamp_dealer_invocation(m_io_service)); dealer_invocation->set_session(session_itr->second); dealer_invocation->set_request_id(call_message->get_request_id()); dealer_invocation->set_timeout( std::bind(&wamp_dealer::invocation_timeout_handler, this, request_id, std::placeholders::_1), timeout_ms); m_pending_invocations.insert(std::make_pair(request_id, std::move(dealer_invocation))); m_pending_callee_invocations[session->get_session_id()].insert(request_id); m_pending_caller_invocations[session_id].insert(request_id); } }
void wamp_dealer::process_call_message(const wamp_session_id& session_id, wamp_call_message* call_message) { auto session_itr = m_sessions.find(session_id); if (session_itr == m_sessions.end()) { throw std::logic_error("dealer session does not exist"); } BONEFISH_TRACE("%1%, %2%", *session_itr->second % *call_message); // If the session placing the call does not support the caller role // than do not allow the call to be processed and send an error. if (!session_itr->second->get_role(wamp_role_type::CALLER)) { send_error(session_itr->second->get_transport(), call_message->get_type(), call_message->get_request_id(), "wamp.error.role_violation"); return; } const auto procedure = call_message->get_procedure(); if (!is_valid_uri(procedure)) { send_error(session_itr->second->get_transport(), call_message->get_type(), call_message->get_request_id(), "wamp.error.invalid_uri"); return; } auto procedure_registrations_itr = m_procedure_registrations.find(procedure); if (procedure_registrations_itr == m_procedure_registrations.end()) { send_error(session_itr->second->get_transport(), call_message->get_type(), call_message->get_request_id(), "wamp.error.no_such_procedure"); return; } std::shared_ptr<wamp_session> session = procedure_registrations_itr->second->get_session(); const wamp_request_id request_id = m_request_id_generator.generate(); const wamp_registration_id& registration_id = procedure_registrations_itr->second->get_registration_id(); wamp_call_options call_options; call_options.unmarshal(call_message->get_options()); // You can't rely on simply assigning the call options to the invocation // details. Some call options may only be applicable to the dealer and // not the callee. Likewise, some invocation details may be in addition // to whatever is provided in the call options. As a result, we only copy // specific options over to the invocation details. wamp_invocation_details invocation_details; if (call_options.get_option_or("receive_progress", false)) { invocation_details.set_detail("receive_progress", true); } std::unique_ptr<wamp_invocation_message> invocation_message( new wamp_invocation_message(call_message->release_zone())); invocation_message->set_request_id(request_id); invocation_message->set_registration_id(registration_id); invocation_message->set_details(invocation_details.marshal(invocation_message->get_zone())); invocation_message->set_arguments(call_message->get_arguments()); invocation_message->set_arguments_kw(call_message->get_arguments_kw()); BONEFISH_TRACE("%1%, %2%", *session_itr->second % *invocation_message); if (!session->get_transport()->send_message(std::move(*invocation_message))) { BONEFISH_TRACE("sending invocation message to callee failed: network failure"); send_error(session_itr->second->get_transport(), call_message->get_type(), call_message->get_request_id(), "wamp.error.network_failure"); return; } unsigned timeout_ms = call_options.get_option_or<unsigned>("timeout", 0); // We only setup the invocation state after sending the message is successful. // This saves us from having to cleanup any state if the send fails. std::unique_ptr<wamp_dealer_invocation> dealer_invocation( new wamp_dealer_invocation(m_io_service)); dealer_invocation->set_session(session_itr->second); dealer_invocation->set_request_id(call_message->get_request_id()); dealer_invocation->set_timeout( std::bind(&wamp_dealer::invocation_timeout_handler, this, request_id, std::placeholders::_1), timeout_ms); m_pending_invocations.insert(std::make_pair(request_id, std::move(dealer_invocation))); m_pending_callee_invocations[session->get_session_id()].insert(request_id); m_pending_caller_invocations[session_id].insert(request_id); }
void wamp_dealer::process_yield_message(const wamp_session_id& session_id, wamp_yield_message* yield_message) { auto session_itr = m_sessions.find(session_id); if (session_itr == m_sessions.end()) { throw std::logic_error("dealer session does not exist"); } // It is considered to be a normal condition if we cannot find the // associated invocation. Typically this occurs if the invocation // timed out or if the callers session has ended. BONEFISH_TRACE("%1%, %2%", *session_itr->second % *yield_message); const auto request_id = yield_message->get_request_id(); auto pending_invocations_itr = m_pending_invocations.find(request_id); if (pending_invocations_itr == m_pending_invocations.end()) { BONEFISH_TRACE("unable to find invocation ... timed out or session closed"); return; } const auto& dealer_invocation = pending_invocations_itr->second; std::shared_ptr<wamp_session> session = dealer_invocation->get_session(); // We can't have a pending invocation without a pending caller/callee // as they are tracked in a synchronized manner. So to have one without // the other is considered to be an error. auto pending_callee_invocations_itr = m_pending_callee_invocations.find(session_itr->second->get_session_id()); if (pending_callee_invocations_itr == m_pending_callee_invocations.end()) { throw std::logic_error("dealer pending callee invocations out of sync"); } pending_callee_invocations_itr->second.erase(request_id); auto pending_caller_invocations_itr = m_pending_caller_invocations.find(session->get_session_id()); if (pending_caller_invocations_itr == m_pending_caller_invocations.end()) { throw std::logic_error("dealer pending caller invocations out of sync"); } pending_caller_invocations_itr->second.erase(request_id); std::unique_ptr<wamp_result_message> result_message( new wamp_result_message(std::move(yield_message->release_zone()))); result_message->set_request_id(dealer_invocation->get_request_id()); result_message->set_arguments(yield_message->get_arguments()); result_message->set_arguments_kw(yield_message->get_arguments_kw()); // If we fail to send the result message it is most likely that the // underlying network connection has been closed/lost which means // that the caller is no longer reachable on this session. So all // we do here is trace the fact that this event occured. BONEFISH_TRACE("%1%, %2%", *session_itr->second % *result_message); if (!session->get_transport()->send_message(std::move(*result_message))) { BONEFISH_TRACE("failed to send result message to caller: network failure"); } // The failure to send a message in the event of a network failure // will detach the session. When this happens the pending invocations // be cleaned up. So we don't use an iterator here to erase the pending // invocation because it may have just been invalidated above. m_pending_callee_invocations[session_id].erase(request_id); m_pending_caller_invocations[session->get_session_id()].erase(request_id); m_pending_invocations.erase(request_id); }
void wamp_dealer::process_yield_message(const wamp_session_id& session_id, wamp_yield_message* yield_message) { auto session_itr = m_sessions.find(session_id); if (session_itr == m_sessions.end()) { throw std::logic_error("dealer session does not exist"); } // It is considered to be a normal condition if we cannot find the // associated invocation. Typically this occurs if the invocation // timed out or if the callers session has ended. BONEFISH_TRACE("%1%, %2%", *session_itr->second % *yield_message); const auto request_id = yield_message->get_request_id(); auto pending_invocations_itr = m_pending_invocations.find(request_id); if (pending_invocations_itr == m_pending_invocations.end()) { BONEFISH_TRACE("unable to find invocation ... timed out or session closed"); return; } const auto& dealer_invocation = pending_invocations_itr->second; std::shared_ptr<wamp_session> session = dealer_invocation->get_session(); // We can't have a pending invocation without a pending caller/callee // as they are tracked in a synchronized manner. So to have one without // the other is considered to be an error. assert(m_pending_callee_invocations.count(session_itr->second->get_session_id())); assert(m_pending_caller_invocations.count(session->get_session_id())); wamp_yield_options yield_options; yield_options.unmarshal(yield_message->get_options()); // You can't rely on simply assigning the yield options to the result // details. Some yield options may only be applicable to the dealer. // Likewise, some result details may be in addition to whatever is // provided in the yield options. As a result, we only copy specific // options over to the result details. wamp_result_details result_details; if (yield_options.get_option_or("progress", false)) { result_details.set_detail("progress", true); } std::unique_ptr<wamp_result_message> result_message( new wamp_result_message(yield_message->release_zone())); result_message->set_request_id(dealer_invocation->get_request_id()); result_message->set_details(result_details.marshal(result_message->get_zone())); result_message->set_arguments(yield_message->get_arguments()); result_message->set_arguments_kw(yield_message->get_arguments_kw()); // If we fail to send the result message it is most likely that the // underlying network connection has been closed/lost which means // that the caller is no longer reachable on this session. So all // we do here is trace the fact that this event occured. BONEFISH_TRACE("%1%, %2%", *session_itr->second % *result_message); if (!session->get_transport()->send_message(std::move(*result_message))) { BONEFISH_TRACE("failed to send result message to caller: network failure"); } // If the call has more results in progress then do not cleanup any of the // invocation state and just bail out here. if (yield_options.get_option_or("progress", false)) { return; } // Otherwise, the call still has results in progress so do not remove the // invocation. The failure to send a message in the event of a network failure // will detach the session. When this happens the pending invocations // be cleaned up. So we don't use an iterator here to erase the pending // invocation because it may have just been invalidated above if an // error occured. m_pending_callee_invocations[session_id].erase(request_id); m_pending_caller_invocations[session->get_session_id()].erase(request_id); m_pending_invocations.erase(request_id); }
void wamp_message_processor::process_message( const std::unique_ptr<wamp_message>& message, std::unique_ptr<wamp_transport>&& transport, wamp_connection_base* connection) { BONEFISH_TRACE("processing message: %1%", message_type_to_string(message->get_type())); switch (message->get_type()) { case wamp_message_type::AUTHENTICATE: break; case wamp_message_type::CALL: { std::shared_ptr<wamp_router> router = m_routers->get_router(connection->get_realm()); if (router) { wamp_call_message* call_message = static_cast<wamp_call_message*>(message.get()); router->process_call_message(connection->get_session_id(), call_message); } break; } case wamp_message_type::CANCEL: break; case wamp_message_type::ERROR: { std::shared_ptr<wamp_router> router = m_routers->get_router(connection->get_realm()); if (router) { wamp_error_message* error_message = static_cast<wamp_error_message*>(message.get()); router->process_error_message(connection->get_session_id(), error_message); } break; } case wamp_message_type::GOODBYE: { std::shared_ptr<wamp_router> router = m_routers->get_router(connection->get_realm()); if (router) { wamp_goodbye_message* goodbye_message = static_cast<wamp_goodbye_message*>(message.get()); router->process_goodbye_message(connection->get_session_id(), goodbye_message); router->detach_session(connection->get_session_id()); } connection->clear_data(); break; } case wamp_message_type::HELLO: { wamp_hello_message* hello_message = static_cast<wamp_hello_message*>(message.get()); std::shared_ptr<wamp_router> router = m_routers->get_router(hello_message->get_realm()); if (!router) { std::unique_ptr<wamp_abort_message> abort_message(new wamp_abort_message); abort_message->set_reason("wamp.error.no_such_realm"); transport->send_message(std::move(*abort_message)); } else { wamp_session_id id; auto generator = router->get_session_id_generator(); do { id = generator->generate(); } while(router->has_session(id)); connection->set_session_id(id); connection->set_realm(hello_message->get_realm()); // We need to setup the sessions roles before attaching the session // so that we know whether or not to also attach the session to the // dealer and the broker. wamp_hello_details hello_details; hello_details.unmarshal(hello_message->get_details()); auto session = std::make_shared<wamp_session>( id, hello_message->get_realm(), std::move(transport)); session->set_roles(hello_details.get_roles()); router->attach_session(session); router->process_hello_message(id, hello_message); } break; } case wamp_message_type::PUBLISH: { std::shared_ptr<wamp_router> router = m_routers->get_router(connection->get_realm()); if (router) { wamp_publish_message* publish_message = static_cast<wamp_publish_message*>(message.get()); router->process_publish_message(connection->get_session_id(), publish_message); } break; } case wamp_message_type::REGISTER: { std::shared_ptr<wamp_router> router = m_routers->get_router(connection->get_realm()); if (router) { wamp_register_message* register_message = static_cast<wamp_register_message*>(message.get()); router->process_register_message(connection->get_session_id(), register_message); } break; } case wamp_message_type::SUBSCRIBE: { std::shared_ptr<wamp_router> router = m_routers->get_router(connection->get_realm()); if (router) { wamp_subscribe_message* subscribe_message = static_cast<wamp_subscribe_message*>(message.get()); router->process_subscribe_message(connection->get_session_id(), subscribe_message); } break; } case wamp_message_type::UNREGISTER: { std::shared_ptr<wamp_router> router = m_routers->get_router(connection->get_realm()); if (router) { wamp_unregister_message* unregister_message = static_cast<wamp_unregister_message*>(message.get()); router->process_unregister_message(connection->get_session_id(), unregister_message); } break; } case wamp_message_type::UNSUBSCRIBE: { std::shared_ptr<wamp_router> router = m_routers->get_router(connection->get_realm()); if (router) { wamp_unsubscribe_message* unsubscribe_message = static_cast<wamp_unsubscribe_message*>(message.get()); router->process_unsubscribe_message(connection->get_session_id(), unsubscribe_message); } break; } case wamp_message_type::YIELD: { std::shared_ptr<wamp_router> router = m_routers->get_router(connection->get_realm()); if (router) { wamp_yield_message* yield_message = static_cast<wamp_yield_message*>(message.get()); router->process_yield_message(connection->get_session_id(), yield_message); } break; } default: break; } }