crossbario / autobahn-cpp Goto Github PK
View Code? Open in Web Editor NEWWAMP for C++ in Boost/Asio
Home Page: https://crossbar.io/autobahn
License: Boost Software License 1.0
WAMP for C++ in Boost/Asio
Home Page: https://crossbar.io/autobahn
License: Boost Software License 1.0
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?
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.
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:
Best Regards
Brian Birke.
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.
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.
WAMP-over-RawSocket is implemented in AutobahnCpp, but needs adjustment to the revised section in the spec: https://github.com/tavendo/WAMP/blob/master/spec/advanced.md#rawsocket-transport
See also: crossbario/autobahn-python#291
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;
}
m_io.get_io_service()
instead of being passed manually.Is there another reason why the io_service should be passed in explicitly?
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
When trying example from master/examples with bonefish router.
./bonefish -w 8080 -t 8000 -r default -d
Publisher example does not work as expected.
[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
[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.
It is produced by: std::cerr << "received event: " << event.argument<uint64_t>(0) << std::endl;
To fixt it change uint64_t => std::string like this: event.argumentstd::string(0)
Regards,
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.
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)
(see title)
I'm opening this issue more as a discussion thread covering the following topics:
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):
boost::fusion
headers.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.
After merging Dave's fork back in, the following likely needs updates:
I init a simple crossbar.io hello:cpp template and there are obscure errors.
Here my autobahnCpp-CompilationBug gist with infos
boost with too much template is hard to understand.
I know this information who not help in anything with the problem above.
http://stackoverflow.com/questions/22597948/using-boostfuture-with-then-continuations/
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?
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 ..
It looks like the current implementation (https://github.com/tavendo/AutobahnCpp/blob/b44ee2e0582923fe8e175eff1ec726582575f4e2/autobahn/autobahn_impl.hpp#L530) does the following:
uint64_t
int64_t
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:
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.
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:
I have a pull request coming shortly to address this.
I want to use the boost libraries installed from the package manager of my operating system. Do AutobahnCpp work with boost 1.55?
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,
From #21. Apologies for the formatting.
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:
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.
Here is an example for endpoint_fvm_t: https://github.com/tavendo/AutobahnCpp/blob/master/examples/register2.cpp#L63
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.
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 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.
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:
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:
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.
Consider remerging https://github.com/davidchappelle/AutobahnCpp
The code here:
Only supports 3 of the implemented provide methods. It looks like the rest just don't work. Is it possible to temporarily remove the unsupported provide types for the time being?
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.
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?
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
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.