Is there good standard way to simulate Req/Res over Websocket? - node.js

I'm building my app entirely over websocket. While I see benefit of being able to send data to client without having client to request data to me. There are still benefits to req/res type of interaction as you can put all logic together.
Ex:
Take registration. You send data, server sends a OK or ERROR if something is wrong.
Right there it's three events! DataFromClient, RegistrationFailed, RegistrationSuccess. But with REST I could have made one POST request and handle if else in one function.
It shouldn't be too hard to create a library that allows you do to push notification + Req/Res type of interaction. It'd be even better if routes could be defined like express routes.

There is no "standard" way to implement request/response with webSocket. It is not part of the webSocket standard. It can certainly be done (more below on this), but there is no "standard" way of doing it.
The socket.io library which is built on top of webSocket has a response option built into any message it sends.
The sender does something like this:
socket.emit("msgName", someData, function(data) {
// data is the "response" to sending this message
console.log(data);
});
The recipient of a message that is expecting a response does something like this to cause the response to be sent:
socket.on("msgName", (someData, fn) => {
// send response
fn(someOtherData);
});
You could implement your own response scheme in webSocket. You'd have to add to your data format a uniqueID for each request so you could send that same uniqueID back in the response so the receiver would know which response belongs with which request.

Related

Extract callback function from payload

I am using Node.js for emitting event to socket via socket.emit(eventName[, ...args][, ack]).
On client side, an Arduino, I'm using the SocketIoClient.h library (link: https://github.com/timum-viw/socket.io-client).
So the server is sending an event and a callback function as
socket.emit("event",function(x){
console.log(x);
})
which is received on the client side in payload field as
clientSocket.on("event",event);
void event(const char * payload, size_t length);
How can I call this callback function from the client side, which is supposed to be present in the payload?
If I understood you correctly, you are trying to use the acknowledgement mechanism offered by Socket.IO (I inferred this from the tags of your question).
For more details, refer to
https://socket.io/docs/server-api/#socket-emit-eventname-args-ack
Acknowledgment for socket.io custom event
This mechanism is specific to the Socket.IO library and therefore can't be used if you are not using it on both the client and the server sides.
As an alternative, I would suggest to dedicate a specific event to the acknowledgement you wish to have. In other words, when your client receives your event "event", it will send a "received" event to the server as acknowledgement along with the data your callback should be called with.
For instance on your server you would have:
socket.on("received", function (x) {
console.log(x); //Will display "Yay! Got it!"
});
socket.emit("event");
and on the client something like
socket.on("event", function () {
socket.emit("received", "Yay! Got it!");
});
(I'm not familiar with the C library you use but the point is the underlying logic).

Mixing POST request with websocket response - is it a bad practice?

Here is a short snippet of node.js code (express.js and socket.io). Could sending POST requests and emitting socket responces be considered as a bad practice and why?E.g.:
var io = require('socket.io')(http);
app.post('/tickets', jsonParser, function(req, res) {
io.emit('ticket', req.body);
return res.sendStatus(200);
}
I see no problem with that. I actually created a notification system that receives the message and destination as a post and sends notifications to multiple sockets like that.
From your code it looks like that's what your are doing, someone creates a ticket and you send a notification to all listeners.
That seems to be the most practical way and added bonus of being a proper api for use with external server like php or .net. If you're just using it from your own node app than perhaps you could just make it a socket event instead unless you are planning on getting requests from outside your app.

express response with a callback

Is it possible to send a response from express, and wait for a return response before continuing?
A typical scenario is something like this
Server A sends a request to server B.
Server B processes the request and sends to back to server A
Server B waits for a response from server A before continuing
Server A does further processing of the response from Server B and sends it back to Server B
Server B then handles the rest of the processing required.
My understanding is that normally this is handled with callbacks. In express I would expect to do something like
res.write('response', callback);
function callback() {
//do stuff
}
I don't see that this is possible with the res.write method though. Is there another method I can use with express to get this functionality? I've never used socket.io before, but this seems like a scenario where websockets would be useful. Am I wrong in this assumption?
res.on('finish', callback);
is sent when the last of the data is given to the OS to deal with.
http://nodejs.org/api/http.html#http_event_finish
If you need to know when the client receives/processes the data, however, the client must send something back to the server, in which case socket.io could help.
I appreciate all the responses and help from everyone, I ended up using sessions to get what I needed.
var session = require('express-session');
Thanks again

Connecting to a Reliable Webservice with Nodejs

My application needs to receive a result from Reliable Webservice. Here is the scenario:-
First I send a CreateSequence request. Then the server replies with a CreateSequenceResponse message. Next I send the actual request to the webservice.
Then the webservice send a response with 202 accept code and sends result in a later message. All these messages contain the header Connection: keep-alive.
I made request with http.ClientRequest. I could capture all responses except the result. http.ClientRequest fires only one response event.
How can I receive the message which contains the result?
Is there any way to listen to socket for remaining data (socket.on('data') did not work). I checked this with ReliableStockQuoteService shipped with Apache Synapse. I appreciate if someone can help me.
When you get the response event, you are given a single argument, which is an http.IncomingMessage, which is a Readable stream. This means that you should bind your application logic on the data event of the response object, not on the request itself.
req.on('response', function (res) {
res.on('data', console.log);
});
Edit: Here is a good article on how to make HTTP requests using Node.

reuse socket id on reconnect, socket.io, node.js

is it possible to reuse a socket.id or use it multiple times?
Let's assume a user views multiple pages of the same site in different browser tabs. I want to use a single socket.id, socket to handle them all.
If a user receives a notification it should popup on all tabs with a single socket.emit.
It is possible
From the dates of previous responses I assume it might not have been possible in previous versions of socket.io, but I can confirm that I'm successfully reusing socket ids on reconnections with socket.io 2.3.0.
You just need to override io.engine.generateId. Whatever that method returns will be the id assigned to the socket. Here are the docs about generateId.
As far as I've experimented myself, there are two situations when that method is called. During connections and reconnections.
The method io.engine.generateId receives the originating request object as the argument, so we can use it to figure out if we want to reuse the id or get a fresh new one.
Example
As an example, I'll show how you would reuse an id sent from the client, or create a new one when the client doesn't send it. The id will be sent on the handshake request as the query param socketId.
1. Override io.engine.generateId
First you need to override io.engine.generateId, which is the method that assigns IDs. On the server you need to do something like this.
const url = require('url')
const base64id = require('base64id')
io.engine.generateId = req => {
const parsedUrl = new url.parse(req.url)
const prevId = parsedUrl.searchParams.get('socketId')
// prevId is either a valid id or an empty string
if (prevId) {
return prevId
}
return base64id.generateId()
}
That way, whenever you send the query param socketId in the handshake request, it will be set as the socket id. If you don't send it you'll generate a new one using base64id. The reason to use that library in particular is because that's what the original method does. Here you can find the source code.
2. Send the information on the connection request
Once you have that, you need to send the socketId param from the client. This is described in the docs.
const socket = io.connect(process.env.WEBSOCKET_URL, {
query: {
socketId: existingSocketId || ''
}
})
process.env.WEBSOCKET_URL would be the URL where your web socket is listening.
Note that this will work when connecting, but you might want to update the query on reconnection.
3. Send the information on the reconnection request
On the same section of the docs it explains how to update the query params before reconnection. You just need to do something like this.
socket.on('reconnect_attempt', () => {
socket.io.opts.query = {
socketId: existingSocketId || ''
}
});
Just like that you'll be reusing the same socket id as long as it is sent from the client.
Security concerns
It's probably a bad idea to trust information sent from the client to assign the socket id. I'd recommend sending a cryptographically signed payload, storing that payload in the client, and send it back to the server when connecting and reconnecting. That way the server can check that the payload can be trusted by verifying the signature.
Using the same example above, we would send something like this to the client, maybe .on('connect'):
{
socketId: 'foo',
signature: SHA_256('foo' + VERY_SECRET_PASSWORD)
}
The client would store that payload and send it back on connecting or reconnecting, in the same way we were sending socketId before.
Once the server receives the signed payload, inside io.engine.generateId we could check that the signature in the payload matches the hash we produce using the ID and the VERY_SECRET_PASSWORD.
You can't reuse Socket.IO connection IDs since they are created during the client-server handshake, but there are alternative methods. I don't have any examples, but you can modify the Socket.IO client to pass along a query string when the handshake is being performed. Then you can tell the server to handle the client based on the query string, and later fetch all client IDs with a certain query string.
Another method you could use would be to use namespaces. Assuming you have some type of session system, you could create a session-specific namespace, and connect clients with that session ID straight to that namespace.
Multiple sites?
No, that's not possible. It would be possible if you open those sites into iframes in your webapp, I guess.
Another option would be to build a browser plugin that opens a socket connection.

Resources