mirror of
https://github.com/FrankerFaceZ/FrankerFaceZ.git
synced 2025-07-25 20:18:31 +00:00
golint part 1
This commit is contained in:
parent
aa6f090fcc
commit
66cc124e37
9 changed files with 136 additions and 112 deletions
|
@ -74,20 +74,18 @@ func commandLineConsole() {
|
||||||
|
|
||||||
shell.Register("authorizeeveryone", func(args ...string) (string, error) {
|
shell.Register("authorizeeveryone", func(args ...string) (string, error) {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
if server.Configuation.SendAuthToNewClients {
|
if server.Configuration.SendAuthToNewClients {
|
||||||
return "All clients are recieving auth challenges upon claiming a name.", nil
|
return "All clients are recieving auth challenges upon claiming a name.", nil
|
||||||
} else {
|
|
||||||
return "All clients are not recieving auth challenges upon claiming a name.", nil
|
|
||||||
}
|
}
|
||||||
|
return "All clients are not recieving auth challenges upon claiming a name.", nil
|
||||||
} else if args[0] == "on" {
|
} else if args[0] == "on" {
|
||||||
server.Configuation.SendAuthToNewClients = true
|
server.Configuration.SendAuthToNewClients = true
|
||||||
return "All new clients will recieve auth challenges upon claiming a name.", nil
|
return "All new clients will recieve auth challenges upon claiming a name.", nil
|
||||||
} else if args[0] == "off" {
|
} else if args[0] == "off" {
|
||||||
server.Configuation.SendAuthToNewClients = false
|
server.Configuration.SendAuthToNewClients = false
|
||||||
return "All new clients will not recieve auth challenges upon claiming a name.", nil
|
return "All new clients will not recieve auth challenges upon claiming a name.", nil
|
||||||
} else {
|
|
||||||
return "Usage: authorizeeveryone [ on | off ]", nil
|
|
||||||
}
|
}
|
||||||
|
return "Usage: authorizeeveryone [ on | off ]", nil
|
||||||
})
|
})
|
||||||
|
|
||||||
shell.Register("panic", func(args ...string) (string, error) {
|
shell.Register("panic", func(args ...string) (string, error) {
|
||||||
|
|
|
@ -11,14 +11,14 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
var configFilename *string = flag.String("config", "config.json", "Configuration file, including the keypairs for the NaCl crypto library, for communicating with the backend.")
|
var configFilename = flag.String("config", "config.json", "Configuration file, including the keypairs for the NaCl crypto library, for communicating with the backend.")
|
||||||
var generateKeys *bool = flag.Bool("genkeys", false, "Generate NaCl keys instead of serving requests.\nArguments: [int serverId] [base64 backendPublic]\nThe backend public key can either be specified in base64 on the command line, or put in the json file later.")
|
var flagGenerateKeys = flag.Bool("genkeys", false, "Generate NaCl keys instead of serving requests.\nArguments: [int serverId] [base64 backendPublic]\nThe backend public key can either be specified in base64 on the command line, or put in the json file later.")
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if *generateKeys {
|
if *flagGenerateKeys {
|
||||||
GenerateKeys(*configFilename)
|
generateKeys(*configFilename)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateKeys(outputFile string) {
|
func generateKeys(outputFile string) {
|
||||||
if flag.NArg() < 1 {
|
if flag.NArg() < 1 {
|
||||||
fmt.Println("Specify a numeric server ID after -genkeys")
|
fmt.Println("Specify a numeric server ID after -genkeys")
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
|
|
|
@ -19,39 +19,39 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var backendHttpClient http.Client
|
var backendHTTPClient http.Client
|
||||||
var backendUrl string
|
var backendURL string
|
||||||
var responseCache *cache.Cache
|
var responseCache *cache.Cache
|
||||||
|
|
||||||
var getBacklogUrl string
|
var getBacklogURL string
|
||||||
var postStatisticsUrl string
|
var postStatisticsURL string
|
||||||
var addTopicUrl string
|
var addTopicURL string
|
||||||
var announceStartupUrl string
|
var announceStartupURL string
|
||||||
|
|
||||||
var backendSharedKey [32]byte
|
var backendSharedKey [32]byte
|
||||||
var serverId int
|
var serverID int
|
||||||
|
|
||||||
var messageBufferPool sync.Pool
|
var messageBufferPool sync.Pool
|
||||||
|
|
||||||
func SetupBackend(config *ConfigFile) {
|
func setupBackend(config *ConfigFile) {
|
||||||
backendHttpClient.Timeout = 60 * time.Second
|
backendHTTPClient.Timeout = 60 * time.Second
|
||||||
backendUrl = config.BackendUrl
|
backendURL = config.BackendURL
|
||||||
if responseCache != nil {
|
if responseCache != nil {
|
||||||
responseCache.Flush()
|
responseCache.Flush()
|
||||||
}
|
}
|
||||||
responseCache = cache.New(60*time.Second, 120*time.Second)
|
responseCache = cache.New(60*time.Second, 120*time.Second)
|
||||||
|
|
||||||
getBacklogUrl = fmt.Sprintf("%s/backlog", backendUrl)
|
getBacklogURL = fmt.Sprintf("%s/backlog", backendURL)
|
||||||
postStatisticsUrl = fmt.Sprintf("%s/stats", backendUrl)
|
postStatisticsURL = fmt.Sprintf("%s/stats", backendURL)
|
||||||
addTopicUrl = fmt.Sprintf("%s/topics", backendUrl)
|
addTopicURL = fmt.Sprintf("%s/topics", backendURL)
|
||||||
announceStartupUrl = fmt.Sprintf("%s/startup", backendUrl)
|
announceStartupURL = fmt.Sprintf("%s/startup", backendURL)
|
||||||
|
|
||||||
messageBufferPool.New = New4KByteBuffer
|
messageBufferPool.New = New4KByteBuffer
|
||||||
|
|
||||||
var theirPublic, ourPrivate [32]byte
|
var theirPublic, ourPrivate [32]byte
|
||||||
copy(theirPublic[:], config.BackendPublicKey)
|
copy(theirPublic[:], config.BackendPublicKey)
|
||||||
copy(ourPrivate[:], config.OurPrivateKey)
|
copy(ourPrivate[:], config.OurPrivateKey)
|
||||||
serverId = config.ServerId
|
serverID = config.ServerID
|
||||||
|
|
||||||
box.Precompute(&backendSharedKey, &theirPublic, &ourPrivate)
|
box.Precompute(&backendSharedKey, &theirPublic, &ourPrivate)
|
||||||
}
|
}
|
||||||
|
@ -60,8 +60,10 @@ func getCacheKey(remoteCommand, data string) string {
|
||||||
return fmt.Sprintf("%s/%s", remoteCommand, data)
|
return fmt.Sprintf("%s/%s", remoteCommand, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Publish a message to clients with no caching.
|
// HBackendPublishRequest handles the /uncached_pub route.
|
||||||
// The scope must be specified because no attempt is made to recognize the command.
|
// The backend can POST here to publish a message to clients with no caching.
|
||||||
|
// The POST arguments are `cmd`, `args`, `channel`, and `scope`.
|
||||||
|
// The `scope` argument is required because no attempt is made to infer the scope from the command, unlike /cached_pub.
|
||||||
func HBackendPublishRequest(w http.ResponseWriter, r *http.Request) {
|
func HBackendPublishRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
formData, err := UnsealRequest(r.Form)
|
formData, err := UnsealRequest(r.Form)
|
||||||
|
@ -95,7 +97,7 @@ func HBackendPublishRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
switch target {
|
switch target {
|
||||||
case MsgTargetTypeSingle:
|
case MsgTargetTypeSingle:
|
||||||
// TODO
|
// TODO
|
||||||
case MsgTargetTypeChat:
|
case MsgTargetTypeChat:
|
||||||
count = PublishToChannel(channel, cm)
|
count = PublishToChannel(channel, cm)
|
||||||
case MsgTargetTypeMultichat:
|
case MsgTargetTypeMultichat:
|
||||||
|
@ -111,14 +113,18 @@ func HBackendPublishRequest(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Fprint(w, count)
|
fmt.Fprint(w, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BackendForwardedError string
|
// ErrForwardedFromBackend is an error returned by the backend server.
|
||||||
|
type ErrForwardedFromBackend string
|
||||||
|
|
||||||
func (bfe BackendForwardedError) Error() string {
|
func (bfe ErrForwardedFromBackend) Error() string {
|
||||||
return string(bfe)
|
return string(bfe)
|
||||||
}
|
}
|
||||||
|
|
||||||
var AuthorizationNeededError = errors.New("Must authenticate Twitch username to use this command")
|
// ErrAuthorizationNeeded is emitted when the backend replies with HTTP 401.
|
||||||
|
// Indicates that an attempt to validate `ClientInfo.TwitchUsername` should be attempted.
|
||||||
|
var ErrAuthorizationNeeded = errors.New("Must authenticate Twitch username to use this command")
|
||||||
|
|
||||||
|
// SendRemoteCommandCached performs a RPC call on the backend, but caches responses.
|
||||||
func SendRemoteCommandCached(remoteCommand, data string, auth AuthInfo) (string, error) {
|
func SendRemoteCommandCached(remoteCommand, data string, auth AuthInfo) (string, error) {
|
||||||
cached, ok := responseCache.Get(getCacheKey(remoteCommand, data))
|
cached, ok := responseCache.Get(getCacheKey(remoteCommand, data))
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -127,8 +133,12 @@ func SendRemoteCommandCached(remoteCommand, data string, auth AuthInfo) (string,
|
||||||
return SendRemoteCommand(remoteCommand, data, auth)
|
return SendRemoteCommand(remoteCommand, data, auth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendRemoteCommand performs a RPC call on the backend by POSTing to `/cmd/$remoteCommand`.
|
||||||
|
// The form data is as follows: `clientData` is the JSON in the `data` parameter
|
||||||
|
// (should be retrieved from ClientMessage.Arguments), and either `username` or
|
||||||
|
// `usernameClaimed` depending on whether AuthInfo.UsernameValidates is true is AuthInfo.TwitchUsername.
|
||||||
func SendRemoteCommand(remoteCommand, data string, auth AuthInfo) (responseStr string, err error) {
|
func SendRemoteCommand(remoteCommand, data string, auth AuthInfo) (responseStr string, err error) {
|
||||||
destUrl := fmt.Sprintf("%s/cmd/%s", backendUrl, remoteCommand)
|
destURL := fmt.Sprintf("%s/cmd/%s", backendURL, remoteCommand)
|
||||||
var authKey string
|
var authKey string
|
||||||
if auth.UsernameValidated {
|
if auth.UsernameValidated {
|
||||||
authKey = "usernameClaimed"
|
authKey = "usernameClaimed"
|
||||||
|
@ -146,7 +156,7 @@ func SendRemoteCommand(remoteCommand, data string, auth AuthInfo) (responseStr s
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := backendHttpClient.PostForm(destUrl, sealedForm)
|
resp, err := backendHTTPClient.PostForm(destURL, sealedForm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -160,13 +170,12 @@ func SendRemoteCommand(remoteCommand, data string, auth AuthInfo) (responseStr s
|
||||||
responseStr = string(respBytes)
|
responseStr = string(respBytes)
|
||||||
|
|
||||||
if resp.StatusCode == 401 {
|
if resp.StatusCode == 401 {
|
||||||
return "", AuthorizationNeededError
|
return "", ErrAuthorizationNeeded
|
||||||
} else if resp.StatusCode != 200 {
|
} else if resp.StatusCode != 200 {
|
||||||
if resp.Header.Get("Content-Type") == "application/json" {
|
if resp.Header.Get("Content-Type") == "application/json" {
|
||||||
return "", BackendForwardedError(responseStr)
|
return "", ErrForwardedFromBackend(responseStr)
|
||||||
} else {
|
|
||||||
return "", httpError(resp.StatusCode)
|
|
||||||
}
|
}
|
||||||
|
return "", httpError(resp.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.Header.Get("FFZ-Cache") != "" {
|
if resp.Header.Get("FFZ-Cache") != "" {
|
||||||
|
@ -182,7 +191,7 @@ func SendRemoteCommand(remoteCommand, data string, auth AuthInfo) (responseStr s
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendAggregatedData(sealedForm url.Values) error {
|
func SendAggregatedData(sealedForm url.Values) error {
|
||||||
resp, err := backendHttpClient.PostForm(postStatisticsUrl, sealedForm)
|
resp, err := backendHTTPClient.PostForm(postStatisticsURL, sealedForm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -204,7 +213,7 @@ func FetchBacklogData(chatSubs []string) ([]ClientMessage, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := backendHttpClient.PostForm(getBacklogUrl, sealedForm)
|
resp, err := backendHTTPClient.PostForm(getBacklogURL, sealedForm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -227,12 +236,14 @@ func FetchBacklogData(chatSubs []string) ([]ClientMessage, error) {
|
||||||
return messages, nil
|
return messages, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotOkError struct {
|
// ErrBackendNotOK indicates that the backend replied with something other than the string "ok".
|
||||||
|
type ErrBackendNotOK struct {
|
||||||
Response string
|
Response string
|
||||||
Code int
|
Code int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (noe NotOkError) Error() string {
|
// Implements the error interface.
|
||||||
|
func (noe ErrBackendNotOK) Error() string {
|
||||||
return fmt.Sprintf("backend returned %d: %s", noe.Code, noe.Response)
|
return fmt.Sprintf("backend returned %d: %s", noe.Code, noe.Response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +269,7 @@ func sendTopicNotice(topic string, added bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := backendHttpClient.PostForm(addTopicUrl, sealedForm)
|
resp, err := backendHTTPClient.PostForm(addTopicURL, sealedForm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -271,7 +282,7 @@ func sendTopicNotice(topic string, added bool) error {
|
||||||
|
|
||||||
respStr := string(respBytes)
|
respStr := string(respBytes)
|
||||||
if respStr != "ok" {
|
if respStr != "ok" {
|
||||||
return NotOkError{Code: resp.StatusCode, Response: respStr}
|
return ErrBackendNotOK{Code: resp.StatusCode, Response: respStr}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -281,15 +292,15 @@ func httpError(statusCode int) error {
|
||||||
return fmt.Errorf("backend http error: %d", statusCode)
|
return fmt.Errorf("backend http error: %d", statusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateKeys(outputFile, serverId, theirPublicStr string) {
|
func GenerateKeys(outputFile, serverID, theirPublicStr string) {
|
||||||
var err error
|
var err error
|
||||||
output := ConfigFile{
|
output := ConfigFile{
|
||||||
ListenAddr: "0.0.0.0:8001",
|
ListenAddr: "0.0.0.0:8001",
|
||||||
SocketOrigin: "localhost:8001",
|
SocketOrigin: "localhost:8001",
|
||||||
BackendUrl: "http://localhost:8002/ffz",
|
BackendURL: "http://localhost:8002/ffz",
|
||||||
}
|
}
|
||||||
|
|
||||||
output.ServerId, err = strconv.Atoi(serverId)
|
output.ServerID, err = strconv.Atoi(serverID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,13 +12,14 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A command is how the client refers to a function on the server. It's just a string.
|
// Command is a string indicating which RPC is requested.
|
||||||
|
// The Commands sent from Client -> Server and Server -> Client are disjoint sets.
|
||||||
type Command string
|
type Command string
|
||||||
|
|
||||||
// A function that is called to respond to a Command.
|
// CommandHandler is a RPC handler assosciated with a Command.
|
||||||
type CommandHandler func(*websocket.Conn, *ClientInfo, ClientMessage) (ClientMessage, error)
|
type CommandHandler func(*websocket.Conn, *ClientInfo, ClientMessage) (ClientMessage, error)
|
||||||
|
|
||||||
var CommandHandlers = map[Command]CommandHandler{
|
var commandHandlers = map[Command]CommandHandler{
|
||||||
HelloCommand: HandleHello,
|
HelloCommand: HandleHello,
|
||||||
"setuser": HandleSetUser,
|
"setuser": HandleSetUser,
|
||||||
"ready": HandleReady,
|
"ready": HandleReady,
|
||||||
|
@ -40,7 +41,7 @@ var CommandHandlers = map[Command]CommandHandler{
|
||||||
const ChannelInfoDelay = 2 * time.Second
|
const ChannelInfoDelay = 2 * time.Second
|
||||||
|
|
||||||
func HandleCommand(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) {
|
func HandleCommand(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) {
|
||||||
handler, ok := CommandHandlers[msg.Command]
|
handler, ok := commandHandlers[msg.Command]
|
||||||
if !ok {
|
if !ok {
|
||||||
handler = HandleRemoteCommand
|
handler = HandleRemoteCommand
|
||||||
}
|
}
|
||||||
|
@ -65,13 +66,13 @@ func HandleCommand(conn *websocket.Conn, client *ClientInfo, msg ClientMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleHello(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg ClientMessage, err error) {
|
func HandleHello(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg ClientMessage, err error) {
|
||||||
version, clientId, err := msg.ArgumentsAsTwoStrings()
|
version, clientID, err := msg.ArgumentsAsTwoStrings()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client.Version = version
|
client.Version = version
|
||||||
client.ClientID = uuid.FromStringOrNil(clientId)
|
client.ClientID = uuid.FromStringOrNil(clientID)
|
||||||
if client.ClientID == uuid.Nil {
|
if client.ClientID == uuid.Nil {
|
||||||
client.ClientID = uuid.NewV4()
|
client.ClientID = uuid.NewV4()
|
||||||
}
|
}
|
||||||
|
@ -125,7 +126,7 @@ func HandleSetUser(conn *websocket.Conn, client *ClientInfo, msg ClientMessage)
|
||||||
client.UsernameValidated = false
|
client.UsernameValidated = false
|
||||||
client.Mutex.Unlock()
|
client.Mutex.Unlock()
|
||||||
|
|
||||||
if Configuation.SendAuthToNewClients {
|
if Configuration.SendAuthToNewClients {
|
||||||
client.MsgChannelKeepalive.Add(1)
|
client.MsgChannelKeepalive.Add(1)
|
||||||
go client.StartAuthorization(func(_ *ClientInfo, _ bool) {
|
go client.StartAuthorization(func(_ *ClientInfo, _ bool) {
|
||||||
client.MsgChannelKeepalive.Done()
|
client.MsgChannelKeepalive.Done()
|
||||||
|
@ -200,7 +201,7 @@ func GetSubscriptionBacklog(conn *websocket.Conn, client *ClientInfo) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if backendUrl == "" {
|
if backendURL == "" {
|
||||||
return // for testing runs
|
return // for testing runs
|
||||||
}
|
}
|
||||||
messages, err := FetchBacklogData(subs)
|
messages, err := FetchBacklogData(subs)
|
||||||
|
@ -250,12 +251,17 @@ func HandleTrackFollow(conn *websocket.Conn, client *ClientInfo, msg ClientMessa
|
||||||
return ResponseSuccess, nil
|
return ResponseSuccess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AggregateEmoteUsage is a map from emoteID to a map from chatroom name to usage count.
|
||||||
var AggregateEmoteUsage map[int]map[string]int = make(map[int]map[string]int)
|
var AggregateEmoteUsage map[int]map[string]int = make(map[int]map[string]int)
|
||||||
|
|
||||||
|
// AggregateEmoteUsageLock is the lock for AggregateEmoteUsage.
|
||||||
var AggregateEmoteUsageLock sync.Mutex
|
var AggregateEmoteUsageLock sync.Mutex
|
||||||
var ErrorNegativeEmoteUsage = errors.New("Emote usage count cannot be negative")
|
|
||||||
|
// ErrNegativeEmoteUsage is emitted when the submitted emote usage is negative.
|
||||||
|
var ErrNegativeEmoteUsage = errors.New("Emote usage count cannot be negative")
|
||||||
|
|
||||||
func HandleEmoticonUses(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg ClientMessage, err error) {
|
func HandleEmoticonUses(conn *websocket.Conn, client *ClientInfo, msg ClientMessage) (rmsg ClientMessage, err error) {
|
||||||
// arguments is [1]map[EmoteId]map[RoomName]float64
|
// arguments is [1]map[emoteID]map[ChatroomName]float64
|
||||||
|
|
||||||
mapRoot := msg.Arguments.([]interface{})[0].(map[string]interface{})
|
mapRoot := msg.Arguments.([]interface{})[0].(map[string]interface{})
|
||||||
|
|
||||||
|
@ -268,7 +274,7 @@ func HandleEmoticonUses(conn *websocket.Conn, client *ClientInfo, msg ClientMess
|
||||||
for _, val2 := range mapInner {
|
for _, val2 := range mapInner {
|
||||||
var count int = int(val2.(float64))
|
var count int = int(val2.(float64))
|
||||||
if count <= 0 {
|
if count <= 0 {
|
||||||
err = ErrorNegativeEmoteUsage
|
err = ErrNegativeEmoteUsage
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -278,16 +284,16 @@ func HandleEmoticonUses(conn *websocket.Conn, client *ClientInfo, msg ClientMess
|
||||||
defer AggregateEmoteUsageLock.Unlock()
|
defer AggregateEmoteUsageLock.Unlock()
|
||||||
|
|
||||||
for strEmote, val1 := range mapRoot {
|
for strEmote, val1 := range mapRoot {
|
||||||
var emoteId int
|
var emoteID int
|
||||||
emoteId, err = strconv.Atoi(strEmote)
|
emoteID, err = strconv.Atoi(strEmote)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
destMapInner, ok := AggregateEmoteUsage[emoteId]
|
destMapInner, ok := AggregateEmoteUsage[emoteID]
|
||||||
if !ok {
|
if !ok {
|
||||||
destMapInner = make(map[string]int)
|
destMapInner = make(map[string]int)
|
||||||
AggregateEmoteUsage[emoteId] = destMapInner
|
AggregateEmoteUsage[emoteID] = destMapInner
|
||||||
}
|
}
|
||||||
|
|
||||||
mapInner := val1.(map[string]interface{})
|
mapInner := val1.(map[string]interface{})
|
||||||
|
@ -322,23 +328,23 @@ func DoSendAggregateData() {
|
||||||
|
|
||||||
reportForm := url.Values{}
|
reportForm := url.Values{}
|
||||||
|
|
||||||
followJson, err := json.Marshal(follows)
|
followJSON, err := json.Marshal(follows)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
log.Print(err)
|
||||||
} else {
|
} else {
|
||||||
reportForm.Set("follows", string(followJson))
|
reportForm.Set("follows", string(followJSON))
|
||||||
}
|
}
|
||||||
|
|
||||||
strEmoteUsage := make(map[string]map[string]int)
|
strEmoteUsage := make(map[string]map[string]int)
|
||||||
for emoteId, usageByChannel := range emoteUsage {
|
for emoteID, usageByChannel := range emoteUsage {
|
||||||
strEmoteId := strconv.Itoa(emoteId)
|
strEmoteID := strconv.Itoa(emoteID)
|
||||||
strEmoteUsage[strEmoteId] = usageByChannel
|
strEmoteUsage[strEmoteID] = usageByChannel
|
||||||
}
|
}
|
||||||
emoteJson, err := json.Marshal(strEmoteUsage)
|
emoteJSON, err := json.Marshal(strEmoteUsage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print(err)
|
log.Print(err)
|
||||||
} else {
|
} else {
|
||||||
reportForm.Set("emotes", string(emoteJson))
|
reportForm.Set("emotes", string(emoteJSON))
|
||||||
}
|
}
|
||||||
|
|
||||||
form, err := SealRequest(reportForm)
|
form, err := SealRequest(reportForm)
|
||||||
|
@ -380,6 +386,7 @@ type BunchSubscriberList struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CacheStatus byte
|
type CacheStatus byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CacheStatusNotFound = iota
|
CacheStatusNotFound = iota
|
||||||
CacheStatusFound
|
CacheStatusFound
|
||||||
|
@ -396,7 +403,7 @@ var BunchCacheLastCleanup time.Time
|
||||||
func bunchCacheJanitor() {
|
func bunchCacheJanitor() {
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
time.Sleep(30*time.Minute)
|
time.Sleep(30 * time.Minute)
|
||||||
BunchCacheCleanupSignal.Signal()
|
BunchCacheCleanupSignal.Signal()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
@ -406,13 +413,13 @@ func bunchCacheJanitor() {
|
||||||
// Unlocks CachedBunchLock, waits for signal, re-locks
|
// Unlocks CachedBunchLock, waits for signal, re-locks
|
||||||
BunchCacheCleanupSignal.Wait()
|
BunchCacheCleanupSignal.Wait()
|
||||||
|
|
||||||
if BunchCacheLastCleanup.After(time.Now().Add(-1*time.Second)) {
|
if BunchCacheLastCleanup.After(time.Now().Add(-1 * time.Second)) {
|
||||||
// skip if it's been less than 1 second
|
// skip if it's been less than 1 second
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// CachedBunchLock is held here
|
// CachedBunchLock is held here
|
||||||
keepIfAfter := time.Now().Add(-5*time.Minute)
|
keepIfAfter := time.Now().Add(-5 * time.Minute)
|
||||||
for req, resp := range BunchCache {
|
for req, resp := range BunchCache {
|
||||||
if !resp.Timestamp.After(keepIfAfter) {
|
if !resp.Timestamp.After(keepIfAfter) {
|
||||||
delete(BunchCache, req)
|
delete(BunchCache, req)
|
||||||
|
@ -518,7 +525,7 @@ const AuthorizationFailedErrorString = "Failed to verify your Twitch username."
|
||||||
func doRemoteCommand(conn *websocket.Conn, msg ClientMessage, client *ClientInfo) {
|
func doRemoteCommand(conn *websocket.Conn, msg ClientMessage, client *ClientInfo) {
|
||||||
resp, err := SendRemoteCommandCached(string(msg.Command), msg.origArguments, client.AuthInfo)
|
resp, err := SendRemoteCommandCached(string(msg.Command), msg.origArguments, client.AuthInfo)
|
||||||
|
|
||||||
if err == AuthorizationNeededError {
|
if err == ErrAuthorizationNeeded {
|
||||||
client.StartAuthorization(func(_ *ClientInfo, success bool) {
|
client.StartAuthorization(func(_ *ClientInfo, success bool) {
|
||||||
if success {
|
if success {
|
||||||
doRemoteCommand(conn, msg, client)
|
doRemoteCommand(conn, msg, client)
|
||||||
|
|
|
@ -16,34 +16,38 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const MAX_PACKET_SIZE = 1024
|
// SuccessCommand is a Reply Command to indicate success in reply to a C2S Command.
|
||||||
|
|
||||||
// Sent by the server in ClientMessage.Command to indicate success.
|
|
||||||
const SuccessCommand Command = "ok"
|
const SuccessCommand Command = "ok"
|
||||||
|
|
||||||
// Sent by the server in ClientMessage.Command to indicate failure.
|
// ErrorCommand is a Reply Command to indicate that a C2S Command failed.
|
||||||
const ErrorCommand Command = "error"
|
const ErrorCommand Command = "error"
|
||||||
|
|
||||||
// This must be the first command sent by the client once the connection is established.
|
// HelloCommand is a C2S Command.
|
||||||
|
// HelloCommand must be the Command of the first ClientMessage sent during a connection.
|
||||||
|
// Sending any other command will result in a CloseFirstMessageNotHello.
|
||||||
const HelloCommand Command = "hello"
|
const HelloCommand Command = "hello"
|
||||||
|
|
||||||
|
// AuthorizeCommand is a S2C Command sent as part of Twitch username validation.
|
||||||
const AuthorizeCommand Command = "do_authorize"
|
const AuthorizeCommand Command = "do_authorize"
|
||||||
|
|
||||||
// A handler returning a ClientMessage with this Command will prevent replying to the client.
|
// AsyncResponseCommand is a pseudo-Reply Command.
|
||||||
// It signals that the work has been handed off to a background goroutine.
|
// It indicates that the Reply Command to the client's C2S Command will be delivered
|
||||||
|
// on a goroutine over the ClientInfo.MessageChannel and should not be delivered immediately.
|
||||||
const AsyncResponseCommand Command = "_async"
|
const AsyncResponseCommand Command = "_async"
|
||||||
|
|
||||||
|
// ResponseSuccess is a Reply ClientMessage with the MessageID not yet filled out.
|
||||||
var ResponseSuccess = ClientMessage{Command: SuccessCommand}
|
var ResponseSuccess = ClientMessage{Command: SuccessCommand}
|
||||||
var ResponseFailure = ClientMessage{Command: "False"}
|
|
||||||
|
|
||||||
var Configuation *ConfigFile
|
// Configuration is the active ConfigFile.
|
||||||
|
var Configuration *ConfigFile
|
||||||
|
|
||||||
// Set up a websocket listener and register it on /.
|
// SetupServerAndHandle starts all background goroutines and registers HTTP listeners on the given ServeMux.
|
||||||
// (Uses http.DefaultServeMux .)
|
// Essentially, this function completely preps the server for a http.ListenAndServe call.
|
||||||
|
// (Uses http.DefaultServeMux if `serveMux` is nil.)
|
||||||
func SetupServerAndHandle(config *ConfigFile, serveMux *http.ServeMux) {
|
func SetupServerAndHandle(config *ConfigFile, serveMux *http.ServeMux) {
|
||||||
Configuation = config
|
Configuration = config
|
||||||
|
|
||||||
SetupBackend(config)
|
setupBackend(config)
|
||||||
|
|
||||||
if serveMux == nil {
|
if serveMux == nil {
|
||||||
serveMux = http.DefaultServeMux
|
serveMux = http.DefaultServeMux
|
||||||
|
@ -66,7 +70,7 @@ func SetupServerAndHandle(config *ConfigFile, serveMux *http.ServeMux) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("Unable to seal requests:", err)
|
log.Fatalln("Unable to seal requests:", err)
|
||||||
}
|
}
|
||||||
resp, err := backendHttpClient.PostForm(announceStartupUrl, announceForm)
|
resp, err := backendHTTPClient.PostForm(announceStartupURL, announceForm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
} else {
|
} else {
|
||||||
|
@ -82,6 +86,7 @@ func SetupServerAndHandle(config *ConfigFile, serveMux *http.ServeMux) {
|
||||||
go ircConnection()
|
go ircConnection()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SocketUpgrader is the websocket.Upgrader currently in use.
|
||||||
var SocketUpgrader = websocket.Upgrader{
|
var SocketUpgrader = websocket.Upgrader{
|
||||||
ReadBufferSize: 1024,
|
ReadBufferSize: 1024,
|
||||||
WriteBufferSize: 1024,
|
WriteBufferSize: 1024,
|
||||||
|
@ -90,6 +95,8 @@ var SocketUpgrader = websocket.Upgrader{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BannerHTML is the content served to web browsers viewing the socket server website.
|
||||||
|
// Memes go here.
|
||||||
var BannerHTML []byte
|
var BannerHTML []byte
|
||||||
|
|
||||||
func ServeWebsocketOrCatbag(w http.ResponseWriter, r *http.Request) {
|
func ServeWebsocketOrCatbag(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -118,7 +125,6 @@ var ExpectedStringAndBool = errors.New("Error: Expected array of string, bool as
|
||||||
var ExpectedStringAndIntGotFloat = errors.New("Error: Second argument was a float, expected an integer.")
|
var ExpectedStringAndIntGotFloat = errors.New("Error: Second argument was a float, expected an integer.")
|
||||||
|
|
||||||
var CloseGotBinaryMessage = websocket.CloseError{Code: websocket.CloseUnsupportedData, Text: "got binary packet"}
|
var CloseGotBinaryMessage = websocket.CloseError{Code: websocket.CloseUnsupportedData, Text: "got binary packet"}
|
||||||
var CloseGotMessageId0 = websocket.CloseError{Code: websocket.ClosePolicyViolation, Text: "got messageid 0"}
|
|
||||||
var CloseTimedOut = websocket.CloseError{Code: websocket.CloseNoStatusReceived, Text: "no ping replies for 5 minutes"}
|
var CloseTimedOut = websocket.CloseError{Code: websocket.CloseNoStatusReceived, Text: "no ping replies for 5 minutes"}
|
||||||
var CloseFirstMessageNotHello = websocket.CloseError{
|
var CloseFirstMessageNotHello = websocket.CloseError{
|
||||||
Text: "Error - the first message sent must be a 'hello'",
|
Text: "Error - the first message sent must be a 'hello'",
|
||||||
|
@ -296,6 +302,8 @@ func CloseConnection(conn *websocket.Conn, closeMsg *websocket.CloseError) {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendMessage sends a ClientMessage over the websocket connection with a timeout.
|
||||||
|
// If marshalling the ClientMessage fails, this function will panic.
|
||||||
func SendMessage(conn *websocket.Conn, msg ClientMessage) {
|
func SendMessage(conn *websocket.Conn, msg ClientMessage) {
|
||||||
messageType, packet, err := MarshalClientMessage(msg)
|
messageType, packet, err := MarshalClientMessage(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -305,7 +313,7 @@ func SendMessage(conn *websocket.Conn, msg ClientMessage) {
|
||||||
conn.WriteMessage(messageType, packet)
|
conn.WriteMessage(messageType, packet)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unpack a message sent from the client into a ClientMessage.
|
// UnmarshalClientMessage unpacks websocket TextMessage into a ClientMessage provided in the `v` parameter.
|
||||||
func UnmarshalClientMessage(data []byte, payloadType int, v interface{}) (err error) {
|
func UnmarshalClientMessage(data []byte, payloadType int, v interface{}) (err error) {
|
||||||
var spaceIdx int
|
var spaceIdx int
|
||||||
|
|
||||||
|
@ -317,12 +325,12 @@ func UnmarshalClientMessage(data []byte, payloadType int, v interface{}) (err er
|
||||||
if spaceIdx == -1 {
|
if spaceIdx == -1 {
|
||||||
return ProtocolError
|
return ProtocolError
|
||||||
}
|
}
|
||||||
messageId, err := strconv.Atoi(dataStr[:spaceIdx])
|
messageID, err := strconv.Atoi(dataStr[:spaceIdx])
|
||||||
if messageId < -1 || messageId == 0 {
|
if messageID < -1 || messageID == 0 {
|
||||||
return ProtocolErrorNegativeID
|
return ProtocolErrorNegativeID
|
||||||
}
|
}
|
||||||
|
|
||||||
out.MessageID = messageId
|
out.MessageID = messageID
|
||||||
dataStr = dataStr[spaceIdx+1:]
|
dataStr = dataStr[spaceIdx+1:]
|
||||||
|
|
||||||
spaceIdx = strings.IndexRune(dataStr, ' ')
|
spaceIdx = strings.IndexRune(dataStr, ' ')
|
||||||
|
@ -334,8 +342,8 @@ func UnmarshalClientMessage(data []byte, payloadType int, v interface{}) (err er
|
||||||
out.Command = Command(dataStr[:spaceIdx])
|
out.Command = Command(dataStr[:spaceIdx])
|
||||||
}
|
}
|
||||||
dataStr = dataStr[spaceIdx+1:]
|
dataStr = dataStr[spaceIdx+1:]
|
||||||
argumentsJson := dataStr
|
argumentsJSON := dataStr
|
||||||
out.origArguments = argumentsJson
|
out.origArguments = argumentsJSON
|
||||||
err = out.parseOrigArguments()
|
err = out.parseOrigArguments()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
|
|
@ -189,4 +189,4 @@ func _subscribeWhileRlocked(channelName string, value chan<- ClientMessage) {
|
||||||
AddToSliceC(&list.Members, value)
|
AddToSliceC(&list.Members, value)
|
||||||
list.Unlock()
|
list.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ func TCountOpenFDs() uint64 {
|
||||||
|
|
||||||
const IgnoreReceivedArguments = 1 + 2i
|
const IgnoreReceivedArguments = 1 + 2i
|
||||||
|
|
||||||
func TReceiveExpectedMessage(tb testing.TB, conn *websocket.Conn, messageId int, command Command, arguments interface{}) (ClientMessage, bool) {
|
func TReceiveExpectedMessage(tb testing.TB, conn *websocket.Conn, messageID int, command Command, arguments interface{}) (ClientMessage, bool) {
|
||||||
var msg ClientMessage
|
var msg ClientMessage
|
||||||
var fail bool
|
var fail bool
|
||||||
messageType, packet, err := conn.ReadMessage()
|
messageType, packet, err := conn.ReadMessage()
|
||||||
|
@ -42,8 +42,8 @@ func TReceiveExpectedMessage(tb testing.TB, conn *websocket.Conn, messageId int,
|
||||||
tb.Error(err)
|
tb.Error(err)
|
||||||
return msg, false
|
return msg, false
|
||||||
}
|
}
|
||||||
if msg.MessageID != messageId {
|
if msg.MessageID != messageID {
|
||||||
tb.Error("Message ID was wrong. Expected", messageId, ", got", msg.MessageID, ":", msg)
|
tb.Error("Message ID was wrong. Expected", messageID, ", got", msg.MessageID, ":", msg)
|
||||||
fail = true
|
fail = true
|
||||||
}
|
}
|
||||||
if msg.Command != command {
|
if msg.Command != command {
|
||||||
|
@ -65,8 +65,8 @@ func TReceiveExpectedMessage(tb testing.TB, conn *websocket.Conn, messageId int,
|
||||||
return msg, !fail
|
return msg, !fail
|
||||||
}
|
}
|
||||||
|
|
||||||
func TSendMessage(tb testing.TB, conn *websocket.Conn, messageId int, command Command, arguments interface{}) bool {
|
func TSendMessage(tb testing.TB, conn *websocket.Conn, messageID int, command Command, arguments interface{}) bool {
|
||||||
SendMessage(conn, ClientMessage{MessageID: messageId, Command: command, Arguments: arguments})
|
SendMessage(conn, ClientMessage{MessageID: messageID, Command: command, Arguments: arguments})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ func TSetup(testserver **httptest.Server, urls *TURLs) {
|
||||||
DumpCache()
|
DumpCache()
|
||||||
|
|
||||||
conf := &ConfigFile{
|
conf := &ConfigFile{
|
||||||
ServerId: 20,
|
ServerID: 20,
|
||||||
UseSSL: false,
|
UseSSL: false,
|
||||||
SocketOrigin: "localhost:2002",
|
SocketOrigin: "localhost:2002",
|
||||||
BannerHTML: `
|
BannerHTML: `
|
||||||
|
@ -160,7 +160,7 @@ func TSetup(testserver **httptest.Server, urls *TURLs) {
|
||||||
BackendPublicKey: []byte{19, 163, 37, 157, 50, 139, 193, 85, 229, 47, 166, 21, 153, 231, 31, 133, 41, 158, 8, 53, 73, 0, 113, 91, 13, 181, 131, 248, 176, 18, 1, 107},
|
BackendPublicKey: []byte{19, 163, 37, 157, 50, 139, 193, 85, 229, 47, 166, 21, 153, 231, 31, 133, 41, 158, 8, 53, 73, 0, 113, 91, 13, 181, 131, 248, 176, 18, 1, 107},
|
||||||
}
|
}
|
||||||
gconfig = conf
|
gconfig = conf
|
||||||
SetupBackend(conf)
|
setupBackend(conf)
|
||||||
|
|
||||||
if testserver != nil {
|
if testserver != nil {
|
||||||
serveMux := http.NewServeMux()
|
serveMux := http.NewServeMux()
|
||||||
|
|
|
@ -12,12 +12,12 @@ const CryptoBoxKeyLength = 32
|
||||||
|
|
||||||
type ConfigFile struct {
|
type ConfigFile struct {
|
||||||
// Numeric server id known to the backend
|
// Numeric server id known to the backend
|
||||||
ServerId int
|
ServerID int
|
||||||
ListenAddr string
|
ListenAddr string
|
||||||
// Hostname of the socket server
|
// Hostname of the socket server
|
||||||
SocketOrigin string
|
SocketOrigin string
|
||||||
// URL to the backend server
|
// URL to the backend server
|
||||||
BackendUrl string
|
BackendURL string
|
||||||
|
|
||||||
// SSL/TLS
|
// SSL/TLS
|
||||||
UseSSL bool
|
UseSSL bool
|
||||||
|
@ -168,19 +168,19 @@ func (bct BacklogCacheType) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements json.Unmarshaler
|
// Implements json.Unmarshaler
|
||||||
func (pbct *BacklogCacheType) UnmarshalJSON(data []byte) error {
|
func (bct *BacklogCacheType) UnmarshalJSON(data []byte) error {
|
||||||
var str string
|
var str string
|
||||||
err := json.Unmarshal(data, &str)
|
err := json.Unmarshal(data, &str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if str == "" {
|
if str == "" {
|
||||||
*pbct = CacheTypeInvalid
|
*bct = CacheTypeInvalid
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
val := BacklogCacheTypeByName(str)
|
val := BacklogCacheTypeByName(str)
|
||||||
if val != CacheTypeInvalid {
|
if val != CacheTypeInvalid {
|
||||||
*pbct = val
|
*bct = val
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ErrorUnrecognizedCacheType
|
return ErrorUnrecognizedCacheType
|
||||||
|
@ -224,19 +224,19 @@ func (mtt MessageTargetType) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements json.Unmarshaler
|
// Implements json.Unmarshaler
|
||||||
func (pmtt *MessageTargetType) UnmarshalJSON(data []byte) error {
|
func (mtt *MessageTargetType) UnmarshalJSON(data []byte) error {
|
||||||
var str string
|
var str string
|
||||||
err := json.Unmarshal(data, &str)
|
err := json.Unmarshal(data, &str)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if str == "" {
|
if str == "" {
|
||||||
*pmtt = MsgTargetTypeInvalid
|
*mtt = MsgTargetTypeInvalid
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
mtt := MessageTargetTypeByName(str)
|
mtt := MessageTargetTypeByName(str)
|
||||||
if mtt != MsgTargetTypeInvalid {
|
if mtt != MsgTargetTypeInvalid {
|
||||||
*pmtt = mtt
|
*mtt = mtt
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ErrorUnrecognizedTargetType
|
return ErrorUnrecognizedTargetType
|
||||||
|
|
|
@ -54,7 +54,7 @@ func SealRequest(form url.Values) (url.Values, error) {
|
||||||
retval := url.Values{
|
retval := url.Values{
|
||||||
"nonce": []string{nonceString},
|
"nonce": []string{nonceString},
|
||||||
"msg": []string{cipherString},
|
"msg": []string{cipherString},
|
||||||
"id": []string{strconv.Itoa(serverId)},
|
"id": []string{strconv.Itoa(serverID)},
|
||||||
}
|
}
|
||||||
|
|
||||||
return retval, nil
|
return retval, nil
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue