예제 #1
0
// refreshCmd handle refresh command to update connection with new
// timestamp - this is only required when connection lifetime option set.
func (c *client) refreshCmd(cmd *RefreshClientCommand) (*response, error) {

	resp := newResponse("refresh")

	user := cmd.User
	info := cmd.Info
	timestamp := cmd.Timestamp
	token := cmd.Token

	c.app.RLock()
	secret := c.app.config.Secret
	c.app.RUnlock()

	isValid := auth.CheckClientToken(secret, string(user), timestamp, info, token)
	if !isValid {
		logger.ERROR.Println("invalid refresh token for user", user)
		return nil, ErrInvalidToken
	}

	ts, err := strconv.Atoi(timestamp)
	if err != nil {
		logger.ERROR.Println(err)
		return nil, ErrInvalidMessage
	}

	c.app.RLock()
	closeDelay := c.app.config.ExpiredConnectionCloseDelay
	connLifetime := c.app.config.ConnLifetime
	version := c.app.config.Version
	c.app.RUnlock()

	body := &ConnectBody{}
	body.Version = version
	body.Expires = connLifetime > 0
	body.TTL = connLifetime
	body.Client = c.UID

	if connLifetime > 0 {
		// connection check enabled
		timeToExpire := int64(ts) + connLifetime - time.Now().Unix()
		if timeToExpire > 0 {
			// connection refreshed, update client timestamp and set new expiration timeout
			c.timestamp = int64(ts)
			c.defaultInfo = []byte(info)
			if c.expireTimer != nil {
				c.expireTimer.Stop()
			}
			duration := time.Duration(timeToExpire)*time.Second + closeDelay
			c.expireTimer = time.AfterFunc(duration, c.expire)
		} else {
			body.Expired = true
		}
	}
	resp.Body = body
	return resp, nil
}
예제 #2
0
// refreshCmd handle refresh command to update connection with new
// timestamp - this is only required when connection lifetime project option set.
func (c *client) refreshCmd(cmd *refreshClientCommand) (*response, error) {

	resp := newResponse("refresh")

	pk := cmd.Project
	user := cmd.User
	info := cmd.Info
	timestamp := cmd.Timestamp
	token := cmd.Token

	project, exists := c.app.projectByKey(pk)
	if !exists {
		return nil, ErrProjectNotFound
	}

	isValid := auth.CheckClientToken(project.Secret, string(pk), string(user), timestamp, info, token)
	if !isValid {
		logger.ERROR.Println("invalid refresh token for user", user)
		return nil, ErrInvalidToken
	}

	ts, err := strconv.Atoi(timestamp)
	if err != nil {
		logger.ERROR.Println(err)
		return nil, ErrInvalidMessage
	}

	body := &refreshBody{}

	connLifetime := project.ConnLifetime
	if connLifetime > 0 {
		// connection check enabled
		timeToExpire := int64(ts) + connLifetime - time.Now().Unix()
		if timeToExpire > 0 {
			// connection refreshed, update client timestamp and set new expiration timeout
			c.timestamp = int64(ts)
			c.defaultInfo = []byte(info)
			if c.expireTimer != nil {
				c.expireTimer.Stop()
			}
			duration := time.Duration(timeToExpire+c.app.config.ExpiredConnectionCloseDelay) * time.Second
			c.expireTimer = time.AfterFunc(duration, c.expire)
		} else {
			return nil, ErrConnectionExpired
		}
		body.TTL = &connLifetime
	}
	resp.Body = body
	return resp, nil
}
예제 #3
0
// connectCmd handles connect command from client - client must send this
// command immediately after establishing Websocket or SockJS connection with
// Centrifugo
func (c *client) connectCmd(cmd *ConnectClientCommand) (*response, error) {

	resp := newResponse("connect")

	if c.authenticated {
		logger.ERROR.Println("connect error: client already authenticated")
		return nil, ErrInvalidMessage
	}

	user := cmd.User
	info := cmd.Info

	c.app.RLock()
	secret := c.app.config.Secret
	insecure := c.app.config.Insecure
	closeDelay := c.app.config.ExpiredConnectionCloseDelay
	connLifetime := c.app.config.ConnLifetime
	version := c.app.config.Version
	presenceInterval := c.app.config.PresencePingInterval
	c.app.RUnlock()

	var timestamp string
	var token string
	if !insecure {
		timestamp = cmd.Timestamp
		token = cmd.Token
	} else {
		timestamp = ""
		token = ""
	}

	if !insecure {
		isValid := auth.CheckClientToken(secret, string(user), timestamp, info, token)
		if !isValid {
			logger.ERROR.Println("invalid token for user", user)
			return nil, ErrInvalidToken
		}
	}

	if !insecure {
		ts, err := strconv.Atoi(timestamp)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, ErrInvalidMessage
		}
		c.timestamp = int64(ts)
	} else {
		c.timestamp = time.Now().Unix()
	}

	c.User = user

	body := &ConnectBody{}
	body.Version = version
	body.Expires = connLifetime > 0
	body.TTL = connLifetime

	var timeToExpire int64 = 0

	if connLifetime > 0 && !insecure {
		timeToExpire = c.timestamp + connLifetime - time.Now().Unix()
		if timeToExpire <= 0 {
			body.Expired = true
			resp.Body = body
			return resp, nil
		}
	}

	c.authenticated = true
	c.defaultInfo = []byte(info)
	c.Channels = map[Channel]bool{}
	c.channelInfo = map[Channel][]byte{}

	if c.staleTimer != nil {
		c.staleTimer.Stop()
	}

	c.presenceTimer = time.AfterFunc(presenceInterval, c.updatePresence)

	err := c.app.addConn(c)
	if err != nil {
		logger.ERROR.Println(err)
		return nil, ErrInternalServerError
	}

	if c.app.mediator != nil {
		c.app.mediator.Connect(c.UID, c.User)
	}

	if timeToExpire > 0 {
		duration := closeDelay + time.Duration(timeToExpire)*time.Second
		c.expireTimer = time.AfterFunc(duration, c.expire)
	}

	body.Client = c.UID
	resp.Body = body
	return resp, nil
}
예제 #4
0
// connectCmd handles connect command from client - client must send this
// command immediately after establishing Websocket or SockJS connection with
// Centrifugo
func (c *client) connectCmd(cmd *connectClientCommand) (*response, error) {

	resp := newResponse("connect")

	if c.authenticated {
		logger.ERROR.Println("wrong connect message: client already authenticated")
		return nil, ErrInvalidMessage
	}

	pk := cmd.Project
	user := cmd.User
	info := cmd.Info

	c.app.RLock()
	insecure := c.app.config.Insecure
	closeDelay := c.app.config.ExpiredConnectionCloseDelay
	c.app.RUnlock()

	var timestamp string
	var token string
	if !insecure {
		timestamp = cmd.Timestamp
		token = cmd.Token
	} else {
		timestamp = ""
		token = ""
	}

	project, exists := c.app.projectByKey(pk)
	if !exists {
		return nil, ErrProjectNotFound
	}

	if !insecure {
		isValid := auth.CheckClientToken(project.Secret, string(pk), string(user), timestamp, info, token)
		if !isValid {
			logger.ERROR.Println("invalid token for user", user)
			return nil, ErrInvalidToken
		}
	}

	if !insecure {
		ts, err := strconv.Atoi(timestamp)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, ErrInvalidMessage
		}
		c.timestamp = int64(ts)
	} else {
		c.timestamp = time.Now().Unix()
	}

	c.User = user
	c.Project = pk

	body := &connectBody{}

	var timeToExpire int64 = 0

	connLifetime := project.ConnLifetime
	if connLifetime > 0 && !insecure {
		timeToExpire := c.timestamp + connLifetime - time.Now().Unix()
		if timeToExpire <= 0 {
			body.Expired = true
			body.TTL = &connLifetime
			resp.Body = body
			return resp, nil
		}
	}

	c.authenticated = true
	c.defaultInfo = []byte(info)
	c.Channels = map[Channel]bool{}
	c.channelInfo = map[Channel][]byte{}

	go c.presencePing()

	err := c.app.addConn(c)
	if err != nil {
		logger.ERROR.Println(err)
		return nil, ErrInternalServerError
	}

	if c.app.mediator != nil {
		c.app.mediator.Connect(c.Project, c.UID, c.User)
	}

	if timeToExpire > 0 {
		duration := time.Duration(timeToExpire+closeDelay) * time.Second
		c.expireTimer = time.AfterFunc(duration, c.expire)
	}

	body.Client = &c.UID
	if connLifetime > 0 {
		body.TTL = &connLifetime
	}
	resp.Body = body
	return resp, nil
}