Exemple #1
0
// Handle takes an inbound Request, unmarshals it, dispatches it to the handler, and serialises the result as a
// Response. Note that the response may be nil.
func (e Endpoint) Handle(req mercury.Request) (rsp mercury.Response, err error) {
	// Unmarshal the request body (unless there already is one)
	if req.Body() == nil && e.Request != nil {
		if um := e.unmarshaler(req); um != nil {
			if werr := terrors.Wrap(um.UnmarshalPayload(req), nil); werr != nil {
				log.Warnf("[Mercury:Server] Cannot unmarshal request payload: %v", werr)
				terr := werr.(*terrors.Error)
				terr.Code = terrors.ErrBadRequest
				rsp, err = nil, terr
				return
			}
		}
	}

	defer func() {
		if v := recover(); v != nil {
			traceVerbose := make([]byte, 1024)
			runtime.Stack(traceVerbose, true)
			log.Criticalf("[Mercury:Server] Recovered from handler panic for request %s:\n%v\n%s", req.Id(), v,
				string(traceVerbose))
			rsp, err = nil, terrors.InternalService("panic", fmt.Sprintf("Panic in handler %s:\n%s", req.Endpoint(),
				string(traceVerbose)), nil)
		}
	}()
	rsp, err = e.Handler(req)
	return
}
Exemple #2
0
// ErrorResponse constructs a response for the given request, with the given error as its contents. Mercury clients
// know how to unmarshal these errors.
func ErrorResponse(req mercury.Request, err error) mercury.Response {
	rsp := req.Response(nil)
	var terr *terrors.Error
	if err != nil {
		terr = terrors.Wrap(err, nil).(*terrors.Error)
	}
	rsp.SetBody(terrors.Marshal(terr))
	if err := tmsg.ProtoMarshaler().MarshalBody(rsp); err != nil {
		log.Errorf("[Mercury:Server] Failed to marshal error response: %v", err)
		return nil // Not much we can do here
	}
	rsp.SetIsError(true)
	return rsp
}
Exemple #3
0
func (c *client) Add(cl Call) Client {
	cc := clientCall{
		uid:      cl.Uid,
		rspProto: cl.Response,
	}
	req, err := cl.Request()
	if err != nil {
		cc.err = terrors.Wrap(err, nil).(*terrors.Error)
	} else {
		cc.req = req
	}
	c.addCall(cc)
	return c
}
Exemple #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
}