Dynamic queues not created when subscribing to topic Spring WebSocket with STOMP? - spring

I am developing push notification to all subset of users who subscribed to particular event.
User subscribes to topic in RabbitMQ with format: user-id.event-type.id.
I use Spring Websocket, Stomp, RabbitMQ and on frontend SockJS and Angular JS.
User should be notified of all actions (comments etc, date change) about event.
What we have so far:
First I authenticate through REST webservice endpoint, and put my token to Cookie. Then we connect to
websocket. Users subscribes to topic (/topic/user-45.meeting.1235) and they get notification. But my problem is some users do not receive notification. For second user, for some reason queue is not created in RabbitMQ. Anyone knows why?
This is my broker settings in Spring applicationContext.xml:
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/stomp">
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:stomp-broker-relay relay-host="localhost" relay-port="61613" system-login="guest" system-passcode="guest" prefix="/queue, /topic"/>
</websocket:message-broker>
and this is how subscribe through Sockjs:
var ws = new SockJS('http://' + location.host + path);
var stompClient = Stomp.over(ws);
stompClient.connect({
username: '',
password: '',
host: '/'
}, function () {
stompClient.subscribe('/topic/user-45.meeting.' + obj.id,
function (message) {
console.log(message);
}, {
persistent: true
});
});
UPDATED
If we specify unique Id field in SUBSCRIBE frame, it creates unique queue for each user. Is this way to go?

As per my knowledge you need to subscribe the \queue not \topic.By doing this you don't need to customize topic name for different users,that will be handled by sockjs depending on logged user.And at server side you can also send messages to particular user by using \queue\user\{username}\{name of queue}

Related

Ngxs + WebSocket | How to listen the socket events at client?

I am using Ngxs/websocket-plugin in my project.
I connected the WebSocket with
this.store.dispatch(new ConnectWebSocket());
and dispatching the socket messages with
sendMessage(from: string, message: string) {
const event = new SendWebSocketMessage({
type: 'message',
from,
message
});
this.store.dispatch(event);
}
The socket events are being received on the server but I am not getting how to listen to the socket event at the client?
The socket messages are being dispatched in the store by the ngxs/websocket-plugin.
This plugin will receive a message from the server and dispatch the message as an action with the corresponding type value. If the type property doesn't match any client-side #Action methods (with an Action with the corresponding static type property value) then no State will respond to the message.

MassTransit message encryption and routing slip events

I have enabled encryption for my RabbitMQ bus as per MassTransit documentation:
bus = Bus.Factory.CreateUsingRabbitMq(rabbit =>
{
rabbit.Durable = true;
rabbit.Host(new Uri(settings.ServerUri), h =>
{
h.Username(settings.Username);
h.Password(settings.Password);
});
rabbit.ClearMessageDeserializers();
rabbit.UseEncryption(Convert.FromBase64String("..."));
...
});
I have also added subscription for a routing slip completed event:
var builder = new RoutingSlipBuilder(NewId.NextGuid());
builder.AddActivity(...);
await builder.AddSubscription(queueUri, RoutingSlipEvents.Completed,
x => x.Send<xxxRoutingSlipCompleted>(new { ctx.Data.CorrelationId }));
While all other messages get encrypted as expected, routing slip events get sent in plain text (as can be seen in RabbitMQ queue) and result in the following exception:
System.Runtime.Serialization.SerializationException:
No deserializer was registered for the message content type: application/vnd.masstransit+json.
Supported content types include application/vnd.masstransit.v2+aes
at MassTransit.Serialization.SupportedMessageDeserializers.Deserialize(ReceiveContext receiveContext)
at MassTransit.Pipeline.Filters.DeserializeFilter.Send(ReceiveContext context, IPipe`1 next)
at GreenPipes.Filters.RescueFilter`2.GreenPipes.IFilter<TContext>.Send(TContext context, IPipe`1 next)
Is there some additional configuration that needs to be applied to routing slips (I can't see anything relevant on the ISendEndpoint interface) or is this a bug in MassTransit?

Websocket request succeeds in writing to DynamoDB but returns Internal Server Error

I created an AWS API Gateway route for Websocket connections. I started with the AWS provided Simple Web Chat templates but have modified it to fit my needs. The API Gateway calls a Lambda function that writes to a DynamoDB table.
I am able to make a websocket connection but when I make my next request to insert some data the data appears successfully in my DynamoDB table but the response I get back is Internal Server Error.
I don't understand what is causing the Internal Server Error. When I look in the CloudWatch logs I just see normal traffic with no errors.
I could use some help understanding what is going wrong or how I can troubleshoot this better.
Here is the Lamba function that is being called:
const AWS = require("aws-sdk");
const customId = require("custom-id");
const ddb = new AWS.DynamoDB.DocumentClient({
apiVersion: "2012-08-10",
region: process.env.AWS_REGION,
});
exports.handler = async (event) => {
const uniqueId = customId({
randomLength: 1,
});
const data = {
uniqueId: uniqueId,
members: [
{
connectionId: event.requestContext.connectionId,
},
],
events: [],
parameters: [],
};
const putParams = {
TableName: process.env.EVENT_TABLE_NAME,
Item: data,
};
try {
await ddb.put(putParams).promise();
} catch (err) {
return {
statusCode: 400,
body: "Failed to create: " + JSON.stringify(err),
};
}
return { statusCode: 200, body: putParams };
};
Image of AWS CloudWatch Logs
The error returned by wcat looks like this:
{"message": "Internal server error", "connectionId":"NZxV_ddNIAMCJrw=", "requestId":"NZxafGiyoAMFoAA="}
I just had the same problem. The issue in my case was because API Gateway did not have permission to call the Lambda function in order to process a message arriving from the websocket. The 'internal server error' in this case is API Gateway saying it had some problem when it tried to invoke the Lambda function to handle the websocket message.
I was using CDK to deploy the infrastructure, and I created one WebSocketLambdaIntegration for the connect, disconnect and default websocket handlers, but this doesn't work. You have to create separate WebSocketLambdaIntegration instances even if you are calling the same Lambda function for all websocket events, otherwise CDK does not set the correct permissions.
I could see this was the problem because 1) I was using the same Lambda function for the connect, disconnect and default routes, and 2) in CloudWatch Logs I was only seeing log messages for one of these routes, in this case the 'connect' one. When I sent a message over the websocket, I was not seeing the expected log messages from the Lambda that was supposed to be handling incoming websocket messages. When I disconnected from the websocket, I did not see the expected log messages from the 'disconnect' handler.
This was because CDK had only given Lambda invoke permission to specific routes on the API Gateway websocket stage, and it had only authorised the 'connect' route, not the others.
Fixing the CDK stack so that it correctly assigned permissions, allowing API Gateway to invoke my Lambda for all websocket routes, fixed the problem.
I see it now. It was the last line. I changed it and now it works fine.
return { statusCode: 200, body: JSON.stringify(putParams) };

Twilio awaits response, I don't want server to respond

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.

User specific publish (subscriptions)

Here's a small issue I faced and couldn't find much info in the documentation. I am trying to create private chat messages. We have the following code to subscribe a user to a topic:
export const resolvers = {
Subscription: {
somethingChanged: {
subscribe: () => pubsub.asyncIterator('chat_messages'),
},
},
}
and to publish
pubsub.publish('chat_messages', { somethingChanged: { sender_id: 1, receiver_id: 2, message: 'test' }});
I have used onConnect to verify that the user is authenticated
const server = new ApolloServer({
typeDefs,
resolvers,
subscriptions: {
onConnect: (connectionParams, webSocket) => {
...
if (!authenticated) throw error
...
},
},
...
})
This works well when I want to subscribe users to a particular topic for example. But how do I implement, private user to user communication? I have tried the withFilter but can't seem to implement user specific authorization(with respect to a message) checks.
Here is a demo: https://github.com/mrdulin/apollo-server-express-starter/tree/master/src/subscription/demo-1
With these features:
jwt based auth for websocket connection
User channel which means who can receive message, who can not.
There are some conceptions you need know:
there are two types user: requestUser and subscribeUsers(include requestUser)
you should write the code in filterFn, for who can receive the message which requestUser send.
For example:
There are three subscribe users: s1(client-1), s2(client-2), s3(client-3)
When a request user(client-4) send a message(maybe mutation), you can get subscribe users and request users through context argument of filterFn.
According to these two type users' informations. You can write your own bussiness logic in filterFn to decide who can receive message, who can't.
P.S. beside context, you can get variables argument in filterFn from client. That will give more information to decide who can receive message and who can't
Sorry for my English!

Resources