// NewHandler returns the handler for `flixy new`. func NewHandler(so socketio.Socket) func(string) { sockid := so.Id() sockip := getRemoteIP(so) return func(jsonmsg string) { var data models.NewMessage if err := json.Unmarshal([]byte(jsonmsg), &data); err != nil { log.WithFields(log.Fields{ "verb": "flixy new", "member_sockid": sockid, "member_remote": sockip, }).Error(err) } log.WithFields(log.Fields{ "verb": "flixy new", "member_sockid": sockid, "member_remote": sockip, }).Debugf("client beginning new session creation", sockid) sid := makeNewSessionID() vid := data.VideoID if vid == 0 { log.WithFields(log.Fields{ "verb": "flixy new", "member_sockid": sockid, "member_remote": sockip, }).Warn("invalid video id?") so.Emit("flixy invalid new data", jsonmsg) return } time := data.Time nick := data.Nick if nick == "" { nick = "(no nick)" } s := models.NewSession(sid, vid, time) m := s.AddMember(so, nick) members[sockid] = m sessions[sid] = s so.Emit("flixy new session", s.GetWireSession()) // TODO make this the new style logging :-) log.WithFields(log.Fields{ "verb": "flixy new", "member_sockid": sockid, "member_remote": sockip, }).Info("new session created") } }
// PlayHandler returns the handler for `flixy play`. func PlayHandler(so socketio.Socket) func(string) { sockid := so.Id() sockip := getRemoteIP(so) return func(jsonmsg string) { var data models.PlayMessage if err := json.Unmarshal([]byte(jsonmsg), &data); err != nil { log.WithFields(log.Fields{ "verb": "flixy play", "member_sockid": sockid, "member_remote": sockip, }).Error(err) return } sid := data.SessionID log.WithFields(log.Fields{ "verb": "flixy play", "member_sockid": sockid, "member_remote": sockip, "session_id": sid, }).Debug("user beginning session play") s, ok := sessions[sid] if !ok { log.WithFields(log.Fields{ "verb": "flixy play", "member_sockid": sockid, "member_remote": sockip, "invalid_sid": sid, }).Warn("invalid session id") so.Emit("flixy invalid session id", sid) return } s.Play() log.WithFields(log.Fields{ "verb": "flixy play", "member_sockid": sockid, "member_remote": sockip, }).Debug("playing") } }
// JoinHandler returns the handler for `flixy join`. func JoinHandler(so socketio.Socket) func(string) { sockid := so.Id() sockip := getRemoteIP(so) return func(jsonmsg string) { var data models.JoinMessage if err := json.Unmarshal([]byte(jsonmsg), &data); err != nil { log.WithFields(log.Fields{ "verb": "flixy join", "member_sockid": sockid, "member_remote": sockip, }).Error(err) return } sid := data.SessionID nick := data.Nick if nick == "" { nick = "(no nick)" } s, ok := sessions[sid] if !ok { log.WithFields(log.Fields{ "verb": "flixy join", "member_sockid": sockid, "member_remote": sockip, "invalid_sid": sid, }).Warn("invalid session id") so.Emit("flixy invalid session id", sid) return } s.AddMember(so, nick) log.WithFields(log.Fields{ "verb": "flixy play", "member_sockid": sockid, "member_remote": sockip, "session_id": sid, }).Debug("joining a session") } }
// SeekHandler returns the handler for `flixy seek`. func SeekHandler(so socketio.Socket) func(string) { sockid := so.Id() sockip := getRemoteIP(so) return func(jsonmsg string) { var data models.SeekMessage if err := json.Unmarshal([]byte(jsonmsg), &data); err != nil { log.WithFields(log.Fields{ "verb": "flixy seek", "member_sockid": sockid, "member_remote": sockip, }).Error(err) return } sid := data.SessionID ts := data.Time s, ok := sessions[sid] if !ok { log.WithFields(log.Fields{ "verb": "flixy seek", "member_sockid": sockid, "member_remote": sockip, "invalid_sid": sid, }).Warn("invalid session id") so.Emit("flixy invalid session id", sid) return } log.WithFields(log.Fields{ "verb": "flixy seek", "member_sockid": sockid, "member_remote": sockip, "session": sid, }).Debug("setting time") s.SetTime(ts) } }
// SyncHandler returns the handler for `flixy get sync`. func SyncHandler(so socketio.Socket) func(string) { sockid := so.Id() sockip := getRemoteIP(so) return func(jsonmsg string) { var data models.GetSyncMessage if err := json.Unmarshal([]byte(jsonmsg), &data); err != nil { log.WithFields(log.Fields{ "verb": "flixy get sync", "member_sockid": sockid, "member_remote": sockip, }).Error(err) return } sid := data.SessionID s, ok := sessions[sid] if !ok { log.WithFields(log.Fields{ "verb": "flixy get sync", "member_sockid": sockid, "member_remote": sockip, "invalid_sid": sid, }).Warn("invalid session id") so.Emit("flixy invalid session id", sid) return } log.WithFields(log.Fields{ "verb": "flixy get sync", "member_sockid": sockid, "member_remote": sockip, }).Debug("getting sync state") so.Emit("flixy sync", s.GetWireSession()) } }
func TestLogstashFormatter(t *testing.T) { assert := assert.New(t) lf := LogstashFormatter{Type: "abc"} fields := logrus.Fields{ "message": "def", "level": "ijk", "type": "lmn", "one": 1, "pi": 3.14, "bool": true, } entry := logrus.WithFields(fields) entry.Message = "msg" entry.Level = logrus.InfoLevel b, _ := lf.Format(entry) var data map[string]interface{} dec := json.NewDecoder(bytes.NewReader(b)) dec.UseNumber() dec.Decode(&data) // base fields assert.Equal(json.Number("1"), data["@version"]) assert.NotEmpty(data["@timestamp"]) assert.Equal("abc", data["type"]) assert.Equal("msg", data["message"]) assert.Equal("info", data["level"]) // substituted fields assert.Equal("def", data["fields.message"]) assert.Equal("ijk", data["fields.level"]) assert.Equal("lmn", data["fields.type"]) // formats assert.Equal(json.Number("1"), data["one"]) assert.Equal(json.Number("3.14"), data["pi"]) assert.Equal(true, data["bool"]) }
// main is the entry point to the flixy server. func main() { log.Info("Starting flixy!") server, err := socketio.NewServer(nil) if err != nil { log.Fatal(err) } // TODO can/should this be moved to its own function? it might make the // architecture of the various other handlers simpler, if implemented // in the right way. server.On("connection", func(so socketio.Socket) { sockid := so.Id() sockip := getRemoteIP(so) so.On("flixy get sync", SyncHandler(so)) so.On("flixy new", NewHandler(so)) so.On("flixy pause", PauseHandler(so)) so.On("flixy play", PlayHandler(so)) so.On("flixy join", JoinHandler(so)) so.On("flixy seek", SeekHandler(so)) log.WithFields(log.Fields{ "member_sockid": sockid, "member_remote": sockip, }).Info("connected") }) // TODO move this to its own handler (we're all grown up now!!!!) server.On("disconnection", func(so socketio.Socket) { sockid := so.Id() sockip := getRemoteIP(so) m, ok := members[sockid] if !ok { log.WithFields(log.Fields{ "verb": "disconnection", "member_sockid": sockid, "member_remote": sockip, }).Warn("member never in a session disconnected") // If a socket has never been a member, then it has no // sessions to be removed from, and thus we have // nothing further to do. return } log.Infof("%v disconnected", sockid) delete(members, sockid) s := m.Session // TODO this could be a lot prettier. I wish it could go in the // `RemoveMember` func itself, but I can't actually think of // how to remove a session from within its own context. numLeft := s.RemoveMember(sockid) // NOTE what happens if a user connects to the session here? if numLeft == 0 { sid := s.SessionID delete(sessions, sid) } }) // TODO this should probably go to its own handler, too. server.On("error", func(so socketio.Socket, err error) { // TODO how can this even happen? log.Error("error:", err) }) mux := http.NewServeMux() api := routes.New() // TODO remove this api.Get("/status", func(w http.ResponseWriter, r *http.Request) { // print status here // this is just for debugging for now, we need more in-depth stuff soon enc := json.NewEncoder(w) wms := make(map[string]models.WireSession) for k, v := range sessions { wms[k] = v.GetWireSession() } enc.Encode(wms) }) // `/sessions/:sid` will 302 the user to the proper Netflix URL if it's // a valid SID, setting the session ID in the URL as it does so. It // will *also* return the session as a JSON object. api.Get("/sessions/:sid", func(w http.ResponseWriter, r *http.Request) { params := r.URL.Query() session, present := sessions[params.Get(":sid")] if !present { w.WriteHeader(404) return } w.Header().Set("Location", session.GetNetflixURL()) w.WriteHeader(302) routes.ServeJson(w, session.GetWireSession()) }) mux.Handle("/socket.io/", server) mux.Handle("/", api) n := negroni.New() n.Use(negronilogrus.NewCustomMiddleware(loglevel, &log.TextFormatter{}, "web")) n.Use(negroni.NewRecovery()) middleware.Inject(n) n.UseHandler(mux) n.Run(fmt.Sprintf("%s:%d", opts.Host, opts.Port)) }