Faye does not publish when using a browser on another computer in the network - ruby-on-rails-3.1

I have faye implementation in my rails application. The publish method works correctly when both browsers are on the same computer. When I access the application from another browser on another computer, it only works from client to server and does not publish to other clients. Also the publish event does not push to client when there are changes in the browser on the server.
Controller publish code:
def publish(channel, data)
message = {
:channel => channel,
:data => data,
:ext => {:faye_token => FAYE_OUTGOING_AUTH_TOKEN}
}
uri = URI.parse('http://localhost:9292/faye')
Net::HTTP.post_form(uri, :message => message.to_json)
end
Command to run faye:
rackup faye.ru -s thin -E production -d
Example:
A: Server,
B: Client1,
C: Client2
A B and C are different computers in same network, and are all subscribed to the same channel.
If I input data on B, A will see the data but C will not see the data until I refresh the page (Which is getting the data from db).
If I input data on A, it does not get published to the other clients.
If I input data on C, to a channel that only C and B are subscribed to, only C gets to see the data, and it is not published to B.
If A, B, and C were different browsers on the same computer, all the above cases would work.
I have ran this in Development mode, and have tried WEBrick, Unicorn, and Thin.
Any help would be appreciated.
Thanks.

To resolve the issue I replaced all instances of "localhost" with the address of the server on which Faye is running. This includes for subscribing clients to channels as well.
Hope it helps,
Cheers!

Hey Babak: I am also facing same kind of problem, I am using nodejs + express + faye. so I should add ip_addr:port to each subscribe,client
var client = new Faye.Client('/faye',{
endpoints:{
websocket:'http:ws.chat-yummyfoods.rhcloud.com'
}
,timeout: 20
});
client.disable('websocket');
console.log("client:"+client);
var subscription=client.subscribe('/channel', function(message) {
console.log("Message:"+message.text);
$('#messages').append('<p>'+message.text+'</p>');
});
subscription.then(function(){
console.log('subscribe is active');
alert('subscribe is active');
});

Related

Secure websockets with Cro

Briefly: I created a service on an internet server using Cro and websocket. Very simple using Cro examples. No problem when sending and receiving data from an HTML page when the page is served as localhost. When the page is served using https, the websocket cannot be established.
How is the wss protocol be used with Cro?
Update: After installing cro and running cro stub :secure, the service.p6 has some more code not explicit in the documentation.
More detail:
I have a docker file running on the internet server, Cro is set to listen on 35145, so the docker command is docker --rm -t myApp -p 35145:35145
The service file contains
use Cro::HTTP::Log::File;
use Cro::HTTP::Server;
use Cro::HTTP::Router;
use Cro::HTTP::Router::WebSocket;
my $host = %*ENV<RAKU_WEB_REPL_HOST> // '0.0.0.0';
my $port = %*ENV<RAKU_WEB_REPL_PORT> // 35145;
my Cro::Service $http = Cro::HTTP::Server.new(
http => <1.1>,
:$host,
:$port,
application => routes(),
after => [
Cro::HTTP::Log::File.new(logs => $*OUT, errors => $*ERR)
]
);
$http.start;
react {
whenever signal(SIGINT) {
say "Shutting down...";
$http.stop;
done;
}
}
sub routes() {
route {
get -> 'raku' {
web-socket :json, -> $incoming {
supply whenever $incoming -> $message {
my $json = await $message.body;
if $json<code> {
my $stdout, $stderr;
# process code
emit({ :$stdout, :$stderr })
}
}
}
}
}
}
In the HTML I have a textarea container with an id raku-code. The js script has the following (I set websocketHost and websocketPort elsewhere in the script) in a handler that fires after the DOM is ready:
const connect = function() {
// Return a promise, which will wait for the socket to open
return new Promise((resolve, reject) => {
// This calculates the link to the websocket.
const socketProtocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:');
const socketUrl = `${socketProtocol}//${websocketHost}:${websocketPort}/raku`;
socket = new WebSocket(socketUrl);
// This will fire once the socket opens
socket.onopen = (e) => {
// Send a little test data, which we can use on the server if we want
socket.send(JSON.stringify({ "loaded" : true }));
// Resolve the promise - we are connected
resolve();
}
// This will fire when the server sends the user a message
socket.onmessage = (data) => {
let parsedData = JSON.parse(data.data);
const resOut = document.getElementById('raku-ws-stdout');
const resErr = document.getElementById('raku-ws-stderr');
resOut.textContent = parsedData.stdout;
resErr.textContent = parsedData.stderr;
}
When an HTML file with this JS script is set up, and served locally I can send data to the Cro app running on an internet server, and the Cro App (running in a docker image) processes and returns data which is placed in the right HTML container. Using Firefox and the developer tools, I can see that the ws connection is created.
But when I serve the same file via Apache which forces access via https, Firefox issues an error that the 'wss' connection cannot be created. In addition, if I force a 'ws' connection in the JS script, Firefox prevents the creation of a non-secure connection.
a) How do I change the Cro coding to allow for wss? From the Cro documentation it seems I need to add a Cro::TLS listener, but it isn't clear where to instantiate the listener.
b) If this is to be in a docker file, would I need to include the secret encryption keys in the image, which is not something I would like to do?
c) Is there a way to put the Cro app behind the Apache server so that the websocket is decrypted/encrypted by Apache?
How do I change the Cro coding to allow for wss? From the Cro documentation it seems I need to add a Cro::TLS listener, but it isn't clear where to instantiate the listener.
Just pass the needed arguments to Cro::HTTP::Server, it will set up the listener for you.
If this is to be in a docker file, would I need to include the secret encryption keys in the image, which is not something I would like to do?
No. You can keep them in a volume, or bind-mount them from the host machine.
Is there a way to put the Cro app behind the Apache server so that the websocket is decrypted/encrypted by Apache?
Yes, same as with any other app. Use mod_proxy, mod_proxy_wstunnel and a ProxyPass command. Other frontends such as nginx, haproxy, or envoy will also do the job.
Though is not a pure cro solution, but you can
run your cro app on (none ssl/https) http/web socket port - localhost
and then have an Nginx server (configured to serve https/ssl trafic) to handle incoming public https/ssl requests and bypass them
as a plain http traffic to your app using
nginx reverse proxy mechanism (this is also often referred as an ssl termination), that way you
remove a necessity to handle https/ssl on cro side.
The only hurdle here might be if a web sockets
protocol is handled well by Nginx proxy. I’ve never tried that but probably you should be fine according to the Nginx docs - https://www.nginx.com/blog/websocket-nginx/

Blazor & Custom Certificates

I'm investigating the idea of using Blazor WASM to build a retail application that would run on an office Intranet. The application would be installed on a given machine, to be accessed via browser from any of several machines on the LAN.
The biggest stumbling block I'm running into is the question of how to go about securing the channel.
The app itself would run as a Windows Service, listening on port 443 on one of the workstations, e.g. https://reception/. But how do we tell Blazor to use a self-signed TLS cert for that hostname?
If there's a better way to go about this, I'm all ears. I can't use Let's Encrypt certs, because neither the application nor its hostname will be exposed to the public Internet.
There is a glut of information on working with Blazor to build such an app, but most if not all demos run on localhost. That works fine for dev, but not for production (in a self-hosting scenario, anyway). There doesn't seem to be much discussion at all of this aspect of things.
How can we use a custom certificate for browser requests from the client to a Blazor WASM app?
Any ideas?
I was able to get this working using some slightly modified sample code from the official documentation:
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenAnyIP(443, listenOptions =>
{
listenOptions.UseHttps(httpsOptions =>
{
var testCert = CertificateLoader.LoadFromStoreCert(
"test", "My", StoreLocation.CurrentUser,
allowInvalid: true);
var certs = new Dictionary<string, X509Certificate2>(
StringComparer.OrdinalIgnoreCase)
{
["test"] = testCert
};
httpsOptions.ServerCertificateSelector = (connectionContext, name) =>
{
if (name is not null && certs.TryGetValue(name, out var cert))
{
return cert;
}
return testCert;
};
});
});
});
The easiest way to handle SSL is to use IIS that will act as a proxy for your Blazor app.
IIS will give you easy access to well documented SSL settings.
https://learn.microsoft.com/en-us/aspnet/core/blazor/host-and-deploy/webassembly?view=aspnetcore-6.0#standalone-deployment

Why is my Websocket connection not working in the Developer Console?

I'm trying to connect to a server using websockets. I've set up the port on the server, and am trying to connect through the Dev console in Chrome (also tried Firefox and got the same result).
I connect from the console using:
var websocket = new WebSocket('ws://localhost:5001');
This gives me the message "undefined". However, if I do
websocket.readyState
I get "1".
I then do
websocket.binaryType = 'arraybuffer';
which prints "arraybuffer".
If I then do something like
websocket.send("1+1");
it says undefined.
However, if I do all of this in an HTML file with JavaScript, it connects fine and I get the result "2", so it looks as though the Websocket itself is ok, and what I'm typing in is ok, but it's something to do with it being in the Dev Console that's the issue.
I don't know anything much about setting up Websockets.
I solved this by adding an onmessage function to the websocket:
websocket.onmessage = function (result) {
console.log(result.data);
}

stompjs + rabbitmq - create Auto-Delete queues

We're using RabbitMQ + StompJS (w/ SockJS & Spring Websocket as middleware, FWIW) to facilitate broadcasting messages over websockets. Everything is working great, except no matter what we try StompJS creates the Queues as non-auto-delete, meaning we end up with TONS of queues.
We're working around it right now with a policy that cleans out inactive queues after several hours, but we'd rather just have auto-delete queues that terminate after all clients disconnect.
We've attempted setting headers auto_delete, auto-delete, autoDelete and every other possible incantation we can find.
If we stop an inspect the frames before they're transmitted (at the lowest possible level in the depths of StompJS's source) we can see those headers being present. However, they don't seem to be making it to RabbitMQ (or it just doesn't look at them on the "SUBSCRIPTION" command??) and creates them as non-auto-delete.
Interestingly, if we create the queue manually beforehand as auto-delete, the StompJS registration calls error out because the requested SUBSCRIBE expected non-auto-delete. This suggests that it's StompJS (or SockJS) that explicitly state non-auto-delete, but we've poured over the source and ruled that out.
So, the million dollar question: how can we have auto-delete queues with StompJS? Please, pretty please, and thanks in advance :)
Example registration
function reg(dest, callback, headers){
stomp.subscribe(dest, callback, headers);
}
function cb(payload){
console.log(JSON.parse(payload.body));
}
reg('/queue/foobar', cb, {});
Setup details
RabbitMQ 3.5.2 and StompJS 2.3.3
** Note **
If I subscribe directly to the exchange (with destinations like /exchange/foo or /topic/foo) the exchange will be defined as auto-delete. It's only queues that aren't auto-delete.
I'm using StompJS/RabbitMQ in production and I'm not seeing this issue. I can't say for sure what your problem is, but I can detail my setup in the hope you might spot some differences that may help.
I'm running against Rabbit MQ 3.0.1.
I'm using SockJS 0.3.4, I seem to recall having some issues using a more recent release from GitHub, but unfortunately I didn't take notes so I'm not sure what the issue was.
I'm using StompJS 2.3.4
For reasons I won't go into here - I've disabled the WebSockets transport, by whitelisting all the other transports.
Here's some simplified code showing how I connect:
var socket = new SockJS(config.stompUrl, null, { protocols_whitelist: ['xdr-streaming', 'xhr-streaming', 'iframe-eventsource', 'iframe-htmlfile', 'xdr-polling', 'xhr-polling', 'iframe-xhr-polling', 'jsonp-polling'] });
var client = Stomp.over(socket);
client.debug = function () { };
client.heartbeat.outgoing = 0;
client.heartbeat.incoming = 0;
client.connect(config.rabbitUsername, config.rabbitPassword, function () {
onConnected();
}, function () {
reconnect(d);
}, '/');
And here's how I disconnect:
// close the socket first, otherwise STOMP throws an error on disconnect
socket.close();
client.disconnect(function () {
isConnected = false;
});
And here's how I subscribe (this happens inside my onConnected function):
client.subscribe('/topic/{routing-key}', function (x) {
var message = JSON.parse(x.body);
// do stuff with message
});
My first recommendation would be to try the specific versions of the client libs I've listed. I had some issues getting these to play nicely - and these versions work for me.
It is possible with RabbitMQ 3.6.0+ by setting auto-delete in subscribe headers to true. Please see https://www.rabbitmq.com/stomp.html#queue-parameters for details.

Realtime display using redis pubsub in ruby?

I have stream of data coming to me via an http hit. I want to update data in realtime. I have started pushing HTTP hits data to a redis pubsub. Now I want to show it to users.
I want to update user's screen as soon as I get some data on redis channel. I want to use ruby as that is the language I am comfortable with.
I would use Sinatra's "stream" feature coupled with EventSource on the client side. Leaves IE out, though.
Here's some mostly functional server side code pulled from https://github.com/redis/redis-rb/blob/master/examples/pubsub.rb (another option is https://github.com/pietern/hiredis-rb):
get '/the_stream', provides: 'text/event-stream' do
stream :keep_open do |out|
redis = Redis.new
redis.subscribe(:channel1, :channel2) do |on|
on.message do |channel, msg|
out << "data: #{msg}\n\n" # This is an EventSource message
end
end
end
end
Client side. Most modern browsers support EventSource, except IE:
var stream = new EventSource('/the_stream');
stream.onmessage = function(e) {
alert("I just got this from the server: " + e.data);
}
As of I know you can do this via Faye check this link out
There are couple approach If you wish you can try
I remember myself building a Long Polling server using thin and sinatra to achieve something like this now If u wish you can do the same
I know of few like this and this flash client that you can use to connect directly to redis
There is EventMachine Websocket implementation u can use and hook it up with HTML 5 and Flash for non HTML 5 browser
Websocket-Rack
Other Approach you can try just a suggestion since most of them arent written in ruby
Juggernaut ( I dont think it based on Redis Pub-sub Thing also there used to ruby thing earlier not sure of now)
Socket.io
Webd.is
NULLMQ Not a redis pub sub but this is Zero MQ implementation in javascript
There are few other approach you can find If u google up :)
Hope this help

Resources