Пример #1
0
func (s *server) handle(trans transport.Transport, req_ tmsg.Request) {
	req := mercury.FromTyphonRequest(req_)
	req, rsp := s.applyRequestMiddleware(req)

	if rsp == nil {
		if ep, ok := s.Endpoint(req.Endpoint()); !ok {
			log.Warnf("[Mercury:Server] Received request %s for unknown endpoint %s", req.Id(), req.Endpoint())
			rsp = ErrorResponse(req, errEndpointNotFound)
		} else {
			if rsp_, err := ep.Handle(req); err != nil {
				log.Debugf("[Mercury:Server] Got error from endpoint %s for request %s: %v", ep.Name, req.Id(), err)
				rsp = ErrorResponse(req, err)
				// @todo happy to remove this verbose logging once we have tracing... For now it will allow us to debug things
				log.Debugf("[Mercury:Server] Full request: %+v", req.Body())
				log.Debugf("[Mercury:Server] Full error: %+v", rsp.Body())
			} else if rsp_ == nil {
				rsp = req.Response(nil)
			} else {
				rsp = rsp_
			}
		}
	}
	rsp = s.applyResponseMiddleware(rsp, req)
	if rsp != nil {
		trans.Respond(req, rsp)
	}
}
Пример #2
0
func (s *server) handle(trans transport.Transport, req_ tmsg.Request) {
	req := mercury.FromTyphonRequest(req_)
	req, rsp := s.applyRequestMiddleware(req)

	if rsp == nil {
		if ep, ok := s.Endpoint(req.Endpoint()); !ok {
			log.Warn(req, "[Mercury:Server] Received request %s for unknown endpoint %s", req.Id(), req.Endpoint())
			rsp = ErrorResponse(req, errEndpointNotFound)
		} else {
			if rsp_, err := ep.Handle(req); err != nil {
				rsp = ErrorResponse(req, err)
				log.Info(req, "[Mercury:Server] Error from endpoint %s for %v: %v", ep.Name, req, err, map[string]string{
					"request_payload": string(req.Payload())})
			} else if rsp_ == nil {
				rsp = req.Response(nil)
			} else {
				rsp = rsp_
			}
		}
	}
	rsp = s.applyResponseMiddleware(rsp, req)
	if rsp != nil {
		trans.Respond(req, rsp)
	}
}
Пример #3
0
func (s *server) start(trans transport.Transport) (*tomb.Tomb, error) {
	s.workerTombM.Lock()
	if s.workerTomb != nil {
		s.workerTombM.Unlock()
		return nil, ErrAlreadyRunning
	}
	tm := new(tomb.Tomb)
	s.workerTomb = tm
	s.workerTombM.Unlock()

	stop := func() {
		trans.StopListening(s.Name())
		s.workerTombM.Lock()
		s.workerTomb = nil
		s.workerTombM.Unlock()
	}

	var inbound chan tmsg.Request
	connect := func() error {
		select {
		case <-trans.Ready():
			inbound = make(chan tmsg.Request, 500)
			return trans.Listen(s.Name(), inbound)

		case <-time.After(connectTimeout):
			log.Warnf("[Mercury:Server] Timed out after %s waiting for transport readiness", connectTimeout.String())
			return ttrans.ErrTimeout
		}
	}

	// Block here purposefully (deliberately not in the goroutine below, because we want to report a connection error
	// to the caller)
	if err := connect(); err != nil {
		stop()
		return nil, err
	}

	tm.Go(func() error {
		defer stop()
		for {
			select {
			case req, ok := <-inbound:
				if !ok {
					// Received because the channel closed; try to reconnect
					log.Warn("[Mercury:Server] Inbound channel closed; trying to reconnect…")
					if err := connect(); err != nil {
						log.Criticalf("[Mercury:Server] Could not reconnect after channel close: %s", err)
						return err
					}
				} else {
					go s.handle(trans, req)
				}

			case <-tm.Dying():
				return tomb.ErrDying
			}
		}
	})
	return tm, nil
}
Пример #4
0
// performCall executes a single Call, unmarshals the response (if there is a response proto), and pushes the updted
// clientCall down the response channel
func (c *client) performCall(call clientCall, middleware []ClientMiddleware, trans transport.Transport,
	timeout time.Duration, completion chan<- clientCall) {

	req := call.req

	// Ensure we have a request ID before the request middleware is executed
	if id := req.Id(); id == "" {
		_uuid, err := uuid.NewV4()
		if err != nil {
			log.Errorf("[Mercury:Client] Failed to generate request uuid: %v", err)
			call.err = terrors.Wrap(err, nil).(*terrors.Error)
			completion <- call
			return
		}
		req.SetId(_uuid.String())
	}

	// Apply request middleware
	for _, md := range middleware {
		req = md.ProcessClientRequest(req)
	}

	log.Debugf("[Mercury:Client] Sending request to %s/%s…", req.Service(), req.Endpoint())

	rsp_, err := trans.Send(req, timeout)
	if err != nil {
		call.err = terrors.Wrap(err, nil).(*terrors.Error)
	} else if rsp_ != nil {
		rsp := mercury.FromTyphonResponse(rsp_)

		// Servers set header Content-Error: 1 when sending errors. For those requests, unmarshal the error, leaving the
		// call's response nil
		if rsp.IsError() {
			errRsp := rsp.Copy()
			if unmarshalErr := c.unmarshaler(rsp, &tperrors.Error{}).UnmarshalPayload(errRsp); unmarshalErr != nil {
				call.err = terrors.WrapWithCode(unmarshalErr, nil, terrors.ErrBadResponse).(*terrors.Error)
			} else {
				err := errRsp.Body().(*tperrors.Error)
				call.err = terrors.Unmarshal(err)
			}

			// Set the response Body to a nil – but typed – interface to avoid type conversion panics if Body
			// properties are accessed in spite of the error
			// Relevant: http://golang.org/doc/faq#nil_error
			if call.rspProto != nil {
				bodyT := reflect.TypeOf(call.rspProto)
				rsp.SetBody(reflect.New(bodyT.Elem()).Interface())
			}

		} else if call.rspProto != nil {
			rsp.SetBody(call.rspProto)
			if err := c.unmarshaler(rsp, call.rspProto).UnmarshalPayload(rsp); err != nil {
				call.err = terrors.WrapWithCode(err, nil, terrors.ErrBadResponse).(*terrors.Error)
			}
		}

		call.rsp = rsp
	}

	// Apply response/error middleware (in reverse order)
	for i := len(middleware) - 1; i >= 0; i-- {
		mw := middleware[i]
		if call.err != nil {
			mw.ProcessClientError(call.err, call.req)
		} else {
			call.rsp = mw.ProcessClientResponse(call.rsp, call.req)
		}
	}

	completion <- call
}