Example #1
0
func (gcmConnector *GCMConnector) broadcastMessage(msg server.MsgAndRoute) {
	topic := msg.Message.Path
	payload := gcmConnector.parseMessageToMap(msg.Message)
	guble.Info("broadcasting message with topic %v ...", string(topic))

	subscriptions := gcmConnector.kvStore.Iterate(GCM_REGISTRATIONS_SCHEMA, "")
	count := 0
	for {
		select {
		case entry, ok := <-subscriptions:
			if !ok {
				guble.Info("send message to %v receivers", count)
				return
			}
			gcmId := entry[0]
			//TODO collect 1000 gcmIds and send them in one request!
			broadcastMessage := gcm.NewMessage(payload, gcmId)
			go func() {
				//TODO error handling of response!
				_, err := gcmConnector.sender.Send(broadcastMessage, 3)
				guble.Debug("sent broadcast message to gcmId=%v", gcmId)
				if err != nil {
					guble.Err("error sending broadcast message to cgmid=%v: %v", gcmId, err.Error())
				}
			}()
			count++
		}
	}
}
Example #2
0
func (service *Service) Stop() error {
	errors := make(map[string]error)
	for _, stopable := range service.stopListener {
		name := reflect.TypeOf(stopable).String()
		stoppedChan := make(chan bool)
		errorChan := make(chan error)
		guble.Info("stopping %v ...", name)
		go func() {
			err := stopable.Stop()
			if err != nil {
				errorChan <- err
				return
			}
			stoppedChan <- true
		}()
		select {
		case err := <-errorChan:
			guble.Err("error while stopping %v: %v", name, err.Error)
			errors[name] = err
		case <-stoppedChan:
			guble.Info("stopped %v", name)
		case <-time.After(service.StopGracePeriod):
			errors[name] = fmt.Errorf("error while stopping %v: not returned after %v seconds", name, service.StopGracePeriod)
			guble.Err(errors[name].Error())
		}
	}
	if len(errors) > 0 {
		return fmt.Errorf("Errors while stopping modules %q", errors)
	}
	return nil
}
Example #3
0
func waitForTermination(callback func()) {
	sigc := make(chan os.Signal)
	signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
	guble.Info("Got singal '%v' .. exit greacefully now", <-sigc)
	callback()
	guble.Info("exit now")
	os.Exit(0)
}
Example #4
0
func DefaultConnectionFactory(url string, origin string) (WSConnection, error) {
	guble.Info("connecting to %v", url)
	header := http.Header{"Origin": []string{origin}}
	conn, _, err := websocket.DefaultDialer.Dial(url, header)
	if err != nil {
		return nil, err
	}
	guble.Info("connected to %v", url)
	return conn, nil
}
Example #5
0
// Registers the supplied module on this service.
// This method checks the module for the following interfaces and
// does the expected tegistrations:
//   Stopable: notify when the service stops
//   Endpoint: Register the handler function of the Endpoint in the http service at prefix
//   SetRouter: Provide the router
//   SetMessageEntry: Provide the message entry
//
// If the module does not have a HandlerFunc, the prefix parameter is ignored
func (service *Service) Register(module interface{}) {
	name := reflect.TypeOf(module).String()

	switch m := module.(type) {
	case Stopable:
		guble.Info("register %v as StopListener", name)
		service.AddStopListener(m)
	}

	switch m := module.(type) {
	case Startable:
		guble.Info("register %v as StartListener", name)
		service.AddStartListener(m)
	}

	switch m := module.(type) {
	case Endpoint:
		guble.Info("register %v as Endpoint to %v", name, m.GetPrefix())
		service.AddHandler(m.GetPrefix(), m)
	}

	// do the injections ...

	switch m := module.(type) {
	case SetKVStore:
		guble.Debug("inject KVStore to %v", name)
		m.SetKVStore(service.kvStore)
	}

	switch m := module.(type) {
	case SetMessageStore:
		guble.Debug("inject MessageStore to %v", name)
		m.SetMessageStore(service.messageStore)
	}

	switch m := module.(type) {
	case SetRouter:
		guble.Debug("inject Router to %v", name)
		m.SetRouter(service.router)
	}

	switch m := module.(type) {
	case SetMessageEntry:
		guble.Debug("inject MessageEntry to %v", name)
		m.SetMessageEntry(service.messageSink)
	}

	switch m := module.(type) {
	case SetAccessManager:
		guble.Debug("inject AccessManager to %v", name)
		m.SetAccessManager(service.accessManager)
	}
}
Example #6
0
func (am RestAccessManager) AccessAllowed(accessType AccessType, userId string, path guble.Path) bool {

	u, _ := url.Parse(string(am))
	q := u.Query()
	if accessType == READ {
		q.Set("type", "read")
	} else {
		q.Set("type", "write")
	}

	q.Set("userId", userId)
	q.Set("path", string(path))

	resp, err := http.DefaultClient.Get(u.String())

	if err != nil {
		guble.Warn("RestAccessManager: %v", err)
		return false
	}
	defer resp.Body.Close()
	responseBody, err := ioutil.ReadAll(resp.Body)

	if err != nil || resp.StatusCode != 200 {
		guble.Info("error getting permission", err)
		guble.Debug("error getting permission", responseBody)
		return false
	}

	guble.Debug("RestAccessManager: %v, %v, %v, %v", accessType, userId, path, string(responseBody))

	return "true" == string(responseBody)

}
Example #7
0
func (gcmConnector *GCMConnector) sendMessageToGCM(msg server.MsgAndRoute) {
	gcmId := msg.Route.ApplicationId

	payload := gcmConnector.parseMessageToMap(msg.Message)

	var messageToGcm = gcm.NewMessage(payload, gcmId)
	guble.Info("sending message to %v ...", gcmId)
	result, err := gcmConnector.sender.Send(messageToGcm, 5)
	if err != nil {
		guble.Err("error sending message to cgmid=%v: %v", gcmId, err.Error())
		return
	}

	errorJson := result.Results[0].Error
	if errorJson != "" {
		gcmConnector.handleJsonError(errorJson, gcmId, msg.Route)
	} else {
		guble.Debug("delivered message to gcm cgmid=%v: %v", gcmId, errorJson)
	}

	//we only send to one receiver, so we know that we can replace the old id with the first registration id (=canonical id)
	if result.CanonicalIDs != 0 {
		gcmConnector.replaceSubscriptionWithCanonicalID(msg.Route, result.Results[0].RegistrationID)
	}
}
Example #8
0
func (srv *WSHandler) receiveLoop() {
	var message []byte
	for {
		err := srv.clientConn.Receive(&message)
		if err != nil {
			guble.Info("applicationId=%v closed the connection", srv.applicationId)
			srv.cleanAndClose()
			break
		}

		//guble.Debug("websocket_connector, raw message received: %v", string(message))
		cmd, err := guble.ParseCmd(message)
		if err != nil {
			srv.sendError(guble.ERROR_BAD_REQUEST, "error parsing command. %v", err.Error())
			continue
		}
		switch cmd.Name {
		case guble.CMD_SEND:
			srv.handleSendCmd(cmd)
		case guble.CMD_RECEIVE:
			srv.handleReceiveCmd(cmd)
		case guble.CMD_CANCEL:
			srv.handleCancelCmd(cmd)
		default:
			srv.sendError(guble.ERROR_BAD_REQUEST, "unknown command %v", cmd.Name)
		}
	}
}
Example #9
0
func (gcmConnector *GCMConnector) subscribe(topic string, userid string, gcmid string) {
	guble.Info("gcm connector registration to userid=%q, gcmid=%q: %q", userid, gcmid, topic)

	route := server.NewRoute(topic, gcmConnector.channelFromRouter, gcmid, userid)

	gcmConnector.router.Subscribe(route)
	gcmConnector.saveSubscription(userid, topic, gcmid)
}
Example #10
0
func (gcmConnector *GCMConnector) replaceSubscriptionWithCanonicalID(route *server.Route, newGcmId string) {
	oldGcmId := route.ApplicationId
	topic := string(route.Path)
	userId := route.UserId

	guble.Info("replacing old gcmId %v with canonicalId %v", oldGcmId, newGcmId)
	gcmConnector.removeSubscription(route, oldGcmId)
	gcmConnector.subscribe(topic, userId, newGcmId)
}
Example #11
0
func (ws *WebServer) Start() error {
	guble.Info("starting up at %v", ws.addr)
	ws.server = &http.Server{Addr: ws.addr, Handler: ws.mux}
	var err error
	ws.ln, err = net.Listen("tcp", ws.addr)
	if err != nil {
		return err
	}

	go func() {
		err = ws.server.Serve(tcpKeepAliveListener{ws.ln.(*net.TCPListener)})

		if err != nil && !strings.HasSuffix(err.Error(), "use of closed network connection") {
			guble.Err("ListenAndServe %s", err.Error())
		}
		guble.Info("http server stopped")
	}()
	return nil
}
Example #12
0
func (srv *WSHandler) cleanAndClose() {
	guble.Info("closing applicationId=%v", srv.applicationId)

	for path, rec := range srv.receiver {
		rec.Stop()
		delete(srv.receiver, path)
	}

	srv.clientConn.Close()
}
Example #13
0
// Opens the database file.
// If the directory does not exist, it will be created.
func (kvStore *SqliteKVStore) Open() error {
	directoryPath := filepath.Dir(kvStore.filename)
	if err := ensureWriteableDirectory(directoryPath); err != nil {
		guble.Err("error db directory not writeable %q: %q", kvStore.filename, err)
		return err
	}

	guble.Info("opening sqldb %v", kvStore.filename)
	gormdb, err := gorm.Open("sqlite3", kvStore.filename)
	if err != nil {
		guble.Err("error opening sqlite3 db %q: %q", kvStore.filename, err)
		return err
	}

	if err := gormdb.DB().Ping(); err != nil {
		guble.Err("error pinging database %q: %q", kvStore.filename, err.Error())
	} else {
		guble.Debug("can ping database %q", kvStore.filename)
	}

	//gormdb.LogMode(true)
	gormdb.DB().SetMaxIdleConns(2)
	gormdb.DB().SetMaxOpenConns(5)
	gormdb.SingularTable(true)

	if err := gormdb.AutoMigrate(&kvEntry{}).Error; err != nil {
		guble.Err("error in schema migration: %q", err)
		return err
	} else {
		guble.Debug("ensured db schema")
	}

	if !kvStore.syncOnWrite {
		guble.Info("setting db: PRAGMA synchronous = OFF")
		if err := gormdb.Exec("PRAGMA synchronous = OFF").Error; err != nil {
			guble.Err("error setting PRAGMA synchronous = OFF: %v", err)
			return err
		}
	}
	kvStore.db = &gormdb
	return nil
}
Example #14
0
func (router *PubSubRouter) unsubscribe(r *Route) {
	guble.Info("unsubscribe applicationId=%v, path=%v", r.ApplicationId, r.Path)
	routeList, present := router.routes[r.Path]
	if !present {
		return
	}
	router.routes[r.Path] = remove(routeList, r)
	if len(router.routes[r.Path]) == 0 {
		delete(router.routes, r.Path)
	}
}
Example #15
0
func (router *PubSubRouter) deliverMessage(route Route, message *guble.Message) {
	defer guble.PanicLogger()
	select {
	case route.C <- MsgAndRoute{Message: message, Route: &route}:
		// fine, we could send the message
	default:
		guble.Info("queue was full, closing delivery for route=%v to applicationId=%v", route.Path, route.ApplicationId)
		close(route.C)
		router.unsubscribe(&route)
	}
}
Example #16
0
func (srv *WSHandler) handleReceiveCmd(cmd *guble.Cmd) {
	rec, err := NewReceiverFromCmd(srv.applicationId, cmd, srv.sendChannel, srv.messageSouce, srv.messageStore)

	if err != nil {
		guble.Info("client error in handleReceiveCmd: %v", err.Error())
		srv.sendError(guble.ERROR_BAD_REQUEST, err.Error())
		return
	}
	srv.receiver[rec.path] = rec
	rec.Start()
}
Example #17
0
func (router *PubSubRouter) handleMessage(message *guble.Message) {
	if guble.InfoEnabled() {
		guble.Info("routing message: %v", message.MetadataLine())
	}

	for currentRoutePath, currentRouteList := range router.routes {
		if matchesTopic(message.Path, currentRoutePath) {
			for _, route := range currentRouteList {
				router.deliverMessage(route, message)
			}
		}
	}
}
Example #18
0
func (router *PubSubRouter) subscribe(r *Route) {
	guble.Info("subscribe applicationId=%v, path=%v", r.ApplicationId, r.Path)

	routeList, present := router.routes[r.Path]
	if !present {
		routeList = []Route{}
		router.routes[r.Path] = routeList
	}

	// try to remove, to avoid double subscriptions of the same app
	routeList = remove(routeList, r)

	router.routes[r.Path] = append(routeList, *r)
}
Example #19
0
func (srv *WSHandler) sendLoop() {
	for {
		select {
		case raw := <-srv.sendChannel:
			if guble.DebugEnabled() {
				if len(raw) < 80 {
					guble.Debug("send to client (userId=%v, applicationId=%v, totalSize=%v): %v", srv.userId, srv.applicationId, len(raw), string(raw))
				} else {
					guble.Debug("send to client (userId=%v, applicationId=%v, totalSize=%v): %v...", srv.userId, srv.applicationId, len(raw), string(raw[0:79]))
				}
			}
			if err := srv.clientConn.Send(raw); err != nil {
				guble.Info("applicationId=%v closed the connection", srv.applicationId)
				srv.cleanAndClose()
				break
			}
		}
	}
}
Example #20
0
func (gcmConnector *GCMConnector) loadSubscriptions() {
	subscriptions := gcmConnector.kvStore.Iterate(GCM_REGISTRATIONS_SCHEMA, "")
	count := 0
	for {
		select {
		case entry, ok := <-subscriptions:
			if !ok {
				guble.Info("renewed %v gcm subscriptions", count)
				return
			}
			gcmId := entry[0]
			splitedValue := strings.SplitN(entry[1], ":", 2)
			userid := splitedValue[0]
			topic := splitedValue[1]

			guble.Debug("renew gcm subscription: user=%v, topic=%v, gcmid=%v", userid, topic, gcmId)
			route := server.NewRoute(topic, gcmConnector.channelFromRouter, gcmId, userid)
			gcmConnector.router.Subscribe(route)
			count++
		}
	}
}
Example #21
0
		db := store.NewSqliteKVStore(path.Join(args.StoragePath, "kv-store.db"), true)
		if err := db.Open(); err != nil {
			panic(err)
		}
		return db
	default:
		panic(fmt.Errorf("unknown key value backend: %q", args.KVBackend))
	}
}

var CreateMessageStoreBackend = func(args Args) store.MessageStore {
	switch args.MSBackend {
	case "none", "":
		return store.NewDummyMessageStore()
	case "file":
		guble.Info("using FileMessageStore in directory: %q", args.StoragePath)
		testfile := path.Join(args.StoragePath, "testfile")
		f, err := os.Create(testfile)
		if err != nil {
			panic(fmt.Errorf("directory for message store not present/writeable %q: %v", args.StoragePath, err))
		}
		f.Close()
		os.Remove(testfile)

		return store.NewFileMessageStore(args.StoragePath)
	default:
		panic(fmt.Errorf("unknown message store backend: %q", args.MSBackend))
	}
}

var CreateModules = func(args Args) []interface{} {