func (this *Server) getSession(svc *service, req *message.ConnectMessage, resp *message.ConnackMessage) error { // If CleanSession is set to 0, the server MUST resume communications with the // client based on state from the current session, as identified by the client // identifier. If there is no session associated with the client identifier the // server must create a new session. // // If CleanSession is set to 1, the client and server must discard any previous // session and start a new one. This session lasts as long as the network c // onnection. State data associated with this session must not be reused in any // subsequent session. var err error // Check to see if the client supplied an ID, if not, generate one and set // clean session. if len(req.ClientId()) == 0 { req.SetClientId([]byte(fmt.Sprintf("internalclient%d", svc.id))) req.SetCleanSession(true) } cid := string(req.ClientId()) // If CleanSession is NOT set, check the session store for existing session. // If found, return it. if !req.CleanSession() { if svc.sess, err = this.sessMgr.Get(cid); err == nil { resp.SetSessionPresent(true) if err := svc.sess.Update(req); err != nil { return err } } } // If CleanSession, or no existing session found, then create a new one if svc.sess == nil { if svc.sess, err = this.sessMgr.New(cid); err != nil { return err } resp.SetSessionPresent(false) if err := svc.sess.Init(req); err != nil { return err } } return nil }
func (this *Session) Update(msg *message.ConnectMessage) error { this.mu.Lock() defer this.mu.Unlock() this.cbuf = make([]byte, msg.Len()) this.Cmsg = message.NewConnectMessage() if _, err := msg.Encode(this.cbuf); err != nil { return err } if _, err := this.Cmsg.Decode(this.cbuf); err != nil { return err } return nil }
func (this *Session) Init(msg *message.ConnectMessage) error { this.mu.Lock() defer this.mu.Unlock() if this.initted { return fmt.Errorf("Session already initialized") } this.cbuf = make([]byte, msg.Len()) this.Cmsg = message.NewConnectMessage() if _, err := msg.Encode(this.cbuf); err != nil { return err } if _, err := this.Cmsg.Decode(this.cbuf); err != nil { return err } if this.Cmsg.WillFlag() { this.Will = message.NewPublishMessage() this.Will.SetQoS(this.Cmsg.WillQos()) this.Will.SetTopic(this.Cmsg.WillTopic()) this.Will.SetPayload(this.Cmsg.WillMessage()) this.Will.SetRetain(this.Cmsg.WillRetain()) } this.topics = make(map[string]byte, 1) this.id = string(msg.ClientId()) this.Pub1ack = newAckqueue(defaultQueueSize) this.Pub2in = newAckqueue(defaultQueueSize) this.Pub2out = newAckqueue(defaultQueueSize) this.Suback = newAckqueue(defaultQueueSize) this.Unsuback = newAckqueue(defaultQueueSize) this.Pingack = newAckqueue(defaultQueueSize) this.initted = true return nil }
// Connect is for MQTT clients to open a connection to a remote server. It needs to // know the URI, e.g., "tcp://127.0.0.1:1883", so it knows where to connect to. It also // needs to be supplied with the MQTT CONNECT message. func (this *Client) Connect(uri string, msg *message.ConnectMessage) (err error) { this.checkConfiguration() if msg == nil { return fmt.Errorf("msg is nil") } u, err := url.Parse(uri) if err != nil { return err } if u.Scheme != "tcp" { return ErrInvalidConnectionType } conn, err := net.Dial(u.Scheme, u.Host) if err != nil { return err } defer func() { if err != nil { conn.Close() } }() if msg.KeepAlive() < minKeepAlive { msg.SetKeepAlive(minKeepAlive) } if err = writeMessage(conn, msg); err != nil { return err } conn.SetReadDeadline(time.Now().Add(time.Second * time.Duration(this.ConnectTimeout))) resp, err := getConnackMessage(conn) if err != nil { return err } if resp.ReturnCode() != message.ConnectionAccepted { return resp.ReturnCode() } this.svc = &service{ id: atomic.AddUint64(&gsvcid, 1), client: true, conn: conn, keepAlive: int(msg.KeepAlive()), connectTimeout: this.ConnectTimeout, ackTimeout: this.AckTimeout, timeoutRetries: this.TimeoutRetries, } err = this.getSession(this.svc, msg, resp) if err != nil { return err } p := topics.NewMemProvider() topics.Register(this.svc.sess.ID(), p) this.svc.topicsMgr, err = topics.NewManager(this.svc.sess.ID()) if err != nil { return err } if err := this.svc.start(); err != nil { this.svc.stop() return err } this.svc.inStat.increment(int64(msg.Len())) this.svc.outStat.increment(int64(resp.Len())) return nil }