diff --git a/socketserver/server/commands.go b/socketserver/server/commands.go index 6865d2e2..8f77b2e4 100644 --- a/socketserver/server/commands.go +++ b/socketserver/server/commands.go @@ -17,7 +17,7 @@ import ( // The Commands sent from Client -> Server and Server -> Client are disjoint sets. type Command string -// CommandHandler is a RPC handler assosciated with a Command. +// CommandHandler is a RPC handler associated with a Command. type CommandHandler func(*websocket.Conn, *ClientInfo, ClientMessage) (ClientMessage, error) var commandHandlers = map[Command]CommandHandler{ @@ -43,10 +43,10 @@ var commandHandlers = map[Command]CommandHandler{ func internCommands() { CommandPool = NewStringPool() - CommandPool._Intern_Setup(HelloCommand) + CommandPool._Intern_Setup(string(HelloCommand)) CommandPool._Intern_Setup("ping") - CommandPool._Intern_Setup(SetUserCommand) - CommandPool._Intern_Setup(ReadyCommand) + CommandPool._Intern_Setup(string(SetUserCommand)) + CommandPool._Intern_Setup(string(ReadyCommand)) CommandPool._Intern_Setup("sub") CommandPool._Intern_Setup("unsub") CommandPool._Intern_Setup("track_follow") @@ -155,8 +155,8 @@ func C2SSetUser(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rm } client.Mutex.Lock() - client.TwitchUsername = username client.UsernameValidated = false + client.TwitchUsername = username client.Mutex.Unlock() if Configuration.SendAuthToNewClients { @@ -262,7 +262,7 @@ func C2STrackFollow(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) now := time.Now() followEventsLock.Lock() - followEvents = append(followEvents, followEvent{client.TwitchUsername, channel, following, now}) + followEvents = append(followEvents, followEvent{User: client.TwitchUsername, Channel: channel, NowFollowing: following, Timestamp: now}) followEventsLock.Unlock() return ResponseSuccess, nil diff --git a/socketserver/server/handlecore.go b/socketserver/server/handlecore.go index 677f54dc..1486d502 100644 --- a/socketserver/server/handlecore.go +++ b/socketserver/server/handlecore.go @@ -60,7 +60,7 @@ var Configuration *ConfigFile var janitorsOnce sync.Once -var CommandPool StringPool +var CommandPool *StringPool // SetupServerAndHandle starts all background goroutines and registers HTTP listeners on the given ServeMux. // Essentially, this function completely preps the server for a http.ListenAndServe call. @@ -573,7 +573,7 @@ func MarshalClientMessage(clientMessage interface{}) (payloadType int, data []by return websocket.TextMessage, []byte(dataStr), nil } -// Convenience method: Parse the arguments of the ClientMessage as a single string. +// ArgumentsAsString parses the arguments of the ClientMessage as a single string. func (cm *ClientMessage) ArgumentsAsString() (string1 string, err error) { var ok bool string1, ok = cm.Arguments.(string) @@ -585,7 +585,7 @@ func (cm *ClientMessage) ArgumentsAsString() (string1 string, err error) { } } -// Convenience method: Parse the arguments of the ClientMessage as a single int. +// ArgumentsAsInt parses the arguments of the ClientMessage as a single int. func (cm *ClientMessage) ArgumentsAsInt() (int1 int64, err error) { var ok bool var num float64 @@ -599,7 +599,7 @@ func (cm *ClientMessage) ArgumentsAsInt() (int1 int64, err error) { } } -// Convenience method: Parse the arguments of the ClientMessage as an array of two strings. +// ArgumentsAsTwoStrings parses the arguments of the ClientMessage as an array of two strings. func (cm *ClientMessage) ArgumentsAsTwoStrings() (string1, string2 string, err error) { var ok bool var ary []interface{} @@ -630,7 +630,7 @@ func (cm *ClientMessage) ArgumentsAsTwoStrings() (string1, string2 string, err e } } -// Convenience method: Parse the arguments of the ClientMessage as an array of a string and an int. +// ArgumentsAsStringAndInt parses the arguments of the ClientMessage as an array of a string and an int. func (cm *ClientMessage) ArgumentsAsStringAndInt() (string1 string, int int64, err error) { var ok bool var ary []interface{} @@ -663,7 +663,7 @@ func (cm *ClientMessage) ArgumentsAsStringAndInt() (string1 string, int int64, e } } -// Convenience method: Parse the arguments of the ClientMessage as an array of a string and an int. +// ArgumentsAsStringAndBool parses the arguments of the ClientMessage as an array of a string and an int. func (cm *ClientMessage) ArgumentsAsStringAndBool() (str string, flag bool, err error) { var ok bool var ary []interface{} diff --git a/socketserver/server/intern.go b/socketserver/server/intern.go index e2eb8e1e..fb319d5a 100644 --- a/socketserver/server/intern.go +++ b/socketserver/server/intern.go @@ -32,6 +32,7 @@ func (p *StringPool) Intern(s string) Command { if exists { return ss } - p.lookup[s] = Command(string([]byte(s))) - return s + ss = Command(string([]byte(s))) // make a copy + p.lookup[s] = ss + return ss } diff --git a/socketserver/server/irc.go b/socketserver/server/irc.go index 568cb534..98234829 100644 --- a/socketserver/server/irc.go +++ b/socketserver/server/irc.go @@ -4,7 +4,6 @@ import ( "bytes" "crypto/rand" "encoding/base64" - "errors" irc "github.com/fluffle/goirc/client" "log" "strings" @@ -87,10 +86,6 @@ const AuthChannelName = "frankerfacezauthorizer" const AuthChannel = "#" + AuthChannelName const AuthCommand = "AUTH" -const DEBUG = "DEBUG" - -var errChallengeNotFound = errors.New("did not find a challenge solved by that message") - // is_init_func func ircConnection() { diff --git a/socketserver/server/publisher.go b/socketserver/server/publisher.go index 7c454a35..a11c471e 100644 --- a/socketserver/server/publisher.go +++ b/socketserver/server/publisher.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "net/http" - "sort" "strconv" "strings" "sync" @@ -33,36 +32,37 @@ var S2CCommandsCacheInfo = map[Command]PushCommandCacheInfo{ type BacklogCacheType int const ( - // This is not a cache type. + // CacheTypeInvalid is the sentinel value. CacheTypeInvalid BacklogCacheType = iota - // This message cannot be cached. + // CacheTypeNever is a message that cannot be cached. CacheTypeNever - // Save only the last copy of this message, and always send it when the backlog is requested. + // CacheTypeLastOnly means to save only the last copy of this message, + // and always send it when the backlog is requested. CacheTypeLastOnly - // Save this backlog data to disk with its timestamp. - // Send it when the backlog is requested, or after a reconnect if it was updated. + // CacheTypePersistent means to save the last copy of this message, + // and always send it when the backlog is requested, but do not clean it periodically. CacheTypePersistent ) type MessageTargetType int const ( - // This is not a message target. + // MsgTargetTypeInvalid is the sentinel value. MsgTargetTypeInvalid MessageTargetType = iota - // This message is targeted to all users in a chat + // MsgTargetTypeChat is a message is targeted to all users in a particular chat. MsgTargetTypeChat - // This message is targeted to all users in multiple chats + // MsgTargetTypeMultichat is a message is targeted to all users in multiple chats. MsgTargetTypeMultichat - // This message is sent to all FFZ users. + // MsgTargetTypeGlobal is a message sent to all FFZ users. MsgTargetTypeGlobal ) // note: see types.go for methods on these -// Returned by BacklogCacheType.UnmarshalJSON() +// ErrorUnrecognizedCacheType is returned by BacklogCacheType.UnmarshalJSON() var ErrorUnrecognizedCacheType = errors.New("Invalid value for cachetype") -// Returned by MessageTargetType.UnmarshalJSON() +// ErrorUnrecognizedTargetType is returned by MessageTargetType.UnmarshalJSON() var ErrorUnrecognizedTargetType = errors.New("Invalid value for message target") type LastSavedMessage struct { @@ -72,11 +72,11 @@ type LastSavedMessage struct { // map is command -> channel -> data -// CacheTypeLastOnly. Cleaned up by reaper goroutine every ~hour. +// CachedLastMessages is of CacheTypeLastOnly. Cleaned up by reaper goroutine every ~hour. var CachedLastMessages = make(map[Command]map[string]LastSavedMessage) var CachedLSMLock sync.RWMutex -// CacheTypePersistent. Never cleaned. +// PersistentLastMessages is of CacheTypePersistent. Never cleaned. var PersistentLastMessages = make(map[Command]map[string]LastSavedMessage) var PersistentLSMLock sync.RWMutex @@ -135,50 +135,11 @@ func SendBacklogForNewClient(client *ClientInfo) { CachedLSMLock.RUnlock() } -// insertionSort implements insertion sort. -// CacheTypeTimestamps should use insertion sort for O(N) average performance. -// (The average case is the array is still sorted after insertion of the new item.) -func insertionSort(ary sort.Interface) { - for i := 1; i < ary.Len(); i++ { - for j := i; j > 0 && ary.Less(j, j-1); j-- { - ary.Swap(j, j-1) - } - } -} - type timestampArray interface { Len() int GetTime(int) time.Time } -func findFirstNewMessage(ary timestampArray, disconnectTime time.Time) (idx int) { - len := ary.Len() - i := len - - // Walk backwards until we find GetTime() before disconnectTime - step := 1 - for i > 0 { - i -= step - if i < 0 { - i = 0 - } - if !ary.GetTime(i).After(disconnectTime) { - break - } - step = int(float64(step)*1.5) + 1 - } - - // Walk forwards until we find GetTime() after disconnectTime - for i < len && !ary.GetTime(i).After(disconnectTime) { - i++ - } - - if i == len { - return -1 - } - return i -} - func SaveLastMessage(which map[Command]map[string]LastSavedMessage, locker sync.Locker, cmd Command, channel string, timestamp time.Time, data string, deleting bool) { locker.Lock() defer locker.Unlock() @@ -195,7 +156,7 @@ func SaveLastMessage(which map[Command]map[string]LastSavedMessage, locker sync. if deleting { delete(chanMap, channel) } else { - chanMap[channel] = LastSavedMessage{timestamp, data} + chanMap[channel] = LastSavedMessage{Timestamp: timestamp, Data: data} } } @@ -224,7 +185,8 @@ func HTTPBackendDropBacklog(w http.ResponseWriter, r *http.Request) { } } -// Publish a message to clients, and update the in-server cache for the message. +// HTTPBackendCachedPublish handles the /cached_pub route. +// It publishes a message to clients, and then updates the in-server cache for the message. // notes: // `scope` is implicit in the command func HTTPBackendCachedPublish(w http.ResponseWriter, r *http.Request) { diff --git a/socketserver/server/stats.go b/socketserver/server/stats.go index 24fd52dc..8b4d8d9c 100644 --- a/socketserver/server/stats.go +++ b/socketserver/server/stats.go @@ -73,7 +73,7 @@ func commandCounter() { } } -// StatsDataVersion +// StatsDataVersion is the version of the StatsData struct. const StatsDataVersion = 5 const pageSize = 4096 diff --git a/socketserver/server/subscriptions.go b/socketserver/server/subscriptions.go index ecd73a0a..890e2e19 100644 --- a/socketserver/server/subscriptions.go +++ b/socketserver/server/subscriptions.go @@ -99,7 +99,9 @@ func UnsubscribeSingleChat(client *ClientInfo, channelName string) { ChatSubscriptionLock.RUnlock() } -// Unsubscribe the client from all channels, AND clear the CurrentChannels / WatchingChannels fields. +// UnsubscribeAll will unsubscribe the client from all channels, +// AND clear the CurrentChannels / WatchingChannels fields. +// // Locks: // - read lock to top-level maps // - write lock to SubscriptionInfos diff --git a/socketserver/server/subscriptions_test.go b/socketserver/server/subscriptions_test.go index c0c04720..c24f88e8 100644 --- a/socketserver/server/subscriptions_test.go +++ b/socketserver/server/subscriptions_test.go @@ -30,9 +30,9 @@ func TestSubscriptionAndPublish(t *testing.T) { const TestData3 = false var TestData4 = []interface{}{"str1", "str2", "str3"} - S2CCommandsCacheInfo[TestCommandChan] = PushCommandCacheInfo{CacheTypeLastOnly, MsgTargetTypeChat} - S2CCommandsCacheInfo[TestCommandMulti] = PushCommandCacheInfo{CacheTypeLastOnly, MsgTargetTypeMultichat} - S2CCommandsCacheInfo[TestCommandGlobal] = PushCommandCacheInfo{CacheTypeLastOnly, MsgTargetTypeGlobal} + S2CCommandsCacheInfo[TestCommandChan] = PushCommandCacheInfo{Caching: CacheTypeLastOnly, Target: MsgTargetTypeChat} + S2CCommandsCacheInfo[TestCommandMulti] = PushCommandCacheInfo{Caching: CacheTypeLastOnly, Target: MsgTargetTypeMultichat} + S2CCommandsCacheInfo[TestCommandGlobal] = PushCommandCacheInfo{Caching: CacheTypeLastOnly, Target: MsgTargetTypeGlobal} var server *httptest.Server var urls TURLs diff --git a/socketserver/server/types.go b/socketserver/server/types.go index 04614b2a..3bbca724 100644 --- a/socketserver/server/types.go +++ b/socketserver/server/types.go @@ -9,8 +9,6 @@ import ( "time" ) -const CryptoBoxKeyLength = 32 - const NegativeOne = ^uint64(0) type ConfigFile struct { @@ -94,7 +92,7 @@ type ClientInfo struct { // If it seems to be a performance problem, we can split this. Mutex sync.Mutex - // TODO(riking) - does this need to be protected cross-thread? + // Info about the client's username and whether or not we have verified it. AuthInfo RemoteAddr net.Addr @@ -187,15 +185,15 @@ func BacklogCacheTypeByName(name string) (bct BacklogCacheType) { return } -// Implements Stringer +// String implements Stringer func (bct BacklogCacheType) String() string { return bct.Name() } -// Implements json.Marshaler +// MarshalJSON implements json.Marshaler func (bct BacklogCacheType) MarshalJSON() ([]byte, error) { return json.Marshal(bct.Name()) } -// Implements json.Unmarshaler +// UnmarshalJSON implements json.Unmarshaler func (bct *BacklogCacheType) UnmarshalJSON(data []byte) error { var str string err := json.Unmarshal(data, &str) @@ -240,15 +238,15 @@ func MessageTargetTypeByName(name string) (mtt MessageTargetType) { return } -// Implements Stringer +// String implements Stringer func (mtt MessageTargetType) String() string { return mtt.Name() } -// Implements json.Marshaler +// MarshalJSON implements json.Marshaler func (mtt MessageTargetType) MarshalJSON() ([]byte, error) { return json.Marshal(mtt.Name()) } -// Implements json.Unmarshaler +// UnmarshalJSON implements json.Unmarshaler func (mtt *MessageTargetType) UnmarshalJSON(data []byte) error { var str string err := json.Unmarshal(data, &str) diff --git a/socketserver/server/usercount.go b/socketserver/server/usercount.go index d46316d0..5390a3e8 100644 --- a/socketserver/server/usercount.go +++ b/socketserver/server/usercount.go @@ -17,7 +17,7 @@ import ( "io" ) -// uuidHash implements a hash for uuid.UUID by XORing the random bits. +// UuidHash implements a hash for uuid.UUID by XORing the random bits. type UuidHash uuid.UUID func (u UuidHash) Sum64() uint64 {