Coder Social home page Coder Social logo

crossbario / autobahn-cpp Goto Github PK

View Code? Open in Web Editor NEW
250.0 34.0 104.0 3.04 MB

WAMP for C++ in Boost/Asio

Home Page: https://crossbar.io/autobahn

License: Boost Software License 1.0

C++ 95.24% CMake 3.72% Makefile 0.50% Shell 0.25% Python 0.29%
wamp real-time rpc pubsub boost asio autobahn

autobahn-cpp's Issues

Strange error when compiling with Mingw 64 bit

OS: Windows 8.1 64 bit
Compiler: GCC x86_64-w64-mingw32

First i applied the following obvious fix on top of autobahn_impl.hpp:

ifdef WIN32

include <WinSock2.h>

include <io.h>

else

include <arpa/inet.h>

include <unistd.h>

endif

The compilation error now looks like this:

In file included from C:/ ... /mingw-w64/x86_64-4.9.2-posix-seh-rt_v3-rev1/mingw64/x86_64-w64-mingw32/include/windows.h:71:0,
from C:/ ... /include/msgpack/unpack.hpp:44,
from C:/ ... /include/msgpack.hpp:22,
from C:/ ... /autobahn/autobahn.hpp:32,
from C:/ ... /examples/stdio.cpp:24:
C:/ ... /autobahn/autobahn.hpp:436:13: error: expected identifier before numeric constant
ERROR = 8,
^
C:/ ... /autobahn/autobahn.hpp:436:13: error: expected '}' before numeric constant
C:/ ... /autobahn/autobahn.hpp:436:13: error: expected unqualified-id before numeric constant
In file included from C:/ ... /examples/stdio.cpp:24:0:

Implement an abstraction for wamp transports

The session API is templated and requires that both an input stream and output stream be provided. For example:

autobahn::session<boost::asio::local::stream_protocol::socket, boost::asio::local::stream_protocol::socket>
autobahn::session<boost::asio::ip::tcp::socket, boost::asio::ip::tcp::socket>
autobahn::session<int, int>

Calls to boost::asio::write, boost::asio::read, and boost::asio::async_read are then issued internally as required to send and receive messages. This is restricting such that the templated types must be compatible with boost::asio semantics for reading and writing data. In the near future, support will likely be added for Websocket, HTTP/2, and a native application transport to call directly into a WAMP router. In order to do this, we will need to be able to have the session API internally write to these types of transports. As a result we need to provide further abstraction in place of the templated streams that are provided to the session. For example:

class wamp_stream
{
public:
    wamp_stream();
    virtual wamp_stream();

    virtual int read(char* buffer, int length) = 0;
    virtual void async_read(char* buffer, int length, read_handler handler) = 0;
    virtual void write(const char* buffer, int length) = 0;
};

class wamp_websocket_stream : public wamp_stream
{
...
};

class wamp_http2_stream : public wamp_stream
{
...
};

class wamp_native_stream : public wamp_stream
{
...
};

Then we can change the session API to no longer require templates for the input and output streams and instead pass in objects of the desired types:

autobahn::session(boost::asio::io_service& io, wamp_stream& in, wamp_stream& out, bool debug=false)

As an added benefit, this would also allow the session API to participate in a factory pattern which could be desireable for implementations requiring dynamic session generation based on transport type.

Implement native session support

I am looking at how I can integrate native support into AutobahnCpp. The main driver here is to integrate with Bonefish which is the C++ WAMP Router implementation. I was initially building on top of PR #56 and trying to implement a wamp_native_transport. I am starting to feel that this is not the correct approach as the message passing back and forth between the router and the component doesn't quite work within the API write and async_read. I am going to continue trying to find an elegant way to make things work using the existing wamp_session and deriving a wamp_native_transport however, I am opening this issue to gather some feedback from others in the community. Perhaps the best approach would be to implement a wamp_native_session rather than trying to reuse wamp_session? Thoughts?

Simple method invocation isn't working

I wrote a simple client that provides a boost::any add(anyvec, anymap) method. However, when I try to invoke that method as a wamp call the autobahn-cpp framework gives me the following error:

Could not process invocation - unimplemented endpoint type
NSt3__18functionIFN5boost3anyERKNS_6vectorIS2_NS_9allocatorIS2_EEEERKNS_3mapINS_12basic_stringIcNS_11char_traitsIcEENS4_IcEEEES2_NS_4lessISE_EENS4_INS_4pairIKSE_S2_EEEEEEEEE
PFN5boost3anyERKNSt3__16vectorIS0_NS1_9allocatorIS0_EEEERKNS1_3mapINS1_12basic_stringIcNS1_11char_traitsIcEENS3_IcEEEES0_NS1_4lessISD_EENS3_INS1_4pairIKSD_S0_EEEEEEE

Seems like this is more or less broken. Here is some simple example code showing how the typeid is not working as expected.

#include <functional>
#include <boost/any.hpp>
#define BOOST_THREAD_PROVIDES_FUTURE
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
#define BOOST_THREAD_PROVIDES_FUTURE_WHEN_ALL_WHEN_ANY
#include <boost/thread/future.hpp>
#include <iostream>
#include <vector>

typedef std::map<std::string, boost::any> anymap;
typedef std::vector<boost::any> anyvec;
typedef std::pair<anyvec, anymap> anyvecmap;

typedef std::function<boost::any(const anyvec&, const anymap&)> endpoint_t;
typedef std::function<anyvec(const anyvec&, const anymap&)> endpoint_v_t;
typedef std::function<anymap(const anyvec&, const anymap&)> endpoint_m_t;
typedef std::function<anyvecmap(const anyvec&, const anymap&)> endpoint_vm_t;

typedef std::map<uint64_t, boost::any> endpoints_t;

boost::any add2(const anyvec& args, const anymap& kwargs)
{
    return 1 + 1;
}

int main(int argc, char** argv)
{
    endpoints_t endpoints;
    endpoints[0] = &add2;

    boost::any func = &add2;

    std::cerr << "typeid for raw function: " << typeid(add2).name() << std::endl;
    std::cerr << "typeid for variant function: " << func.type().name() << std::endl;
    std::cerr << "typeid for endpoint_t function: " << typeid(endpoint_t).name() << std::endl;

    return 0;
}

wampcra / ticket authentication

Hello,

I have tried ( have done, but prefer to call it at try :-) ) to implement the missing bits for a wampcra and ticket authentication.

I am a little cold with c++ ( not really used it for 7 years ), and newer touched C++11. So the implementation is merely a copy-paste from other code sections - which should prove its quality :-) - but anyway if some proper one could take a fast look.

My mission is to put it in such a condition, it can be merged back into this repositary.

The code is located :
repositary: https://github.com/brianbirke/AutobahnCpp.git
branch : auth

Remarks about the code:

  • I added a simple interface on the session - just to make it more obvious of how to use authentication. This might or might not be a good idea.
  • I am using some openssl crypto-functions - making dependencies even if authentication is not used. It is not really fair, is it?? :-). Please comment on how to avoid this??
  • else, it is coded with crossbar as the reference - and it works with that.

Best Regards
Brian Birke.

Example Publisher fails with bonefish

When trying example from master/examples with bonefish router.
./bonefish -w 8080 -t 8000 -r default -d
Publisher example does not work as expected.

  1. Bonefish and Python Publisher works -

[rawsocket_transport.cpp:39][send_message] sending message: event
[wamp_message_processor.cpp:46][process_message] processing message: publish
[wamp_broker.cpp:108][process_publish_message] session [3648815945442074,default,open], publish [12, {}, com.examples.subscriptions.topic1, [12], nil]
[wamp_broker.cpp:141][process_publish_message] session [7090515283965860,default,open], event [1, 2156265064422709, {}, [12], nil]
[rawsocket_transport.cpp:39][send_message] sending message: event
[wamp_message_processor.cpp:46][process_message] processing message: goodbye
[wamp_router_impl.cpp:211][process_goodbye_message] session [3648815945442074,default,closed], goodbye [{}, wamp.close.normal]
[websocket_transport.cpp:39][send_message] sending message: goodbye
[wamp_dealer.cpp:79][detach_session] detach session: session [3648815945442074,default,closed]
[wamp_dealer.cpp:84][detach_session] cleaning up session registrations
[wamp_dealer.cpp:113][detach_session] cleaning up pending caller invocations
[wamp_dealer.cpp:133][detach_session] cleaning up pending callee invocations

  1. Bonefish and Cpp Publisher fails -

[rawsocket_transport.cpp:39][send_message] sending message: event
[wamp_message_processor.cpp:46][process_message] processing message: publish
[wamp_broker.cpp:108][process_publish_message] session [3648815945442074,default,open], publish [12, {}, com.examples.subscriptions.topic1, [12], nil]
[wamp_broker.cpp:141][process_publish_message] session [7090515283965860,default,open], event [1, 2156265064422709, {}, [12], nil]
[rawsocket_transport.cpp:39][send_message] sending message: event
[wamp_message_processor.cpp:46][process_message] processing message: goodbye
[wamp_router_impl.cpp:211][process_goodbye_message] session [3648815945442074,default,closed], goodbye [{}, wamp.close.normal]
[websocket_transport.cpp:39][send_message] sending message: goodbye
[wamp_dealer.cpp:79][detach_session] detach session: session [3648815945442074,default,closed]
[wamp_dealer.cpp:84][detach_session] cleaning up session registrations
[wamp_dealer.cpp:113][detach_session] cleaning up pending caller invocations
[wamp_dealer.cpp:133][detach_session] cleaning up pending callee invocations

Not sure if this is a autobahn-cpp issue or a bonefish one. since @davidchappelle is also a contributor here, Im submitting it here.

Endpoints returning futures don't work

From #21. Apologies for the formatting.


@taion #21 (comment):

Actually, it turns out that none of the endpoints that expect futures actually work. The issue is that you actually wait on the future here:

https://github.com/tavendo/AutobahnCpp/blob/b44ee2e0582923fe8e175eff1ec726582575f4e2/autobahn/autobahn_impl.hpp#L771

However, this ends up blocking the running thread. If my endpoint does something like

return session.call(procedure, args).then(/* do some work*/);

then the procedure will never actually return, because the running thread is blocked on the done.wait() call, and cannot process the return value from the called procedure and resolve the future.

You can't wait for the future to be resolved there.


@oberstet #21 (comment):

Here is an example for endpoint_fvm_t: https://github.com/tavendo/AutobahnCpp/blob/master/examples/register2.cpp#L63


@taion #21 (comment):

The reason that example works is because the future is ready, so done.wait() returns immediately.

If waiting for the future actually requires handling some input (e.g. you invoke another method and wait for it to finish), then it will not work, because you've blocked the main thread from actually processing the response.


@oberstet #21 (comment):

I see.

I guess it would be good if we first had an example that demonstrates the issue, and then come up with a solution.

This is the harder problem - extending the solution to the variants should then be straightforward.


The issue with variants is no longer there, but endpoints that return futures still don't work. Some combination of boost::asio::io_service::work and std::shared_ptr will probably be required to resolve this.

msgpack-c version dependency

The msgpack-c version dependency is not properly documented.

The documentation in README.md suggests using HEAD on msgpack-c/master, but this is not really a workable solution. For example, the msgpack-c developers just renamed msgpack::type::DOUBLE to msgpack::type::FLOAT.

I think it would make more sense to back out b44ee2e and specifically depend on msgpack-c 0.5.9, which will at least be a fixed target.

Additionally, the install instructions for msgpack-c are not quite correct. The C++ bindings are header-only, and thus don't require building.

"provide" v. "register"

It looks like all the other Autobahn implementations use something like session.register() to register a new RPC method. AutobahnCpp on the other hand uses session.provide(). Would it make sense to change to session.register() for consistency?

Won't compile in MSVC as promises are not copyable.

MSVC 2013 reasonably refuses to compile
m_register_requests[m_request_id] = register_request_t(endpoint);
in session::_provide as register_request_t has a boost::promise member that is not copyable. I've solved it with replacing the statement with
m_register_requests.emplace(std::make_pair(m_request_id, register_request_t(endpoint)));
and adding a move constructor to register_request_t:

register_request_t(register_request_t &&other) 
    : m_endpoint(std::move(other.m_endpoint))
    , m_res(std::move(other.m_res))
{}

Regards,
Ivan.

De-serialization of numbers

It looks like the current implementation (https://github.com/tavendo/AutobahnCpp/blob/b44ee2e0582923fe8e175eff1ec726582575f4e2/autobahn/autobahn_impl.hpp#L530) does the following:

  • Positive integers are deserialized to uint64_t
  • Negative integers are deserialized to int64_t
  • Floats are deserialized to double

This makes it really hard to interoperate between JSON and MsgPack clients in certain cases, such as when working with JavaScript and C++. This creates the following problem:

  • JavaScript doesn't distinguish between integers and floats
  • msgpack-python (used in the Autobahn MsgPack serializer) infers the MsgPack type from the Python type

For example, let's say I invoke a procedure registered by a C++ client that I intend to take a float from a JavaScript client. If I call it with 5.0, then I need to any_cast to a uint64_t. If I call it with -5.0, I need to any_cast to an int64_t. If I call it with 5.1, then I need to any_cast to a double. This is really painful.

I understand that WAMP only formally supports non-negative integers, but I feel like this makes coding between AutobahnJS and AutobahnCpp clients unnecessarily difficult. I feel like there should at least be a convenience method that will allow me to generically read a number without having to care about whether it's a positive integer, a negative integer, or a floating-point value.

Support msgpack BIN format

In b44ee2e you just switched over from the old RAW type to the newer STR.

By switching over to the new version, the new type BIN also got added to msgpack-c. This could be supported through std::vector<char>, which is what msgpack-c supports by default for binary data now. Skimming through your code it seems that supporting it is feasible as long as you put it on a higher priority than anyvec, otherwise it'll get packed as ARRAY of POSITIVE_INTEGER/NEGATIVE_INTEGER objects rather than a single BIN item.

Handshake should be synchronous

The capabilities handshake that is performed at the start of a session should be a synchronous operation or at least return a future that can be waited upon so that you can be prevented from trying to join a session before the handshake has completed. We must ensure that the wamp router that we are connecting to can even support our capabilities before trying to establish a session. Based on the current coding style the future returned by a call to start a session should look as follows:

boost::promise<bool> m_handshake;

inline
boost::future<bool> start();

I will write-up a patch for review.

websocket over HTTP via JSON

I ran into an issue that library cant work via default websocket protocol on a mixed http/wamp server that doesn't support msgpack.

To solve such issue I have created my own fork (https://github.com/CepGamer/autobahn-cpp, not finished yet) that creates websocket connection over http (using http upgrade request) and communicates using this socket. Also I had to translate JSON messages to and from msgpack by creating a wrap (changed get_message_body() function to produce msgpack object from json string and send() function to do the opposite).

It's messed up and not yet ready (lots of copy/paste and hardcode), but I'm planning to finish it. I wanted to know, whether such additions is needed or maybe I am doing it wrong (maybe there is a better way :) ) or I just haven't found it in the lib.

Regards,
Sergei Bolotov

Implement the concept of a component

Using the session API directly can result in a lot of boilerplate code involving future continuations. Furthermore, a fair amount of networking code also becomes redundant. This can be avoided by introducing the concept of a component that wraps a session object and provides some conveniences. Different types of components can then be defined to address the various use cases for transports. For example:

  • Stdio
  • TCP
  • Unix Domain Sockets
  • Websocket
  • Http2
  • etc

I have a pull request coming shortly to address this.

Better error handling

A lot of places that should return errors over WAMP instead just log out an error and do nothing.

(I will be submitting PRs on this issue)

Wrapping, and conversion utilities

I'm opening this issue more as a discussion thread covering the following topics:

  1. Conversion of types to anyvec/anymap.
  2. Wrapping external library calls to fit the RPC paradigm. (e.g. any function( const anyvec&, const anymap& ))

If this have been discussed elsewhere, I apologize! (I couldn't find it. :-) )

I believe that many users of this package will be writing code to wrap c/c++ libraries and enable communication/RPC with web servers, python, etc. As such, I think it would make sense to add some utilities to help facilitate this. I am somewhat volunteering to help as well as asking for some input as to what others think is best.

What I have already done (all of the following is done in boost-based template headers):

  1. Written templated conversion of basic, array, and sequence (struct) types to anyvec and anymap. This allows, e.g., the transparent conversion of C/C++ structs to anyvec/anymap (and hence JSON) objects. This requires the boost::fusion headers.
  2. I have written some fairly general wrapping templates to ease wrapping external library calls to the Autobahn RPC call.
  3. I have made some steps towards trying to improve the current serialization/conversion system, though this is not final. In particular, I would envision something like what happens in boost::python, where users can also register how types can be converted from and to python. In this case, it would support the conversion from and to anyvec and anymap (and the supported 'basic' types).

I would be happy to hear comments and to show what I already have. I have been writing this for our own usage in-house, but I've been trying to make it general enough to be more useful.

Thanks.

Relicense to Boost

msgpack-c has been relicensed to the Boost license as per msgpack/msgpack-c#366 and @oberstet has previously indicated that he'd like to change the license of autobahn-cpp from Apache to a more liberal one in order to allow use with GPL-licensed software (see msgpack-c issue for a chart).

Now that msgpack-c is Boost licensed and its 1.3.0 version has been released with the new license, this change could now happen in autobahn-cpp as well.

crossbar-autobahncpp metaevents

How to get router metaevents of session list and which sessionid calling an RPC in cpp?
I tried to find an equivalent cpp function to the JS session.call("wamp.registration.list").then(session.log, session.log).
In JS router, "disclose_me" can be set in 3rd options arg of session.register(procedure, endpoint, options). But in cpp, the equivalent method to "register" is "provide", i.e. auto r1 = session.provide("com.example.add2", &add2), and has only two options.

Also tried looking through the hpp files, and the online docs (javascript metaevents), and also read this wamp-proto/wamp-proto#157

Suitability of Apache license

Hi, I've been looking at whether AutobahnCpp would make a good fit for my project, and noticed that you're using the Apache 2.0 license (like msgpack-c, where I also recently raised the same issue). I don't know what exactly influenced your choice of license and IANAL, so don't take my word for the full truth.

At http://www.apache.org/licenses/LICENSE-2.0 (definition of "Derivative Works") the license states explicitly that code is not considered a Derivative Work if it's linked together or otherwise bound by name. However, AutobahnCpp is header-only which means it will be be directly compiled into my object files, without any linking or binding by name.

By any common reading of open source copyright law, this means that any code using AutobahnCpp will automatically be "infected" with the Apache license. That has a number of implications which might range from careless programmers unknowingly shipping their whole application under the Apache license, to careless programmers violating copyright law if they use msgpack-c in GPLv2 code. The only way for me to avoid this situation is to put a linking boundary in somewhere between AutobahnCpp and my code, or to avoid using it in the first place.

I'm sure you had some thoughts on why to use Apache 2.0, so I'd be interested to hear those. Given that this is a fairly new project, chances are that the intricacies of open-source license application weren't considered in great detail. If this is the case then I'd like to simply bring this issue to your attention and let you decide whether or how to act on it, at a point where the number of copyright holders that need to be consulted is still manageable. I personally would likely use the Boost license from http://www.boost.org/users/license.html as it's been specifically designed for header-only libraries.

Let me know what you think. Thanks,

  • Jakob

No support for authentication?

I am trying to implement this in a project, but I cannot see any support for any authentication methods besides anonymous. Is that so, or am I missing something obvious?

Continuations run on the wrong thread

I noticed this while thinking about #42. The current use of boost::future::then does not play nicely with the asio event loop. Specifically, then creates a new async thread that waits on the future, then runs the callback.

This is highly undesirable behavior. asio only runs its callbacks on threads that have called io_service::run, but using continuations on async threads in this manner entirely violate that unless the user keeps around the io_service and remembers to manually call io_service::post or something.

Even in that case, I think that the correct behavior would be to actually execute the continuation in-band immediately after resolving the promise, rather than potentially crossing thread boundaries twice to do so.

@oberstet What do you think of either explicitly taking a callback instead of returning a future, or else providing a separate e.g. autobahn::future class implementing autobahn::future::then that invokes the continuation in a correct manner? The current implementation has a pretty high risk of multithreading bugs in more complex applications.


I've modified the register1 example to explicitly show this behavior by logging the thread IDs. The output looks something like:

Running on 105700
Starting ASIO I/O loop ..
Main thread ID 0x7fff75881300
Connected to server
Connected callback thread ID 0x7fff75881300
Session joined to realm with session ID 2018155454
Join continuation thread ID 0x101de4000
Registered with registration ID 1163389419
Registered continuation thread ID 0x101e67000
Someone is calling add2() ..
add2 invocation thread ID 0x7fff75881300

You can see that, while the asio callbacks run on the main thread as desired, the continuations do not.

Why take io_service in session constructor?

  1. m_io seems to be unused.
  2. It seems that when doing async function scheduling, you probably want to execute them on the same thread as the socket? The io_service could just be taken from m_io.get_io_service() instead of being passed manually.

Is there another reason why the io_service should be passed in explicitly?

Decide about "call" API

Currently, a session.call will return a boost::future that resolves to boost::any.

We need to handle the cases where the RPC returns a anyvec, anymap or anyvecmap.

We might also need to handle how to return call result details ..

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.