// Test closing the client end of the connection. func TestConnectionCloseInterrupt2(t *testing.T) { want := amqp.Error{Name: "x", Description: "bad"} pairs := newPairs(t, 1, true) results := make(chan result) // Collect expected errors // Connection.Close() interrupts Send, Receive, Disposition. snd, rcv := pairs.senderReceiver() go doSend(snd, results) rcv.Receive() rcv, snd = pairs.receiverSender() go doReceive(rcv, results) snd, rcv = pairs.senderReceiver() ack := snd.SendWaitable(amqp.NewMessage()) go doDisposition(ack, results) pairs.client.Connection().Close(want) for i := 0; i < 3; i++ { if r := <-results; want != r.err { // TODO aconway 2015-10-06: Not propagating the correct error, seeing nil. t.Logf("want %v got %v", want, r.err) } } }
// Test closing the server end of a connection. func TestConnectionCloseInterrupt1(t *testing.T) { want := amqp.Error{Name: "x", Description: "bad"} pairs := newPairs(t, 1, true) results := make(chan result) // Collect expected errors // Connection.Close() interrupts Send, Receive, Disposition. snd, rcv := pairs.senderReceiver() go doSend(snd, results) rcv.Receive() rcv, snd = pairs.receiverSender() go doReceive(rcv, results) snd, rcv = pairs.senderReceiver() ack := snd.SendWaitable(amqp.NewMessage()) rcv.Receive() go doDisposition(ack, results) pairs.server.Close(want) for i := 0; i < 3; i++ { if r := <-results; want != r.err { t.Errorf("want %v got %v", want, r) } } }
// Senders get credit immediately if receivers have prefetch set func TestSendReceivePrefetch(t *testing.T) { pairs := newPairs(t, 1, true) s, r := pairs.senderReceiver() s.SendAsyncTimeout(amqp.NewMessage(), nil, nil, time.Second) // Should not block for credit. if _, err := r.Receive(); err != nil { t.Error(err) } }
// Message decodes the message containined in a delivery. // // Must be called in the correct link context with this delivery as the current message, // handling an MMessage event is always a safe context to call this function. // // Will return an error if message is incomplete or not current. func (delivery Delivery) Message() (m amqp.Message, err error) { if !delivery.Readable() { return nil, fmt.Errorf("delivery is not readable") } if delivery.Partial() { return nil, fmt.Errorf("delivery has partial message") } data := make([]byte, delivery.Pending()) result := delivery.Link().Recv(data) if result != len(data) { return nil, fmt.Errorf("cannot receive message: %s", PnErrorCode(result)) } m = amqp.NewMessage() err = m.Decode(data) return }
// Senders do not get credit till Receive() if receivers don't have prefetch func TestSendReceiveNoPrefetch(t *testing.T) { pairs := newPairs(t, 1, false) s, r := pairs.senderReceiver() done := make(chan struct{}, 1) go func() { s.SendAsyncTimeout(amqp.NewMessage(), nil, nil, time.Second) // Should block for credit. close(done) }() select { case <-done: t.Errorf("send should be blocked on credit") default: if _, err := r.Receive(); err != nil { t.Error(err) } else { <-done } // Should be unblocked now } }
// Test closing the client end of the connection. func TestConnectionCloseInterrupt2(t *testing.T) { want := amqp.Errorf("x", "bad") pairs := newPairs(t) results := make(chan result) // Collect expected errors // Connection.Close() interrupts Send, Receive, Disposition. snd, rcv := pairs.senderReceiver() go doReceive(rcv, results) sm, err := snd.Send(amqp.NewMessage()) fatalIf(t, err) go doDisposition(sm, results) snd, rcv = pairs.senderReceiver() go doSend(snd, results) rcv, snd = pairs.receiverSender() go doReceive(rcv, results) pairs.client.Close(want) for i := 0; i < 3; i++ { if r := <-results; want != r.err { // TODO aconway 2015-10-06: Not propagating the correct error, seeing nil. t.Logf("want %v got %v", want, r.err) } } }
func main() { flag.Usage = usage flag.Parse() urls := flag.Args() // Non-flag arguments are URLs to receive from if len(urls) == 0 { log.Println("No URL provided") flag.Usage() os.Exit(1) } sentChan := make(chan electron.Outcome) // Channel to receive acknowledgements. var wait sync.WaitGroup wait.Add(len(urls)) // Wait for one goroutine per URL. _, prog := path.Split(os.Args[0]) container := electron.NewContainer(fmt.Sprintf("%v:%v", prog, os.Getpid())) connections := make(chan electron.Connection, len(urls)) // Connctions to close on exit // Start a goroutine for each URL to send messages. for _, urlStr := range urls { util.Debugf("Connecting to %v\n", urlStr) go func(urlStr string) { defer wait.Done() // Notify main() that this goroutine is done. url, err := amqp.ParseURL(urlStr) // Like net/url.Parse() but with AMQP defaults. util.ExitIf(err) // Open a new connection conn, err := net.Dial("tcp", url.Host) // Note net.URL.Host is actually "host:port" util.ExitIf(err) c, err := container.Connection(conn) util.ExitIf(err) connections <- c // Save connection so we can Close() when main() ends // Create a Sender using the path of the URL as the AMQP address s, err := c.Sender(electron.Target(url.Path)) util.ExitIf(err) // Loop sending messages. for i := int64(0); i < *count; i++ { m := amqp.NewMessage() body := fmt.Sprintf("%v-%v", url.Path, i) m.Marshal(body) s.SendAsync(m, sentChan, body) // Outcome will be sent to sentChan } }(urlStr) } // Wait for all the acknowledgements expect := int(*count) * len(urls) util.Debugf("Started senders, expect %v acknowledgements\n", expect) for i := 0; i < expect; i++ { out := <-sentChan // Outcome of async sends. if out.Error != nil { util.Debugf("acknowledgement[%v] %v error: %v\n", i, out.Value, out.Error) } else { util.Debugf("acknowledgement[%v] %v (%v)\n", i, out.Value, out.Status) } } fmt.Printf("Received all %v acknowledgements\n", expect) wait.Wait() // Wait for all goroutines to finish. close(connections) for c := range connections { // Close all connections if c != nil { c.Close(nil) } } }
func doSend(snd Sender, results chan result) { _, err := snd.Send(amqp.NewMessage()) results <- result{"send", err} }
// Test timeout versions of waiting functions. func TestTimeouts(t *testing.T) { var err error rchan := make(chan Receiver, 1) client, server := newClientServer(t, func(i Incoming) { switch i := i.(type) { case *IncomingReceiver: rchan <- i.AcceptReceiver(1, false) // Issue credit only on receive default: i.Accept() } }) defer func() { closeClientServer(client, server) }() // Open client sender snd, err := client.Sender(Target("test")) if err != nil { t.Fatal(err) } rcv := <-rchan // Test send with timeout short := time.Millisecond long := time.Second m := amqp.NewMessage() if _, err = snd.SendTimeout(m, 0); err != Timeout { // No credit, expect timeout. t.Error("want Timeout got", err) } if _, err = snd.SendTimeout(m, short); err != Timeout { // No credit, expect timeout. t.Error("want Timeout got", err) } // Test receive with timeout if _, err = rcv.ReceiveTimeout(0); err != Timeout { // No credit, expect timeout. t.Error("want Timeout got", err) } // Test receive with timeout if _, err = rcv.ReceiveTimeout(short); err != Timeout { // No credit, expect timeout. t.Error("want Timeout got", err) } // There is now a credit on the link due to receive sm, err := snd.SendTimeout(m, long) if err != nil { t.Fatal(err) } // Disposition should timeout if _, err = sm.DispositionTimeout(long); err != Timeout { t.Error("want Timeout got", err) } if _, err = sm.DispositionTimeout(short); err != Timeout { t.Error("want Timeout got", err) } // Receive and accept rm, err := rcv.ReceiveTimeout(long) if err != nil { t.Fatal(err) } rm.Accept() // Sender get ack d, err := sm.DispositionTimeout(long) if err != nil || d != Accepted { t.Errorf("want (rejected, nil) got (%v, %v)", d, err) } }
func doSend(snd Sender, results chan result) { err := snd.SendSync(amqp.NewMessage()).Error results <- result{"send", err} }
// Test timeout versions of waiting functions. func TestTimeouts(t *testing.T) { var err error rchan := make(chan Receiver, 1) client, server := newClientServer(t) go func() { for i := range server.Incoming() { switch i := i.(type) { case *IncomingReceiver: i.SetCapacity(1) i.SetPrefetch(false) rchan <- i.Accept().(Receiver) // Issue credit only on receive default: i.Accept() } } }() defer func() { closeClientServer(client, server) }() // Open client sender snd, err := client.Sender(Target("test")) if err != nil { t.Fatal(err) } rcv := <-rchan // Test send with timeout short := time.Millisecond long := time.Second m := amqp.NewMessage() if err := snd.SendSyncTimeout(m, 0).Error; err != Timeout { // No credit, expect timeout. t.Error("want Timeout got", err) } if err := snd.SendSyncTimeout(m, short).Error; err != Timeout { // No credit, expect timeout. t.Error("want Timeout got", err) } // Test receive with timeout if _, err = rcv.ReceiveTimeout(0); err != Timeout { // No credit, expect timeout. t.Error("want Timeout got", err) } // Test receive with timeout if _, err = rcv.ReceiveTimeout(short); err != Timeout { // No credit, expect timeout. t.Error("want Timeout got", err) } // There is now a credit on the link due to receive ack := make(chan Outcome) snd.SendAsyncTimeout(m, ack, nil, short) // Disposition should timeout select { case <-ack: t.Errorf("want Timeout got %#v", ack) case <-time.After(short): } // Receive and accept rm, err := rcv.ReceiveTimeout(long) if err != nil { t.Fatal(err) } rm.Accept() // Sender get ack if a := <-ack; a.Status != Accepted || a.Error != nil { t.Errorf("want (accepted, nil) got %#v", a) } }