ryanb / private_pub

Handle pub/sub messaging through private channels in Rails using Faye.
MIT License
864 stars 228 forks source link

Broadcast message on client unsubscribe #76

Open mariusbutuc opened 11 years ago

mariusbutuc commented 11 years ago

I'm using private_pub to implement a one-to-one chat-like application.

Here is my story: as a user, I would like to receive a message when my partner leaves the chat – closes the window, etc.

Looking through the Faye Monitoring docs here is my attempt at binding on unsubscribe:

# Run with: rackup private_pub.ru -s thin -E production
require "bundler/setup"
require "yaml"
require "faye"
require "private_pub"
require "active_support/core_ext"

Faye::WebSocket.load_adapter('thin')

PrivatePub.load_config(File.expand_path("../config/private_pub.yml", __FILE__), ENV["RAILS_ENV"] || "development")

wts_pubsub = PrivatePub.faye_app

wts_pubsub.bind(:subscribe) do |client_id, channel|
  puts "[#{Time.now}] Client #{client_id} joined  #{channel}"
end

wts_pubsub.bind(:unsubscribe) do |client_id, channel|
  puts "[#{Time.now}] Client #{client_id} disconnected from #{channel}"
  PrivatePub.publish_to channel, { marius_says: 'quitter' }
end

run wts_pubsub

but I keep getting timeouts: [ERROR] [Faye::RackAdapter] Timeout::Error

Prying into PrivatePub#publish_to, data holds what I expect both when I'm publishing from the Rails or the private_pub app, but the private_pub app keeps hanging.

How can I get publishing from private_pub to work?

ryanb commented 11 years ago

I'm not certain the Rackup app can trigger PrivatePub.publish_to. This tries to open a socket connection to the rack app, but since it's busy trying to open a connection it isn't able to receive one and therefore times out. At least that's my guess. I haven't tested it.

mariusbutuc commented 11 years ago

Is there an alternative way to handle broadcasting a message when a client either disconnects or unsubscribes?

rjansen72 commented 10 years ago

I'm also trying to do something similar. My first attempt was exactly what you did - use PrivatePub.publish_to but saw it cause a hang. My next attempt was to use Faye's client directly from the app - the Faye::RakeAdapter class provides a get_client method for just this purpose. The first issue you will hit is that messages you send do not contain the necessary items that PrivatePub will verify. I worked around that calling PrivatePub.message providing bogus channel / data params just so I could get the "ext" data that I needed. I then included that ext data in my own message. Unfortunately this did not work, as it appears to construct a message with slightly different structure than what PrivatePub authenticate_publish expects.

More specifically, Faye::Client.publish sends a message with the format: { "channel" => "channel_name", "data" => { custom_attr_1: "custom_value", "ext": { "private_pub_token": "private_token_here" } } }

But the PrivatePub authorize_publish assumes the "ext" is at the root level, not nested inside of "data".

I'm not sure whether the issue is that Faye generates different message formats based on the source of the message or whether private_pub needs to handle these different cases. I resolved this by modifying private_pub to be more lenient where the "ext" hash might be located. Hope this helps.