func (cs *clientState) handleCmdUnsubscribe(curFrame *frame.Frame) { if id, ok := curFrame.Headers.Get("id"); ok { if sub, exists := cs.subs[id]; exists { dest.Unsubscribe(sub.dest, sub) delete(cs.subs, id) } else { cs.ErrorString(fmt.Sprintf("subscription id '%s' doesn't exist.", id)) } } else { cs.ErrorString("an id is required to UNSUBSCRIBE.") } }
func (cs *clientState) HandleIncomingFrames(getFrame frameProvider) { defer func() { for _, sub := range cs.subs { dest.Unsubscribe(sub.dest, sub) } // Clean up everything. close(cs.outgoing) close(cs.incomingMsgs) }() go func() { for subMsg := range cs.incomingMsgs { sub := subMsg.sub msg := subMsg.msg f := frame.NewFrame() f.Cmd = "MESSAGE" f.Headers.Add("message-id", strconv.FormatUint(msg.Id, 10)) f.Headers.Add("subscription", sub.id) if sub.ackMode != ackModeAuto { f.Headers.Add("ack", strconv.FormatUint(uint64(cs.ackId), 10)) cs.ackId++ } for k, values := range msg.Frame.Headers { for _, v := range values { f.Headers.Add(k, v) } } f.Body = subMsg.msg.Frame.Body // XXX - We need to implement ack handling here. cs.outgoing <- f } }() var curFrame *frame.Frame processFrame := func() { curFrame = getFrame() if curFrame != nil { log.Printf("conn %d cmd %s", cs.id, curFrame.Cmd) return } curFrame = nil cs.ErrorString("failed to parse frame. good bye!") return } handleReceipt := func() { if v, ok := curFrame.Headers.Get("receipt"); ok { resp := frame.NewFrame() resp.Cmd = "RECEIPT" resp.Headers.Add("receipt-id", v) cs.outgoing <- resp } } // Before connection. for cs.phase == opened { processFrame() if curFrame == nil { return } if err := curFrame.ValidateFrame(); err != nil { cs.ErrorString(err.Error()) break } switch curFrame.Cmd { case "CONNECT", "STOMP": if _, ok := curFrame.Headers.Get("receipt"); ok { cs.ErrorString("receipt not allowed during connect.") break } supVersion, ok := curFrame.Headers.Get("accept-version") if !ok { cs.ErrorString("an accept-version header is required") break } validVersion := false for _, v := range strings.Split(supVersion, ",") { if v == "1.2" { validVersion = true } } if !validVersion { cs.ErrorString("this server only supports standard version 1.2") break } cs.version = "1.2" cs.phase = connected resp := frame.NewFrame() resp.Cmd = "CONNECTED" resp.Headers.Add("version", cs.version) if _, ok := curFrame.Headers.Get("heart-beat"); ok { resp.Headers.Add("heart-beat", "0,0") } cs.outgoing <- resp default: cs.ErrorString("unknown/unallowed command.") } } // Now we're connected. for cs.phase == connected { processFrame() if curFrame == nil { return } if err := curFrame.ValidateFrame(); err != nil { cs.ErrorString(err.Error()) break } switch curFrame.Cmd { case "CONNECT": cs.ErrorString("you're already connected.") case "DISCONNECT": log.Printf("conn %d requested disconnect", cs.id) cs.phase = disconnected case "SUBSCRIBE": cs.handleCmdSubscribe(curFrame) case "UNSUBSCRIBE": cs.handleCmdUnsubscribe(curFrame) case "SEND": dst, ok := curFrame.Headers.Get("destination") if !ok { cs.ErrorString("SEND requires a destination.") } log.Printf("conn %d sending to destination %s", cs.id, dst) dest.Send(dest.DestId(dst), curFrame) default: cs.ErrorString("unknown command.") } handleReceipt() } }