func (s *subscriptions) ResetSocketSubscriptions(socketId SocketId) []*subscription {
	var retSubs []*subscription
	s.lock.Lock()
	if set, ok := s.subsBySocketId[socketId]; ok {
		for _, sub := range set {
			retSubs = append(retSubs, sub)
			delete(s.subsByChanID, sub.ChanID)
			delete(s.subsBySubID, sub.SubID())
		}
	}
	s.subsBySocketId[socketId] = make(SubscriptionSet, 0)
	s.lock.Unlock()
	return retSubs
}
Reset clears all subscriptions assigned to the given socket ID, and returns
a slice of the existing subscriptions prior to reset
package websocket
import (
	"fmt"
	"github.com/op/go-logging"
	"strings"
	"sync"
	"time"
)
type SubscriptionRequest struct {
	SubID string `json:"subId"`
	Event string `json:"event"`
	APIKey      string   `json:"apiKey,omitempty"`
	AuthSig     string   `json:"authSig,omitempty"`
	AuthPayload string   `json:"authPayload,omitempty"`
	AuthNonce   string   `json:"authNonce,omitempty"`
	Filter      []string `json:"filter,omitempty"`
	DMS         int      `json:"dms,omitempty"`  
	Channel   string `json:"channel,omitempty"`
	Symbol    string `json:"symbol,omitempty"`
	Precision string `json:"prec,omitempty"`
	Frequency string `json:"freq,omitempty"`
	Key       string `json:"key,omitempty"`
	Len       string `json:"len,omitempty"`
	Pair      string `json:"pair,omitempty"`
}
const MaxChannels = 25
func (s *SubscriptionRequest) String() string {
	if s.Key == "" && s.Channel != "" && s.Symbol != "" {
		return fmt.Sprintf("%s %s", s.Channel, s.Symbol)
	}
	if s.Channel != "" && s.Symbol != "" && s.Precision != "" && s.Frequency != "" {
		return fmt.Sprintf("%s %s %s %s", s.Channel, s.Symbol, s.Precision, s.Frequency)
	}
	if s.Channel != "" && s.Symbol != "" {
		return fmt.Sprintf("%s %s", s.Channel, s.Symbol)
	}
	return ""
}
type HeartbeatDisconnect struct {
	Subscription *subscription
	Error        error
}
type UnsubscribeRequest struct {
	Event  string `json:"event"`
	ChanID int64  `json:"chanId"`
}
type subscription struct {
	ChanID     int64
	SocketId   SocketId
	pending    bool
	Public     bool
	Request    *SubscriptionRequest
	hbDeadline time.Time
}
func isPublic(request *SubscriptionRequest) bool {
	switch request.Channel {
	case ChanBook:
		return true
	case ChanCandles:
		return true
	case ChanTicker:
		return true
	case ChanTrades:
		return true
	case ChanStatus:
		return true
	}
	return false
}
func newSubscription(socketId SocketId, request *SubscriptionRequest) *subscription {
	return &subscription{
		ChanID:  -1,
		SocketId: socketId,
		Request: request,
		pending: true,
		Public:  isPublic(request),
	}
}
func (s subscription) SubID() string {
	return s.Request.SubID
}
func (s subscription) Pending() bool {
	return s.pending
}
func newSubscriptions(heartbeatTimeout time.Duration, log *logging.Logger) *subscriptions {
	subs := &subscriptions{
		subsBySubID:  make(map[string]*subscription),
		subsByChanID: make(map[int64]*subscription),
		subsBySocketId: make(map[SocketId]SubscriptionSet),
		hbTimeout:    heartbeatTimeout,
		hbShutdown:   make(chan struct{}),
		hbDisconnect: make(chan HeartbeatDisconnect),
		hbSleep:      heartbeatTimeout / time.Duration(4),
		log:          log,
		lock:         &sync.RWMutex{},
	}
	go subs.control()
	return subs
}
type heartbeat struct {
	ChanID int64
	*time.Time
}
type subscriptions struct {
	lock         *sync.RWMutex
	log          *logging.Logger
	subsBySocketId map[SocketId]SubscriptionSet  
	subsBySubID  map[string]*subscription  
	subsByChanID map[int64]*subscription   
	hbActive     bool
	hbDisconnect chan HeartbeatDisconnect  
	hbTimeout    time.Duration
	hbSleep      time.Duration
	hbShutdown   chan struct{}
}
type SubscriptionSet []*subscription
func (s SubscriptionSet) Len() int {
	return len(s)
}
func (s SubscriptionSet) Less(i, j int) bool {
	return strings.Compare(s[i].SubID(), s[j].SubID()) < 0
}
func (s SubscriptionSet) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s SubscriptionSet) RemoveByChannelId(chanId int64) SubscriptionSet {
	rIndex := -1
	for i, sub := range s {
		if sub.ChanID == chanId {
			rIndex = i
			break
		}
	}
	if rIndex >= 0 {
		return append(s[:rIndex], s[rIndex+1:]...)
	}
	return s
}
func (s SubscriptionSet) RemoveBySubscriptionId(subID string) SubscriptionSet {
	rIndex := -1
	for i, sub := range s {
		if sub.SubID() == subID {
			rIndex = i
			break
		}
	}
	if rIndex >= 0 {
		return append(s[:rIndex], s[rIndex+1:]...)
	}
	return s
}
func (s *subscriptions) heartbeat(chanID int64) {
	s.lock.Lock()
	defer s.lock.Unlock()
	if sub, ok := s.subsByChanID[chanID]; ok {
		sub.hbDeadline = time.Now().Add(s.hbTimeout)
	}
}
func (s *subscriptions) sweep(exp time.Time) {
	s.lock.RLock()
	if !s.hbActive {
		s.lock.RUnlock()
		return
	}
	disconnects := make([]HeartbeatDisconnect, 0)
	for _, sub := range s.subsByChanID {
		if exp.After(sub.hbDeadline) {
			s.hbActive = false
			hbErr := HeartbeatDisconnect{
				Subscription: sub,
				Error: fmt.Errorf("heartbeat disconnect on channel %d expired at %s (%s timeout)", sub.ChanID, sub.hbDeadline, s.hbTimeout),
			}
			disconnects = append(disconnects, hbErr)
		}
	}
	s.lock.RUnlock()
	for _, dis := range disconnects {
		s.hbDisconnect <- dis
	}
}
func (s *subscriptions) control() {
	for {
		select {
		case <-s.hbShutdown:
			return
		default:
		}
		s.sweep(time.Now())
		time.Sleep(s.hbSleep)
	}
}
func (s *subscriptions) Close() {
	s.ResetAll()
	close(s.hbShutdown)
}