1
0
Fork 0
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:
Kane York 2015-11-15 18:43:34 -08:00
parent aa6f090fcc
commit 66cc124e37
9 changed files with 136 additions and 112 deletions

View file

@ -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) {

View file

@ -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)

View file

@ -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)
} }

View file

@ -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)

View file

@ -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

View file

@ -189,4 +189,4 @@ func _subscribeWhileRlocked(channelName string, value chan<- ClientMessage) {
AddToSliceC(&list.Members, value) AddToSliceC(&list.Members, value)
list.Unlock() list.Unlock()
} }
} }

View file

@ -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()

View file

@ -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

View file

@ -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