Files
ipe/conn.go
T
claudemiro be66978479 Changed the data field from Client WebHook
The value correct is the value associated with the event, not the data
attached to the subscription.
2015-01-13 00:38:48 -03:00

228 lines
5.1 KiB
Go

// Copyright 2014 Claudemiro Alves Feitosa Neto. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package main
import (
"encoding/json"
"errors"
"strings"
"sync"
"time"
"strconv"
log "github.com/golang/glog"
"github.com/gorilla/websocket"
)
// This mutex is used to sync the generation of the new ID
var mutex = &sync.Mutex{}
// This variable store the current user id
// Every call to newID this variable is incremented
var currentID = 0
// A subscriber
type Subscriber struct {
Id string
SocketID string
Socket *websocket.Conn
}
// A Channel Subscription
type Subscription struct {
Subscriber *Subscriber
Data string
}
// Create a new Subscription
func NewSubscription(subscriber *Subscriber, data string) *Subscription {
return &Subscription{Subscriber: subscriber, Data: data}
}
// A channel
type Channel struct {
sync.Mutex
CreatedAt time.Time
ChannelID string
Subscriptions map[string]*Subscription
}
// Return true if the channel has at least one subscriber
func (c *Channel) IsOccupied() bool {
return c.TotalSubscriptions() > 0
}
// Check if the type of the channel is presence or is private
func (c *Channel) IsPresenceOrPrivate() bool {
return c.IsPresence() || c.IsPrivate()
}
// Check if the type of the channel is public
func (c *Channel) IsPublic() bool {
return !c.IsPresenceOrPrivate()
}
// Check if the type of the channel is presence
func (c *Channel) IsPresence() bool {
return strings.HasPrefix(c.ChannelID, "presence-")
}
// Check if the type of the channel is private
func (c *Channel) IsPrivate() bool {
return strings.HasPrefix(c.ChannelID, "private-")
}
// Get the total of subscribers
func (c *Channel) TotalSubscriptions() int {
return len(c.Subscriptions)
}
// Get the total of users.
// For now, totalUsers is equal to totalSubscribers
func (c *Channel) TotalUsers() int {
return c.TotalSubscriptions()
}
// Add a new subscriber to the channel
func (c *Channel) Subscribe(a *App, s *Subscriber, data string) {
log.Infof("Subscribing %s to channel %s", s.SocketID, c.ChannelID)
c.Lock()
c.Subscriptions[s.SocketID] = NewSubscription(s, data)
c.Unlock()
if c.IsPresence() {
// Publish pusher_internal:member_added - Para todos
// WebHook
a.TriggerMemberAddedHook(c, s)
// pusher_internal:subscription_succeeded
data := make(map[string]SubscriptionSucceeedEventPresenceData, 1)
data["presence"] = NewSubscriptionSucceedEventPresenceData(c)
js, err := json.Marshal(data)
if err != nil {
log.Error(err)
}
if err := s.Publish(NewSubscriptionSucceededEvent(c.ChannelID, string(js))); err != nil {
log.Error(err)
}
}
// WebHook
if c.TotalSubscriptions() == 1 {
a.TriggerChannelOccupiedHook(c)
}
}
// IsSubscribed check if the user is subscribed
func (c *Channel) IsSubscribed(s *Subscriber) bool {
_, exists := c.Subscriptions[s.SocketID]
return exists
}
// Remove the subscriber from the channel
// It destroy the channel if the channels does not have any subscribers.
func (c *Channel) Unsubscribe(a *App, s *Subscriber) error {
log.Infof("Unsubscribing %s from channel %s", s.SocketID, c.ChannelID)
c.Lock()
defer c.Unlock()
_, exists := c.Subscriptions[s.SocketID]
if !exists {
return errors.New("Subscription not found")
}
delete(c.Subscriptions, s.SocketID)
if c.IsPresence() {
// Publish pusher_internal:member_removed
// Webhook
a.TriggerMemberRemovedHook(c, s)
}
// WebHook
if c.TotalSubscriptions() == 0 {
a.TriggerChannelVacatedHook(c)
}
return nil
}
// Create a new Channel
func NewChannel(channelID string) *Channel {
log.Infof("Creating a new channel: %s", channelID)
return &Channel{ChannelID: channelID, CreatedAt: time.Now(), Subscriptions: make(map[string]*Subscription)}
}
// This function generate a sequencial ID
func newID() string {
mutex.Lock()
defer mutex.Unlock()
currentID += 1
return strconv.Itoa(currentID)
}
// Create a new Subscriber
func NewSubscriber(socketID string, s *websocket.Conn) *Subscriber {
id := newID()
log.Infof("Creating a new Subscriber %+v with id %s", socketID, id)
return &Subscriber{Id: id, SocketID: socketID, Socket: s}
}
// Publish messages to all Subscribers
func (c *Channel) Publish(a *App, event RawEvent, ignore string) error {
b, err := event.Data.MarshalJSON()
if err != nil {
return err
}
var v interface{}
if err := json.Unmarshal(b, &v); err != nil {
return err
}
log.Infof("Publishing message %+v to channel %s", v, c.ChannelID)
for _, subs := range c.Subscriptions {
if subs.Subscriber.SocketID != ignore {
js := NewResponseEvent(event.Event, event.Channel, v)
if err := subs.Subscriber.Publish(js); err != nil {
continue
}
} else {
// Webhook
if strings.HasPrefix(event.Event, "client-") {
a.TriggerClientEventHook(c, subs, event.Event, v)
}
}
}
return nil
}
// Publish the message to websocket atached to this client
func (s *Subscriber) Publish(m interface{}) error {
if err := s.Socket.WriteJSON(m); err != nil {
log.Errorf("Error sending message to subscriber %+v, %s", s, err)
return err
}
return nil
}