zeromq / zmqpp : Forward metadata with message - c++

With zmq and zmqpp, I am looking for a way to forward meta-data along with the message it belong. The goal is to get the 'User-Id' in a worker behind a load balancer which own the secure socket.
Here is a simple example, you can see the metadata disappear after being forwarded. I am not sure: is this metadata 'vanishment' a bug or a feature? Is there any workaround?
#include "zmqpp/zmqpp.hpp"
#include "zmqpp/curve.hpp"
int main()
{
zmqpp::curve::keypair client_keypair = zmqpp::curve::generate_keypair();
zmqpp::curve::keypair server_keypair = zmqpp::curve::generate_keypair();
std::cout << "Client Public Key: " << client_keypair.public_key << std::endl;
std::cout << "Server Public Key: " << server_keypair.public_key << std::endl;
zmqpp::context context;
zmqpp::auth authenticator(context);
authenticator.set_verbose(false);
authenticator.configure_curve(client_keypair.public_key);
zmqpp::socket curve_rep(context, zmqpp::socket_type::rep);
curve_rep.set(zmqpp::socket_option::curve_server, true);
curve_rep.set(zmqpp::socket_option::curve_secret_key, server_keypair.secret_key);
curve_rep.bind("tcp://127.0.0.1:4242");
zmqpp::socket curve_req(context, zmqpp::socket_type::req);
curve_req.set(zmqpp::socket_option::curve_server_key, server_keypair.public_key);
curve_req.set(zmqpp::socket_option::curve_public_key, client_keypair.public_key);
curve_req.set(zmqpp::socket_option::curve_secret_key, client_keypair.secret_key);
curve_req.connect("tcp://127.0.0.1:4242");
zmqpp::socket internal_rep(context, zmqpp::socket_type::rep);
internal_rep.bind("inproc://clear");
zmqpp::socket internal_req(context, zmqpp::socket_type::req);
internal_req.connect("inproc://clear");
{
zmqpp::message msg;
msg << "Hello";
curve_req.send(msg);
}
{
zmqpp::message msg;
curve_rep.receive(msg);
// read User-Id
std::string user_id;
std::cout << "- Before forward: ";
if (msg.get_property("User-Id", user_id))
std::cout << user_id << std::endl;
else
std::cout << "No user id" << std::endl;
// Forward message
internal_req.send(msg);
}
{
zmqpp::message msg;
internal_rep.receive(msg);
// read User-Id
std::string user_id;
std::cout << "- After forward: ";
if (msg.get_property("User-Id", user_id))
std::cout << user_id << std::endl;
else
std::cout << "No user id" << std::endl;
std::string content;
msg >> content;
std::cout << "- Message: " << content << std::endl;
}
{
zmqpp::message msg;
msg << "world !";
internal_rep.send(msg);
}
{
zmqpp::message msg;
internal_req.receive(msg);
// Forward message
curve_rep.send(msg);
}
{
zmqpp::message msg;
curve_req.receive(msg);
std::cout << "- Message: " << msg.get<std::string>(0) << std::endl;
}
return 0;
}
Output:
Client Public Key: }-}3(fH/r!I/9*tJX0bN/TT]Y2Qd#{IqszYzBX.g
Server Public Key: !#kpBlDrmW#e3jW)q6FumkKGjv#7lU?y9mD(QWd8
auth: Starting ZAP Authentication Server
- Before forward: }-}3(fH/r!I/9*tJX0bN/TT]Y2Qd#{IqszYzBX.g
- After forward: No user id
- Message: Hello
- Message: world !
auth: Shutdown ZAP Authentication Server

This can be a confusing element of ZMQ. The meta-data to which you are referring is not a part of the ZMQ message itself, it's part of the connection over which you received that message. The fact that you can access it as a property of that message is an artifact of the ZMQ wireline protocol, how data is transferred from one socket to another with all of the information that the receiving socket needs to appropriately process that message. A ZMQ message has no "headers" per se, it just has "frames", and the ZMQ sockets know how to handle those frames when they include metadata.
So, the short answer is that the sockets, being given details to negotiate the Curve crypto, send crypto metadata along with the message and the receiving socket, being set up with crypto details, knows what to do with that meta data. When you send it over "normal" sockets with no crypto, that metadata is stripped. If the backend pair of sockets used crypto, it would no longer have the metadata that applied to the frontend, it would have a User-Id that applied to the broker backend socket.
If you want to add the metadata to the message so it gets sent back to the backend, you'll have to append it to the message data, either directly or in a new message frame (multi-part message) and handle it yourself, it won't be metadata anymore it'll be first-class data.

Related

Send request from a server to a connected client with RSocket C++

could someone kindly help me with the RSocket issue? I'm trying to send a request from the Server to a connected Client, but the Client Responder does not fire callbacks. I'm using the RSocket-cpp library.
Server code:
TcpConnectionAcceptor::Options opts;
opts.address = folly::SocketAddress("::", FLAGS_port);
opts.threads = 2;
// RSocket server accepting on TCP
auto rs = std::make_unique<TcpConnectionAcceptor>(std::move(opts));
auto serverThread = std::thread([&rs] {
// start accepting connections
rs->start(
[](std::unique_ptr<DuplexConnection> connection, folly::EventBase& eventBase) {
LOG(INFO) << "new incoming connected" << std::endl;
auto client = RSocket::createClientFromConnection(
std::move(connection), *eventBase.getEventBase(), SetupParameters(), nullptr, std::make_shared<GenericRequestResponseResponder>());
LOG(INFO) << "send data" << std::endl;
client->getRequester()->requestResponse(Payload("hello2"))->subscribe([](Payload p) {
LOG(INFO) << "Received1 >> " << p.moveDataToString() << std::endl;
});
LOG(INFO) << "request is sent from server" << std::endl;
});
});
Output:
I0327 07:11:33.583813 23622 RequestResponseHelloWorld_Server.cpp:95] new incoming connected
I0327 07:11:33.602982 23622 RequestResponseHelloWorld_Server.cpp:100] send data
I0327 07:11:33.604566 23622 RequestResponseHelloWorld_Server.cpp:105] request is sent from server
Client code:
class GenericRequestResponseResponder : public rsocket::RSocketResponder
{
public:
std::shared_ptr<Single<Payload>> handleRequestResponse(
Payload request,
StreamId /*streamId*/) override
{
LOG(INFO) << "GenericRequestResponseResponder.handleRequestResponse "
<< request << std::endl;
// string from payload data
auto requestString = request.moveDataToString();
return Single<Payload>::create(
[name = std::move(requestString)](auto subscriber) {
std::stringstream ss;
ss << "Ack " << name << "!";
std::string s = ss.str();
subscriber->onSubscribe(SingleSubscriptions::empty());
subscriber->onSuccess(Payload(s, "metadata"));
});
}
void handleFireAndForget(
rsocket::Payload request,
rsocket::StreamId /*streamId*/) override
{
LOG(INFO) << "GenericRequestResponseResponder.handleRequestResponse "
<< request << std::endl;
}
};
folly::SocketAddress address{folly::SocketAddress(host, port)};
std::shared_ptr<TelemetryConnection> connection{nullptr};
RSocket::createConnectedClient(
std::make_unique<TcpConnectionFactory>(
*m_worker->getEventBase(), std::move(address)), SetupParameters(), std::make_shared<GenericRequestResponseResponder>())
.thenValue([this, host, port, &connection](auto&& client) {
LOG(INFO) << "client is created" << std::endl;
m_clientList.append(client);
})
.thenError(
folly::tag_t<std::exception>{},
[&](const std::exception&) {
LOG(ERROR) << "connection failed";
}).get();
I was expecting GenericRequestResponseResponder::``handleRequestResponse fired when the server sends the request, but client output is empty

Connection To Poco Websocket Server gets Disconnected even when the server side is still listening

Update: I can run the server and client perfect locally when they are on the same host. The below scenario is when they are on separate machines(more specifically one is running on ARM64 Petalinux and other on Ubuntu). Perhaps I need to set some options on server side etc
I have a poco websocket server written over the standard sample provided by Poco and a javascript client. While trying to openwebsocket it will get connected in first go sometimes after trying 2-3 times and so on. Now once connected, it might send the message or just gets disconnected on the client side. While on the server side there is no error and I am pretty sure its still open because when i try to open another connection from the client side it will get connected on different client port.
Now the interesting thing is I wrote a Poco websocket client and sending/receiving message continuously without delay in a loop, the client remains active for sometime and then both server and client says Exception connection reset by peer. Next when i put a delay in the loop while sending/receiving messages on client side (say 5s), the client will send/receive for say 3-4 times and then the client encounters Exception connection reset by peer and now this time no such message on server side. Its really boggling me since I couldn't find the solution no matter what i try.
PS: I have timeout of 10 days for websocket server
The server side request handling is as follow:
std::cout << "Request Received" << std::endl;
Poco::Util::Application& app = Poco::Util::Application::instance();
try
{
std::cout << "Trying to connect..." << std::endl;
Poco::Net::WebSocket ws(request, response);
ws.setReceiveTimeout(Poco::Timespan(10, 0, 0, 0, 0)); //Timeout of 10 days
app.logger().information("WebSocket connection established!");
int flags, n;
do
{
char buffer[1024] = {'\0'};
std::cout << "Waiting for incoming frame" << std::endl;
n = ws.receiveFrame(buffer, sizeof(buffer), flags);
strncpy(buffer, "Hello Client!!", 1024);
ws.sendFrame(buffer, strlen(buffer), Poco::Net::WebSocket::FRAME_TEXT);
}
while (n > 0 && (flags & Poco::Net::WebSocket::FRAME_OP_BITMASK) != Poco::Net::WebSocket::FRAME_OP_CLOSE);
std::cout << "Websocket Closed" << std::endl;
app.logger().information("WebSocket connection closed.");
}
catch (std::exception& e)
{
std::cout << "Exception " << e.what() << std::endl;
}
(As one can see, server will post a message when a websocket is closed but nothing is displayed when the client says connection reset by peer)
The poco websocket client side is:
HTTPClientSession cs("IP", port);
HTTPRequest request(HTTPRequest::HTTP_GET, "/?encoding=text",HTTPMessage::HTTP_1_1);
request.set("origin", "http://localhost");
HTTPResponse response;
bool run = true;
try {
WebSocket* m_psock = new WebSocket(cs, request, response);
char buffer[1024] = {'\0'};
std::string str = "Hello Server!!";
do {
char receiveBuff[256];
strncpy(buffer, str.c_str(), 1024);
int len=m_psock->sendFrame(buffer,strlen(buffer),WebSocket::FRAME_TEXT);
std::cout << "Sent bytes " << len << std::endl;
std::cout << "Sent Message: " << buffer << std::endl;
int flags=0;
int rlen=m_psock->receiveFrame(receiveBuff,256,flags);
std::cout << "Received bytes " << rlen << std::endl;
std::cout << receiveBuff << std::endl;
//std::cin >> str;
sleep(5);
} while (1);
m_psock->close();
}
catch (std::exception &e)
{
std::cout << "Exception " << e.what();
}
FYI: I can't process the connection reset by peer, because it directly goes to exception without printing received bytes as 0

Grpc: Grpc C++ client and Grpc java server, asynchronous bidirectional stream

I am trying to write a cpp client for a bi-directional stream api.
With the following client code, I am able to instantiate a Stream observer on the server. However the problem is with the invocation of the onNext function on the Server StreamObserver.
Is there a certain protocol to make this call using a cpp client and java server ?
Proto file:
// A client-to-server stream RPC to append data
rpc append(stream ratis.common.RaftClientRequestProto)
returns (stream ratis.common.RaftClientReplyProto) {}
Server code
#Override
public void onNext(RaftClientRequestProto request) {
try {
final RaftClientRequest r = ClientProtoUtils.toRaftClientRequest(request);
LOG.info("recieved request " + r.getCallId());
final PendingAppend p = new PendingAppend(r);
slidingWindow.receivedRequest(p, this::processClientRequestAsync);
} catch (Throwable e) {
responseError(e, () -> "onNext for " + ClientProtoUtils.toString(request));
}
}
Client code
RaftClientRequestProto req = create_request(read_requet, sizeof(ContainerCommandRequestProto));
grpc::ClientContext ctx;
std::shared_ptr<ClientReaderWriter<RaftClientRequestProto, RaftClientReplyProto>> cli_stream(stub->append(&ctx));
std::thread writer([cli_stream]() {
RaftClientReplyProto resp;
std::cout << "goind to read " << std::endl;
cli_stream->Read(&resp);
std::cout << "read done" << std::endl;
});
std::cout << "Thread started" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(10000));
std::cout << "Doing writes" << std::endl;
cli_stream->Write(req);
cli_stream->WritesDone();
std::cout << "writes done" << std::endl;
This issue was because of difference in protobuf version.

Reading Flatbuffers objects sent via ZMQ in C++ throws unhandled exception

I am trying to send a reasonably big Flatbuffers object over the network via ZMQ and then read it using C++. When accessing the object, I get unhandled exceptions that I don't know how to solve. Even this minimal example fails:
The flatbuffers schema:
namespace flatbuffer;
table TestBuf {
testStatus:bool;
testNumber:double;
testInt:int;
}
root_type TestBuf;
The main.cpp using the REP socket:
int main() {
zmq::context_t context(1);
zmq::socket_t socket(context, ZMQ_REP);
socket.bind("tcp://*:5555");
std::cout << "Listening for requests." << std::endl;
std::cout << "-----" << std::endl;
double count = 0;
while (1) {
zmq::message_t request;
socket.recv(&request);
// Read incoming data
auto reqmsg = flatbuffer::GetTestBuf(&request);
std::cout << "Received: " << reqmsg << std::endl;
flatbuffers::FlatBufferBuilder fbb;
flatbuffer::TestBufBuilder builder(fbb);
count++;
builder.add_testNumber(count);
std::cout << "Sending " << count << std::endl;
auto response = builder.Finish();
fbb.Finish(response);
// Send the flatbuffer
int buffersize = fbb.GetSize();
zmq::message_t message(buffersize);
memcpy((void *)message.data(), fbb.GetBufferPointer(), buffersize);
socket.send(message);
}
return 0;
}
The main.cpp using the REQ socket:
int main() {
// Prepare ZMQ context and socket
zmq::context_t context(1);
zmq::socket_t socket(context, ZMQ_REQ);
std::cout << "Sending out data requests." << std::endl;
socket.connect("tcp://localhost:5555");
double count = 0;
while (1) {
// Formulate response
flatbuffers::FlatBufferBuilder fbb;
flatbuffer::TestBufBuilder builder(fbb);
count++;
builder.add_testNumber(count);
auto response = builder.Finish();
fbb.Finish(response);
// Send the flatbuffer
std::cout << "Sending. " << count << ". ";
int buffersize = fbb.GetSize();
zmq::message_t message(buffersize);
memcpy((void *)message.data(), fbb.GetBufferPointer(), buffersize);
socket.send(message);
std::cout << "Sent. ";
// Receive reply
zmq::message_t reply;
socket.recv(&reply);
// Read the data
auto inmsg = flatbuffer::GetTestBuf(&reply);
std::cout << " Received reply: " << inmsg << std::endl;
//auto num = inmsg->testNumber();
//std::cout << num << " test number.";
}
return 0;
}
This code runs fine and displays (I think) the raw buffer each program is receiving. Strangely, it is not changing, although the content of the message should be. If I uncomment the last two lines and try to access inmsg->testNumber(), I get this error message:
Unhandled exception at 0x000000013F373C53 in KUKAREQ.exe: 0xC0000005: Access violation reading location 0x00000000004B35D8.
I have sent Flatbuffers objects through ZMQ successfully before, but I have not read them in C++. I am fairly sure I followed the Flatbuffers tutorial closely, but something is obviously going wrong. Pointers? Buffer sizes? Either way I would appreciate help.
Edit: To clarify my comment on the accepted answer, the offending line was:
auto inmsg = flatbuffer::GetTestBuf(&reply);
It has to be changed to:
auto inmsg = flatbuffer::GetTestBuf(reply.data());
Whoever reads this question may also be interested to know that I later came across a bug which occurs when the FlatBufferBuilder functions are not called in the correct order. Apparently the order in which the Flatbuffers object is built is important. Finding that one took me a while - novices watch out.
Not familiar with ZeroMQ, but flatbuffer::GetTestBuf(&request) this looks problematic.. you need to pass the buffer, not the message structure. Likely request.data() or similar works better.
In general, if it crashes in FlatBuffers, you should use the verifier to verify the buffer you're passing to FlatBuffers. If that fails, it means you're not passing legal data to FlatBuffers, as is the case here.
Also, you may want to check if ZeroMQ can send buffers without copying, will be faster.

C++ REST SDK (casablanca): How to get the address of the client connected to http_listener?

I have a small HTTP server written with Casablanca and I would like to get the IP address of the client that sent the request. I looked at the members of http_request and could not find it.
web::http::experimental::listener::http_listener listener(U("http://*/http_server"));
listener.support(web::http::methods::POST, [](web::http::http_request const& request)
{
std::cout << "client: " << "[ip address]" << " requested " << request << std::endl;
});