Ejemplo n.º 1
0
func (cs *clientState) handleCmdSubscribe(f *frame.Frame) {
	s := &clientSub{}
	s.client = cs

	if id, ok := f.Headers.Get("id"); ok && len(id) > 0 {
		s.id = id
	} else {
		cs.ErrorString("id header required on SUBSCRIBE")
		return
	}

	if ack, ok := f.Headers.Get("ack"); ok {
		switch ack {
		case "auto":
			s.ackMode = ackModeAuto
		case "client":
			s.ackMode = ackModeClient
		case "client-individual":
			s.ackMode = ackModeClientIndividual
		default:
			cs.ErrorString(fmt.Sprintf("ack mode '%s' invalid on SUBSCRIBE", ack))
			return
		}
	} else {
		s.ackMode = ackModeAuto
	}

	if dst, ok := f.Headers.Get("destination"); ok && len(dst) > 1 {
		s.dest = dest.DestId(dst)
	} else {
		cs.ErrorString("a destination headers is required for SUBSCRIBE")
		return
	}

	if _, exists := cs.subs[s.id]; exists {
		cs.ErrorString(fmt.Sprintf("a subscription IDed '%s' already exists.", s.id))
		return
	}

	if err := dest.Subscribe(s.dest, s); err != nil {
		cs.ErrorString(fmt.Sprintf("failed to subscribe '%s'", err))
		return
	}

	cs.subs[s.id] = s
}
Ejemplo n.º 2
0
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()
	}
}