Just getting started with rxjs so sorry if the question is basic. I'm trying to implement a websocket client and subscription in nodejs server-side environment with it. I need to connect to a websocket server, subscribe to a channel by sending a specific message, ingest incoming messages. I want the implementation to reconnect and re-subscribe after a disconnect, thus maintaining a persistent open subscription. I've found how to do reconnect and so far the prototype looks as follows:
import WebSocket from 'ws';
import { retry, RetryConfig } from 'rxjs/operators'
import { webSocket } from 'rxjs/webSocket';
const ws$ = webSocket({
url: '...',
WebSocketCtor: WebSocket as any // apparently typings bug
});
// set up reconnects
const retryConfig: RetryConfig = {
delay: 3000,
};
ws$.pipe(retry(retryConfig));
// subscribe to channel by sending a message
ws$.next({cmd: 'subscribe', channel: 'updates'});
ws$.subscribe({
next: (msg) => console.log('msg', msg),
error: (e) => console.error('error', e),
complete: () => console.log('complete')
});
This seems to work so far but after a disconnect it only re-opens the connection and I'm wondering how could I execute custom logic in that case? Specifically here I'd like to re-send the subscription message.
Apart from just an instruction how to do this I'd really appreciate an explanation on why is it so, as rxjs seems to be a bit of a learning curve. Thank you!
Related
I am having problems with SvelteKit and SocketIO. I'm connecting to a NestJS back-end with a default SocketIO gateway and connecting works fine, but executing a socket.emit inside a function fails to trigger entirely. Not sure if this is SocketIO or SvelteKit related, but executing an emit outside of a function works. Here is my code snippet.
<script>
import io from 'socket.io-client';
let socket = io('http://localhost:5000');
socket.on("connect", () => {
console.log(socket.id);
});
socket.on("messages", (arg) => {
console.log(arg);
});
socket.emit("messages", "executes at load", (err) => {
console.log(err);
});
function onSendMessage() {
console.log('executing function');
socket.emit("messages", "test", (err) => {
console.log(err);
});
}
</script>
<button on:click={onSendMessage}>
Send Message
</button>
In this situation ''executes at load'' is printed to the console because it is emitted and the server sends the response back which the socket.on catches. It also prints the ID of the connection through socket.on("connect"). but it never will print ''test'' if i press the button. Pressing the button does console log the ''executing function''. Tested all functionality on Postman as well and the server works. Executing the function manually directly in the script tag without the button onclick results in the same behaviour of the emit not executing. Anyone has an idea?
After a long time of agony I discovered the problem. I think it has to do with the fact that it was trying to establish an XHR Polling connection on the clientside but not on the SSR side of SvelteKit, and it seems that XHR Polling does not support CORS but websockets do.
All I had to do was specify the transport as ''websocket'' on both the frontend and backend and it works perfectly!
I am new to web-sockets. I was reading this article on medium and in a part of the client code, the code disconnects the socket every time a new message is emitted.
Why is it so? Does this have some design advantage, is this how it is done?
Why not leave the socket connected for more message streams?
-----EDIT------
Here is the code that I am talking about. The code is from this blog
newMessageReceived() {
const observable = new Observable<{ user: String, message: String}>(observer => {
this.socket.on('new message', (data) => {
observer.next(data);
});
return () => {
// Why is the coder disconnecting the socket here?
this.socket.disconnect();
};
});
return observable;
}
I am using a Slack webhook to process incoming SMS messages from Twilio. However, the way I have it set up, It seems that Twilio is expecting the web server (slack) to respond to it. This causes errors to be generated in Twilio, and I obviously don't want errors because I'll be getting emails.
I am using the twilio-ruby gem in Ruby to send out the SMS messages, and using the slack-ruby-client to monitor incoming messages from Slack.
How do I stop Twilio from trying to expect a response from the web server when it POSTS to the Slack webhook? Is that even possible or do I have this all configured incorrectly?
EDIT
Here's the function that I have which sends the forwarded SMS to Slack:
const https = require("https");
// Make sure to declare SLACK_WEBHOOK_PATH in your Environment
// variables at
// https://www.twilio.com/console/runtime/functions/configure
exports.handler = (context, event, callback) => {
// Extract the bits of the message we want
const { To, From, Body } = event;
// Construct a payload for slack's incoming webhooks
const slackBody = JSON.stringify({
text: `!asi SMS\nFrom: ${From}\nMessage: ${Body}`
});
// Form our request specification
const options = {
host: "hooks.slack.com",
port: 443,
path: context.SLACK_WEBHOOK_PATH,
method: "POST",
headers: {
"Content-Type": "application/json",
"Content-Length": slackBody.length
}
};
// send the request
const post = https.request(options, res => {
// only respond once we're done, or Twilio's functions
// may kill our execution before we finish.
res.on("end", () => {
// respond with an empty message
callback(null, new Twilio.twiml.MessagingResponse());
});
});
post.write(slackBody);
post.end();
};
Twilio developer evangelist here.
Twilio is always going to expect at least a 200 response or will timeout at 15 seconds for incoming message webhooks.
You could avoid the error messages by using something in between Twilio and Slack, like Zapier (example in this blog post) or using a Twilio Function (as described here) or with Twilio Studio (from the documentation here).
Hope one of those ideas helps!
Update
Further to my earlier answer, and given the code you used to make the call, I have an update.
When making a request using Node's built in https module you will not get the end event until you have read the data. This is what is causing the timeout between Twilio and the Twilio Function, you are never responding to it because you don't consume the data from the request.
In a quick test I found that just listening for the data event meant that the end event did fire. So update your function to:
const post = https.request(options, res => {
// only respond once we're done, or Twilio's functions
// may kill our execution before we finish.
res.on("data", () => {});
res.on("end", () => {
// respond with an empty message
callback(null, new Twilio.twiml.MessagingResponse());
});
});
And it should work.
In Flutter, I wanna listen to websocket disconnect event, how to achieve that?
The websocket connect will be drop when app goes to background, I still not found a method to let it continuesly running in background (does anyone have solution?), So I have to detect if a websocket connect is lost or something, so that I can re-connect when lost connection.
Pls help if anyone knows how to achieve that.
You can find out if websocket is closed by implementing onDone callback. See the example below:
_channel = IOWebSocketChannel.connect(
'ws://yourserver.com:port',
);
///
/// Start listening to new notifications / messages
///
_channel.stream.listen(
(dynamic message) {
debugPrint('message $message');
},
onDone: () {
debugPrint('ws channel closed');
},
onError: (error) {
debugPrint('ws error $error');
},
);
Hope that helps.
If your server closes the connection just use pinginterval like this
ws.pingInterval = const Duration(seconds: 5);
onDone should be called.
basic ping pong is enough.
Other answers around SO and the web suggest that you can't just keep sockets open in the background (which seems reasonable, you'd be keeping open network connections that may affect battery life). Depending on your use case, you might be better looking at Push Notifications or something that checks on a schedule.
How to keep iphone ios xmpp connection alive while in the background?
Websocket paused when android app goes to background
https://www.quora.com/How-do-I-keep-Socket-IO-running-in-the-background-on-iOS
WebSocketChannel channel = WebSocketChannel.connect(uri );
Stream stream = channel.stream;
stream.listen((event) {
print('Event from Stream: $event');
},onError: (e){
Future.delayed(Duration(seconds: 10)).then((value) {
connectAndListen();
},);
},
onDone: (() {
Future.delayed(Duration(seconds: 10)).then((value) {
connectAndListen();
},);
})
);
I recommend you to use this multiplatform websocket package https://pub.dev/packages/websocket_universal , there you can even track all WS events happening (and even built-in ping measurment if you need any):
import 'package:websocket_universal/websocket_universal.dart';
/// Example works with Postman Echo server
void main() async {
/// Postman echo ws server (you can use your own server URI)
/// 'wss://ws.postman-echo.com/raw'
/// For local server it could look like 'ws://127.0.0.1:42627/websocket'
const websocketConnectionUri = 'wss://ws.postman-echo.com/raw';
const textMessageToServer = 'Hello server!';
const connectionOptions = SocketConnectionOptions(
pingIntervalMs: 3000, // send Ping message every 3000 ms
timeoutConnectionMs: 4000, // connection fail timeout after 4000 ms
/// see ping/pong messages in [logEventStream] stream
skipPingMessages: false,
/// Set this attribute to `true` if do not need any ping/pong
/// messages and ping measurement. Default is `false`
pingRestrictionForce: false,
);
/// Example with simple text messages exchanges with server
/// (not recommended for applications)
/// [<String, String>] generic types mean that we receive [String] messages
/// after deserialization and send [String] messages to server.
final IMessageProcessor<String, String> textSocketProcessor =
SocketSimpleTextProcessor();
final textSocketHandler = IWebSocketHandler<String, String>.createClient(
websocketConnectionUri, // Postman echo ws server
textSocketProcessor,
connectionOptions: connectionOptions,
);
// Listening to webSocket status changes
textSocketHandler.socketHandlerStateStream.listen((stateEvent) {
// ignore: avoid_print
print('> status changed to ${stateEvent.status}');
});
// Listening to server responses:
textSocketHandler.incomingMessagesStream.listen((inMsg) {
// ignore: avoid_print
print('> webSocket got text message from server: "$inMsg" '
'[ping: ${textSocketHandler.pingDelayMs}]');
});
// Listening to debug events inside webSocket
textSocketHandler.logEventStream.listen((debugEvent) {
// ignore: avoid_print
print('> debug event: ${debugEvent.socketLogEventType}'
' [ping=${debugEvent.pingMs} ms]. Debug message=${debugEvent.message}');
});
// Listening to outgoing messages:
textSocketHandler.outgoingMessagesStream.listen((inMsg) {
// ignore: avoid_print
print('> webSocket sent text message to server: "$inMsg" '
'[ping: ${textSocketHandler.pingDelayMs}]');
});
// Connecting to server:
final isTextSocketConnected = await textSocketHandler.connect();
if (!isTextSocketConnected) {
// ignore: avoid_print
print('Connection to [$websocketConnectionUri] failed for some reason!');
return;
}
textSocketHandler.sendMessage(textMessageToServer);
await Future<void>.delayed(const Duration(seconds: 30));
// Disconnecting from server:
await textSocketHandler.disconnect('manual disconnect');
// Disposing webSocket:
textSocketHandler.close();
}
My issue is:
$scope.socket = new SockJS("ws/ws");
$scope.stompClient = Stomp.over($scope.socket); // Connect to
$scope.stompClient.connect("test", "test", connectCallback, errorCallback);
function connectCallback () {
$scope.stompClient.subscribe('/testurl',someFunc());
}
Here my connectCallback method is not executing when connect is called. Neither errorCallback is executed. It just skips over this line although the socket is opened but I further want to subscribe inside connectCallback definition.
Can u suggest what issue it can be?
I try RabbitMQ with plugin STOMP
https://gist.github.com/tineo/f4e1f977d78d0db438c8
and i use a PHP for create queues, channels, exchanges
https://gist.github.com/tineo/9b25444fa5095f2ef225
Maybe you can reuse my files with Spring.