Example #1
0
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());
}
Example #2
0
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");
    }
}
Example #3
0
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));
}
Example #4
0
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");
    }
}
Example #5
0
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);
}
Example #6
0
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);
    }
}
Example #8
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");
    }
}
Example #9
0
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");
    }
}
Example #10
0
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;
}
Example #11
0
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));
}
Example #12
0
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");
    }
}
Example #13
0
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);
}
Example #14
0
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());
    }
}
Example #15
0
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);
}
Example #16
0
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);
}
Example #17
0
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);
}
Example #18
0
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);
}
Example #19
0
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);
    }
}
Example #20
0
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);
}
Example #21
0
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);
}
Example #22
0
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);
}
Example #23
0
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;
    }
}