예제 #1
0
파일: accessmanager.go 프로젝트: mley/guble
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)

}
예제 #2
0
파일: receiver.go 프로젝트: tkrille/guble
func (rec *Receiver) receiveFromSubscription() {
	for {
		select {
		case msgAndRoute, ok := <-rec.route.C:
			if !ok {
				guble.Debug("messageSouce closed the channel returning from subscription", rec.applicationId)
				return
			}
			if guble.DebugEnabled() {
				guble.Debug("deliver message to applicationId=%v: %v", rec.applicationId, msgAndRoute.Message.MetadataLine())
			}
			if msgAndRoute.Message.Id > rec.lastSendId {
				rec.lastSendId = msgAndRoute.Message.Id
				rec.sendChannel <- msgAndRoute.Message.Bytes()
			} else {
				guble.Debug("dropping message %v, because it was already sent to client", msgAndRoute.Message.Id)
			}
		case <-rec.cancelChannel:
			rec.shouldStop = true
			rec.messageSouce.Unsubscribe(rec.route)
			rec.route = nil
			rec.sendOK(guble.SUCCESS_CANCELED, string(rec.path))
			return
		}

	}
}
예제 #3
0
func (srv *WSHandler) checkAccess(raw []byte) bool {
	guble.Debug("raw message: %v", string(raw))
	if raw[0] == byte('/') {
		path := getPathFromRawMessage(raw)
		guble.Debug("Received msg %v %v", srv.userId, path)
		return len(path) == 0 || srv.accessManager.AccessAllowed(READ, srv.userId, path)

	}
	return true
}
예제 #4
0
파일: service.go 프로젝트: mley/guble
// 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)
	}
}
예제 #5
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)
	}
}
예제 #6
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++
		}
	}
}
예제 #7
0
파일: router.go 프로젝트: tkrille/guble
func (router *PubSubRouter) Go() *PubSubRouter {
	go func() {
		for {
			func() {
				defer guble.PanicLogger()

				select {
				case message := <-router.messageIn:
					router.handleMessage(message)
					runtime.Gosched()
				case subscriber := <-router.subscribeChan:
					router.subscribe(subscriber.route)
					subscriber.doneNotify <- true
				case unsubscriber := <-router.unsubscribeChan:
					router.unsubscribe(unsubscriber.route)
					unsubscriber.doneNotify <- true
				case <-router.stop:
					router.closeAllRoutes()
					guble.Debug("stopping message router")
					break
				}
			}()
		}
	}()
	return router
}
예제 #8
0
func (gcmConnector *GCMConnector) parseMessageToMap(msg *guble.Message) map[string]interface{} {
	payload := map[string]interface{}{}
	if msg.Body[0] == '{' {
		json.Unmarshal(msg.Body, &payload)
	} else {
		payload["message"] = msg.BodyAsString()
	}
	guble.Debug("parsed message is: %v", payload)
	return payload
}
예제 #9
0
func (gcmConnector *GCMConnector) handleJsonError(jsonError string, gcmId string, route *server.Route) {
	if jsonError == "NotRegistered" {
		guble.Debug("remove not registered cgm registration cgmid=%v", gcmId)
		gcmConnector.removeSubscription(route, gcmId)
	} else if jsonError == "InvalidRegistration" {
		guble.Err("the cgmid=%v is not registered. %v", gcmId, jsonError)
	} else {
		guble.Err("unexpected error while sending to cgm cgmid=%v: %v", gcmId, jsonError)
	}
}
예제 #10
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
			}
		}
	}
}
예제 #11
0
파일: sqlite.go 프로젝트: tkrille/guble
// 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
}
func messagePartitionReader(name string, a *assert.Assertions, store *MessagePartition, n int, done chan bool) {
	lastReadMessage := 0
	for lastReadMessage < n {

		msgC := make(chan MessageAndId)
		errorC := make(chan error)

		guble.Debug("[%v] start fetching at: %v", name, lastReadMessage+1)
		store.Fetch(FetchRequest{
			Partition:     "myMessages",
			StartId:       uint64(lastReadMessage + 1),
			Direction:     1,
			Count:         math.MaxInt32,
			MessageC:      msgC,
			ErrorCallback: errorC,
			StartCallback: make(chan int, 1),
		})

	fetch:

		for {
			select {
			case msgAndId, open := <-msgC:
				if !open {
					guble.Debug("[%v] stop fetching at %v", name, lastReadMessage)
					break fetch
				}
				a.Equal(lastReadMessage+1, int(msgAndId.Id))
				lastReadMessage = int(msgAndId.Id)
			case err := <-errorC:
				a.Fail("received error", err.Error())
				<-done
				return
			}
		}

	}
	guble.Debug("[%v] ready, got %v", name, lastReadMessage)
	done <- true
}
예제 #13
0
파일: receiver.go 프로젝트: tkrille/guble
func (rec *Receiver) fetch() error {
	var err error

	fetch := store.FetchRequest{
		Partition:     rec.path.Partition(),
		MessageC:      make(chan store.MessageAndId, 3),
		ErrorCallback: make(chan error),
		StartCallback: make(chan int),
		Prefix:        []byte(rec.path),
		Count:         rec.maxCount,
	}

	if rec.startId >= 0 {
		fetch.Direction = 1
		fetch.StartId = uint64(rec.startId)
		if rec.maxCount == 0 {
			fetch.Count = math.MaxInt32
		}
	} else {
		fetch.Direction = -1
		if fetch.StartId, err = rec.messageStore.MaxMessageId(rec.path.Partition()); err != nil {
			return err
		}
		if rec.maxCount == 0 {
			fetch.Count = -1 * int(rec.startId)
		}
	}

	rec.messageStore.Fetch(fetch)

	for {
		select {
		case numberOfResults := <-fetch.StartCallback:
			rec.sendOK(guble.SUCCESS_FETCH_START, fmt.Sprintf("%v %v", rec.path, numberOfResults))
		case msgAndId, open := <-fetch.MessageC:
			if !open {
				rec.sendOK(guble.SUCCESS_FETCH_END, string(rec.path))
				return nil
			}
			guble.Debug("replay send %v, %v", msgAndId.Id, string(msgAndId.Message))
			rec.lastSendId = msgAndId.Id
			rec.sendChannel <- msgAndId.Message
		case err := <-fetch.ErrorCallback:
			return err
		case <-rec.cancelChannel:
			rec.shouldStop = true
			rec.sendOK(guble.SUCCESS_CANCELED, string(rec.path))
			// TODO implement cancellation in message store
			return nil
		}
	}
}
예제 #14
0
파일: router.go 프로젝트: mley/guble
func (router *PubSubRouter) HandleMessage(message *guble.Message) error {
	guble.Debug("Route.HandleMessage: %v %v", message.PublisherUserId, message.Path)
	if !router.accessManager.AccessAllowed(WRITE, message.PublisherUserId, message.Path) {
		return errors.New("User not allowed to post message to topic.")
	}

	if float32(len(router.messageIn))/float32(cap(router.messageIn)) > 0.9 {
		guble.Warn("router.messageIn channel very full: current=%v, max=%v\n", len(router.messageIn), cap(router.messageIn))
		time.Sleep(time.Millisecond)
	}
	router.messageIn <- message
	return nil
}
예제 #15
0
파일: service.go 프로젝트: tkrille/guble
func (service *Service) Start() error {
	el := guble.NewErrorList("Errors occured while startup the service: ")

	for _, startable := range service.startListener {
		name := reflect.TypeOf(startable).String()

		guble.Debug("starting module %v", name)
		if err := startable.Start(); err != nil {
			guble.Err("error on startup module %v", name)
			el.Add(err)
		}
	}
	return el.ErrorOrNil()
}
예제 #16
0
파일: router.go 프로젝트: mley/guble
// Add a route to the subscribers.
// If there is already a route with same Application Id and Path, it will be replaced.
func (router *PubSubRouter) Subscribe(r *Route) (*Route, error) {
	guble.Debug("subscribe %v, %v, %v", router.accessManager, r.UserId, r.Path)
	accessAllowed := router.accessManager.AccessAllowed(READ, r.UserId, r.Path)
	if !accessAllowed {
		return r, errors.New("not allowed")
	}
	req := SubscriptionRequest{
		route:      r,
		doneNotify: make(chan bool),
	}
	router.subscribeChan <- req
	<-req.doneNotify
	return r, nil
}
예제 #17
0
파일: client.go 프로젝트: tkrille/guble
func (c *Client) readLoop() error {
	for {
		if _, msg, err := c.ws.ReadMessage(); err != nil {
			c.Connected = false
			if c.shouldStop() {
				return nil
			} else {
				guble.Err("read error: %v", err.Error())
				c.errors <- clientErrorMessage(err.Error())
				return err
			}
		} else {
			guble.Debug("raw> %s", msg)
			c.handleIncommoingMessage(msg)
		}
	}
}
예제 #18
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++
		}
	}
}
예제 #19
0
func (srv *WSHandler) handleSendCmd(cmd *guble.Cmd) {
	guble.Debug("sending %v", string(cmd.Bytes()))
	if len(cmd.Arg) == 0 {
		srv.sendError(guble.ERROR_BAD_REQUEST, "send command requires a path argument, but non given")
		return
	}

	args := strings.SplitN(cmd.Arg, " ", 2)
	msg := &guble.Message{
		Path: guble.Path(args[0]),
		PublisherApplicationId: srv.applicationId,
		PublisherUserId:        srv.userId,
		HeaderJson:             cmd.HeaderJson,
		Body:                   cmd.Body,
	}
	if len(args) == 2 {
		msg.PublisherMessageId = args[1]
	}

	srv.messageSink.HandleMessage(msg)

	srv.sendOK(guble.SUCCESS_SEND, msg.PublisherMessageId)
}