CppWAMP
C++11 client library for the WAMP protocol
Sessions

Except for establishing the underlying transport, WAMP client operations are performed via the wamp::Session class.

Completion Tokens

Asynchronous operations in wamp::Session accept a generic completion token, which can either be:

ErrorOr

The outcome of a Session asynchronous operation is emitted via wamp::ErrorOr<T>, where T is the actual underlying result type. An ErrorOr can contain either the actual result, or a std::error_code if the operation failed. The ErrorOr::get method is used to retrieve the underlying result value, throwing a wamp::error::Failure exception if there was an error.

ErrorOr is similar to the proposed std:expected, which was not known to the CppWAMP author when this library was initially developed.

Stackful Coroutines

Because it makes things easier to demonstrate, the following tutorial pages will use Boost stackful couroutines. Using asynchronous callbacks is covered in Asynchronous Callbacks.

When passing a yield_context to a Session asynchronous operation, the operation will block within the coroutine until it is completed (or failed). While the operation is blocking, control is yielded to other pending asynchronous operations via the I/O context.

The ErrorOr result of a Session coroutine operation is emitted via the the member function's return value.

A yield_context is obtained via boost::asio::spawn.

Creating the Session Object

Once one or more wamp::Connector objects have been created, they are passed to the create factory method of the session API. create then returns a std::shared_ptr to the newly created wamp::Session object.

#include <cppwamp/json.hpp>
#include <cppwamp/tcp.hpp>
using namespace wamp;
auto tcpJson = connector<Json>(ioctx, TcpHost("localhost", 8001));
auto tcpPack = connector<Msgpack>(ioctx, TcpHost("localhost", 8001));
// Create the Session object:
auto session = Session::create(ioctx, {tcpJson, tcpPack});

Connecting and Joining

After the session object is created, the connect operation is used to establish a transport connection to a WAMP router. The join operation is then used to join a WAMP realm on the router.

The following example shows how to spawn a coroutine, and how a yield_context is passed to the connect and join operations.

using namespace wamp;
auto tcpJson = connector<Json>(ioctx, TcpHost("localhost", 8001));
auto tcpPack = connector<Msgpack>(ioctx, TcpHost("localhost", 8001));
boost::asio::spawn(ioctx, [&](boost::asio::yield_context yield)
{
auto session = Session::create(ioctx, {tcpJson, tcpPack});
session->connect(yield).value();
auto sessionInfo = session->join(Realm("somerealm"), yield).value();
// etc.
});
ioctx.run();

Note that while a coroutine operation is suspended, it yields control so that other asynchronous operations can be executed via the I/O context.

Also note the value() method being invoked on the join result to obtain the underlying wamp::SessionInfo. If there was an error during the join operation, value() would throw a wamp::error::Failure exception.

Even though we're not interested in the result of the connect operation in this example, the value() method is invoked anyway so that an exception is thrown if there was a failure in the connect operation.

If failure exceptions are not desired, the bool operator of the ErrorOr result can used used to check for errors:

boost::asio::spawn(ioctx, [&](boost::asio::yield_context yield)
{
auto session = Session::create(ioctx, {tcpJson, tcpPack});
session->connect(yield);
auto info = session->join(Realm("somerealm"), yield);
if (!info)
std::cerr << "Join failed: " << info.error() << "\n";
else
std::cout << "Joined, session ID: " << info.value().id() << "\n";
// etc.
});

Leaving and Disconnecting

To gracefully end a WAMP session, the leave and disconnect operations are used:

using namespace wamp;
boost::asio::spawn(ioctx, [&](boost::asio::yield_context yield)
{
auto session = Session::create(ioctx, {tcpJson, tcpPack});
session->connect(yield).value();
session->join(Realm("somerealm"), yield).value();
while (!finished)
{
// Interact with the WAMP session
}
auto reason = session->leave(yield).value(); // returns the Reason URI and
// other details from the
// router's GOODBYE message
session->disconnect(); // non-blocking
});

To abruptly end a WAMP session, you can skip the leave operation and just disconnect. This may be useful when handling error conditions. Alternatively, you can let the session shared_ptr reference count drop to zero, and the session will abruptly terminate the connection in its destructor:

using namespace wamp;
boost::asio::spawn(ioctx, [&](boost::asio::yield_context yield)
{
auto session = Session::create(ioctx, {tcpJson, tcpPack});
session->connect(yield).value();
session->join(Realm("somerealm"), yield).value();
while (!finished)
{
// Interact with the WAMP session
}
// The session reference count will drop to zero when this lambda
// function exits. The session destructor will then terminate the
// connection.
});

Aborting Coroutine Operations

All pending coroutine operations can be aborted by dropping the client connection via Session::disconnect. Pending post-join operations can be also be aborted via Session::leave. Operations aborted in this manner will contain an error code in their returned wamp::ErrorOr result. There is currently no way to abort a single coroutine operation without dropping the connection or leaving the realm.

Logging

wamp::Session can generate log events at runtime, for warnings and for inbound/outbound WAMP messages. CppWAMP is not opinionated about where the log events should go, so it provides the setWarningHandler and setTraceHandler methods that allow you to register handlers for these log events.

Warnings are generated when there are problems detected that don't prevent CppWAMP from performing its job.

Traces contain a stringified version of the WAMP protocol messages. They can be useful for troubleshooting without having to set up a network analyzer.

Both warning and trace handlers must be callable entities with the signature:

void handler(std::string)

The handlers are posted via the executor that was passed to the session's create method.

With these handlers you may, for example, log the messages to the console.

auto session = wamp::Session::create(ioctx, tcp);
session->setWarningHandler([](std::string message)
{
std::cerr << "CppWAMP warning: " << message << "\n";
});
session->setTraceHandler([](std::string message)
{
std::clog << "WAMP message: " << message << "\n";
});

Authentication

CppWAMP supports the AUTHENTICATE and CHALLENGE messages used for WAMP authentication. Algorithms for computing the cryptographic signatures needed for the WAMP-CRA and WAMP-SCRAM methods are currently not provided by CppWAMP. CppWAMP users must therefore compute these themselves using other cryptographic libraries. Once computed, the signatures can be passed to CppWAMP via wamp::Authentication.

To enable authentication, you must set the challenge handler via wamp::Session::setChallengeHandler. The handler must be a callable entity with the following signature:

void handler(wamp::Challenge)

Within the challenge handler, you must compute the cryptographic signature (or ticket string) and send it back via wamp::Challenge::authenticate.

When later performing the session join operation, you must set the authentication ID via wamp::Realm::withAuthId, as well as the desired/supported authentication methods via wamp::Realm::withAuthMethods.

The following example shows how to perform ticket authentication.

std::string ticket;
void onChallenge(wamp::Challenge challenge)
{
if (challenge.method() == "ticket")
challenge.authenticate(wamp::Authentication(ticket));
else
throw std::runtime_error("Unsupported authentication method");
}
int main()
{
using namespace wamp;
AsioContext ioctx;
auto tcp = connector<Json>(ioctx, TcpHost("localhost", 8001));
boost::asio::spawn(ioctx, [&](boost::asio::yield_context yield)
{
ticket = "guest";
auto session = Session::create(ioctx, tcp);
session->setChallengeHandler(&onChallenge);
session->connect(yield);
auto sessionInfo = session->join(
Realm("somerealm").withAuthId("alice").withAuthMethods({"ticket"}),
yield).value();
ticket.clear();
// etc.
});
ioctx.run();
return 0;
}

Handling Session State Changes

wamp::Session::setStateChangeHandler can be used to register a function that's called whenever the session's wamp::SessionState changes. This can be used, for example, to attempt reconnection when the session is disconnected due to a network failure.

void rejoinSession(Session& session, boost::asio::yield_context yield)
{
session.connect(yield).value();
session.join(Realm("somerealm", yield).value();
// Register RPCs, subscribe to topics, etc.
}
void onStateChange(SessionState state,
std::weak_ptr<Session> weakSession)
{
{
auto session = weakSession.lock();
if (session)
{
boost::asio::spawn(session->userExecutor(),
[session](boost::asio::yield_context yield)
{
// You may want to throttle and limit the reconnection
// attempts here.
rejoinSession(*session, yield);
});
}
}
}
int main()
{
using namespace wamp;
AsioContext ioctx;
auto tcp = connector<Json>(ioctx, TcpHost("localhost", 8001));
boost::asio::spawn(ioctx, [&](boost::asio::yield_context yield)
{
auto session = Session::create(ioctx, tcp);
auto weakSession = std::weak_ptr<Session>(session);
session->setStateChangeHandler(
[weakSession, ioctx](SessionState state)
{
onStateChange(state, weakSession);
});
rejoinSession(*session, yield);
// etc.
});
ioctx.run();
return 0;
}

Next: Error Handling

wamp::Session::create
static Ptr create(AnyIoExecutor exec, const Connector::Ptr &connector)
Creates a new Session instance.
Definition: session.ipp:22
session.hpp
Contains the asynchronous session API used by a client peer in WAMP applications.
wamp::Realm
Realm URI and other options contained within WAMP HELLO messages.
Definition: peerdata.hpp:59
wamp::Authentication
Provides the Signature and Extra dictionary contained within WAMP AUTHENTICATE messages.
Definition: peerdata.hpp:205
wamp::SessionState::disconnected
@ disconnected
The transport connection is not yet established.
wamp::TcpHost
Contains TCP host address information, as well as other socket options.
Definition: tcphost.hpp:103
msgpack.hpp
Contains the Msgpack codec.
wamp
Definition: anyhandler.hpp:36
json.hpp
Contains the JSON codec.
tcp.hpp
Contains facilities for creating TCP transport connectors.
wamp::Challenge
Provides the AuthMethod and Extra dictionary contained within WAMP CHALLENGE messages.
Definition: peerdata.hpp:240
wamp::SessionState::failed
@ failed
WAMP session or transport connection has failed.
wamp::SessionState
SessionState
Enumerates the possible states that a client or router session can be in.
Definition: wampdefs.hpp:34
wamp::AsioContext
boost::asio::io_context AsioContext
Queues and runs I/O completion handlers.
Definition: asiodefs.hpp:34