Esempio n. 1
0
// Request part of Request-Reply design pattern.
func (g *Gilmour) request(topic string, msg *Message, opts *RequestOpts) (*Response, error) {
	if has, err := g.backend.HasActiveSubscribers(g.requestDestination(topic)); err != nil {
		return nil, err
	} else if !has {
		return nil, errors.New("No active listeners for: " + topic)
	}

	if msg == nil {
		msg = NewMessage()
	}

	sender := proto.SenderId()
	msg.setSender(sender)
	respChannel := proto.ResponseTopic(sender)

	if opts == nil {
		opts = NewRequestOpts()
	}

	//Wait for a responseHandler
	f := make(chan *Message, 1)

	rOpts := NewHandlerOpts().setOneShot().sendResponse(false)
	g.ReplyTo(respChannel, func(req *Request, _ *Message) {
		f <- req.gData
	}, rOpts)

	// Send Timeout message to channel, Need to send timeout over the wire,
	// to ensure gilmour cleans up the oneShot response handlers as well.
	timeout := opts.GetTimeout()
	if timeout > 0 {
		time.AfterFunc(time.Duration(timeout)*time.Second, func() {
			g.sendTimeout(sender, respChannel)
		})
	}

	g.publish(g.requestDestination(topic), msg)

	response := newResponse(1)
	response.write(<-f)
	return response, nil
}
Esempio n. 2
0
func subscribeHealth(g *Gilmour) {
	health_topic := proto.HealthTopic(g.getIdent())
	handlerOpts := NewHandlerOpts().SetGroup("exclusive")

	g.ReplyTo(health_topic, func(r *Request, w *Message) {
		topics := []string{}

		resp_topic := proto.ResponseTopic("")

		for t, _ := range g.getAllSubscribers() {
			if strings.HasPrefix(t, resp_topic) || strings.HasPrefix(t, health_topic) {
				//Do Nothing, these are internal topics
			} else {
				topics = append(topics, t)
			}
		}

		w.SetData(topics)
	}, handlerOpts)
}
Esempio n. 3
0
func (g *Gilmour) handleRequest(s *Subscription, topic string, m *Message) {
	senderId := m.GetSender()

	req := &Request{topic, m}
	res := NewMessage()
	res.setSender(proto.ResponseTopic(senderId))

	done := make(chan bool, 1)

	//Executing Request
	go func(done chan<- bool) {

		// Schedule a function to recover in case handler runs into an error.
		// Read more: https://gist.github.com/meson10/d56eface6f87c664d07d

		defer func() {
			err := recover()
			if err == nil {
				return
			}

			const size = 4096
			buf := make([]byte, size)
			buf = buf[:runtime.Stack(buf, false)]
			buffer := string(buf)
			res.SetData(buffer).SetCode(500)

			done <- true

		}()

		s.GetHandler()(req, res)
		done <- true
	}(done)

	// Start a timeout handler, which writes on the Done channel, ahead of the
	// handler. This might result in a RACE condition, as there is no way to
	// kill a goroutine, since they are not preemptive.

	timeout := s.GetOpts().GetTimeout()
	time.AfterFunc(time.Duration(timeout)*time.Second, func() {
		done <- false
	})

	status := <-done

	if s.GetOpts().shouldSendResponse() {
		if status == false {
			g.sendTimeout(senderId, res.GetSender())
		} else {
			if res.GetCode() == 0 {
				res.SetCode(200)
			}

			if err := g.publish(res.GetSender(), res); err != nil {
				ui.Alert(err.Error())
			}
		}

	} else if status == false {
		// Inform the error catcher, If there is no handler for this Request
		// but the request had failed. This is automatically handled in case
		// of a response being written via Publisher.
		request := string(req.bytes())
		errMsg := proto.MakeError(499, topic, request, "", req.Sender(), "")
		g.reportError(errMsg)
	}
}