func (self *Handler) Publish(p *codec.PublishMessage) { //log.Info("Received Publish Message: %s: %+v", p.PacketIdentifier, p) conn := self.Connection // TODO: check permission. // TODO: この部分はengine側にあるべき機能なので治す(というか下のconnectionがやる所?) if p.QosLevel == 1 { ack := codec.NewPubackMessage() ack.PacketIdentifier = p.PacketIdentifier conn.WriteMessageQueue(ack) log.Debug("Send puback message to sender. [%s: %d]", conn.GetId(), ack.PacketIdentifier) } else if p.QosLevel == 2 { ack := codec.NewPubrecMessage() ack.PacketIdentifier = p.PacketIdentifier conn.WriteMessageQueue(ack) log.Debug("Send pubrec message to sender. [%s: %d]", conn.GetId(), ack.PacketIdentifier) } // TODO: QoSによっては適切なMessageIDを追加する // Server / ClientはそれぞれMessageTableが違う if p.QosLevel > 0 { // TODO: と、いうことはメッセージの deep コピーが簡単にできるようにしないとだめ // 色々考えると面倒だけど、ひとまずはフルコピーでやっとこう // id := conn.GetOutGoingTable().NewId() // p.PacketIdentifier = id conn.GetOutGoingTable().Register(p.PacketIdentifier, p, conn) p.Opaque = conn } // NOTE: We don't block here. currently use goroutine but should pass message to background worker. go self.Engine.SendPublishMessage(p, conn.GetId(), conn.IsBridge()) }
// TODO: どっかで綺麗にしたい func NewMyConnection(conf *MyConfig) *MyConnection { if conf == nil { conf = &defaultConfig } c := &MyConnection{ Events: make(map[string]interface{}), Queue: make(chan codec.Message, conf.QueueSize), OfflineQueue: make([]codec.Message, 0), MaxOfflineQueue: conf.OfflineQueueSize, InflightTable: util.NewMessageTable(), SubscribeHistory: make(map[string]int), Mutex: sync.RWMutex{}, Qlobber: util.NewQlobber(), SubscribedTopics: make(map[string]int), Last: time.Now(), CleanSession: true, Keepalive: conf.Keepalive, State: STATE_INIT, Closed: make(chan bool), MaxMessageSize: conf.MaxMessageSize, fch: make(chan bool, 1), } c.t = time.AfterFunc(time.Second*300, func() { // NOTE: assume flush if c.State == STATE_CONNECTED { c.kickFlusher() } }) c.logger = log.Global if conf.Logger != nil { c.logger = conf.Logger } if conf.WritePerSec > 0 { c.balancer = &util.Balancer{ PerSec: conf.WritePerSec, } } if conf.MaxMessageSize > 0 { c.MaxMessageSize = conf.MaxMessageSize } c.Events["connected"] = func() { c.State = STATE_CONNECTED } c.Events["connack"] = func(result uint8) { if result == 0 { c.SetState(STATE_CONNECTED) if c.Reconnect { for key, qos := range c.SubscribeHistory { c.Subscribe(key, qos) } } //TODO: このアホっぽい実装はあとでちゃんとなおす。なおしたい if len(c.OfflineQueue) > 0 { c.Mutex.Lock() var targets []codec.Message for len(c.OfflineQueue) > 0 { targets = append(targets, c.OfflineQueue[0]) c.OfflineQueue = c.OfflineQueue[1:] } c.Mutex.Unlock() for i := 0; i < len(targets); i++ { c.Queue <- targets[i] } } c.setupKicker() } else { c.State = STATE_CLOSED } } // for Wait API c.InflightTable.SetOnFinish(func(id uint16, message codec.Message, opaque interface{}) { if m, ok := message.(*codec.PublishMessage); ok { if m.QosLevel == 1 { if b, ok := opaque.(chan bool); ok { close(b) } } else if m.QosLevel == 2 { if b, ok := opaque.(chan bool); ok { close(b) } } } }) // こっちに集約できるとClientが薄くなれる c.Events["publish"] = func(msg *codec.PublishMessage) { if msg.QosLevel == 1 { ack := codec.NewPubackMessage() ack.PacketIdentifier = msg.PacketIdentifier c.WriteMessageQueue(ack) c.logger.Debug("Send puback message to sender. [%s: %d]", c.GetId(), ack.PacketIdentifier) } else if msg.QosLevel == 2 { ack := codec.NewPubrecMessage() ack.PacketIdentifier = msg.PacketIdentifier c.WriteMessageQueue(ack) c.logger.Debug("Send pubrec message to sender. [%s: %d]", c.GetId(), ack.PacketIdentifier) } } c.Events["puback"] = func(messageId uint16) { c.InflightTable.Unref(messageId) } c.Events["pubrec"] = func(messageId uint16) { ack := codec.NewPubrelMessage() ack.PacketIdentifier = messageId c.Queue <- ack } c.Events["pubrel"] = func(messageId uint16) { ack := codec.NewPubcompMessage() ack.PacketIdentifier = messageId c.Queue <- ack c.InflightTable.Unref(ack.PacketIdentifier) // Unackknowleged } c.Events["pubcomp"] = func(messageId uint16) { c.InflightTable.Unref(messageId) } c.Events["unsuback"] = func(messageId uint16) { mm, err := c.InflightTable.Get(messageId) if err == nil { if v, ok := mm.(*codec.UnsubscribeMessage); ok { delete(c.SubscribeHistory, v.TopicName) } } c.InflightTable.Remove(messageId) } c.Events["subscribe"] = func(p *codec.SubscribeMessage) { } c.Events["suback"] = func(messageId uint16, grunted int) { c.InflightTable.Remove(messageId) } c.Events["unsubscribe"] = func(messageId uint16, granted int, payload []codec.SubscribePayload) { for i := 0; i < len(payload); i++ { delete(c.SubscribeHistory, payload[i].TopicPath) } } // これはコネクション渡したほうがいいんではないだろうか。 c.Events["pingreq"] = func() { // TODO: check Ping count periodically, abort MyConnection when the counter exceeded. c.PingCounter++ } c.Events["pingresp"] = func() { // nothing to do. c.PingCounter-- } c.Events["disconnect"] = func() { // nothing to do ? c.State = STATE_CLOSED } c.Events["error"] = func(err error) { //fmt.Printf("Error: %s\n", err) } c.Events["connect"] = func(msg *codec.ConnectMessage) { } c.Events["parsed"] = func() { } // Write Queue go func() { for { select { case msg := <-c.Queue: state := c.GetState() if state == STATE_CONNECTED || state == STATE_CONNECTING { if msg.GetType() == codec.PACKET_TYPE_PUBLISH { sb := msg.(*codec.PublishMessage) if sb.QosLevel < 0 { c.logger.Error("QoS under zero. %s: %#v", c.Id, sb) break } if sb.QosLevel > 0 { id := c.InflightTable.NewId() sb.PacketIdentifier = id c.InflightTable.Register(id, sb, nil) } } e := c.writeMessage(msg) if e != nil { if v, ok := c.Events["error"].(func(error)); ok { v(e) } } c.invalidateTimer() } else { c.OfflineQueue = append(c.OfflineQueue, msg) } case <-c.Closed: if c.KeepLoop { time.Sleep(time.Second) } else { return } } } }() go c.flusher() return c }