Ejemplo n.º 1
0
func (suite *errorSetSuite) SetupTest() {
	suite.rawErrs = map[string]*terrors.Error{}
	suite.errs = nil

	err := terrors.InternalService("", "uid1", nil)
	err.Params[errUidField] = "uid1"
	err.Params[errServiceField] = "service.uid1"
	err.Params[errEndpointField] = "uid1"
	suite.errs = append(suite.errs, err)
	suite.rawErrs["uid1"] = err

	err = terrors.InternalService("", "uid2", nil)
	err.Params[errUidField] = "uid2"
	err.Params[errServiceField] = "service.uid2"
	err.Params[errEndpointField] = "uid2"
	suite.errs = append(suite.errs, err)
	suite.rawErrs["uid2"] = err

	err = terrors.InternalService("", "uid3", nil)
	err.Params[errUidField] = "uid3"
	err.Params[errServiceField] = "service.uid2" // Same service as uid2
	err.Params[errEndpointField] = "uid3"
	suite.errs = append(suite.errs, err)
	suite.rawErrs["uid3"] = err
}
Ejemplo n.º 2
0
// exec actually executes the requests; called by Go() within a sync.Once.
func (c *client) exec() {
	defer close(c.doneC)

	c.RLock()
	timeout := c.timeout
	calls := c.calls // We don't need to make a copy as calls cannot be mutated once execution begins
	trans := c.transport()
	middleware := c.middleware
	c.RUnlock()

	completedCallsC := make(chan clientCall, len(calls))
	for _, call := range calls {
		if call.err != nil {
			completedCallsC <- call
			continue
		} else if trans == nil {
			call.err = terrors.InternalService("no_transport", "Client has no transport", nil)
			completedCallsC <- call
			continue
		}
		go c.performCall(call, middleware, trans, timeout, completedCallsC)
	}

	// Collect completed calls into a new map
	completedCalls := make(map[string]clientCall, cap(completedCallsC))
	for i := 0; i < cap(completedCallsC); i++ {
		call := <-completedCallsC
		completedCalls[call.uid] = call
	}
	close(completedCallsC)

	c.Lock()
	defer c.Unlock()
	c.calls = completedCalls
}
Ejemplo n.º 3
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
}
Ejemplo n.º 4
0
func (suite *errorSetSuite) TestMultiErrorPriority() {
	br := terrors.BadRequest("missing_param", "foo bar", nil)
	is := terrors.InternalService("something_broke", "hello world", nil)
	suite.Assert().True(higherPriority(is.Code, br.Code))
	se := terrors.New("something_else", "baz", nil)
	suite.Assert().True(higherPriority(is.Code, se.Code))
	suite.Assert().True(higherPriority(br.Code, se.Code))

	es := ErrorSet{se, is, br}
	suite.Assert().Equal(is.Code, es.Combined().(*terrors.Error).Code)
}
Ejemplo n.º 5
0
	"golang.org/x/net/context"
	"gopkg.in/tomb.v2"

	"github.com/mondough/mercury"
	"github.com/mondough/mercury/transport"
	terrors "github.com/mondough/typhon/errors"
	tmsg "github.com/mondough/typhon/message"
	ttrans "github.com/mondough/typhon/transport"
)

const (
	connectTimeout = 30 * time.Second
)

var (
	ErrAlreadyRunning   error = terrors.InternalService("", "Server is already running", nil) // empty dotted code so impl details don't leak outside
	ErrTransportClosed  error = terrors.InternalService("", "Transport closed", nil)
	errEndpointNotFound       = terrors.BadRequest("endpoint_not_found", "Endpoint not found", nil)
	defaultMiddleware   []ServerMiddleware
	defaultMiddlewareM  sync.RWMutex
)

func NewServer(name string) Server {
	defaultMiddlewareM.RLock()
	middleware := defaultMiddleware
	defaultMiddlewareM.RUnlock()

	return &server{
		name:       name,
		middleware: middleware,
	}
Ejemplo n.º 6
0
	"github.com/mondough/typhon/errors"
	"github.com/mondough/typhon/message"
	"github.com/mondough/typhon/transport"
)

const (
	DirectReplyQueue = "amq.rabbitmq.reply-to"

	connectTimeout  = 30 * time.Second
	chanSendTimeout = 10 * time.Second
	respondTimeout  = 10 * time.Second
)

var (
	ErrCouldntConnect   = errors.InternalService("", "Could not connect to RabbitMQ", nil)
	ErrDeliveriesClosed = errors.InternalService("", "Delivery channel closed", nil)
	ErrNoReplyTo        = errors.BadRequest("", "Request does not have appropriate X-Rabbit-ReplyTo header", nil)
)

type rabbitTransport struct {
	tomb          *tomb.Tomb
	connM         sync.RWMutex                       // protects conn + connReady
	conn          *RabbitConnection                  // underlying connection
	connReady     chan struct{}                      // swapped along with conn (reconnecting)
	replyQueue    string                             // message reply queue name
	inflightReqs  map[string]chan<- message.Response // correlation id: response chan
	inflightReqsM sync.Mutex                         // protects inflightReqs
	listeners     map[string]*tomb.Tomb              // service name: tomb
	listenersM    sync.RWMutex                       // protects listeners
}
Ejemplo n.º 7
0
package transport

import (
	"time"

	"github.com/mondough/typhon/errors"
	"github.com/mondough/typhon/message"
	"gopkg.in/tomb.v2"
)

var (
	// ErrAlreadyListening indicates a listener channel is already active for a given service
	ErrAlreadyListening = errors.InternalService("", "Listener already registered for service", nil)
	// ErrTimeout indicates a timeout was exceeded
	ErrTimeout = errors.Timeout("", "Timed out", nil)
)

// A Transport provides a persistent interface to a transport layer. It is capable of sending and receiving Messages
// on behalf of multiple services in parallel.
type Transport interface {
	// A Tomb tracking the lifecycle of the Transport.
	Tomb() *tomb.Tomb
	// Ready vends a channel to wait on until the Transport is ready for use. Note that this will block indefinitely if
	// the Transport never reaches readiness. When ready, the channel is closed (so it's safe to listen in many
	// goroutines)
	Ready() <-chan struct{}
	// Listen for requests destined for a specific service, forwarding them down the passed channel. If another listener
	// is already listening, returns ErrAlreadyListening.
	Listen(serviceName string, inboundChan chan<- message.Request) error
	// StopListening terminates a listener for the passed service, returning whether successful.
	StopListening(serviceName string) bool