Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request : Expose sio::message mapping to/from std::string #87

Open
jhamell opened this issue Mar 22, 2016 · 3 comments

Comments

Projects
None yet
2 participants
@jhamell
Copy link

commented Mar 22, 2016

Hello,

Apologies if this is already available or out of scope, but would it be possible to configure this library to expose the necessary functions to map between sio::message and JSON strings?

Our use case might not be very common, but we are currently loading a socket.io client through Lisp's CFFI, and there is a lot of infrastructure to build to map between the domains (most everything needs reduced to C functions and structs). We're working against a moving target on the server side API, so each minor change cascades into numerous C/C++ modifications.

Ideally, this would be convenient:
sio::message sio::message::create_from_json( const std::string& payload )
std::string sio::message::to_json()

If the string mappings were possible, I could modify our C wrapper to accept something of the sort:

char* emit_with_response( char* messageName, char* jsonPayload )

The Lisp caller can deal with the malloc/free of the strings, and can use existing facilities (cl-json) to pull the raw json strings into json object representations. Our wrapping could manage those into std::string and use the packet/whatever utlities to eventually call emit( messageName, createdPayloadFromString ),

Thanks,
Josh

@jhamell

This comment has been minimized.

Copy link
Author

commented Mar 31, 2016

I think I found a workaround that doesn't require extending the client code, although it's inefficient. I can use the packet_manager to encode json (put_payload) into sio::message::ptr, and vice-versa (::encode ... although need to strip the message type flags from the result). It is duplicating effort, since this is all done within the client itself, doesn't immediately work with binary encoding, but it seems to be a convenient workaround.

@eyalmolad

This comment has been minimized.

Copy link

commented Apr 21, 2016

Hi, I am facing the similar issue. Would you mind posting your workaround? Serializing the sio::message::ptr to json string?

@jhamell

This comment has been minimized.

Copy link
Author

commented Apr 21, 2016

Certainly - as I said, it effectively duplicates the encode/decode process. However, it seemed to be the least amount of effort to achieve my goals.

  1. As I mentioned in another ticket, there is an issue building scoket.io-cpp as a shared lib. I needed my product to be a dynamic library, so I compiled the socket.io-cpp source directly into it. I may have changed visibility on packet_manager, I don't recall.
  2. Added instance variable to my class:
sio::packet_manager manager;
std::mutex packetLock;

The encode/decode processes within packet_manager turn out to be synchronous, so I didn't need to use the mutex + conditional variable callback pattern. The mutex above is to ensure only one thread it dealing with the packet_manager at a given time.

  1. To obtain a message, used the following protected function:
sio::message::ptr getMessage( const std::string& json )
{
    std::lock_guard< std::mutex > guard( packetLock );
    sio::message::ptr message;
    manager.set_decode_callback( [&](sio::packet const& p )
    {
        message = p.get_message();
    });

    // Magic message type / ID
    std::string payload = std::string( "42" ) + json;
    manager.put_payload( payload );

    manager.reset();
    return message;
}

The packet_manager deals with almost-JSON strings; there is the mandatory socket.io message type/flag numeric string prepended to the actual contents. IIRC '42' is Message + Event - I don't think it matters in this context, other than needing to be there for packet_manager to strip from the JSON string.
The call to put_payload triggers processing and the callback.

  1. To obtain a message's JSON string:
std::string getJson( sio::message::ptr msg )
{
    std::lock_guard< std::mutex > guard( packetLock );
    std::stringstream ss;
    sio::packet packet( "/", msg );
    manager.encode( packet, [&](bool isBinary, std::shared_ptr<const std::string> const& json)
    {
        ss << *json;
        assert( !isBinary );
    });
    manager.reset();

    // Need to strip off the message type flags (typically '42',
    // but there are other possible combinations).
    std::string result = ss.str();
    std::size_t indexList = result.find( '[' );
    std::size_t indexObject = result.find( '{' );
    std::size_t indexString = result.find( '"' );
    std::size_t index = indexList;
    if ( indexObject != std::string::npos && indexObject < index )
        index = indexObject;
    if ( indexString != std::string::npos && indexString < index )
        index = indexString;

    if ( index == std::string::npos ) {
        std::cerr << "Error decoding json object" << std::endl << " Body: " << result << std::endl;
        return "";
    }
    return result.substr(index);
}

Use the default namespace ("/"), tell it to encode, and throw an error if it returns binary. The rest is dealing with stripping the event/type flags from the message - anything before the first string/list/object opening symbol. I haven't tested this thoroughly, so you may hit some issues.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.