Пример #1
0
func (s *TxStoreSuite) TestSuccessfulTx(c *C) {
	txs := txStore{}

	err := txs.Begin("tx1")
	c.Check(err, IsNil)

	err = txs.Begin("tx2")
	c.Assert(err, IsNil)

	f1 := frame.New(frame.MESSAGE,
		frame.Destination, "/queue/1")

	f2 := frame.New(frame.MESSAGE,
		frame.Destination, "/queue/2")

	f3 := frame.New(frame.MESSAGE,
		frame.Destination, "/queue/3")

	f4 := frame.New(frame.MESSAGE,
		frame.Destination, "/queue/4")

	err = txs.Add("tx1", f1)
	c.Assert(err, IsNil)
	err = txs.Add("tx1", f2)
	c.Assert(err, IsNil)
	err = txs.Add("tx1", f3)
	c.Assert(err, IsNil)
	err = txs.Add("tx2", f4)

	var tx1 []*frame.Frame

	txs.Commit("tx1", func(f *frame.Frame) error {
		tx1 = append(tx1, f)
		return nil
	})
	c.Check(err, IsNil)

	var tx2 []*frame.Frame

	err = txs.Commit("tx2", func(f *frame.Frame) error {
		tx2 = append(tx2, f)
		return nil
	})
	c.Check(err, IsNil)

	c.Check(len(tx1), Equals, 3)
	c.Check(tx1[0], Equals, f1)
	c.Check(tx1[1], Equals, f2)
	c.Check(tx1[2], Equals, f3)

	c.Check(len(tx2), Equals, 1)
	c.Check(tx2[0], Equals, f4)

	// already committed, so should cause an error
	err = txs.Commit("tx1", func(f *frame.Frame) error {
		c.Fatal("should not be called")
		return nil
	})
	c.Check(err, Equals, txUnknown)
}
Пример #2
0
func (s *MemoryQueueSuite) Test1(c *C) {
	mq := NewMemoryQueueStorage()
	mq.Start()

	f1 := frame.New(frame.MESSAGE,
		frame.Destination, "/queue/test",
		frame.MessageId, "msg-001",
		frame.Subscription, "1")

	err := mq.Enqueue("/queue/test", f1)
	c.Assert(err, IsNil)

	f2 := frame.New(frame.MESSAGE,
		frame.Destination, "/queue/test",
		frame.MessageId, "msg-002",
		frame.Subscription, "1")

	err = mq.Enqueue("/queue/test", f2)
	c.Assert(err, IsNil)

	f3 := frame.New(frame.MESSAGE,
		frame.Destination, "/queue/test2",
		frame.MessageId, "msg-003",
		frame.Subscription, "2")

	err = mq.Enqueue("/queue/test2", f3)
	c.Assert(err, IsNil)

	// attempt to dequeue from a different queue
	f, err := mq.Dequeue("/queue/other-queue")
	c.Check(err, IsNil)
	c.Assert(f, IsNil)

	f, err = mq.Dequeue("/queue/test2")
	c.Check(err, IsNil)
	c.Assert(f, Equals, f3)

	f, err = mq.Dequeue("/queue/test")
	c.Check(err, IsNil)
	c.Assert(f, Equals, f1)

	f, err = mq.Dequeue("/queue/test")
	c.Check(err, IsNil)
	c.Assert(f, Equals, f2)

	f, err = mq.Dequeue("/queue/test")
	c.Check(err, IsNil)
	c.Assert(f, IsNil)

	f, err = mq.Dequeue("/queue/test2")
	c.Check(err, IsNil)
	c.Assert(f, IsNil)
}
Пример #3
0
func (s *StompSuite) Test_successful_connect_with_nonstandard_header(c *C) {
	resetId()
	fc1, fc2 := testutil.NewFakeConn(c)
	stop := make(chan struct{})

	go func() {
		defer func() {
			fc2.Close()
			close(stop)
		}()
		reader := frame.NewReader(fc2)
		writer := frame.NewWriter(fc2)

		f1, err := reader.Read()
		c.Assert(err, IsNil)
		c.Assert(f1.Command, Equals, "CONNECT")
		c.Assert(f1.Header.Get("login"), Equals, "guest")
		c.Assert(f1.Header.Get("passcode"), Equals, "guest")
		c.Assert(f1.Header.Get("host"), Equals, "/")
		c.Assert(f1.Header.Get("x-max-length"), Equals, "50")
		connectedFrame := frame.New("CONNECTED")
		connectedFrame.Header.Add("session", "session-0voRHrG-VbBedx1Gwwb62Q")
		connectedFrame.Header.Add("heart-beat", "0,0")
		connectedFrame.Header.Add("server", "RabbitMQ/3.2.1")
		connectedFrame.Header.Add("version", "1.0")
		writer.Write(connectedFrame)

		f2, err := reader.Read()
		c.Assert(err, IsNil)
		c.Assert(f2.Command, Equals, "DISCONNECT")
		receipt, _ := f2.Header.Contains("receipt")
		c.Check(receipt, Equals, "1")

		writer.Write(frame.New("RECEIPT", frame.ReceiptId, "1"))
	}()

	client, err := Connect(fc1,
		ConnOpt.Login("guest", "guest"),
		ConnOpt.Host("/"),
		ConnOpt.Header("x-max-length", "50"))
	c.Assert(err, IsNil)
	c.Assert(client, NotNil)
	c.Assert(client.Version(), Equals, V10)
	c.Assert(client.Session(), Equals, "session-0voRHrG-VbBedx1Gwwb62Q")
	c.Assert(client.Server(), Equals, "RabbitMQ/3.2.1")

	err = client.Disconnect()
	c.Assert(err, IsNil)

	<-stop
}
Пример #4
0
func (s *FrameSuite) TestDetermineVersion_MultipleVersions(c *C) {
	f := frame.New(frame.CONNECT)
	f.Header.Add(frame.AcceptVersion, "1.2,1.1,1.0,2.0")
	version, err := determineVersion(f)
	c.Check(version, Equals, stomp.V12)
	c.Check(err, IsNil)
}
Пример #5
0
func (s *FrameSuite) TestDetermineVersion_V11_Connect(c *C) {
	f := frame.New(frame.CONNECT)
	f.Header.Add(frame.AcceptVersion, "1.1")
	version, err := determineVersion(f)
	c.Check(version, Equals, stomp.V11)
	c.Check(err, IsNil)
}
Пример #6
0
func (s *FrameSuite) TestDetermineVersion_V10_Stomp(c *C) {
	// the "STOMP" command was introduced in V1.1, so it must
	// have an accept-version header
	f := frame.New(frame.STOMP)
	_, err := determineVersion(f)
	c.Check(err, Equals, missingHeader(frame.AcceptVersion))
}
Пример #7
0
func (co *connOptions) NewFrame() (*frame.Frame, error) {
	f := frame.New(co.FrameCommand)
	if co.Host != "" {
		f.Header.Set(frame.Host, co.Host)
	}

	// heart-beat
	{
		send := co.WriteTimeout / time.Millisecond
		recv := co.ReadTimeout / time.Millisecond
		f.Header.Set(frame.HeartBeat, fmt.Sprintf("%d,%d", send, recv))
	}

	// login, passcode
	if co.Login != "" || co.Passcode != "" {
		f.Header.Set(frame.Login, co.Login)
		f.Header.Set(frame.Passcode, co.Passcode)
	}

	// accept-version
	f.Header.Set(frame.AcceptVersion, strings.Join(co.AcceptVersions, ","))

	// custom header entries -- note that these do not override
	// header values already set as they are added to the end of
	// the header array
	f.Header.AddHeader(co.Header)

	return f, nil
}
Пример #8
0
func (s *FrameSuite) TestHeartBeat(c *C) {
	f := frame.New(frame.CONNECT,
		frame.AcceptVersion, "1.2",
		frame.Host, "XX")

	// no heart-beat header means zero values
	x, y, err := getHeartBeat(f)
	c.Check(x, Equals, 0)
	c.Check(y, Equals, 0)
	c.Check(err, IsNil)

	f.Header.Add("heart-beat", "123,456")
	x, y, err = getHeartBeat(f)
	c.Check(x, Equals, 123)
	c.Check(y, Equals, 456)
	c.Check(err, IsNil)

	f.Header.Set(frame.HeartBeat, "invalid")
	x, y, err = getHeartBeat(f)
	c.Check(x, Equals, 0)
	c.Check(y, Equals, 0)
	c.Check(err, Equals, invalidHeartBeat)

	f.Header.Del(frame.HeartBeat)
	_, _, err = getHeartBeat(f)
	c.Check(err, IsNil)

	f.Command = frame.SEND
	_, _, err = getHeartBeat(f)
	c.Check(err, Equals, invalidOperationForFrame)
}
Пример #9
0
// Unsubscribes and closes the channel C.
func (s *Subscription) Unsubscribe(opts ...func(*frame.Frame) error) error {
	if s.completed {
		return ErrCompletedSubscription
	}
	f := frame.New(frame.UNSUBSCRIBE, frame.Id, s.id)

	for _, opt := range opts {
		if opt == nil {
			return ErrNilOption
		}
		err := opt(f)
		if err != nil {
			return err
		}
	}

	s.conn.sendFrame(f)
	s.completedMutex.Lock()
	if !s.completed {
		s.completed = true
		close(s.C)
	}
	s.completedMutex.Unlock()
	return nil
}
Пример #10
0
// Sets up a connection for testing
func connectHelper(c *C, version Version) (*Conn, *fakeReaderWriter) {
	fc1, fc2 := testutil.NewFakeConn(c)
	stop := make(chan struct{})

	reader := frame.NewReader(fc2)
	writer := frame.NewWriter(fc2)

	go func() {
		f1, err := reader.Read()
		c.Assert(err, IsNil)
		c.Assert(f1.Command, Equals, "CONNECT")
		f2 := frame.New("CONNECTED", "version", version.String())
		writer.Write(f2)
		close(stop)
	}()

	conn, err := Connect(fc1)
	c.Assert(err, IsNil)
	c.Assert(conn, NotNil)
	<-stop
	return conn, &fakeReaderWriter{
		reader: reader,
		writer: writer,
		conn:   fc2,
	}
}
Пример #11
0
func (s *StompSuite) TestHeartBeatReadTimeout(c *C) {
	conn, rw := createHeartBeatConnection(c, 100, 10000, time.Millisecond)

	go func() {
		f1, err := rw.Read()
		c.Assert(err, IsNil)
		c.Assert(f1.Command, Equals, "SUBSCRIBE")
		messageFrame := frame.New("MESSAGE",
			"destination", f1.Header.Get("destination"),
			"message-id", "1",
			"subscription", f1.Header.Get("id"))
		messageFrame.Body = []byte("Message body")
		rw.Write(messageFrame)
	}()

	sub, err := conn.Subscribe("/queue/test1", AckAuto)
	c.Assert(err, IsNil)
	c.Check(conn.readTimeout, Equals, 101*time.Millisecond)
	//println("read timeout", conn.readTimeout.String())

	msg, ok := <-sub.C
	c.Assert(msg, NotNil)
	c.Assert(ok, Equals, true)

	msg, ok = <-sub.C
	c.Assert(msg, NotNil)
	c.Assert(msg.Err, NotNil)
	c.Assert(msg.Err.Error(), Equals, "read timeout")

	msg, ok = <-sub.C
	c.Assert(msg, IsNil)
	c.Assert(ok, Equals, false)
}
Пример #12
0
func createHeartBeatConnection(
	c *C,
	readTimeout, writeTimeout int,
	readTimeoutError time.Duration) (*Conn, *fakeReaderWriter) {
	fc1, fc2 := testutil.NewFakeConn(c)
	stop := make(chan struct{})

	reader := frame.NewReader(fc2)
	writer := frame.NewWriter(fc2)

	go func() {
		f1, err := reader.Read()
		c.Assert(err, IsNil)
		c.Assert(f1.Command, Equals, "CONNECT")
		c.Assert(f1.Header.Get("heart-beat"), Equals, "1,1")
		f2 := frame.New("CONNECTED", "version", "1.2")
		f2.Header.Add("heart-beat", fmt.Sprintf("%d,%d", readTimeout, writeTimeout))
		writer.Write(f2)
		close(stop)
	}()

	conn, err := Connect(fc1,
		ConnOpt.HeartBeat(time.Millisecond, time.Millisecond),
		ConnOpt.HeartBeatError(readTimeoutError))
	c.Assert(conn, NotNil)
	c.Assert(err, IsNil)
	<-stop
	return conn, &fakeReaderWriter{
		reader: reader,
		writer: writer,
		conn:   fc2,
	}
}
Пример #13
0
func (s *FrameSuite) TestDetermineVersion_IncompatibleVersions(c *C) {
	f := frame.New(frame.CONNECT)
	f.Header.Add(frame.AcceptVersion, "0.2,0.1,1.3,2.0")
	version, err := determineVersion(f)
	c.Check(version, Equals, stomp.Version(""))
	c.Check(err, Equals, unknownVersion)
}
Пример #14
0
func (s *TopicSuite) TestTopicWithoutSubscription(c *C) {
	topic := newTopic("destination")

	f := frame.New(frame.MESSAGE,
		frame.Destination, "destination")

	topic.Enqueue(f)
}
Пример #15
0
// Create an ACK or NACK frame. Complicated by version incompatibilities.
func (c *Conn) createAckNackFrame(msg *Message, ack bool) (*frame.Frame, error) {
	if !ack && !c.version.SupportsNack() {
		return nil, ErrNackNotSupported
	}

	if msg.Header == nil || msg.Subscription == nil || msg.Conn == nil {
		return nil, ErrNotReceivedMessage
	}

	if msg.Subscription.AckMode() == AckAuto {
		if ack {
			// not much point sending an ACK to an auto subscription
			return nil, nil
		} else {
			// sending a NACK for an ack:auto subscription makes no
			// sense
			return nil, ErrCannotNackAutoSub
		}
	}

	var f *frame.Frame
	if ack {
		f = frame.New(frame.ACK)
	} else {
		f = frame.New(frame.NACK)
	}

	switch c.version {
	case V10, V11:
		f.Header.Add(frame.Subscription, msg.Subscription.Id())
		if messageId, ok := msg.Header.Contains(frame.MessageId); ok {
			f.Header.Add(frame.MessageId, messageId)
		} else {
			return nil, missingHeader(frame.MessageId)
		}
	case V12:
		if ack, ok := msg.Header.Contains(frame.Ack); ok {
			f.Header.Add(frame.Id, ack)
		} else {
			return nil, missingHeader(frame.Ack)
		}
	}

	return f, nil
}
Пример #16
0
// Unsubscribes and closes the channel C.
func (s *Subscription) Unsubscribe() error {
	if s.completed {
		return ErrCompletedSubscription
	}
	f := frame.New(frame.UNSUBSCRIBE, frame.Id, s.id)
	s.conn.sendFrame(f)
	s.completed = true
	close(s.C)
	return nil
}
Пример #17
0
// Commit will commit the transaction. All messages and acknowledgements
// sent to the STOMP server on this transaction will be processed atomically.
func (tx *Transaction) Commit() error {
	if tx.completed {
		return ErrCompletedTransaction
	}

	f := frame.New(frame.COMMIT, frame.Transaction, tx.id)
	tx.conn.sendFrame(f)
	tx.completed = true

	return nil
}
Пример #18
0
// Sends a RECEIPT frame to the client if the frame f contains
// a receipt header. If the frame does contain a receipt header,
// it will be removed from the frame.
func (c *Conn) sendReceiptImmediately(f *frame.Frame) error {
	if receipt, ok := f.Header.Contains(frame.Receipt); ok {
		// Remove the receipt header from the frame. This is handy
		// for transactions, because the frame has its receipt
		// header removed prior to entering the transaction store.
		// When the frame is processed upon transaction commit, it
		// will not have a receipt header anymore.
		f.Header.Del(frame.Receipt)
		return c.sendImmediately(frame.New(frame.RECEIPT,
			frame.ReceiptId, receipt))
	}
	return nil
}
Пример #19
0
func (s *TopicSuite) TestTopicWithOneSubscription(c *C) {
	sub := &fakeSubscription{}

	topic := newTopic("destination")
	topic.Subscribe(sub)

	f := frame.New(frame.MESSAGE,
		frame.Destination, "destination")

	topic.Enqueue(f)

	c.Assert(len(sub.Frames), Equals, 1)
	c.Assert(sub.Frames[0], Equals, f)
}
Пример #20
0
// Send an ERROR frame to the client and immediately. The error
// message is derived from err. If f is non-nil, it is the frame
// whose contents have caused the error. Include the receipt-id
// header if the frame contains a receipt header.
func (c *Conn) sendErrorImmediately(err error, f *frame.Frame) {
	errorFrame := frame.New(frame.ERROR,
		frame.Message, err.Error())

	// Include a receipt-id header if the frame that prompted the error had
	// a receipt header (as suggested by the STOMP protocol spec).
	if f != nil {
		if receipt, ok := f.Header.Contains(frame.Receipt); ok {
			errorFrame.Header.Add(frame.ReceiptId, receipt)
		}
	}

	// send the frame to the client, ignore any error condition
	// because we are about to close the connection anyway
	_ = c.sendImmediately(errorFrame)
}
Пример #21
0
func (s *TopicSuite) TestTopicWithTwoSubscriptions(c *C) {
	sub1 := &fakeSubscription{}
	sub2 := &fakeSubscription{}

	topic := newTopic("destination")
	topic.Subscribe(sub1)
	topic.Subscribe(sub2)

	f := frame.New(frame.MESSAGE,
		frame.Destination, "destination",
		"xxx", "yyy")

	topic.Enqueue(f)

	c.Assert(len(sub1.Frames), Equals, 1)
	c.Assert(len(sub2.Frames), Equals, 1)
	c.Assert(sub1.Frames[0], Not(Equals), f)
	c.Assert(sub2.Frames[0], Equals, f)
}
Пример #22
0
// Disconnect will disconnect from the STOMP server. This function
// follows the STOMP standard's recommended protocol for graceful
// disconnection: it sends a DISCONNECT frame with a receipt header
// element. Once the RECEIPT frame has been received, the connection
// with the STOMP server is closed and any further attempt to write
// to the server will fail.
func (c *Conn) Disconnect() error {
	if c.closed {
		return nil
	}

	ch := make(chan *frame.Frame)
	c.writeCh <- writeRequest{
		Frame: frame.New(frame.DISCONNECT, frame.Receipt, allocateId()),
		C:     ch,
	}

	response := <-ch
	if response.Command != frame.RECEIPT {
		return newError(response)
	}

	c.closed = true
	return c.conn.Close()
}
Пример #23
0
// Creates a STOMP frame.
func ExampleNewFrame() {
	/*
		Creates a STOMP frame that looks like the following:

			CONNECT
			login:scott
			passcode:tiger
			host:stompserver
			accept-version:1.1,1.2

			^@
	*/
	f := frame.New("CONNECT",
		"login", "scott",
		"passcode", "tiger",
		"host", "stompserver",
		"accept-version", "1.1,1.2")
	doSomethingWith(f)
}
Пример #24
0
// Subscribe creates a subscription on the STOMP server.
// The subscription has a destination, and messages sent to that destination
// will be received by this subscription. A subscription has a channel
// on which the calling program can receive messages.
func (c *Conn) Subscribe(destination string, ack AckMode, opts ...func(*frame.Frame) error) (*Subscription, error) {
	ch := make(chan *frame.Frame)

	subscribeFrame := frame.New(frame.SUBSCRIBE,
		frame.Destination, destination,
		frame.Ack, ack.String())

	for _, opt := range opts {
		if opt == nil {
			return nil, ErrNilOption
		}
		err := opt(subscribeFrame)
		if err != nil {
			return nil, err
		}
	}

	// If the option functions have not specified the "id" header entry,
	// create one.
	id, ok := subscribeFrame.Header.Contains(frame.Id)
	if !ok {
		id = allocateId()
		subscribeFrame.Header.Add(frame.Id, id)
	}

	request := writeRequest{
		Frame: subscribeFrame,
		C:     ch,
	}

	sub := &Subscription{
		id:             id,
		destination:    destination,
		conn:           c,
		ackMode:        ack,
		C:              make(chan *Message, 16),
		completedMutex: &sync.Mutex{},
	}
	go sub.readLoop(ch)

	c.writeCh <- request
	return sub, nil
}
Пример #25
0
func (s *StompSuite) Test_unsuccessful_connect(c *C) {
	fc1, fc2 := testutil.NewFakeConn(c)
	stop := make(chan struct{})

	go func() {
		defer func() {
			fc2.Close()
			close(stop)
		}()

		reader := frame.NewReader(fc2)
		writer := frame.NewWriter(fc2)
		f1, err := reader.Read()
		c.Assert(err, IsNil)
		c.Assert(f1.Command, Equals, "CONNECT")
		f2 := frame.New("ERROR", "message", "auth-failed")
		writer.Write(f2)
	}()

	conn, err := Connect(fc1)
	c.Assert(conn, IsNil)
	c.Assert(err, ErrorMatches, "auth-failed")
}
Пример #26
0
func createSendFrame(destination, contentType string, body []byte, opts []func(*frame.Frame) error) (*frame.Frame, error) {
	// Set the content-length before the options, because this provides
	// an opportunity to remove content-length.
	f := frame.New(frame.SEND, frame.ContentLength, strconv.Itoa(len(body)))
	f.Body = body

	for _, opt := range opts {
		if opt == nil {
			return nil, ErrNilOption
		}
		if err := opt(f); err != nil {
			return nil, err
		}
	}

	f.Header.Set(frame.Destination, destination)

	if contentType != "" {
		f.Header.Set(frame.ContentType, contentType)
	}

	return f, nil
}
Пример #27
0
// Send and ERROR message to the client. The client
// connection will disconnect as soon as the ERROR
// message has been transmitted. The message header
// will be based on the contents of the err parameter.
func (c *Conn) SendError(err error) {
	f := frame.New(frame.ERROR, frame.Message, err.Error())
	c.Send(f) // will close after successful send
}
Пример #28
0
func (c *Conn) handleConnect(f *frame.Frame) error {
	var err error

	if _, ok := f.Header.Contains(frame.Receipt); ok {
		// CONNNECT and STOMP frames are not allowed to have
		// a receipt header.
		return receiptInConnect
	}

	// if either of these fields are absent, pass nil to the
	// authenticator function.
	login, _ := f.Header.Contains(frame.Login)
	passcode, _ := f.Header.Contains(frame.Passcode)
	if !c.config.Authenticate(login, passcode) {
		// sleep to slow down a rogue client a little bit
		log.Println("authentication failed")
		time.Sleep(time.Second)
		return authenticationFailed
	}

	c.version, err = determineVersion(f)
	if err != nil {
		log.Println("protocol version negotiation failed")
		return err
	}
	c.validator = stomp.NewValidator(c.version)

	if c.version == stomp.V10 {
		// don't want to handle V1.0 at the moment
		// TODO: get working for V1.0
		log.Println("unsupported version", c.version)
		return unsupportedVersion
	}

	cx, cy, err := getHeartBeat(f)
	if err != nil {
		log.Println("invalid heart-beat")
		return err
	}

	// Minimum value as per server config. If the client
	// has requested shorter periods than this value, the
	// server will insist on the longer time period.
	min := asMilliseconds(c.config.HeartBeat(), maxHeartBeat)

	// apply a minimum heartbeat
	if cx > 0 && cx < min {
		cx = min
	}
	if cy > 0 && cy < min {
		cy = min
	}

	// the read timeout has already been processed in the readLoop
	// go-routine
	c.writeTimeout = time.Duration(cy) * time.Millisecond

	response := frame.New(frame.CONNECTED,
		frame.Version, string(c.version),
		frame.Server, "stompd/x.y.z", // TODO: get version
		frame.HeartBeat, fmt.Sprintf("%d,%d", cy, cx))

	c.sendImmediately(response)
	c.stateFunc = connected

	// tell the upper layer we are connected
	c.requestChannel <- Request{Op: ConnectedOp, Conn: c}

	return nil
}
Пример #29
0
func (s *StompSuite) Test_successful_connect_and_disconnect(c *C) {
	testcases := []struct {
		Options           []func(*Conn) error
		NegotiatedVersion string
		ExpectedVersion   Version
		ExpectedSession   string
		ExpectedHost      string
		ExpectedServer    string
	}{
		{
			Options:         []func(*Conn) error{ConnOpt.Host("the-server")},
			ExpectedVersion: "1.0",
			ExpectedSession: "",
			ExpectedHost:    "the-server",
			ExpectedServer:  "some-server/1.1",
		},
		{
			Options:           []func(*Conn) error{},
			NegotiatedVersion: "1.1",
			ExpectedVersion:   "1.1",
			ExpectedSession:   "the-session",
			ExpectedHost:      "the-server",
		},
		{
			Options:           []func(*Conn) error{ConnOpt.Host("xxx")},
			NegotiatedVersion: "1.2",
			ExpectedVersion:   "1.2",
			ExpectedSession:   "the-session",
			ExpectedHost:      "xxx",
		},
	}

	for _, tc := range testcases {
		resetId()
		fc1, fc2 := testutil.NewFakeConn(c)
		stop := make(chan struct{})

		go func() {
			defer func() {
				fc2.Close()
				close(stop)
			}()
			reader := frame.NewReader(fc2)
			writer := frame.NewWriter(fc2)

			f1, err := reader.Read()
			c.Assert(err, IsNil)
			c.Assert(f1.Command, Equals, "CONNECT")
			host, _ := f1.Header.Contains("host")
			c.Check(host, Equals, tc.ExpectedHost)
			connectedFrame := frame.New("CONNECTED")
			if tc.NegotiatedVersion != "" {
				connectedFrame.Header.Add("version", tc.NegotiatedVersion)
			}
			if tc.ExpectedSession != "" {
				connectedFrame.Header.Add("session", tc.ExpectedSession)
			}
			if tc.ExpectedServer != "" {
				connectedFrame.Header.Add("server", tc.ExpectedServer)
			}
			writer.Write(connectedFrame)

			f2, err := reader.Read()
			c.Assert(err, IsNil)
			c.Assert(f2.Command, Equals, "DISCONNECT")
			receipt, _ := f2.Header.Contains("receipt")
			c.Check(receipt, Equals, "1")

			writer.Write(frame.New("RECEIPT", frame.ReceiptId, "1"))
		}()

		client, err := Connect(fc1, tc.Options...)
		c.Assert(err, IsNil)
		c.Assert(client, NotNil)
		c.Assert(client.Version(), Equals, tc.ExpectedVersion)
		c.Assert(client.Session(), Equals, tc.ExpectedSession)
		c.Assert(client.Server(), Equals, tc.ExpectedServer)

		err = client.Disconnect()
		c.Assert(err, IsNil)

		<-stop
	}
}
Пример #30
0
func subscribeTransactionHelper(c *C, ackMode AckMode, version Version, abort bool, nack bool) {
	conn, rw := connectHelper(c, version)
	stop := make(chan struct{})

	go func() {
		defer func() {
			rw.Close()
			close(stop)
		}()

		f3, err := rw.Read()
		c.Assert(err, IsNil)
		c.Assert(f3.Command, Equals, "SUBSCRIBE")
		id, ok := f3.Header.Contains("id")
		c.Assert(ok, Equals, true)
		destination := f3.Header.Get("destination")
		c.Assert(destination, Equals, "/queue/test-1")
		ack := f3.Header.Get("ack")
		c.Assert(ack, Equals, ackMode.String())

		for i := 1; i <= 5; i++ {
			messageId := fmt.Sprintf("message-%d", i)
			bodyText := fmt.Sprintf("Message body %d", i)
			f4 := frame.New("MESSAGE",
				frame.Subscription, id,
				frame.MessageId, messageId,
				frame.Destination, destination)
			if version == V12 {
				f4.Header.Add(frame.Ack, messageId)
			}
			f4.Body = []byte(bodyText)
			rw.Write(f4)

			beginFrame, err := rw.Read()
			c.Assert(err, IsNil)
			c.Assert(beginFrame, NotNil)
			c.Check(beginFrame.Command, Equals, "BEGIN")
			tx, ok := beginFrame.Header.Contains(frame.Transaction)

			c.Assert(ok, Equals, true)

			if ackMode.ShouldAck() {
				f5, _ := rw.Read()
				if nack && version.SupportsNack() {
					c.Assert(f5.Command, Equals, "NACK")
				} else {
					c.Assert(f5.Command, Equals, "ACK")
				}
				if version == V12 {
					c.Assert(f5.Header.Get("id"), Equals, messageId)
				} else {
					c.Assert(f5.Header.Get("subscription"), Equals, id)
					c.Assert(f5.Header.Get("message-id"), Equals, messageId)
				}
				c.Assert(f5.Header.Get("transaction"), Equals, tx)
			}

			sendFrame, _ := rw.Read()
			c.Assert(sendFrame, NotNil)
			c.Assert(sendFrame.Command, Equals, "SEND")
			c.Assert(sendFrame.Header.Get("transaction"), Equals, tx)

			commitFrame, _ := rw.Read()
			c.Assert(commitFrame, NotNil)
			if abort {
				c.Assert(commitFrame.Command, Equals, "ABORT")
			} else {
				c.Assert(commitFrame.Command, Equals, "COMMIT")
			}
			c.Assert(commitFrame.Header.Get("transaction"), Equals, tx)
		}

		f6, _ := rw.Read()
		c.Assert(f6.Command, Equals, "UNSUBSCRIBE")
		c.Assert(f6.Header.Get(frame.Receipt), Not(Equals), "")
		c.Assert(f6.Header.Get(frame.Id), Equals, id)
		rw.Write(frame.New(frame.RECEIPT,
			frame.ReceiptId, f6.Header.Get(frame.Receipt)))

		f7, _ := rw.Read()
		c.Assert(f7.Command, Equals, "DISCONNECT")
		rw.Write(frame.New(frame.RECEIPT,
			frame.ReceiptId, f7.Header.Get(frame.Receipt)))
	}()

	sub, err := conn.Subscribe("/queue/test-1", ackMode)
	c.Assert(sub, NotNil)
	c.Assert(err, IsNil)

	for i := 1; i <= 5; i++ {
		msg := <-sub.C
		messageId := fmt.Sprintf("message-%d", i)
		bodyText := fmt.Sprintf("Message body %d", i)
		c.Assert(msg.Subscription, Equals, sub)
		c.Assert(msg.Body, DeepEquals, []byte(bodyText))
		c.Assert(msg.Destination, Equals, "/queue/test-1")
		c.Assert(msg.Header.Get(frame.MessageId), Equals, messageId)

		c.Assert(msg.ShouldAck(), Equals, ackMode.ShouldAck())
		tx := msg.Conn.Begin()
		c.Assert(tx.Id(), Not(Equals), "")
		if msg.ShouldAck() {
			if nack && version.SupportsNack() {
				tx.Nack(msg)
			} else {
				tx.Ack(msg)
			}
		}
		err = tx.Send("/queue/another-queue", "text/plain", []byte(bodyText))
		c.Assert(err, IsNil)
		if abort {
			tx.Abort()
		} else {
			tx.Commit()
		}
	}

	err = sub.Unsubscribe()
	c.Assert(err, IsNil)

	conn.Disconnect()
}