func (p WsMessageReceiver) handlePublish(im interceptorMessage, msg *gowamp.Publish) (interface{}, error) { if string(msg.Topic) == "wamp.session.on_leave" { args := msg.Arguments if len(args) == 0 { return nil, nil } if leavingSessionId, ok := args[0].(gowamp.ID); ok { log.Info("Broadcasting session leave:", leavingSessionId) p.broadcaster.Broadcast(leavingSessionId) return nil, nil } log.Info("Leave:", args) return nil, nil } if string(msg.Topic) == "wamp.session.on_join" { return nil, nil } if len(msg.Arguments) == 0 { return nil, nil } m, ok := msg.Arguments[0].(string) if !ok { m = models.String(msg.Arguments[0]) } var clientMessage messaging.Message converError := clientMessage.FromString(m) if converError != nil { log.Error(converError) return nil, converError } if clientMessage.Origin == messaging.ORIGIN_API { return nil, nil } log.Info("Websocket message received:", clientMessage) data, apiError := p.ClientProcessor.Process(clientMessage) if apiError != nil { log.Error(apiError) } return data, apiError }
func main() { r.SetVerbose(true) defer utils.Recover() utils.ListenSignals() utils.Liveness() if os.Getenv("DEBUG_N") != "true" { gin.SetMode(gin.ReleaseMode) } engine := gin.New() engine.Use(gin.Recovery()) engine.Use(func() gin.HandlerFunc { return func(c *gin.Context) { defer c.Next() log.Info(c.Request.Method, c.Request.URL.Path, c.Writer.Status()) } }()) api.Initialize(engine) engine.Run(config.Get(config.KEY_API_PORT)) }
func NewWebSocketServer() (*gowamp.WebsocketServer, *gowamp.Client, *wsInterceptor, error) { interceptor := NewWsInterceptor() r := gowamp.Realm{} r.Interceptor = interceptor realms := map[string]gowamp.Realm{} realms[config.CONST_DEFAULT_REALM] = r wsServer, err := gowamp.NewWebsocketServer(realms) if err != nil { return nil, nil, nil, err } wsServer.Upgrader.CheckOrigin = func(r *http.Request) bool { //allow connections from any origin return true } c, err := wsServer.GetLocalClient(config.CONST_DEFAULT_REALM, nil) if err != nil { log.Error(err) return nil, nil, nil, err } http.Handle("/", wsServer) http.HandleFunc("/_status", func(w http.ResponseWriter, r *http.Request) { log.Info("_status check") //we are fine }) return wsServer, c, interceptor, nil }
func processHeadersMiddleware() gin.HandlerFunc { return func(c *gin.Context) { optionsHeader := c.Request.Header.Get(CONTEXT_HEADER_OPTIONS) if optionsHeader == "" { optionsHeader = "{}" } var options models.Options options.FromString(optionsHeader) if options.Notify == nil { notify := true options.Notify = ¬ify } if options.Filter == nil { options.Filter = models.JSON{} } if options.Origin == "" { options.Origin = messaging.ORIGIN_API } log.Info("Request options:", optionsHeader) c.Set(CONTEXT_HEADER_OPTIONS, options) c.Next() } }
func (p RpcMessageReceiver) handleRpc(args []interface{}, kwargs map[string]interface{}) *gowamp.CallResult { defer utils.Recover() var msg messaging.Message if msgStr, ok := args[0].(string); ok { err := msg.FromString(msgStr) if err != nil { return p.makeErrorResult(err) } } else { err := models.Convert(args[0], &msg) if err != nil { return p.makeErrorResult(err) } } log.Info("RPC message received:", msg) res, err := p.MessageProcessor.Process(msg) if err != nil { return p.makeErrorResult(err) } return p.makeResult(res) }
func validateAppMiddleware() gin.HandlerFunc { return func(c *gin.Context) { appId := c.Param("appId") log.Info(appId) if appId == "" { log.Error(RestError(c, "Invalid app id.")) } else { c.Next() } } }
func (b *Broadcaster) Broadcast(v interface{}) { defer func() { if err := recover(); err != nil { log.Error(err) } }() log.Info(b.listeners) for _, ch := range b.listeners { ch <- v } }
func GetNewRedisClient() *redis.Client { redisAddr := config.Get(config.KEY_REDIS_ADDR) //TODO: check if will reconnect automatically on lost connection redisClient := redis.NewClient(&redis.Options{ Addr: redisAddr, Password: "", DB: 0, }) log.Info("Connected to redis client on:", redisAddr) return redisClient }
func (c *Client) Connect() { for { conn, err := c.connect() if err == nil { c.isConnected = true c.connection = conn log.Info("Connected to", c.Addr) break } else { log.Error("Error connecting to", c.Addr, err) time.Sleep(time.Second * 5) } } }
func main() { r.SetVerbose(true) defer utils.Recover() utils.ListenSignals() utils.Liveness() err := server.Initialize() if err != nil { panic(err) } log.Info("Realtime service listening") log.Error(http.ListenAndServe(config.Get(config.KEY_REALTIME_PORT), nil)) }
func BlacklistFields(fields []string, data interface{}) map[string]interface{} { if obj, ok := data.(map[string]interface{}); ok { for _, k := range fields { delete(obj, k) } return obj } else if obj, ok := data.(models.JSON); ok { for _, k := range fields { delete(obj, k) } return obj } log.Info("Invalid object to blacklist", data) return make(map[string]interface{}) }
func NewNatsClient(addr string) *NatsClient { connect := func() (interface{}, error) { log.Info("Connecting to nats:", addr) n, err := nats.Connect(addr) if err != nil { return nil, err } conn, err := nats.NewEncodedConn(n, nats.JSON_ENCODER) if err != nil { return nil, err } return conn, nil } c := NewClient(connect, addr) natsClient := &NatsClient{c} natsClient.handleConnection() return natsClient }
func (p WsMessageReceiver) processDatabaseUpdate( val map[string]interface{}, builder *messaging.MessageBuilder, opts models.SubscribeOptions, ) { messageBuilder := *builder pld := models.JSON{} var dbOp string newVal := val["new_val"] oldVal := val["old_val"] if newVal != nil && oldVal == nil { dbOp = messaging.OP_CREATE } else if newVal == nil && oldVal != nil { dbOp = messaging.OP_DELETE } else { dbOp = messaging.OP_UPDATE } //only emit messages with the same operation as the subscriber if dbOp != opts.Operation { return } var dbVal map[string]interface{} if dbOp == messaging.OP_CREATE { if newVal == nil { log.Error("Invalid message:", val) return } dbVal = newVal.(map[string]interface{}) } else if dbOp == messaging.OP_DELETE && oldVal != nil { if oldVal == nil { log.Error("Invalid message:", val) return } dbVal = oldVal.(map[string]interface{}) } else { //update if newVal == nil { log.Error("Invalid message:", val) return } dbVal = newVal.(map[string]interface{}) } pld = pld.FromMap(dbVal) pld = utils.BlacklistFields([]string{db.TYPE_FIELD, db.APP_ID_FIELD}, pld) msg := messageBuilder.Build( dbOp, messaging.ORIGIN_API, pld, models.Options{}, opts.Type, opts.AppId, "", //TODO: token? ) msg.Topic = opts.Topic log.Info("Publishing database feed: ", strings.ToUpper(msg.Operation), opts, val, msg) publishArgs := []interface{}{msg} p.WsClient.Publish(opts.Topic, publishArgs, nil) }
func (p WsMessageReceiver) handleSubscribe(im interceptorMessage, msg *gowamp.Subscribe) { opts := models.SubscribeOptions{} err := models.Convert(msg.Options, &opts) if err != nil { log.Error(err) return } topic := fmt.Sprintf("%v", msg.Topic) topicArguments := strings.Split(topic, ".") opts.Topic = topic if opts.IsSpecial() { baseTopic := messaging.BuildTopicArbitrary(topicArguments[:len(topicArguments)-1]...) opts.BaseTopic = baseTopic } else { opts.BaseTopic = topic } log.Info("Listening for changefeed:", opts) d := db.NewDbService() newValuesChan := make(chan map[string]interface{}) //for create and delete we want to listen for changes for the whole collection, for update only for the specific item if opts.Operation == messaging.OP_CREATE || opts.Operation == messaging.OP_DELETE { err = d.Changes(opts.AppId, opts.Type, opts.Filter, newValuesChan) } else if opts.Operation == messaging.OP_UPDATE { opts.ItemId = topicArguments[len(topicArguments)-1] err = d.ChangesId(opts.ItemId, newValuesChan) } if err != nil { log.Error(err) return } messageBuilder := messaging.GetMessageBuilder() go func() { leaveChan := make(chan interface{}) p.broadcaster.Subscribe(leaveChan) for { select { case val := <-newValuesChan: p.processDatabaseUpdate(val, &messageBuilder, opts) case leaveVal := <-leaveChan: sessionId := leaveVal.(gowamp.ID) if im.sess.Id == sessionId { //TODO: newValuesChan seems to be automatically closed by the rethinkdb driver //investigate whether we need to do something else _, leaveChanOpened := <-leaveChan if leaveChanOpened { close(leaveChan) } p.broadcaster.Remove(leaveChan) return } } } }() }
func (c *ApiClient) SendRequest(url, method string, body interface{}, isArray bool) (interface{}, error) { log.Info( "Sending request", "BaseUrl:", c.BaseUrl, "Url:", url, "Method:", method, "Body:", body, "Token:", c.Token, "AppId:", c.AppId, "NotifyRealtime", c.NotifyRealTime, ) var bodyStr = "" if body != nil { b, err := json.Marshal(body) if err != nil { log.Error(err) return nil, err } bodyStr = string(b) } req, err := http.NewRequest(method, c.BaseUrl+url, strings.NewReader(bodyStr)) if err != nil { log.Error(err) return nil, err } opts := models.Options{} //todo: in app and not in app token if c.Token != "" { req.Header.Set("Authorization", "Bearer "+c.Token) } if c.ClientId != "" { opts.ClientId = &c.ClientId } opts.Notify = &c.NotifyRealTime opts.Filter = c.Filter //opts.Origin = c.Origin optsS, err := opts.String() if err != nil { log.Error(err) return nil, err } req.Header.Set("NeutrinoOptions", optsS) client := http.Client{} res, err := client.Do(req) if err != nil { log.Info(err) return nil, err } if res == nil { log.Error("Unknown error") return nil, nil } if res.StatusCode != http.StatusOK { log.Info(res, err) return nil, err } defer res.Body.Close() if err != nil { log.Error(err) return nil, err } bodyRes, err := ioutil.ReadAll(res.Body) if err != nil { log.Error(err) return nil, err } if string(bodyRes) == "" { log.Info("Empty body response!") return nil, nil } var result interface{} log.Info("API response: ", string(bodyRes)) if isArray { jsonArray := make([]models.JSON, 0) err = json.Unmarshal(bodyRes, &jsonArray) result = jsonArray } else { m := models.JSON{} err = json.Unmarshal(bodyRes, &m) result = m } if err != nil { log.Error(err) return nil, err } return result, nil }